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

View file

@ -983,7 +983,13 @@ impl RustyPipeQuery {
warn!("{} retry attempt #{}. Error: {}.", operation, n, emsg); warn!("{} retry attempt #{}. Error: {}.", operation, n, emsg);
} }
self._try_execute_request_deobf::<R, M, B>( 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 .await
} }

View file

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

View file

@ -535,8 +535,8 @@ pub struct CommentRenderer {
pub author_text: Option<String>, pub author_text: Option<String>,
#[serde(default)] #[serde(default)]
pub author_thumbnail: Thumbnails, pub author_thumbnail: Thumbnails,
#[serde(default)]
/// ID of the author's channel /// ID of the author's channel
#[serde(default)]
#[serde_as(as = "DefaultOnError")] #[serde_as(as = "DefaultOnError")]
pub author_endpoint: Option<AuthorEndpoint>, pub author_endpoint: Option<AuthorEndpoint>,
/// Comment text /// 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. /// Deserialization method that consumes anything and returns an empty value.
/// Intended to be used for a wildcard enum option. /// Intended to be used for a wildcard enum option.
/// ///

File diff suppressed because it is too large Load diff