fix: add second richGridRenderer variety

This commit is contained in:
ThetaDev 2022-10-11 23:43:47 +02:00
parent eb9d3680cc
commit c618c83ff3
7 changed files with 13829 additions and 87 deletions

View file

@ -160,14 +160,12 @@ impl MapResponse<Channel<Paginator<ChannelVideo>>> for response::Channel {
_deobf: Option<&crate::deobfuscate::Deobfuscator>,
) -> Result<MapResult<Channel<Paginator<ChannelVideo>>>, ExtractionError> {
let content = map_channel_content(self.contents, id, self.alerts)?;
let mut warnings = content.warnings;
let grid = match content.c {
let grid = match content {
response::channel::ChannelContent::GridRenderer { items } => Some(items),
_ => None,
};
let mut v_res = grid.map(|g| map_videos(g, lang)).unwrap_or_default();
warnings.append(&mut v_res.warnings);
let v_res = grid.map(|g| map_videos(g, lang)).unwrap_or_default();
Ok(MapResult {
c: map_channel(
@ -178,7 +176,7 @@ impl MapResponse<Channel<Paginator<ChannelVideo>>> for response::Channel {
id,
lang,
)?,
warnings,
warnings: v_res.warnings,
})
}
}
@ -191,14 +189,12 @@ impl MapResponse<Channel<Paginator<ChannelPlaylist>>> for response::Channel {
_deobf: Option<&crate::deobfuscate::Deobfuscator>,
) -> Result<MapResult<Channel<Paginator<ChannelPlaylist>>>, ExtractionError> {
let content = map_channel_content(self.contents, id, self.alerts)?;
let mut warnings = content.warnings;
let grid = match content.c {
let grid = match content {
response::channel::ChannelContent::GridRenderer { items } => Some(items),
_ => None,
};
let mut p_res = grid.map(map_playlists).unwrap_or_default();
warnings.append(&mut p_res.warnings);
let p_res = grid.map(map_playlists).unwrap_or_default();
Ok(MapResult {
c: map_channel(
@ -209,7 +205,7 @@ impl MapResponse<Channel<Paginator<ChannelPlaylist>>> for response::Channel {
id,
lang,
)?,
warnings,
warnings: p_res.warnings,
})
}
}
@ -222,8 +218,8 @@ impl MapResponse<Channel<ChannelInfo>> for response::Channel {
_deobf: Option<&crate::deobfuscate::Deobfuscator>,
) -> Result<MapResult<Channel<ChannelInfo>>, ExtractionError> {
let content = map_channel_content(self.contents, id, self.alerts)?;
let mut warnings = content.warnings;
let meta = match content.c {
let mut warnings = Vec::new();
let meta = match content {
response::channel::ChannelContent::ChannelAboutFullMetadataRenderer(meta) => Some(meta),
_ => None,
};
@ -462,53 +458,48 @@ fn map_channel_content(
contents: Option<response::channel::Contents>,
id: &str,
alerts: Option<Vec<response::Alert>>,
) -> Result<MapResult<response::channel::ChannelContent>, ExtractionError> {
) -> Result<response::channel::ChannelContent, ExtractionError> {
match contents {
Some(contents) => {
let mut tabs = contents.two_column_browse_results_renderer.tabs;
let content = some_or_bail!(
tabs.try_swap_remove(0),
Ok(MapResult::error("no tab".to_owned()))
)
.tab_renderer
.content;
let tabs = contents.two_column_browse_results_renderer.tabs;
if tabs.is_empty() {
return Err(ExtractionError::NoData);
}
let (channel_content, target_id) = match content {
response::channel::TabContent::SectionListRenderer {
mut contents,
target_id,
} => {
let mut itemsection = some_or_bail!(
contents.try_swap_remove(0),
Ok(MapResult::error("no sectionlist".to_owned()))
)
.item_section_renderer
.contents;
let (channel_content, target_id) = tabs
.into_iter()
.filter_map(|tab| {
let content = tab.tab_renderer.content;
match (content.section_list_renderer, content.rich_grid_renderer) {
(Some(mut section_list_renderer), _) => {
let content =
section_list_renderer.contents.try_swap_remove(0).and_then(
|mut i| i.item_section_renderer.contents.try_swap_remove(0),
);
let content = some_or_bail!(
itemsection.try_swap_remove(0),
Ok(MapResult::error("no channel content".to_owned()))
);
(content, target_id)
}
response::channel::TabContent::RichGridRenderer {
contents,
target_id,
} => (
response::channel::ChannelContent::GridRenderer { items: contents },
target_id,
),
};
content.map(|c| (c, section_list_renderer.target_id))
}
(None, Some(rich_grid_renderer)) => Some((
response::channel::ChannelContent::GridRenderer {
items: rich_grid_renderer.contents,
},
rich_grid_renderer.target_id,
)),
(None, None) => None,
}
})
.next()
.ok_or_else(|| ExtractionError::InvalidData("could not extract content".into()))?;
if let Some(target_id) = target_id {
// YouTube falls back to the featured page if the channel does not have a "videos" tab.
// This is the case for YouTube Music channels.
if target_id.starts_with(&format!("browse-feed{}featured", id)) {
return Ok(MapResult::ok(response::channel::ChannelContent::None));
return Ok(response::channel::ChannelContent::None);
}
}
Ok(MapResult::ok(channel_content))
Ok(channel_content)
}
None => Err(response::alerts_to_err(alerts)),
}
@ -534,7 +525,8 @@ mod tests {
#[case::live("live", "UChs0pSaEoNLV4mevBFGaoKA")]
#[case::empty("empty", "UCxBa895m48H5idw5li7h-0g")]
#[case::upcoming("upcoming", "UCcvfHa-GHSOHFAjU0-Ie57A")]
#[case::shorts("20221011_richgrid", "UCh8gHdtzO2tXd593_bjErWg")]
#[case::richgrid("20221011_richgrid", "UCh8gHdtzO2tXd593_bjErWg")]
#[case::richgrid2("20221011_richgrid2", "UC2DjFE7Xf11URZqWBigcVOQ")]
fn map_channel_videos(#[case] name: &str, #[case] id: &str) {
let filename = format!("testfiles/channel/channel_videos_{}.json", name);
let json_path = Path::new(&filename);

View file

@ -983,7 +983,13 @@ impl RustyPipeQuery {
warn!("{} retry attempt #{}. Error: {}.", operation, n, emsg);
}
self._try_execute_request_deobf::<R, M, B>(
ctype, operation, id, endpoint, body, deobf, false,
ctype,
operation,
id,
endpoint,
body,
deobf,
self.client.inner.n_query_retries < 2,
)
.await
}

View file

@ -12,6 +12,7 @@ use crate::serializer::{text::Text, MapResult, VecLogError};
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Channel {
#[serde(default)]
#[serde_as(as = "DefaultOnError")]
pub header: Option<Header>,
pub contents: Option<Contents>,
@ -55,25 +56,37 @@ pub struct TabRendererWrap {
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum TabContent {
#[serde(rename_all = "camelCase")]
SectionListRenderer {
contents: Vec<ItemSectionRendererWrap>,
/// - **Videos**: browse-feedUC2DjFE7Xf11URZqWBigcVOQvideos (...)
/// - **Playlists**: browse-feedUC2DjFE7Xf11URZqWBigcVOQplaylists104 (...)
/// - **Info**: None
target_id: Option<String>,
},
pub struct TabContent {
#[serde(default)]
#[serde_as(as = "DefaultOnError")]
pub section_list_renderer: Option<SectionListRenderer>,
/// Seems to be currently A/B tested, as of 11.10.2022
#[serde(rename_all = "camelCase")]
RichGridRenderer {
#[serde_as(as = "VecLogError<_>")]
contents: MapResult<Vec<VideoListItem>>,
/// - **Videos**: browse-feedUC2DjFE7Xf11URZqWBigcVOQvideos (...)
/// - **Playlists**: browse-feedUC2DjFE7Xf11URZqWBigcVOQplaylists104 (...)
/// - **Info**: None
target_id: Option<String>,
},
#[serde(default)]
#[serde_as(as = "DefaultOnError")]
pub rich_grid_renderer: Option<RichGridRenderer>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SectionListRenderer {
pub contents: Vec<ItemSectionRendererWrap>,
/// - **Videos**: browse-feedUC2DjFE7Xf11URZqWBigcVOQvideos (...)
/// - **Playlists**: browse-feedUC2DjFE7Xf11URZqWBigcVOQplaylists104 (...)
/// - **Info**: None
pub target_id: Option<String>,
}
/// Seems to be currently A/B tested, as of 11.10.2022
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RichGridRenderer {
#[serde_as(as = "VecLogError<_>")]
pub contents: MapResult<Vec<VideoListItem>>,
/// - **Videos**: browse-feedUC2DjFE7Xf11URZqWBigcVOQvideos (...)
/// - **Playlists**: browse-feedUC2DjFE7Xf11URZqWBigcVOQplaylists104 (...)
/// - **Info**: None
pub target_id: Option<String>,
}
#[derive(Debug, Deserialize)]

View file

@ -535,8 +535,8 @@ pub struct CommentRenderer {
pub author_text: Option<String>,
#[serde(default)]
pub author_thumbnail: Thumbnails,
#[serde(default)]
/// ID of the author's channel
#[serde(default)]
#[serde_as(as = "DefaultOnError")]
pub author_endpoint: Option<AuthorEndpoint>,
/// Comment text

View file

@ -41,25 +41,6 @@ where
}
}
impl<T> MapResult<T>
where
T: Default,
{
pub fn error(msg: String) -> Self {
Self {
c: T::default(),
warnings: vec![msg],
}
}
pub fn ok(c: T) -> Self {
Self {
c,
warnings: Vec::new(),
}
}
}
/// Deserialization method that consumes anything and returns an empty value.
/// Intended to be used for a wildcard enum option.
///

File diff suppressed because it is too large Load diff