Compare commits

..

3 commits

Author SHA1 Message Date
22e298ff98 fix: a/b test 8: parsing view count for tracks
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-23 11:28:38 +02:00
93e5ad22e9 feat(cli): add vdata argument 2023-08-22 23:35:07 +02:00
e2eda901b1 fix: add support for new channel about data model 2023-08-22 22:58:28 +02:00
35 changed files with 463 additions and 274 deletions

View file

@ -7,6 +7,9 @@ unittest:
testyt: testyt:
cargo test --features=rss --test youtube cargo test --features=rss --test youtube
testyt-localized:
YT_LANG=th cargo test --features=rss --test youtube
testintl: testintl:
#!/usr/bin/env bash #!/usr/bin/env bash
LANGUAGES=( LANGUAGES=(

View file

@ -20,8 +20,11 @@ struct Cli {
#[clap(subcommand)] #[clap(subcommand)]
command: Commands, command: Commands,
/// Always generate a report (used for debugging) /// Always generate a report (used for debugging)
#[clap(long)] #[clap(long, global = true)]
report: bool, report: bool,
/// YouTube visitor data cookie
#[clap(long, global = true)]
vdata: Option<String>,
} }
#[derive(Subcommand)] #[derive(Subcommand)]
@ -391,13 +394,14 @@ async fn main() {
let cli = Cli::parse(); let cli = Cli::parse();
let mut storage_dir = dirs::data_dir().expect("no data dir"); let mut rp = RustyPipe::builder().visitor_data_opt(cli.vdata);
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);
if cli.report { if cli.report {
rp = rp.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(); let rp = rp.build().unwrap();

View file

@ -24,6 +24,7 @@ pub enum ABTest {
TrendsPageHeaderRenderer = 5, TrendsPageHeaderRenderer = 5,
DiscographyPage = 6, DiscographyPage = 6,
ShortDateFormat = 7, ShortDateFormat = 7,
TrackViewcount = 8,
PlaylistsForShorts = 9, PlaylistsForShorts = 9,
} }
@ -96,6 +97,7 @@ pub async fn run_test(
ABTest::DiscographyPage => discography_page(&query).await, ABTest::DiscographyPage => discography_page(&query).await,
ABTest::ShortDateFormat => short_date_format(&query).await, ABTest::ShortDateFormat => short_date_format(&query).await,
ABTest::PlaylistsForShorts => playlists_for_shorts(&query).await, ABTest::PlaylistsForShorts => playlists_for_shorts(&query).await,
ABTest::TrackViewcount => track_viewcount(&query).await,
} }
.unwrap(); .unwrap();
pb.inc(1); pb.inc(1);
@ -246,6 +248,20 @@ pub async fn short_date_format(rp: &RustyPipeQuery) -> Result<bool> {
})) }))
} }
pub async fn track_viewcount(rp: &RustyPipeQuery) -> Result<bool> {
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<bool> { pub async fn playlists_for_shorts(rp: &RustyPipeQuery) -> Result<bool> {
let playlist = rp.playlist("UUSHh8gHdtzO2tXd593_bjErWg").await?; let playlist = rp.playlist("UUSHh8gHdtzO2tXd593_bjErWg").await?;
let v1 = playlist let v1 = playlist

View file

@ -293,7 +293,7 @@ The data model for the video shelves did not change.
**NEW** **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 ## [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** **OLD**
![A/B test 4 old screenshot](./_img/ab_6_old.png) ![A/B test 6 old screenshot](./_img/ab_6_old.png)
**NEW** **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 ## [7] Short timeago format
- **Encountered on:** 28.05.2023 - **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 YouTube changed their date format from the long format (_21 hours ago_, _3 days ago_) to
a short format (_21h ago_, _3d ago_). 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 ## [9] Playlists for Shorts
- **Encountered on:** 26.06.2023 - **Encountered on:** 26.06.2023

BIN
notes/_img/ab_8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
notes/_img/ab_8_old.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -339,7 +339,7 @@ impl MapResponse<MusicSearchSuggestion> for response::MusicSearchSuggestion {
_deobf: Option<&crate::deobfuscate::DeobfData>, _deobf: Option<&crate::deobfuscate::DeobfData>,
_vdata: Option<&str>, _vdata: Option<&str>,
) -> Result<MapResult<MusicSearchSuggestion>, ExtractionError> { ) -> Result<MapResult<MusicSearchSuggestion>, ExtractionError> {
let mut mapper = MusicListMapper::new(lang); let mut mapper = MusicListMapper::new_search_suggest(lang);
let mut terms = Vec::new(); let mut terms = Vec::new();
for section in self.contents { for section in self.contents {

View file

@ -16,7 +16,9 @@ use crate::{
}; };
use super::{ use super::{
url_endpoint::{BrowseEndpointWrap, MusicPageType, NavigationEndpoint, PageType}, url_endpoint::{
BrowseEndpointWrap, MusicPageType, MusicVideoType, NavigationEndpoint, PageType,
},
ContentsRenderer, MusicContinuationData, Thumbnails, ThumbnailsWrap, ContentsRenderer, MusicContinuationData, Thumbnails, ThumbnailsWrap,
}; };
@ -429,6 +431,7 @@ pub(crate) struct MusicListMapper {
artists: Option<(Vec<ArtistId>, bool)>, artists: Option<(Vec<ArtistId>, bool)>,
album: Option<AlbumId>, album: Option<AlbumId>,
artist_page: bool, artist_page: bool,
search_suggestion: bool,
items: Vec<MusicItem>, items: Vec<MusicItem>,
warnings: Vec<String>, warnings: Vec<String>,
/// True if unknown items were mapped /// True if unknown items were mapped
@ -450,6 +453,20 @@ impl MusicListMapper {
artists: None, artists: None,
album: None, album: None,
artist_page: false, 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(), items: Vec::new(),
warnings: Vec::new(), warnings: Vec::new(),
has_unknown: false, has_unknown: false,
@ -463,6 +480,7 @@ impl MusicListMapper {
artists: Some((vec![artist], false)), artists: Some((vec![artist], false)),
album: None, album: None,
artist_page: true, artist_page: true,
search_suggestion: false,
items: Vec::new(), items: Vec::new(),
warnings: Vec::new(), warnings: Vec::new(),
has_unknown: false, has_unknown: false,
@ -476,6 +494,7 @@ impl MusicListMapper {
artists: Some((artists, by_va)), artists: Some((artists, by_va)),
album: Some(album), album: Some(album),
artist_page: false, artist_page: false,
search_suggestion: false,
items: Vec::new(), items: Vec::new(),
warnings: Vec::new(), warnings: Vec::new(),
has_unknown: false, has_unknown: false,
@ -515,6 +534,7 @@ impl MusicListMapper {
let c1 = columns.next(); let c1 = columns.next();
let c2 = columns.next(); let c2 = columns.next();
let c3 = columns.next(); let c3 = columns.next();
let c4 = columns.next();
let title = c1.as_ref().map(|col| col.renderer.text.to_string()); let title = c1.as_ref().map(|col| col.renderer.text.to_string());
@ -532,10 +552,8 @@ impl MusicListMapper {
c1.and_then(|c1| { c1.and_then(|c1| {
c1.renderer.text.0.into_iter().next().and_then(|t| match t { c1.renderer.text.0.into_iter().next().and_then(|t| match t {
crate::serializer::text::TextComponent::Video { crate::serializer::text::TextComponent::Video {
video_id, video_id, vtype, ..
is_video, } => Some((MusicPageType::Track { vtype }, video_id)),
..
} => Some((MusicPageType::Track { is_video }, video_id)),
crate::serializer::text::TextComponent::Browse { crate::serializer::text::TextComponent::Browse {
page_type, page_type,
browse_id, browse_id,
@ -549,8 +567,12 @@ impl MusicListMapper {
item.playlist_item_data.map(|d| { item.playlist_item_data.map(|d| {
( (
MusicPageType::Track { MusicPageType::Track {
is_video: self.album.is_none() vtype: MusicVideoType::from_is_video(
&& !first_tn.map(|tn| tn.height == tn.width).unwrap_or_default(), self.album.is_none()
&& !first_tn
.map(|tn| tn.height == tn.width)
.unwrap_or_default(),
),
}, },
d.video_id, d.video_id,
) )
@ -561,7 +583,9 @@ impl MusicListMapper {
util::video_id_from_thumbnail_url(&tn.url).map(|id| { util::video_id_from_thumbnail_url(&tn.url).map(|id| {
( (
MusicPageType::Track { 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, id,
) )
@ -571,19 +595,28 @@ impl MusicListMapper {
match pt_id { match pt_id {
// Track // 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 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<TextComponents>,
album: Option<TextComponents>,
duration: Option<TextComponents>,
view_count: Option<TextComponents>,
}
let p = match item.flex_column_display_style {
// Search result // Search result
FlexColumnDisplayStyle::TwoLines => { FlexColumnDisplayStyle::TwoLines => {
// Is this a related track? // Is this a related track (from the "similar titles" tab in the player)?
if !is_video && item.item_height == ItemHeight::Compact { if vtype != MusicVideoType::Video && item.item_height == ItemHeight::Compact
( {
c2.map(TextComponents::from), Parsed {
c3.map(TextComponents::from), artists: c2.map(TextComponents::from),
None, album: c3.map(TextComponents::from),
) ..Default::default()
}
} else { } else {
let mut subtitle_parts = c2 let mut subtitle_parts = c2
.ok_or_else(|| format!("track {id}: could not get subtitle"))? .ok_or_else(|| format!("track {id}: could not get subtitle"))?
@ -594,62 +627,98 @@ impl MusicListMapper {
// Is this a related video? // Is this a related video?
if item.item_height == ItemHeight::Compact { 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? // Is it a podcast episode?
else if subtitle_parts.len() <= 3 && c3.is_some() { else if vtype == MusicVideoType::Episode {
(subtitle_parts.next_back(), None, None) Parsed {
artists: subtitle_parts.next_back(),
..Default::default()
}
} else { } else {
// Skip first part (track type) // Skip first part (track type)
if subtitle_parts.len() > 3 if subtitle_parts.len() > 3
|| (is_video && subtitle_parts.len() == 2) || (vtype == MusicVideoType::Video && subtitle_parts.len() == 2)
{ {
subtitle_parts.next(); subtitle_parts.next();
} }
( match vtype {
subtitle_parts.next(), MusicVideoType::Video => Parsed {
subtitle_parts.next(), artists: subtitle_parts.next(),
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 // Playlist item
FlexColumnDisplayStyle::Default => ( FlexColumnDisplayStyle::Default => {
c2.map(TextComponents::from), let artists = c2.map(TextComponents::from);
c3.map(TextComponents::from), let duration = item
item.fixed_columns .fixed_columns
.into_iter() .into_iter()
.next() .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 duration = p
.duration
let (album, view_count) = match (item.flex_column_display_style, is_video) { .and_then(|p| util::parse_video_length(p.first_str()));
// The album field contains the view count for search videos let album = p
(FlexColumnDisplayStyle::TwoLines, true) => ( .album
None, .and_then(|p| p.0.into_iter().find_map(|c| AlbumId::try_from(c).ok()))
album_p.and_then(|p| { .or_else(|| self.album.clone());
util::parse_large_numstr_or_warn( let view_count = p.view_count.and_then(|p| {
p.first_str(), util::parse_large_numstr_or_warn(p.first_str(), self.lang, &mut self.warnings)
self.lang, });
&mut self.warnings, let (mut artists, by_va) = map_artists(p.artists);
)
}),
),
(_, 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);
// Extract artist id from dropdown menu // Extract artist id from dropdown menu
let artist_id = map_artist_id_fallback(item.menu, artists.first()); let artist_id = map_artist_id_fallback(item.menu, artists.first());
@ -685,7 +754,7 @@ impl MusicListMapper {
artist_id, artist_id,
album, album,
view_count, view_count,
is_video, is_video: vtype.is_video(),
track_nr, track_nr,
by_va, by_va,
})); }));
@ -807,7 +876,7 @@ impl MusicListMapper {
match item.navigation_endpoint.music_page() { match item.navigation_endpoint.music_page() {
Some((page_type, id)) => match page_type { Some((page_type, id)) => match page_type {
MusicPageType::Track { is_video } => { MusicPageType::Track { vtype } => {
let (artists, by_va) = map_artists(subtitle_p1); let (artists, by_va) = map_artists(subtitle_p1);
self.items.push(MusicItem::Track(TrackItem { self.items.push(MusicItem::Track(TrackItem {
@ -825,7 +894,7 @@ impl MusicListMapper {
&mut self.warnings, &mut self.warnings,
) )
}), }),
is_video, is_video: vtype.is_video(),
track_nr: None, track_nr: None,
by_va, by_va,
})); }));
@ -976,43 +1045,61 @@ impl MusicListMapper {
})); }));
Some(MusicItemType::Album) Some(MusicItemType::Album)
} }
MusicPageType::Track { is_video } => { MusicPageType::Track { vtype } => {
let (artists, by_va) = map_artists(subtitle_p2); if vtype == MusicVideoType::Episode {
let duration = let (artists, by_va) = map_artists(subtitle_p3);
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,
)
};
self.items.push(MusicItem::Track(TrackItem { self.items.push(MusicItem::Track(TrackItem {
id, id,
name: card.title, name: card.title,
duration, duration: None,
cover: card.thumbnail.into(), cover: card.thumbnail.into(),
artist_id: artists.first().and_then(|a| a.id.clone()), artist_id: artists.first().and_then(|a| a.id.clone()),
artists, artists,
album, album: None,
view_count, view_count: None,
is_video, is_video: vtype.is_video(),
track_nr: None, track_nr: None,
by_va, 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) Some(MusicItemType::Track)
} }
MusicPageType::Playlist => { MusicPageType::Playlist => {

View file

@ -151,6 +151,22 @@ pub(crate) enum MusicVideoType {
Video, Video,
#[serde(rename = "MUSIC_VIDEO_TYPE_ATV")] #[serde(rename = "MUSIC_VIDEO_TYPE_ATV")]
Track, 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)] #[derive(Default, Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
@ -189,7 +205,7 @@ pub(crate) enum MusicPageType {
Artist, Artist,
Album, Album,
Playlist, Playlist,
Track { is_video: bool }, Track { vtype: MusicVideoType },
Unknown, Unknown,
None, None,
} }
@ -221,11 +237,10 @@ impl NavigationEndpoint {
} else { } else {
Some(( Some((
MusicPageType::Track { MusicPageType::Track {
is_video: watch_endpoint vtype: watch_endpoint
.watch_endpoint_music_supported_configs .watch_endpoint_music_supported_configs
.watch_endpoint_music_config .watch_endpoint_music_config
.music_video_type .music_video_type,
== MusicVideoType::Video,
}, },
watch_endpoint.video_id, watch_endpoint.video_id,
)) ))

View file

@ -14,7 +14,7 @@ use crate::{
}, },
param::Language, param::Language,
serializer::{ serializer::{
text::{AccessibilityText, Text, TextComponent}, text::{AccessibilityText, AttributedText, Text, TextComponent},
MapResult, MapResult,
}, },
util::{self, timeago, TryRemove}, util::{self, timeago, TryRemove},
@ -369,6 +369,9 @@ pub(crate) struct ChannelFullMetadata {
#[serde(default)] #[serde(default)]
#[serde_as(as = "VecSkipError<_>")] #[serde_as(as = "VecSkipError<_>")]
pub primary_links: Vec<PrimaryLink>, pub primary_links: Vec<PrimaryLink>,
#[serde(default)]
// #[serde_as(as = "VecSkipError<_>")]
pub links: Vec<ExternalLink>,
} }
#[serde_as] #[serde_as]
@ -380,6 +383,22 @@ pub(crate) struct PrimaryLink {
pub navigation_endpoint: NavigationEndpoint, 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 { trait IsLive {
fn is_live(&self) -> bool; fn is_live(&self) -> bool;
} }
@ -726,6 +745,18 @@ impl YouTubeListMapper<YouTubeItem> {
self.corrected_query = Some(corrected_query); self.corrected_query = Some(corrected_query);
} }
YouTubeListItem::ChannelAboutFullMetadataRenderer(meta) => { YouTubeListItem::ChannelAboutFullMetadataRenderer(meta) => {
let mut links = meta
.primary_links
.into_iter()
.filter_map(|l| l.navigation_endpoint.url().map(|url| (l.title, url)))
.collect::<Vec<_>>();
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 { self.channel_info = Some(ChannelInfo {
create_date: timeago::parse_textual_date_or_warn( create_date: timeago::parse_textual_date_or_warn(
self.lang, self.lang,
@ -736,11 +767,7 @@ impl YouTubeListMapper<YouTubeItem> {
view_count: meta view_count: meta
.view_count_text .view_count_text
.and_then(|txt| util::parse_numeric_or_warn(&txt, &mut self.warnings)), .and_then(|txt| util::parse_numeric_or_warn(&txt, &mut self.warnings)),
links: meta links,
.primary_links
.into_iter()
.filter_map(|l| l.navigation_endpoint.url().map(|url| (l.title, url)))
.collect(),
}); });
} }
YouTubeListItem::RichItemRenderer { content } => { YouTubeListItem::RichItemRenderer { content } => {

View file

@ -67,7 +67,10 @@ MusicSearchSuggestion(
), ),
], ],
artist_id: Some("UC56hLMPuEsERdmTBbR_JGHA"), artist_id: Some("UC56hLMPuEsERdmTBbR_JGHA"),
album: None, album: Some(AlbumId(
id: "MPREb_kz546sNB1mH",
name: "Was Spaß macht...",
)),
view_count: None, view_count: None,
is_video: false, is_video: false,
track_nr: None, track_nr: None,

View file

@ -142,14 +142,15 @@ fn get_sig_fn(player_js: &str) -> Result<String, DeobfError> {
let function_pattern = Regex::new(&function_pattern_str) let function_pattern = Regex::new(&function_pattern_str)
.map_err(|_| DeobfError::Other("could not parse function pattern regex"))?; .map_err(|_| DeobfError::Other("could not parse function pattern regex"))?;
let deobfuscate_function = "var ".to_owned() let deobfuscate_function = format!(
+ function_pattern "var {};",
function_pattern
.captures(player_js) .captures(player_js)
.ok_or(DeobfError::Extraction("deobf function"))? .ok_or(DeobfError::Extraction("deobf function"))?
.get(1) .get(1)
.unwrap() .unwrap()
.as_str() .as_str()
+ ";"; );
static HELPER_OBJECT_NAME_REGEX: Lazy<Regex> = static HELPER_OBJECT_NAME_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r#";([A-Za-z0-9_\$]{2,3})\...\("#).unwrap()); 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<String, DeobfError> {
.as_str() .as_str()
.parse::<usize>() .parse::<usize>()
.or(Err(DeobfError::Other("could not parse array_num")))?; .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( let array_pattern = Regex::new(&array_pattern_str).or(Err(DeobfError::Other(
"could not parse helper pattern regex", "could not parse helper pattern regex",
)))?; )))?;

View file

@ -170,6 +170,7 @@ mod tests {
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use crate::client::response::url_endpoint::MusicVideoType;
use crate::serializer::text; use crate::serializer::text;
static TEXT_SOURCE: Lazy<text::TextComponents> = Lazy::new(|| { static TEXT_SOURCE: Lazy<text::TextComponents> = 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::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::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::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::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::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() }, text::TextComponent::Text { text: "\n\nSubscribe to aespa Official YouTube Channel!\n".to_owned() },

View file

@ -19,7 +19,7 @@ SAttributed {
text: "aespa 에스파 'Black ...", text: "aespa 에스파 'Black ...",
video_id: "Ky5RT5oGg0w", video_id: "Ky5RT5oGg0w",
start_time: 0, start_time: 0,
is_video: true, vtype: Video,
}, },
Text { Text {
text: "\n\n🎟\u{fe0f} aespa Showcase SYNK in LA! Tickets now on sale: ", text: "\n\n🎟\u{fe0f} aespa Showcase SYNK in LA! Tickets now on sale: ",

View file

@ -94,8 +94,7 @@ pub(crate) enum TextComponent {
text: String, text: String,
video_id: String, video_id: String,
start_time: u32, start_time: u32,
/// True if the item is a video, false if it is a YTM track vtype: MusicVideoType,
is_video: bool,
}, },
Browse { Browse {
text: String, text: String,
@ -167,11 +166,10 @@ fn map_text_component(text: String, nav: Option<NavigationEndpoint>) -> TextComp
text, text,
video_id: watch_endpoint.video_id, video_id: watch_endpoint.video_id,
start_time: watch_endpoint.start_time_seconds, start_time: watch_endpoint.start_time_seconds,
is_video: watch_endpoint vtype: watch_endpoint
.watch_endpoint_music_supported_configs .watch_endpoint_music_supported_configs
.watch_endpoint_music_config .watch_endpoint_music_config
.music_video_type .music_video_type,
== MusicVideoType::Video,
}, },
Some(NavigationEndpoint::Browse { Some(NavigationEndpoint::Browse {
browse_endpoint, browse_endpoint,
@ -200,8 +198,12 @@ impl<'de> Deserialize<'de> for TextComponent {
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let mut text = RichTextInternal::deserialize(deserializer)?; let text = RichTextInternal::deserialize(deserializer)?;
Ok(text.runs.swap_remove(0).into()) 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<D>(deserializer: D) -> Result<TextComponent, D::Error>
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<TextComponent> for crate::model::ChannelId { impl TryFrom<TextComponent> for crate::model::ChannelId {
type Error = (); type Error = ();
@ -404,6 +420,17 @@ impl TextComponent {
} }
} }
impl From<TextComponent> for String {
fn from(value: TextComponent) -> Self {
match value {
TextComponent::Video { text, .. }
| TextComponent::Browse { text, .. }
| TextComponent::Web { text, .. }
| TextComponent::Text { text } => text,
}
}
}
impl TextComponents { impl TextComponents {
/// Return the string representation of the first text component /// Return the string representation of the first text component
pub fn first_str(&self) -> &str { pub fn first_str(&self) -> &str {
@ -583,7 +610,7 @@ mod tests {
text: "DEEP", text: "DEEP",
video_id: "wZIoIgz5mbs", video_id: "wZIoIgz5mbs",
start_time: 0, start_time: 0,
is_video: true, vtype: Video,
}, },
} }
"###); "###);

View file

@ -224,7 +224,7 @@ pub fn retry_delay(
/// Also strips google analytics tracking parameters /// Also strips google analytics tracking parameters
/// (`utm_source`, `utm_medium`, `utm_campaign`, `utm_content`) because google analytics is bad. /// (`utm_source`, `utm_medium`, `utm_campaign`, `utm_content`) because google analytics is bad.
pub fn sanitize_yt_url(url: &str) -> String { pub fn sanitize_yt_url(url: &str) -> String {
fn sanitize_yt_url_inner(url: &str) -> Option<String> { fn try_sanitize_yt_url(url: &str) -> Option<String> {
let mut parsed_url = Url::parse(url).ok()?; let mut parsed_url = Url::parse(url).ok()?;
// Convert redirect url // Convert redirect url
@ -260,7 +260,7 @@ pub fn sanitize_yt_url(url: &str) -> String {
Some(parsed_url.to_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 { pub fn div_ceil(a: u32, b: u32) -> u32 {

View file

@ -35,7 +35,7 @@ MusicAlbum(
id: "MPREb_u1I69lSAe5v", id: "MPREb_u1I69lSAe5v",
name: "Waldbrand", name: "Waldbrand",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,
@ -56,7 +56,7 @@ MusicAlbum(
id: "MPREb_u1I69lSAe5v", id: "MPREb_u1I69lSAe5v",
name: "Waldbrand", name: "Waldbrand",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(2), track_nr: Some(2),
by_va: false, by_va: false,
@ -77,7 +77,7 @@ MusicAlbum(
id: "MPREb_u1I69lSAe5v", id: "MPREb_u1I69lSAe5v",
name: "Waldbrand", name: "Waldbrand",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(3), track_nr: Some(3),
by_va: false, by_va: false,
@ -98,7 +98,7 @@ MusicAlbum(
id: "MPREb_u1I69lSAe5v", id: "MPREb_u1I69lSAe5v",
name: "Waldbrand", name: "Waldbrand",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(4), track_nr: Some(4),
by_va: false, by_va: false,
@ -119,7 +119,7 @@ MusicAlbum(
id: "MPREb_u1I69lSAe5v", id: "MPREb_u1I69lSAe5v",
name: "Waldbrand", name: "Waldbrand",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(5), track_nr: Some(5),
by_va: false, by_va: false,

View file

@ -35,7 +35,7 @@ MusicAlbum(
id: "MPREb_u1I69lSAe5v", id: "MPREb_u1I69lSAe5v",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,
@ -56,7 +56,7 @@ MusicAlbum(
id: "MPREb_u1I69lSAe5v", id: "MPREb_u1I69lSAe5v",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(2), track_nr: Some(2),
by_va: false, by_va: false,
@ -77,7 +77,7 @@ MusicAlbum(
id: "MPREb_u1I69lSAe5v", id: "MPREb_u1I69lSAe5v",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(3), track_nr: Some(3),
by_va: false, by_va: false,
@ -98,7 +98,7 @@ MusicAlbum(
id: "MPREb_u1I69lSAe5v", id: "MPREb_u1I69lSAe5v",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(4), track_nr: Some(4),
by_va: false, by_va: false,
@ -119,7 +119,7 @@ MusicAlbum(
id: "MPREb_u1I69lSAe5v", id: "MPREb_u1I69lSAe5v",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(5), track_nr: Some(5),
by_va: false, by_va: false,

View file

@ -1,6 +1,5 @@
--- ---
source: tests/youtube.rs source: tests/youtube.rs
assertion_line: 1391
expression: album expression: album
--- ---
MusicAlbum( MusicAlbum(
@ -31,7 +30,7 @@ MusicAlbum(
id: "MPREb_bqWA6mAZFWS", id: "MPREb_bqWA6mAZFWS",
name: "Pedha Rasi Peddamma Katha", name: "Pedha Rasi Peddamma Katha",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,
@ -52,7 +51,7 @@ MusicAlbum(
id: "MPREb_bqWA6mAZFWS", id: "MPREb_bqWA6mAZFWS",
name: "Pedha Rasi Peddamma Katha", name: "Pedha Rasi Peddamma Katha",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(2), track_nr: Some(2),
by_va: false, by_va: false,
@ -73,7 +72,7 @@ MusicAlbum(
id: "MPREb_bqWA6mAZFWS", id: "MPREb_bqWA6mAZFWS",
name: "Pedha Rasi Peddamma Katha", name: "Pedha Rasi Peddamma Katha",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(3), track_nr: Some(3),
by_va: false, by_va: false,
@ -94,7 +93,7 @@ MusicAlbum(
id: "MPREb_bqWA6mAZFWS", id: "MPREb_bqWA6mAZFWS",
name: "Pedha Rasi Peddamma Katha", name: "Pedha Rasi Peddamma Katha",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(4), track_nr: Some(4),
by_va: false, by_va: false,

View file

@ -30,7 +30,7 @@ MusicAlbum(
id: "MPREb_bqWA6mAZFWS", id: "MPREb_bqWA6mAZFWS",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,
@ -51,7 +51,7 @@ MusicAlbum(
id: "MPREb_bqWA6mAZFWS", id: "MPREb_bqWA6mAZFWS",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(2), track_nr: Some(2),
by_va: false, by_va: false,
@ -72,7 +72,7 @@ MusicAlbum(
id: "MPREb_bqWA6mAZFWS", id: "MPREb_bqWA6mAZFWS",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(3), track_nr: Some(3),
by_va: false, by_va: false,
@ -93,7 +93,7 @@ MusicAlbum(
id: "MPREb_bqWA6mAZFWS", id: "MPREb_bqWA6mAZFWS",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(4), track_nr: Some(4),
by_va: false, by_va: false,

View file

@ -51,7 +51,7 @@ MusicAlbum(
id: "MPREb_F3Af9UZZVxX", id: "MPREb_F3Af9UZZVxX",
name: "La Ultima Vez (Remix)", name: "La Ultima Vez (Remix)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,

View file

@ -51,7 +51,7 @@ MusicAlbum(
id: "MPREb_F3Af9UZZVxX", id: "MPREb_F3Af9UZZVxX",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,

View file

@ -35,7 +35,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,
@ -56,7 +56,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(2), track_nr: Some(2),
by_va: false, by_va: false,
@ -77,7 +77,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(3), track_nr: Some(3),
by_va: false, by_va: false,
@ -98,7 +98,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(4), track_nr: Some(4),
by_va: false, by_va: false,
@ -119,7 +119,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(5), track_nr: Some(5),
by_va: false, by_va: false,
@ -140,7 +140,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(6), track_nr: Some(6),
by_va: false, by_va: false,
@ -161,7 +161,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(7), track_nr: Some(7),
by_va: false, by_va: false,
@ -182,7 +182,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(8), track_nr: Some(8),
by_va: false, by_va: false,
@ -203,7 +203,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(9), track_nr: Some(9),
by_va: false, by_va: false,
@ -224,7 +224,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(10), track_nr: Some(10),
by_va: false, by_va: false,
@ -245,7 +245,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(11), track_nr: Some(11),
by_va: false, by_va: false,
@ -266,7 +266,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(12), track_nr: Some(12),
by_va: false, by_va: false,
@ -287,7 +287,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(13), track_nr: Some(13),
by_va: false, by_va: false,
@ -308,7 +308,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(14), track_nr: Some(14),
by_va: false, by_va: false,
@ -329,7 +329,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(15), track_nr: Some(15),
by_va: false, by_va: false,
@ -350,7 +350,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(16), track_nr: Some(16),
by_va: false, by_va: false,
@ -371,7 +371,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(17), track_nr: Some(17),
by_va: false, by_va: false,
@ -392,7 +392,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "Märchen enden gut", name: "Märchen enden gut",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(18), track_nr: Some(18),
by_va: false, by_va: false,

View file

@ -35,7 +35,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,
@ -56,7 +56,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(2), track_nr: Some(2),
by_va: false, by_va: false,
@ -77,7 +77,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(3), track_nr: Some(3),
by_va: false, by_va: false,
@ -98,7 +98,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(4), track_nr: Some(4),
by_va: false, by_va: false,
@ -119,7 +119,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(5), track_nr: Some(5),
by_va: false, by_va: false,
@ -140,7 +140,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(6), track_nr: Some(6),
by_va: false, by_va: false,
@ -161,7 +161,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(7), track_nr: Some(7),
by_va: false, by_va: false,
@ -182,7 +182,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(8), track_nr: Some(8),
by_va: false, by_va: false,
@ -203,7 +203,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(9), track_nr: Some(9),
by_va: false, by_va: false,
@ -224,7 +224,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(10), track_nr: Some(10),
by_va: false, by_va: false,
@ -245,7 +245,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(11), track_nr: Some(11),
by_va: false, by_va: false,
@ -266,7 +266,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(12), track_nr: Some(12),
by_va: false, by_va: false,
@ -287,7 +287,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(13), track_nr: Some(13),
by_va: false, by_va: false,
@ -308,7 +308,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(14), track_nr: Some(14),
by_va: false, by_va: false,
@ -329,7 +329,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(15), track_nr: Some(15),
by_va: false, by_va: false,
@ -350,7 +350,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(16), track_nr: Some(16),
by_va: false, by_va: false,
@ -371,7 +371,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(17), track_nr: Some(17),
by_va: false, by_va: false,
@ -392,7 +392,7 @@ MusicAlbum(
id: "MPREb_nlBWQROfvjo", id: "MPREb_nlBWQROfvjo",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(18), track_nr: Some(18),
by_va: false, by_va: false,

View file

@ -35,7 +35,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,
@ -56,7 +56,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(2), track_nr: Some(2),
by_va: false, by_va: false,
@ -77,7 +77,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(3), track_nr: Some(3),
by_va: false, by_va: false,
@ -98,7 +98,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(4), track_nr: Some(4),
by_va: false, by_va: false,
@ -119,7 +119,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(5), track_nr: Some(5),
by_va: false, by_va: false,
@ -140,7 +140,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(6), track_nr: Some(6),
by_va: false, by_va: false,
@ -161,7 +161,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(7), track_nr: Some(7),
by_va: false, by_va: false,
@ -182,7 +182,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(8), track_nr: Some(8),
by_va: false, by_va: false,
@ -203,7 +203,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(9), track_nr: Some(9),
by_va: false, by_va: false,
@ -224,7 +224,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(10), track_nr: Some(10),
by_va: false, by_va: false,
@ -245,7 +245,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(11), track_nr: Some(11),
by_va: false, by_va: false,
@ -266,7 +266,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(12), track_nr: Some(12),
by_va: false, by_va: false,
@ -287,7 +287,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(13), track_nr: Some(13),
by_va: false, by_va: false,
@ -308,7 +308,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)", name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(14), track_nr: Some(14),
by_va: false, by_va: false,

View file

@ -35,7 +35,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,
@ -56,7 +56,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(2), track_nr: Some(2),
by_va: false, by_va: false,
@ -77,7 +77,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(3), track_nr: Some(3),
by_va: false, by_va: false,
@ -98,7 +98,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(4), track_nr: Some(4),
by_va: false, by_va: false,
@ -119,7 +119,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(5), track_nr: Some(5),
by_va: false, by_va: false,
@ -140,7 +140,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(6), track_nr: Some(6),
by_va: false, by_va: false,
@ -161,7 +161,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(7), track_nr: Some(7),
by_va: false, by_va: false,
@ -182,7 +182,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(8), track_nr: Some(8),
by_va: false, by_va: false,
@ -203,7 +203,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(9), track_nr: Some(9),
by_va: false, by_va: false,
@ -224,7 +224,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(10), track_nr: Some(10),
by_va: false, by_va: false,
@ -245,7 +245,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(11), track_nr: Some(11),
by_va: false, by_va: false,
@ -266,7 +266,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(12), track_nr: Some(12),
by_va: false, by_va: false,
@ -287,7 +287,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(13), track_nr: Some(13),
by_va: false, by_va: false,
@ -308,7 +308,7 @@ MusicAlbum(
id: "MPREb_cwzk8EUwypZ", id: "MPREb_cwzk8EUwypZ",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(14), track_nr: Some(14),
by_va: false, by_va: false,

View file

@ -43,7 +43,7 @@ MusicAlbum(
id: "MPREb_bHfHGoy7vuv", id: "MPREb_bHfHGoy7vuv",
name: "Der Himmel reißt auf", name: "Der Himmel reißt auf",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,

View file

@ -43,7 +43,7 @@ MusicAlbum(
id: "MPREb_bHfHGoy7vuv", id: "MPREb_bHfHGoy7vuv",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,

View file

@ -30,7 +30,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "13 Reasons Why (Season 3)", name: "13 Reasons Why (Season 3)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,
@ -51,7 +51,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "13 Reasons Why (Season 3)", name: "13 Reasons Why (Season 3)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(2), track_nr: Some(2),
by_va: false, by_va: false,
@ -72,7 +72,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "13 Reasons Why (Season 3)", name: "13 Reasons Why (Season 3)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(3), track_nr: Some(3),
by_va: false, by_va: false,
@ -93,7 +93,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "13 Reasons Why (Season 3)", name: "13 Reasons Why (Season 3)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(5), track_nr: Some(5),
by_va: false, by_va: false,
@ -114,7 +114,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "13 Reasons Why (Season 3)", name: "13 Reasons Why (Season 3)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(6), track_nr: Some(6),
by_va: false, by_va: false,
@ -135,7 +135,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "13 Reasons Why (Season 3)", name: "13 Reasons Why (Season 3)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(7), track_nr: Some(7),
by_va: false, by_va: false,
@ -156,7 +156,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "13 Reasons Why (Season 3)", name: "13 Reasons Why (Season 3)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(8), track_nr: Some(8),
by_va: false, by_va: false,
@ -177,7 +177,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "13 Reasons Why (Season 3)", name: "13 Reasons Why (Season 3)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(9), track_nr: Some(9),
by_va: false, by_va: false,
@ -198,7 +198,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "13 Reasons Why (Season 3)", name: "13 Reasons Why (Season 3)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(11), track_nr: Some(11),
by_va: false, by_va: false,
@ -219,7 +219,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "13 Reasons Why (Season 3)", name: "13 Reasons Why (Season 3)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(12), track_nr: Some(12),
by_va: false, by_va: false,
@ -240,7 +240,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "13 Reasons Why (Season 3)", name: "13 Reasons Why (Season 3)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(13), track_nr: Some(13),
by_va: false, by_va: false,
@ -261,7 +261,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "13 Reasons Why (Season 3)", name: "13 Reasons Why (Season 3)",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(18), track_nr: Some(18),
by_va: false, by_va: false,

View file

@ -30,7 +30,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,
@ -51,7 +51,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(2), track_nr: Some(2),
by_va: false, by_va: false,
@ -72,7 +72,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(3), track_nr: Some(3),
by_va: false, by_va: false,
@ -93,7 +93,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(5), track_nr: Some(5),
by_va: false, by_va: false,
@ -114,7 +114,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(6), track_nr: Some(6),
by_va: false, by_va: false,
@ -135,7 +135,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(7), track_nr: Some(7),
by_va: false, by_va: false,
@ -156,7 +156,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(8), track_nr: Some(8),
by_va: false, by_va: false,
@ -177,7 +177,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(9), track_nr: Some(9),
by_va: false, by_va: false,
@ -198,7 +198,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(11), track_nr: Some(11),
by_va: false, by_va: false,
@ -219,7 +219,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(12), track_nr: Some(12),
by_va: false, by_va: false,
@ -240,7 +240,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(13), track_nr: Some(13),
by_va: false, by_va: false,
@ -261,7 +261,7 @@ MusicAlbum(
id: "MPREb_AzuWg8qAVVl", id: "MPREb_AzuWg8qAVVl",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(18), track_nr: Some(18),
by_va: false, by_va: false,

View file

@ -30,7 +30,7 @@ MusicAlbum(
id: "MPREb_8QkDeEIawvX", id: "MPREb_8QkDeEIawvX",
name: "Queendom2 FINAL", name: "Queendom2 FINAL",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,
@ -51,7 +51,7 @@ MusicAlbum(
id: "MPREb_8QkDeEIawvX", id: "MPREb_8QkDeEIawvX",
name: "Queendom2 FINAL", name: "Queendom2 FINAL",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(2), track_nr: Some(2),
by_va: false, by_va: false,
@ -72,7 +72,7 @@ MusicAlbum(
id: "MPREb_8QkDeEIawvX", id: "MPREb_8QkDeEIawvX",
name: "Queendom2 FINAL", name: "Queendom2 FINAL",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(3), track_nr: Some(3),
by_va: false, by_va: false,
@ -93,7 +93,7 @@ MusicAlbum(
id: "MPREb_8QkDeEIawvX", id: "MPREb_8QkDeEIawvX",
name: "Queendom2 FINAL", name: "Queendom2 FINAL",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(4), track_nr: Some(4),
by_va: false, by_va: false,
@ -114,7 +114,7 @@ MusicAlbum(
id: "MPREb_8QkDeEIawvX", id: "MPREb_8QkDeEIawvX",
name: "Queendom2 FINAL", name: "Queendom2 FINAL",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(5), track_nr: Some(5),
by_va: false, by_va: false,
@ -135,7 +135,7 @@ MusicAlbum(
id: "MPREb_8QkDeEIawvX", id: "MPREb_8QkDeEIawvX",
name: "Queendom2 FINAL", name: "Queendom2 FINAL",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(6), track_nr: Some(6),
by_va: false, by_va: false,

View file

@ -1,6 +1,5 @@
--- ---
source: tests/youtube.rs source: tests/youtube.rs
assertion_line: 1396
expression: album expression: album
--- ---
MusicAlbum( MusicAlbum(
@ -31,7 +30,7 @@ MusicAlbum(
id: "MPREb_8QkDeEIawvX", id: "MPREb_8QkDeEIawvX",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,
@ -52,7 +51,7 @@ MusicAlbum(
id: "MPREb_8QkDeEIawvX", id: "MPREb_8QkDeEIawvX",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(2), track_nr: Some(2),
by_va: false, by_va: false,
@ -73,7 +72,7 @@ MusicAlbum(
id: "MPREb_8QkDeEIawvX", id: "MPREb_8QkDeEIawvX",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(3), track_nr: Some(3),
by_va: false, by_va: false,
@ -94,7 +93,7 @@ MusicAlbum(
id: "MPREb_8QkDeEIawvX", id: "MPREb_8QkDeEIawvX",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(4), track_nr: Some(4),
by_va: false, by_va: false,
@ -115,7 +114,7 @@ MusicAlbum(
id: "MPREb_8QkDeEIawvX", id: "MPREb_8QkDeEIawvX",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(5), track_nr: Some(5),
by_va: false, by_va: false,
@ -136,7 +135,7 @@ MusicAlbum(
id: "MPREb_8QkDeEIawvX", id: "MPREb_8QkDeEIawvX",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(6), track_nr: Some(6),
by_va: false, by_va: false,

View file

@ -1,6 +1,5 @@
--- ---
source: tests/youtube.rs source: tests/youtube.rs
assertion_line: 1391
expression: album expression: album
--- ---
MusicAlbum( MusicAlbum(
@ -44,7 +43,7 @@ MusicAlbum(
id: "MPREb_h8ltx5oKvyY", id: "MPREb_h8ltx5oKvyY",
name: "Pedha Rasi Peddamma Katha", name: "Pedha Rasi Peddamma Katha",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,
@ -65,7 +64,7 @@ MusicAlbum(
id: "MPREb_h8ltx5oKvyY", id: "MPREb_h8ltx5oKvyY",
name: "Pedha Rasi Peddamma Katha", name: "Pedha Rasi Peddamma Katha",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(2), track_nr: Some(2),
by_va: false, by_va: false,
@ -86,7 +85,7 @@ MusicAlbum(
id: "MPREb_h8ltx5oKvyY", id: "MPREb_h8ltx5oKvyY",
name: "Pedha Rasi Peddamma Katha", name: "Pedha Rasi Peddamma Katha",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(3), track_nr: Some(3),
by_va: false, by_va: false,
@ -107,7 +106,7 @@ MusicAlbum(
id: "MPREb_h8ltx5oKvyY", id: "MPREb_h8ltx5oKvyY",
name: "Pedha Rasi Peddamma Katha", name: "Pedha Rasi Peddamma Katha",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(4), track_nr: Some(4),
by_va: false, by_va: false,

View file

@ -43,7 +43,7 @@ MusicAlbum(
id: "MPREb_h8ltx5oKvyY", id: "MPREb_h8ltx5oKvyY",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(1), track_nr: Some(1),
by_va: false, by_va: false,
@ -64,7 +64,7 @@ MusicAlbum(
id: "MPREb_h8ltx5oKvyY", id: "MPREb_h8ltx5oKvyY",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(2), track_nr: Some(2),
by_va: false, by_va: false,
@ -85,7 +85,7 @@ MusicAlbum(
id: "MPREb_h8ltx5oKvyY", id: "MPREb_h8ltx5oKvyY",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(3), track_nr: Some(3),
by_va: false, by_va: false,
@ -106,7 +106,7 @@ MusicAlbum(
id: "MPREb_h8ltx5oKvyY", id: "MPREb_h8ltx5oKvyY",
name: "[name]", name: "[name]",
)), )),
view_count: None, view_count: "[view_count]",
is_video: false, is_video: false,
track_nr: Some(4), track_nr: Some(4),
by_va: false, by_va: false,

View file

@ -1429,7 +1429,7 @@ fn music_album(#[case] name: &str, #[case] id: &str, rp: RustyPipe, unlocalized:
if unlocalized { if unlocalized {
insta::assert_ron_snapshot!(format!("music_album_{name}"), album, insta::assert_ron_snapshot!(format!("music_album_{name}"), album,
{".cover" => "[cover]"} {".cover" => "[cover]", ".tracks[].view_count" => "[view_count]"}
); );
} else { } else {
insta::assert_ron_snapshot!(format!("music_album_{name}_intl"), album, 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]", ".description" => "[description]",
".artists[].name" => "[name]", ".artists[].name" => "[name]",
".tracks[].name" => "[name]", ".tracks[].name" => "[name]",
".tracks[].view_count" => "[view_count]",
".tracks[].album.name" => "[name]", ".tracks[].album.name" => "[name]",
".tracks[].artists[].name" => "[name]", ".tracks[].artists[].name" => "[name]",
".variants[].artists[].name" => "[name]", ".variants[].artists[].name" => "[name]",
@ -1730,16 +1731,11 @@ async fn music_search_episode() {
let rp = RustyPipe::builder().strict().build().unwrap(); let rp = RustyPipe::builder().strict().build().unwrap();
let res = rp let res = rp
.query() .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 .await
.unwrap(); .unwrap();
let track = &res let track = &res.tracks.iter().find(|a| a.id == "Zq_-LDy7AgE").unwrap();
.items
.items
.iter()
.find(|a| a.id == "Zq_-LDy7AgE")
.unwrap();
assert_eq!( assert_eq!(
track.name, track.name,