diff --git a/Justfile b/Justfile index c2f3fdf..4628423 100644 --- a/Justfile +++ b/Justfile @@ -7,6 +7,9 @@ unittest: testyt: cargo test --features=rss --test youtube +testyt-localized: + YT_LANG=th cargo test --features=rss --test youtube + testintl: #!/usr/bin/env bash LANGUAGES=( diff --git a/cli/src/main.rs b/cli/src/main.rs index a79b12a..3bef694 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -20,8 +20,11 @@ struct Cli { #[clap(subcommand)] command: Commands, /// Always generate a report (used for debugging) - #[clap(long)] + #[clap(long, global = true)] report: bool, + /// YouTube visitor data cookie + #[clap(long, global = true)] + vdata: Option, } #[derive(Subcommand)] @@ -391,13 +394,14 @@ async fn main() { let cli = Cli::parse(); - let mut storage_dir = dirs::data_dir().expect("no data dir"); - storage_dir.push("rustypipe"); - std::fs::create_dir_all(&storage_dir).expect("could not create data dir"); - - let mut rp = RustyPipe::builder().storage_dir(storage_dir); + let mut rp = RustyPipe::builder().visitor_data_opt(cli.vdata); if cli.report { rp = rp.report(); + } else { + let mut storage_dir = dirs::data_dir().expect("no data dir"); + storage_dir.push("rustypipe"); + std::fs::create_dir_all(&storage_dir).expect("could not create data dir"); + rp = rp.storage_dir(storage_dir); } let rp = rp.build().unwrap(); diff --git a/codegen/src/abtest.rs b/codegen/src/abtest.rs index cc9491a..b9d7f09 100644 --- a/codegen/src/abtest.rs +++ b/codegen/src/abtest.rs @@ -24,6 +24,7 @@ pub enum ABTest { TrendsPageHeaderRenderer = 5, DiscographyPage = 6, ShortDateFormat = 7, + TrackViewcount = 8, PlaylistsForShorts = 9, } @@ -96,6 +97,7 @@ pub async fn run_test( ABTest::DiscographyPage => discography_page(&query).await, ABTest::ShortDateFormat => short_date_format(&query).await, ABTest::PlaylistsForShorts => playlists_for_shorts(&query).await, + ABTest::TrackViewcount => track_viewcount(&query).await, } .unwrap(); pb.inc(1); @@ -246,6 +248,20 @@ pub async fn short_date_format(rp: &RustyPipeQuery) -> Result { })) } +pub async fn track_viewcount(rp: &RustyPipeQuery) -> Result { + let res = rp.music_search("lieblingsmensch namika").await?; + + let track = &res + .tracks + .iter() + .find(|a| a.id == "6485PhOtHzY") + .unwrap_or_else(|| { + panic!("could not find track, got {:#?}", &res.tracks); + }); + + Ok(track.view_count.is_some()) +} + pub async fn playlists_for_shorts(rp: &RustyPipeQuery) -> Result { let playlist = rp.playlist("UUSHh8gHdtzO2tXd593_bjErWg").await?; let v1 = playlist diff --git a/notes/AB_Tests.md b/notes/AB_Tests.md index f4294cd..745b1e1 100644 --- a/notes/AB_Tests.md +++ b/notes/AB_Tests.md @@ -293,7 +293,7 @@ The data model for the video shelves did not change. **NEW** -![A/B test 4 old screenshot](./_img/ab_4_new.png) +![A/B test 4 new screenshot](./_img/ab_4_new.png) ## [5] Page header renderer on the Trending page @@ -371,20 +371,32 @@ visitor data cookie to be set, as it was the case with the old system. **OLD** -![A/B test 4 old screenshot](./_img/ab_6_old.png) +![A/B test 6 old screenshot](./_img/ab_6_old.png) **NEW** -![A/B test 4 old screenshot](./_img/ab_6_new.png) +![A/B test 6 screenshot](./_img/ab_6_new.png) ## [7] Short timeago format - **Encountered on:** 28.05.2023 -- **Impact:** 🟡 Medium +- **Impact:** 🟢 Low YouTube changed their date format from the long format (_21 hours ago_, _3 days ago_) to a short format (_21h ago_, _3d ago_). +## [8] Track playback count in search results and artist views + +- **Encountered on:** 29.06.2023 +- **Impact:** 🟡 Medium + +YouTube added the track playback count to search results and top artist tracks. +In exchange, they removed the "Song" type identifier from search results. + +![A/B test 8 old screenshot](./_img/ab_8_old.png) + +![A/B test 8 screenshot](./_img/ab_8.png) + ## [9] Playlists for Shorts - **Encountered on:** 26.06.2023 diff --git a/notes/_img/ab_8.png b/notes/_img/ab_8.png new file mode 100644 index 0000000..42f426b Binary files /dev/null and b/notes/_img/ab_8.png differ diff --git a/notes/_img/ab_8_old.png b/notes/_img/ab_8_old.png new file mode 100644 index 0000000..036e111 Binary files /dev/null and b/notes/_img/ab_8_old.png differ diff --git a/src/client/music_search.rs b/src/client/music_search.rs index f844cf9..5c3d15c 100644 --- a/src/client/music_search.rs +++ b/src/client/music_search.rs @@ -339,7 +339,7 @@ impl MapResponse for response::MusicSearchSuggestion { _deobf: Option<&crate::deobfuscate::DeobfData>, _vdata: Option<&str>, ) -> Result, ExtractionError> { - let mut mapper = MusicListMapper::new(lang); + let mut mapper = MusicListMapper::new_search_suggest(lang); let mut terms = Vec::new(); for section in self.contents { diff --git a/src/client/response/music_item.rs b/src/client/response/music_item.rs index 675b042..6c7f3cf 100644 --- a/src/client/response/music_item.rs +++ b/src/client/response/music_item.rs @@ -16,7 +16,9 @@ use crate::{ }; use super::{ - url_endpoint::{BrowseEndpointWrap, MusicPageType, NavigationEndpoint, PageType}, + url_endpoint::{ + BrowseEndpointWrap, MusicPageType, MusicVideoType, NavigationEndpoint, PageType, + }, ContentsRenderer, MusicContinuationData, Thumbnails, ThumbnailsWrap, }; @@ -429,6 +431,7 @@ pub(crate) struct MusicListMapper { artists: Option<(Vec, bool)>, album: Option, artist_page: bool, + search_suggestion: bool, items: Vec, warnings: Vec, /// True if unknown items were mapped @@ -450,6 +453,20 @@ impl MusicListMapper { artists: None, album: None, artist_page: false, + search_suggestion: false, + items: Vec::new(), + warnings: Vec::new(), + has_unknown: false, + } + } + + pub fn new_search_suggest(lang: Language) -> Self { + Self { + lang, + artists: None, + album: None, + artist_page: false, + search_suggestion: true, items: Vec::new(), warnings: Vec::new(), has_unknown: false, @@ -463,6 +480,7 @@ impl MusicListMapper { artists: Some((vec![artist], false)), album: None, artist_page: true, + search_suggestion: false, items: Vec::new(), warnings: Vec::new(), has_unknown: false, @@ -476,6 +494,7 @@ impl MusicListMapper { artists: Some((artists, by_va)), album: Some(album), artist_page: false, + search_suggestion: false, items: Vec::new(), warnings: Vec::new(), has_unknown: false, @@ -515,6 +534,7 @@ impl MusicListMapper { let c1 = columns.next(); let c2 = columns.next(); let c3 = columns.next(); + let c4 = columns.next(); let title = c1.as_ref().map(|col| col.renderer.text.to_string()); @@ -532,10 +552,8 @@ impl MusicListMapper { c1.and_then(|c1| { c1.renderer.text.0.into_iter().next().and_then(|t| match t { crate::serializer::text::TextComponent::Video { - video_id, - is_video, - .. - } => Some((MusicPageType::Track { is_video }, video_id)), + video_id, vtype, .. + } => Some((MusicPageType::Track { vtype }, video_id)), crate::serializer::text::TextComponent::Browse { page_type, browse_id, @@ -549,8 +567,12 @@ impl MusicListMapper { item.playlist_item_data.map(|d| { ( MusicPageType::Track { - is_video: self.album.is_none() - && !first_tn.map(|tn| tn.height == tn.width).unwrap_or_default(), + vtype: MusicVideoType::from_is_video( + self.album.is_none() + && !first_tn + .map(|tn| tn.height == tn.width) + .unwrap_or_default(), + ), }, d.video_id, ) @@ -561,7 +583,9 @@ impl MusicListMapper { util::video_id_from_thumbnail_url(&tn.url).map(|id| { ( MusicPageType::Track { - is_video: self.album.is_none() && tn.width != tn.height, + vtype: MusicVideoType::from_is_video( + self.album.is_none() && tn.width != tn.height, + ), }, id, ) @@ -571,19 +595,28 @@ impl MusicListMapper { match pt_id { // Track - Some((MusicPageType::Track { is_video }, id)) => { + Some((MusicPageType::Track { vtype }, id)) => { let title = title.ok_or_else(|| format!("track {id}: could not get title"))?; - let (artists_p, album_p, duration_p) = match item.flex_column_display_style { + #[derive(Default)] + struct Parsed { + artists: Option, + album: Option, + duration: Option, + view_count: Option, + } + + let p = match item.flex_column_display_style { // Search result FlexColumnDisplayStyle::TwoLines => { - // Is this a related track? - if !is_video && item.item_height == ItemHeight::Compact { - ( - c2.map(TextComponents::from), - c3.map(TextComponents::from), - None, - ) + // Is this a related track (from the "similar titles" tab in the player)? + if vtype != MusicVideoType::Video && item.item_height == ItemHeight::Compact + { + Parsed { + artists: c2.map(TextComponents::from), + album: c3.map(TextComponents::from), + ..Default::default() + } } else { let mut subtitle_parts = c2 .ok_or_else(|| format!("track {id}: could not get subtitle"))? @@ -594,62 +627,98 @@ impl MusicListMapper { // Is this a related video? if item.item_height == ItemHeight::Compact { - (subtitle_parts.next(), subtitle_parts.next(), None) + Parsed { + artists: subtitle_parts.next(), + view_count: subtitle_parts.next(), + ..Default::default() + } + } + // Is this an item from search suggestion? + else if self.search_suggestion { + // Skip first part (track type) + subtitle_parts.next(); + Parsed { + artists: subtitle_parts.next(), + album: c3.map(TextComponents::from), + view_count: subtitle_parts.next(), + ..Default::default() + } } // Is it a podcast episode? - else if subtitle_parts.len() <= 3 && c3.is_some() { - (subtitle_parts.next_back(), None, None) + else if vtype == MusicVideoType::Episode { + Parsed { + artists: subtitle_parts.next_back(), + ..Default::default() + } } else { // Skip first part (track type) if subtitle_parts.len() > 3 - || (is_video && subtitle_parts.len() == 2) + || (vtype == MusicVideoType::Video && subtitle_parts.len() == 2) { subtitle_parts.next(); } - ( - subtitle_parts.next(), - subtitle_parts.next(), - subtitle_parts.next(), - ) + match vtype { + MusicVideoType::Video => Parsed { + artists: subtitle_parts.next(), + view_count: subtitle_parts.next(), + duration: subtitle_parts.next(), + ..Default::default() + }, + _ => Parsed { + artists: subtitle_parts.next(), + album: subtitle_parts.next(), + duration: subtitle_parts.next(), + view_count: c3.map(TextComponents::from), + }, + } } } } // Playlist item - FlexColumnDisplayStyle::Default => ( - c2.map(TextComponents::from), - c3.map(TextComponents::from), - item.fixed_columns + FlexColumnDisplayStyle::Default => { + let artists = c2.map(TextComponents::from); + let duration = item + .fixed_columns .into_iter() .next() - .map(TextComponents::from), - ), + .map(TextComponents::from); + if self.album.is_some() { + Parsed { + artists, + view_count: c3.map(TextComponents::from), + duration, + ..Default::default() + } + } else if self.artist_page && c4.is_some() { + Parsed { + artists, + view_count: c3.map(TextComponents::from), + album: c4.map(TextComponents::from), + duration, + } + } else { + Parsed { + artists, + album: c3.map(TextComponents::from), + duration, + ..Default::default() + } + } + } }; - let duration = duration_p.and_then(|p| util::parse_video_length(p.first_str())); - - let (album, view_count) = match (item.flex_column_display_style, is_video) { - // The album field contains the view count for search videos - (FlexColumnDisplayStyle::TwoLines, true) => ( - None, - album_p.and_then(|p| { - util::parse_large_numstr_or_warn( - p.first_str(), - self.lang, - &mut self.warnings, - ) - }), - ), - (_, false) => ( - album_p - .and_then(|p| p.0.into_iter().find_map(|c| AlbumId::try_from(c).ok())), - None, - ), - (FlexColumnDisplayStyle::Default, true) => (None, None), - }; - let album = album.or_else(|| self.album.clone()); - - let (mut artists, by_va) = map_artists(artists_p); + let duration = p + .duration + .and_then(|p| util::parse_video_length(p.first_str())); + let album = p + .album + .and_then(|p| p.0.into_iter().find_map(|c| AlbumId::try_from(c).ok())) + .or_else(|| self.album.clone()); + let view_count = p.view_count.and_then(|p| { + util::parse_large_numstr_or_warn(p.first_str(), self.lang, &mut self.warnings) + }); + let (mut artists, by_va) = map_artists(p.artists); // Extract artist id from dropdown menu let artist_id = map_artist_id_fallback(item.menu, artists.first()); @@ -685,7 +754,7 @@ impl MusicListMapper { artist_id, album, view_count, - is_video, + is_video: vtype.is_video(), track_nr, by_va, })); @@ -807,7 +876,7 @@ impl MusicListMapper { match item.navigation_endpoint.music_page() { Some((page_type, id)) => match page_type { - MusicPageType::Track { is_video } => { + MusicPageType::Track { vtype } => { let (artists, by_va) = map_artists(subtitle_p1); self.items.push(MusicItem::Track(TrackItem { @@ -825,7 +894,7 @@ impl MusicListMapper { &mut self.warnings, ) }), - is_video, + is_video: vtype.is_video(), track_nr: None, by_va, })); @@ -976,43 +1045,61 @@ impl MusicListMapper { })); Some(MusicItemType::Album) } - MusicPageType::Track { is_video } => { - let (artists, by_va) = map_artists(subtitle_p2); - let duration = - subtitle_p4.and_then(|p| util::parse_video_length(p.first_str())); - let (album, view_count) = if is_video { - ( - None, - subtitle_p3.and_then(|p| { - util::parse_large_numstr_or_warn( - p.first_str(), - self.lang, - &mut self.warnings, - ) - }), - ) - } else { - ( - subtitle_p3.and_then(|p| { - p.0.into_iter().find_map(|c| AlbumId::try_from(c).ok()) - }), - None, - ) - }; + MusicPageType::Track { vtype } => { + if vtype == MusicVideoType::Episode { + let (artists, by_va) = map_artists(subtitle_p3); - self.items.push(MusicItem::Track(TrackItem { - id, - name: card.title, - duration, - cover: card.thumbnail.into(), - artist_id: artists.first().and_then(|a| a.id.clone()), - artists, - album, - view_count, - is_video, - track_nr: None, - by_va, - })); + self.items.push(MusicItem::Track(TrackItem { + id, + name: card.title, + duration: None, + cover: card.thumbnail.into(), + artist_id: artists.first().and_then(|a| a.id.clone()), + artists, + album: None, + view_count: None, + is_video: vtype.is_video(), + track_nr: None, + by_va, + })); + } else { + let (artists, by_va) = map_artists(subtitle_p2); + let duration = + subtitle_p4.and_then(|p| util::parse_video_length(p.first_str())); + let (album, view_count) = if vtype.is_video() { + ( + None, + subtitle_p3.and_then(|p| { + util::parse_large_numstr_or_warn( + p.first_str(), + self.lang, + &mut self.warnings, + ) + }), + ) + } else { + ( + subtitle_p3.and_then(|p| { + p.0.into_iter().find_map(|c| AlbumId::try_from(c).ok()) + }), + None, + ) + }; + + self.items.push(MusicItem::Track(TrackItem { + id, + name: card.title, + duration, + cover: card.thumbnail.into(), + artist_id: artists.first().and_then(|a| a.id.clone()), + artists, + album, + view_count, + is_video: vtype.is_video(), + track_nr: None, + by_va, + })); + } Some(MusicItemType::Track) } MusicPageType::Playlist => { diff --git a/src/client/response/url_endpoint.rs b/src/client/response/url_endpoint.rs index 4477103..bbdee34 100644 --- a/src/client/response/url_endpoint.rs +++ b/src/client/response/url_endpoint.rs @@ -151,6 +151,22 @@ pub(crate) enum MusicVideoType { Video, #[serde(rename = "MUSIC_VIDEO_TYPE_ATV")] Track, + #[serde(rename = "MUSIC_VIDEO_TYPE_PODCAST_EPISODE")] + Episode, +} + +impl MusicVideoType { + pub fn is_video(self) -> bool { + self != Self::Track + } + + pub fn from_is_video(is_video: bool) -> Self { + if is_video { + Self::Video + } else { + Self::Track + } + } } #[derive(Default, Debug, Clone, Copy, Deserialize, PartialEq, Eq)] @@ -189,7 +205,7 @@ pub(crate) enum MusicPageType { Artist, Album, Playlist, - Track { is_video: bool }, + Track { vtype: MusicVideoType }, Unknown, None, } @@ -221,11 +237,10 @@ impl NavigationEndpoint { } else { Some(( MusicPageType::Track { - is_video: watch_endpoint + vtype: watch_endpoint .watch_endpoint_music_supported_configs .watch_endpoint_music_config - .music_video_type - == MusicVideoType::Video, + .music_video_type, }, watch_endpoint.video_id, )) diff --git a/src/client/response/video_item.rs b/src/client/response/video_item.rs index d030ed4..3283f42 100644 --- a/src/client/response/video_item.rs +++ b/src/client/response/video_item.rs @@ -14,7 +14,7 @@ use crate::{ }, param::Language, serializer::{ - text::{AccessibilityText, Text, TextComponent}, + text::{AccessibilityText, AttributedText, Text, TextComponent}, MapResult, }, util::{self, timeago, TryRemove}, @@ -369,6 +369,9 @@ pub(crate) struct ChannelFullMetadata { #[serde(default)] #[serde_as(as = "VecSkipError<_>")] pub primary_links: Vec, + #[serde(default)] + // #[serde_as(as = "VecSkipError<_>")] + pub links: Vec, } #[serde_as] @@ -380,6 +383,22 @@ pub(crate) struct PrimaryLink { pub navigation_endpoint: NavigationEndpoint, } +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ExternalLink { + pub channel_external_link_view_model: ExternalLinkInner, +} + +#[serde_as] +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ExternalLinkInner { + #[serde_as(as = "AttributedText")] + pub title: TextComponent, + #[serde_as(as = "AttributedText")] + pub link: TextComponent, +} + trait IsLive { fn is_live(&self) -> bool; } @@ -726,6 +745,18 @@ impl YouTubeListMapper { self.corrected_query = Some(corrected_query); } YouTubeListItem::ChannelAboutFullMetadataRenderer(meta) => { + let mut links = meta + .primary_links + .into_iter() + .filter_map(|l| l.navigation_endpoint.url().map(|url| (l.title, url))) + .collect::>(); + for l in meta.links { + let l = l.channel_external_link_view_model; + if let TextComponent::Web { url, .. } = l.link { + links.push((l.title.into(), util::sanitize_yt_url(&url))); + } + } + self.channel_info = Some(ChannelInfo { create_date: timeago::parse_textual_date_or_warn( self.lang, @@ -736,11 +767,7 @@ impl YouTubeListMapper { view_count: meta .view_count_text .and_then(|txt| util::parse_numeric_or_warn(&txt, &mut self.warnings)), - links: meta - .primary_links - .into_iter() - .filter_map(|l| l.navigation_endpoint.url().map(|url| (l.title, url))) - .collect(), + links, }); } YouTubeListItem::RichItemRenderer { content } => { diff --git a/src/client/snapshots/rustypipe__client__music_search__tests__map_music_search_suggestion_default.snap b/src/client/snapshots/rustypipe__client__music_search__tests__map_music_search_suggestion_default.snap index 85ed274..719c930 100644 --- a/src/client/snapshots/rustypipe__client__music_search__tests__map_music_search_suggestion_default.snap +++ b/src/client/snapshots/rustypipe__client__music_search__tests__map_music_search_suggestion_default.snap @@ -67,7 +67,10 @@ MusicSearchSuggestion( ), ], artist_id: Some("UC56hLMPuEsERdmTBbR_JGHA"), - album: None, + album: Some(AlbumId( + id: "MPREb_kz546sNB1mH", + name: "Was Spaß macht...", + )), view_count: None, is_video: false, track_nr: None, diff --git a/src/deobfuscate.rs b/src/deobfuscate.rs index 33cb494..b7e99f5 100644 --- a/src/deobfuscate.rs +++ b/src/deobfuscate.rs @@ -142,14 +142,15 @@ fn get_sig_fn(player_js: &str) -> Result { let function_pattern = Regex::new(&function_pattern_str) .map_err(|_| DeobfError::Other("could not parse function pattern regex"))?; - let deobfuscate_function = "var ".to_owned() - + function_pattern + let deobfuscate_function = format!( + "var {};", + function_pattern .captures(player_js) .ok_or(DeobfError::Extraction("deobf function"))? .get(1) .unwrap() .as_str() - + ";"; + ); static HELPER_OBJECT_NAME_REGEX: Lazy = Lazy::new(|| Regex::new(r#";([A-Za-z0-9_\$]{2,3})\...\("#).unwrap()); @@ -203,7 +204,7 @@ fn get_nsig_fn_name(player_js: &str) -> Result { .as_str() .parse::() .or(Err(DeobfError::Other("could not parse array_num")))?; - let array_pattern_str = format!(r#"var {}\s*=\s*\[(.+?)][;,]"#, regex::escape(function_name)); + let array_pattern_str = format!(r#"var {}\s*=\s*\[(.+?)]"#, regex::escape(function_name)); let array_pattern = Regex::new(&array_pattern_str).or(Err(DeobfError::Other( "could not parse helper pattern regex", )))?; diff --git a/src/model/richtext.rs b/src/model/richtext.rs index 57e8abd..4afe63b 100644 --- a/src/model/richtext.rs +++ b/src/model/richtext.rs @@ -170,6 +170,7 @@ mod tests { use once_cell::sync::Lazy; + use crate::client::response::url_endpoint::MusicVideoType; use crate::serializer::text; static TEXT_SOURCE: Lazy = Lazy::new(|| { @@ -177,7 +178,7 @@ mod tests { text::TextComponent::Text { text: "🎧Listen and download aespa's debut single \"Black Mamba\": ".to_owned() }, text::TextComponent::Web { text: "https://smarturl.it/aespa_BlackMamba".to_owned(), url: "https://www.youtube.com/redirect?event=video_description&redir_token=QUFFLUhqbFY1QmpQamJPSms0Z1FnVTlQUS00ZFhBZnBJZ3xBQ3Jtc0tuRGJBanludGoyRnphb2dZWVd3cUNnS3dEd0FnNHFOZEY1NHBJaHFmLXpaWUJwX3ZucDZxVnpGeHNGX1FpMzFkZW9jQkI2Mi1wNGJ1UVFNN3h1MnN3R3JLMzdxU01nZ01POHBGcmxHU2puSUk1WHRzQQ&q=https%3A%2F%2Fsmarturl.it%2Faespa_BlackMamba&v=ZeerrnuLi5E".to_owned() }, text::TextComponent::Text { text: "\n🐍The Debut Stage ".to_owned() }, - text::TextComponent::Video { text: "https://youtu.be/Ky5RT5oGg0w".to_owned(), video_id: "Ky5RT5oGg0w".to_owned(), start_time: 0, is_video: true }, + text::TextComponent::Video { text: "https://youtu.be/Ky5RT5oGg0w".to_owned(), video_id: "Ky5RT5oGg0w".to_owned(), start_time: 0, vtype: MusicVideoType::Video }, text::TextComponent::Text { text: "\n\n🎟️ aespa Showcase SYNK in LA! Tickets now on sale: ".to_owned() }, text::TextComponent::Web { text: "https://www.ticketmaster.com/event/0A...".to_owned(), url: "https://www.youtube.com/redirect?event=video_description&redir_token=QUFFLUhqbFpUMEZiaXJWWkszaVZXaEM0emxWU1JQV3NoQXxBQ3Jtc0tuU2g4VWNPNE5UY3hoSWYtamFzX0h4bUVQLVJiRy1ubDZrTnh3MUpGdDNSaUo0ZlMyT3lUM28ycUVBdHJLMndGcDhla3BkOFpxSVFfOS1QdVJPVHBUTEV1LXpOV0J2QXdhV05lV210cEJtZUJMeHdaTQ&q=https%3A%2F%2Fwww.ticketmaster.com%2Fevent%2F0A005CCD9E871F6E&v=ZeerrnuLi5E".to_owned() }, text::TextComponent::Text { text: "\n\nSubscribe to aespa Official YouTube Channel!\n".to_owned() }, diff --git a/src/serializer/snapshots/rustypipe__serializer__text__tests__t_attributed_description.snap b/src/serializer/snapshots/rustypipe__serializer__text__tests__t_attributed_description.snap index 4966a2f..25c6179 100644 --- a/src/serializer/snapshots/rustypipe__serializer__text__tests__t_attributed_description.snap +++ b/src/serializer/snapshots/rustypipe__serializer__text__tests__t_attributed_description.snap @@ -19,7 +19,7 @@ SAttributed { text: "aespa 에스파 'Black ...", video_id: "Ky5RT5oGg0w", start_time: 0, - is_video: true, + vtype: Video, }, Text { text: "\n\n🎟\u{fe0f} aespa Showcase SYNK in LA! Tickets now on sale: ", diff --git a/src/serializer/text.rs b/src/serializer/text.rs index f8d070d..c3c9617 100644 --- a/src/serializer/text.rs +++ b/src/serializer/text.rs @@ -94,8 +94,7 @@ pub(crate) enum TextComponent { text: String, video_id: String, start_time: u32, - /// True if the item is a video, false if it is a YTM track - is_video: bool, + vtype: MusicVideoType, }, Browse { text: String, @@ -167,11 +166,10 @@ fn map_text_component(text: String, nav: Option) -> TextComp text, video_id: watch_endpoint.video_id, start_time: watch_endpoint.start_time_seconds, - is_video: watch_endpoint + vtype: watch_endpoint .watch_endpoint_music_supported_configs .watch_endpoint_music_config - .music_video_type - == MusicVideoType::Video, + .music_video_type, }, Some(NavigationEndpoint::Browse { browse_endpoint, @@ -200,8 +198,12 @@ impl<'de> Deserialize<'de> for TextComponent { where D: Deserializer<'de>, { - let mut text = RichTextInternal::deserialize(deserializer)?; - Ok(text.runs.swap_remove(0).into()) + let text = RichTextInternal::deserialize(deserializer)?; + text.runs + .into_iter() + .next() + .map(TextComponent::from) + .ok_or(serde::de::Error::invalid_length(0, &"at least 1")) } } @@ -289,6 +291,20 @@ impl<'de> DeserializeAs<'de, TextComponents> for AttributedText { } } +impl<'de> DeserializeAs<'de, TextComponent> for AttributedText { + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let components: TextComponents = AttributedText::deserialize_as(deserializer)?; + components + .0 + .into_iter() + .next() + .ok_or(serde::de::Error::invalid_length(0, &"at least 1")) + } +} + impl TryFrom for crate::model::ChannelId { type Error = (); @@ -404,6 +420,17 @@ impl TextComponent { } } +impl From for String { + fn from(value: TextComponent) -> Self { + match value { + TextComponent::Video { text, .. } + | TextComponent::Browse { text, .. } + | TextComponent::Web { text, .. } + | TextComponent::Text { text } => text, + } + } +} + impl TextComponents { /// Return the string representation of the first text component pub fn first_str(&self) -> &str { @@ -583,7 +610,7 @@ mod tests { text: "DEEP", video_id: "wZIoIgz5mbs", start_time: 0, - is_video: true, + vtype: Video, }, } "###); diff --git a/src/util/mod.rs b/src/util/mod.rs index 363d48d..5a6b82b 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -224,7 +224,7 @@ pub fn retry_delay( /// Also strips google analytics tracking parameters /// (`utm_source`, `utm_medium`, `utm_campaign`, `utm_content`) because google analytics is bad. pub fn sanitize_yt_url(url: &str) -> String { - fn sanitize_yt_url_inner(url: &str) -> Option { + fn try_sanitize_yt_url(url: &str) -> Option { let mut parsed_url = Url::parse(url).ok()?; // Convert redirect url @@ -260,7 +260,7 @@ pub fn sanitize_yt_url(url: &str) -> String { Some(parsed_url.to_string()) } - sanitize_yt_url_inner(url).unwrap_or_else(|| url.to_string()) + try_sanitize_yt_url(url).unwrap_or_else(|| url.to_string()) } pub fn div_ceil(a: u32, b: u32) -> u32 { diff --git a/tests/snapshots/youtube__music_album_ep.snap b/tests/snapshots/youtube__music_album_ep.snap index 161096d..10e5e03 100644 --- a/tests/snapshots/youtube__music_album_ep.snap +++ b/tests/snapshots/youtube__music_album_ep.snap @@ -35,7 +35,7 @@ MusicAlbum( id: "MPREb_u1I69lSAe5v", name: "Waldbrand", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, @@ -56,7 +56,7 @@ MusicAlbum( id: "MPREb_u1I69lSAe5v", name: "Waldbrand", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(2), by_va: false, @@ -77,7 +77,7 @@ MusicAlbum( id: "MPREb_u1I69lSAe5v", name: "Waldbrand", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(3), by_va: false, @@ -98,7 +98,7 @@ MusicAlbum( id: "MPREb_u1I69lSAe5v", name: "Waldbrand", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(4), by_va: false, @@ -119,7 +119,7 @@ MusicAlbum( id: "MPREb_u1I69lSAe5v", name: "Waldbrand", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(5), by_va: false, diff --git a/tests/snapshots/youtube__music_album_ep_intl.snap b/tests/snapshots/youtube__music_album_ep_intl.snap index 72f3ef4..c48bcd4 100644 --- a/tests/snapshots/youtube__music_album_ep_intl.snap +++ b/tests/snapshots/youtube__music_album_ep_intl.snap @@ -35,7 +35,7 @@ MusicAlbum( id: "MPREb_u1I69lSAe5v", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, @@ -56,7 +56,7 @@ MusicAlbum( id: "MPREb_u1I69lSAe5v", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(2), by_va: false, @@ -77,7 +77,7 @@ MusicAlbum( id: "MPREb_u1I69lSAe5v", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(3), by_va: false, @@ -98,7 +98,7 @@ MusicAlbum( id: "MPREb_u1I69lSAe5v", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(4), by_va: false, @@ -119,7 +119,7 @@ MusicAlbum( id: "MPREb_u1I69lSAe5v", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(5), by_va: false, diff --git a/tests/snapshots/youtube__music_album_no_artist.snap b/tests/snapshots/youtube__music_album_no_artist.snap index c401024..60a3b5b 100644 --- a/tests/snapshots/youtube__music_album_no_artist.snap +++ b/tests/snapshots/youtube__music_album_no_artist.snap @@ -1,6 +1,5 @@ --- source: tests/youtube.rs -assertion_line: 1391 expression: album --- MusicAlbum( @@ -31,7 +30,7 @@ MusicAlbum( id: "MPREb_bqWA6mAZFWS", name: "Pedha Rasi Peddamma Katha", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, @@ -52,7 +51,7 @@ MusicAlbum( id: "MPREb_bqWA6mAZFWS", name: "Pedha Rasi Peddamma Katha", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(2), by_va: false, @@ -73,7 +72,7 @@ MusicAlbum( id: "MPREb_bqWA6mAZFWS", name: "Pedha Rasi Peddamma Katha", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(3), by_va: false, @@ -94,7 +93,7 @@ MusicAlbum( id: "MPREb_bqWA6mAZFWS", name: "Pedha Rasi Peddamma Katha", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(4), by_va: false, diff --git a/tests/snapshots/youtube__music_album_no_artist_intl.snap b/tests/snapshots/youtube__music_album_no_artist_intl.snap index 82c381b..c450608 100644 --- a/tests/snapshots/youtube__music_album_no_artist_intl.snap +++ b/tests/snapshots/youtube__music_album_no_artist_intl.snap @@ -30,7 +30,7 @@ MusicAlbum( id: "MPREb_bqWA6mAZFWS", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, @@ -51,7 +51,7 @@ MusicAlbum( id: "MPREb_bqWA6mAZFWS", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(2), by_va: false, @@ -72,7 +72,7 @@ MusicAlbum( id: "MPREb_bqWA6mAZFWS", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(3), by_va: false, @@ -93,7 +93,7 @@ MusicAlbum( id: "MPREb_bqWA6mAZFWS", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(4), by_va: false, diff --git a/tests/snapshots/youtube__music_album_no_year.snap b/tests/snapshots/youtube__music_album_no_year.snap index f5a730a..e9a913e 100644 --- a/tests/snapshots/youtube__music_album_no_year.snap +++ b/tests/snapshots/youtube__music_album_no_year.snap @@ -51,7 +51,7 @@ MusicAlbum( id: "MPREb_F3Af9UZZVxX", name: "La Ultima Vez (Remix)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, diff --git a/tests/snapshots/youtube__music_album_no_year_intl.snap b/tests/snapshots/youtube__music_album_no_year_intl.snap index 163b70b..2ff6a5f 100644 --- a/tests/snapshots/youtube__music_album_no_year_intl.snap +++ b/tests/snapshots/youtube__music_album_no_year_intl.snap @@ -51,7 +51,7 @@ MusicAlbum( id: "MPREb_F3Af9UZZVxX", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, diff --git a/tests/snapshots/youtube__music_album_one_artist.snap b/tests/snapshots/youtube__music_album_one_artist.snap index 3fe60ce..85f2d34 100644 --- a/tests/snapshots/youtube__music_album_one_artist.snap +++ b/tests/snapshots/youtube__music_album_one_artist.snap @@ -35,7 +35,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, @@ -56,7 +56,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(2), by_va: false, @@ -77,7 +77,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(3), by_va: false, @@ -98,7 +98,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(4), by_va: false, @@ -119,7 +119,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(5), by_va: false, @@ -140,7 +140,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(6), by_va: false, @@ -161,7 +161,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(7), by_va: false, @@ -182,7 +182,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(8), by_va: false, @@ -203,7 +203,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(9), by_va: false, @@ -224,7 +224,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(10), by_va: false, @@ -245,7 +245,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(11), by_va: false, @@ -266,7 +266,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(12), by_va: false, @@ -287,7 +287,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(13), by_va: false, @@ -308,7 +308,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(14), by_va: false, @@ -329,7 +329,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(15), by_va: false, @@ -350,7 +350,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(16), by_va: false, @@ -371,7 +371,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(17), by_va: false, @@ -392,7 +392,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "Märchen enden gut", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(18), by_va: false, diff --git a/tests/snapshots/youtube__music_album_one_artist_intl.snap b/tests/snapshots/youtube__music_album_one_artist_intl.snap index 5314034..e667a66 100644 --- a/tests/snapshots/youtube__music_album_one_artist_intl.snap +++ b/tests/snapshots/youtube__music_album_one_artist_intl.snap @@ -35,7 +35,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, @@ -56,7 +56,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(2), by_va: false, @@ -77,7 +77,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(3), by_va: false, @@ -98,7 +98,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(4), by_va: false, @@ -119,7 +119,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(5), by_va: false, @@ -140,7 +140,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(6), by_va: false, @@ -161,7 +161,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(7), by_va: false, @@ -182,7 +182,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(8), by_va: false, @@ -203,7 +203,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(9), by_va: false, @@ -224,7 +224,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(10), by_va: false, @@ -245,7 +245,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(11), by_va: false, @@ -266,7 +266,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(12), by_va: false, @@ -287,7 +287,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(13), by_va: false, @@ -308,7 +308,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(14), by_va: false, @@ -329,7 +329,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(15), by_va: false, @@ -350,7 +350,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(16), by_va: false, @@ -371,7 +371,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(17), by_va: false, @@ -392,7 +392,7 @@ MusicAlbum( id: "MPREb_nlBWQROfvjo", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(18), by_va: false, diff --git a/tests/snapshots/youtube__music_album_show.snap b/tests/snapshots/youtube__music_album_show.snap index e66b686..f202333 100644 --- a/tests/snapshots/youtube__music_album_show.snap +++ b/tests/snapshots/youtube__music_album_show.snap @@ -35,7 +35,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, @@ -56,7 +56,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(2), by_va: false, @@ -77,7 +77,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(3), by_va: false, @@ -98,7 +98,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(4), by_va: false, @@ -119,7 +119,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(5), by_va: false, @@ -140,7 +140,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(6), by_va: false, @@ -161,7 +161,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(7), by_va: false, @@ -182,7 +182,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(8), by_va: false, @@ -203,7 +203,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(9), by_va: false, @@ -224,7 +224,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(10), by_va: false, @@ -245,7 +245,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(11), by_va: false, @@ -266,7 +266,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(12), by_va: false, @@ -287,7 +287,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(13), by_va: false, @@ -308,7 +308,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(14), by_va: false, diff --git a/tests/snapshots/youtube__music_album_show_intl.snap b/tests/snapshots/youtube__music_album_show_intl.snap index b8dda33..bac426b 100644 --- a/tests/snapshots/youtube__music_album_show_intl.snap +++ b/tests/snapshots/youtube__music_album_show_intl.snap @@ -35,7 +35,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, @@ -56,7 +56,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(2), by_va: false, @@ -77,7 +77,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(3), by_va: false, @@ -98,7 +98,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(4), by_va: false, @@ -119,7 +119,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(5), by_va: false, @@ -140,7 +140,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(6), by_va: false, @@ -161,7 +161,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(7), by_va: false, @@ -182,7 +182,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(8), by_va: false, @@ -203,7 +203,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(9), by_va: false, @@ -224,7 +224,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(10), by_va: false, @@ -245,7 +245,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(11), by_va: false, @@ -266,7 +266,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(12), by_va: false, @@ -287,7 +287,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(13), by_va: false, @@ -308,7 +308,7 @@ MusicAlbum( id: "MPREb_cwzk8EUwypZ", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(14), by_va: false, diff --git a/tests/snapshots/youtube__music_album_single.snap b/tests/snapshots/youtube__music_album_single.snap index 5061942..7b557d0 100644 --- a/tests/snapshots/youtube__music_album_single.snap +++ b/tests/snapshots/youtube__music_album_single.snap @@ -43,7 +43,7 @@ MusicAlbum( id: "MPREb_bHfHGoy7vuv", name: "Der Himmel reißt auf", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, diff --git a/tests/snapshots/youtube__music_album_single_intl.snap b/tests/snapshots/youtube__music_album_single_intl.snap index 3df94d9..c831216 100644 --- a/tests/snapshots/youtube__music_album_single_intl.snap +++ b/tests/snapshots/youtube__music_album_single_intl.snap @@ -43,7 +43,7 @@ MusicAlbum( id: "MPREb_bHfHGoy7vuv", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, diff --git a/tests/snapshots/youtube__music_album_unavailable.snap b/tests/snapshots/youtube__music_album_unavailable.snap index 2efb6db..ef83f26 100644 --- a/tests/snapshots/youtube__music_album_unavailable.snap +++ b/tests/snapshots/youtube__music_album_unavailable.snap @@ -30,7 +30,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "13 Reasons Why (Season 3)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, @@ -51,7 +51,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "13 Reasons Why (Season 3)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(2), by_va: false, @@ -72,7 +72,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "13 Reasons Why (Season 3)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(3), by_va: false, @@ -93,7 +93,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "13 Reasons Why (Season 3)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(5), by_va: false, @@ -114,7 +114,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "13 Reasons Why (Season 3)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(6), by_va: false, @@ -135,7 +135,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "13 Reasons Why (Season 3)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(7), by_va: false, @@ -156,7 +156,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "13 Reasons Why (Season 3)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(8), by_va: false, @@ -177,7 +177,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "13 Reasons Why (Season 3)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(9), by_va: false, @@ -198,7 +198,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "13 Reasons Why (Season 3)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(11), by_va: false, @@ -219,7 +219,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "13 Reasons Why (Season 3)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(12), by_va: false, @@ -240,7 +240,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "13 Reasons Why (Season 3)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(13), by_va: false, @@ -261,7 +261,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "13 Reasons Why (Season 3)", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(18), by_va: false, diff --git a/tests/snapshots/youtube__music_album_unavailable_intl.snap b/tests/snapshots/youtube__music_album_unavailable_intl.snap index 1b7b540..cdf785d 100644 --- a/tests/snapshots/youtube__music_album_unavailable_intl.snap +++ b/tests/snapshots/youtube__music_album_unavailable_intl.snap @@ -30,7 +30,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, @@ -51,7 +51,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(2), by_va: false, @@ -72,7 +72,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(3), by_va: false, @@ -93,7 +93,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(5), by_va: false, @@ -114,7 +114,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(6), by_va: false, @@ -135,7 +135,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(7), by_va: false, @@ -156,7 +156,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(8), by_va: false, @@ -177,7 +177,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(9), by_va: false, @@ -198,7 +198,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(11), by_va: false, @@ -219,7 +219,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(12), by_va: false, @@ -240,7 +240,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(13), by_va: false, @@ -261,7 +261,7 @@ MusicAlbum( id: "MPREb_AzuWg8qAVVl", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(18), by_va: false, diff --git a/tests/snapshots/youtube__music_album_various_artists.snap b/tests/snapshots/youtube__music_album_various_artists.snap index 0a335da..eac36e1 100644 --- a/tests/snapshots/youtube__music_album_various_artists.snap +++ b/tests/snapshots/youtube__music_album_various_artists.snap @@ -30,7 +30,7 @@ MusicAlbum( id: "MPREb_8QkDeEIawvX", name: "<Queendom2> FINAL", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, @@ -51,7 +51,7 @@ MusicAlbum( id: "MPREb_8QkDeEIawvX", name: "<Queendom2> FINAL", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(2), by_va: false, @@ -72,7 +72,7 @@ MusicAlbum( id: "MPREb_8QkDeEIawvX", name: "<Queendom2> FINAL", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(3), by_va: false, @@ -93,7 +93,7 @@ MusicAlbum( id: "MPREb_8QkDeEIawvX", name: "<Queendom2> FINAL", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(4), by_va: false, @@ -114,7 +114,7 @@ MusicAlbum( id: "MPREb_8QkDeEIawvX", name: "<Queendom2> FINAL", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(5), by_va: false, @@ -135,7 +135,7 @@ MusicAlbum( id: "MPREb_8QkDeEIawvX", name: "<Queendom2> FINAL", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(6), by_va: false, diff --git a/tests/snapshots/youtube__music_album_various_artists_intl.snap b/tests/snapshots/youtube__music_album_various_artists_intl.snap index 8f3c587..f44751e 100644 --- a/tests/snapshots/youtube__music_album_various_artists_intl.snap +++ b/tests/snapshots/youtube__music_album_various_artists_intl.snap @@ -1,6 +1,5 @@ --- source: tests/youtube.rs -assertion_line: 1396 expression: album --- MusicAlbum( @@ -31,7 +30,7 @@ MusicAlbum( id: "MPREb_8QkDeEIawvX", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, @@ -52,7 +51,7 @@ MusicAlbum( id: "MPREb_8QkDeEIawvX", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(2), by_va: false, @@ -73,7 +72,7 @@ MusicAlbum( id: "MPREb_8QkDeEIawvX", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(3), by_va: false, @@ -94,7 +93,7 @@ MusicAlbum( id: "MPREb_8QkDeEIawvX", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(4), by_va: false, @@ -115,7 +114,7 @@ MusicAlbum( id: "MPREb_8QkDeEIawvX", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(5), by_va: false, @@ -136,7 +135,7 @@ MusicAlbum( id: "MPREb_8QkDeEIawvX", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(6), by_va: false, diff --git a/tests/snapshots/youtube__music_album_version_no_artist.snap b/tests/snapshots/youtube__music_album_version_no_artist.snap index c5f9504..f1fb92f 100644 --- a/tests/snapshots/youtube__music_album_version_no_artist.snap +++ b/tests/snapshots/youtube__music_album_version_no_artist.snap @@ -1,6 +1,5 @@ --- source: tests/youtube.rs -assertion_line: 1391 expression: album --- MusicAlbum( @@ -44,7 +43,7 @@ MusicAlbum( id: "MPREb_h8ltx5oKvyY", name: "Pedha Rasi Peddamma Katha", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, @@ -65,7 +64,7 @@ MusicAlbum( id: "MPREb_h8ltx5oKvyY", name: "Pedha Rasi Peddamma Katha", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(2), by_va: false, @@ -86,7 +85,7 @@ MusicAlbum( id: "MPREb_h8ltx5oKvyY", name: "Pedha Rasi Peddamma Katha", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(3), by_va: false, @@ -107,7 +106,7 @@ MusicAlbum( id: "MPREb_h8ltx5oKvyY", name: "Pedha Rasi Peddamma Katha", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(4), by_va: false, diff --git a/tests/snapshots/youtube__music_album_version_no_artist_intl.snap b/tests/snapshots/youtube__music_album_version_no_artist_intl.snap index 805a4be..da0a781 100644 --- a/tests/snapshots/youtube__music_album_version_no_artist_intl.snap +++ b/tests/snapshots/youtube__music_album_version_no_artist_intl.snap @@ -43,7 +43,7 @@ MusicAlbum( id: "MPREb_h8ltx5oKvyY", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(1), by_va: false, @@ -64,7 +64,7 @@ MusicAlbum( id: "MPREb_h8ltx5oKvyY", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(2), by_va: false, @@ -85,7 +85,7 @@ MusicAlbum( id: "MPREb_h8ltx5oKvyY", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(3), by_va: false, @@ -106,7 +106,7 @@ MusicAlbum( id: "MPREb_h8ltx5oKvyY", name: "[name]", )), - view_count: None, + view_count: "[view_count]", is_video: false, track_nr: Some(4), by_va: false, diff --git a/tests/youtube.rs b/tests/youtube.rs index 6b54b00..933ef18 100644 --- a/tests/youtube.rs +++ b/tests/youtube.rs @@ -1429,7 +1429,7 @@ fn music_album(#[case] name: &str, #[case] id: &str, rp: RustyPipe, unlocalized: if unlocalized { insta::assert_ron_snapshot!(format!("music_album_{name}"), album, - {".cover" => "[cover]"} + {".cover" => "[cover]", ".tracks[].view_count" => "[view_count]"} ); } else { insta::assert_ron_snapshot!(format!("music_album_{name}_intl"), album, @@ -1439,6 +1439,7 @@ fn music_album(#[case] name: &str, #[case] id: &str, rp: RustyPipe, unlocalized: ".description" => "[description]", ".artists[].name" => "[name]", ".tracks[].name" => "[name]", + ".tracks[].view_count" => "[view_count]", ".tracks[].album.name" => "[name]", ".tracks[].artists[].name" => "[name]", ".variants[].artists[].name" => "[name]", @@ -1730,16 +1731,11 @@ async fn music_search_episode() { let rp = RustyPipe::builder().strict().build().unwrap(); let res = rp .query() - .music_search_videos("Blond - Da muss man dabei gewesen sein: Das Hörspiel - Fall #1") + .music_search("Blond - Da muss man dabei gewesen sein: Das Hörspiel - Fall #1") .await .unwrap(); - let track = &res - .items - .items - .iter() - .find(|a| a.id == "Zq_-LDy7AgE") - .unwrap(); + let track = &res.tracks.iter().find(|a| a.id == "Zq_-LDy7AgE").unwrap(); assert_eq!( track.name,