diff --git a/src/client/response/music_item.rs b/src/client/response/music_item.rs index d552771..cded154 100644 --- a/src/client/response/music_item.rs +++ b/src/client/response/music_item.rs @@ -345,9 +345,8 @@ impl MusicListMapper { } } - fn map_item(&mut self, item: MusicResponseItem) -> Result, String> { + fn map_item(&mut self, item: MusicResponseItem) -> Result { match item { - // List item MusicResponseItem::MusicResponsiveListItemRenderer(item) => { let mut columns = item.flex_columns.into_iter(); let title = columns.next().map(|col| col.renderer.text.to_string()); @@ -357,27 +356,19 @@ impl MusicListMapper { match item.navigation_endpoint { // Artist / Album / Playlist Some(ne) => { - let mut subtitle_parts = c2 - .ok_or_else(|| "could not get subtitle".to_owned())? - .renderer - .text - .split(util::DOT_SEPARATOR) - .into_iter(); - - let (page_type, id) = match ne.music_page() { - Some(music_page) => music_page, - None => { - // Ignore radio items - if subtitle_parts.len() == 1 { - return Ok(None); - } - return Err("invalid navigation endpoint".to_string()); - } - }; + let (page_type, id) = ne + .music_page() + .ok_or_else(|| "could not get navigation endpoint".to_owned())?; let title = title.ok_or_else(|| format!("track {}: could not get title", id))?; + let mut subtitle_parts = c2 + .ok_or_else(|| format!("item {}: could not get subtitle", id))? + .renderer + .text + .split(util::DOT_SEPARATOR) + .into_iter(); let subtitle_p1 = subtitle_parts.next(); let subtitle_p2 = subtitle_parts.next(); let subtitle_p3 = subtitle_parts.next(); @@ -394,7 +385,7 @@ impl MusicListMapper { avatar: item.thumbnail.into(), subscriber_count, })); - Ok(Some(MusicEntityType::Artist)) + Ok(MusicEntityType::Artist) } PageType::Album => { let album_type = subtitle_p1 @@ -415,7 +406,7 @@ impl MusicListMapper { year, by_va, })); - Ok(Some(MusicEntityType::Album)) + Ok(MusicEntityType::Album) } PageType::Playlist => { // Part 1 may be the "Playlist" label @@ -442,11 +433,11 @@ impl MusicListMapper { track_count, from_ytm, })); - Ok(Some(MusicEntityType::Playlist)) + Ok(MusicEntityType::Playlist) } PageType::Channel => { // There may be broken YT channels from the artist search. They can be skipped. - Ok(None) + Ok(MusicEntityType::Artist) } } } @@ -562,11 +553,10 @@ impl MusicListMapper { is_video, track_nr, })); - Ok(Some(MusicEntityType::Track)) + Ok(MusicEntityType::Track) } } } - // Tile MusicResponseItem::MusicTwoRowItemRenderer(item) => { let mut subtitle_parts = item.subtitle.split(util::DOT_SEPARATOR).into_iter(); let subtitle_p1 = subtitle_parts.next(); @@ -591,7 +581,7 @@ impl MusicListMapper { is_video: true, track_nr: None, })); - Ok(Some(MusicEntityType::Track)) + Ok(MusicEntityType::Track) } // Artist / Album / Playlist None => { @@ -646,7 +636,7 @@ impl MusicListMapper { year, by_va, })); - Ok(Some(MusicEntityType::Album)) + Ok(MusicEntityType::Album) } PageType::Playlist => { let from_ytm = subtitle_p2 @@ -667,7 +657,7 @@ impl MusicListMapper { track_count, from_ytm, })); - Ok(Some(MusicEntityType::Playlist)) + Ok(MusicEntityType::Playlist) } PageType::Artist => { let subscriber_count = subtitle_p1.and_then(|p| { @@ -680,7 +670,7 @@ impl MusicListMapper { avatar: item.thumbnail_renderer.into(), subscriber_count, })); - Ok(Some(MusicEntityType::Artist)) + Ok(MusicEntityType::Artist) } PageType::Channel => { Err(format!("channel items unsupported. id: {}", id)) @@ -701,12 +691,7 @@ impl MusicListMapper { res.c .into_iter() .for_each(|item| match self.map_item(item) { - Ok(Some(et)) => { - if etype.is_none() { - etype = Some(et); - } - } - Ok(None) => {} + Ok(t) => etype = Some(t), Err(e) => self.warnings.push(e), }); etype diff --git a/src/client/url_resolver.rs b/src/client/url_resolver.rs index 4446eb1..9ced71a 100644 --- a/src/client/url_resolver.rs +++ b/src/client/url_resolver.rs @@ -72,19 +72,6 @@ impl RustyPipeQuery { Ok(UrlTarget::Playlist { id }) } } - // Album or channel - Some("browse") => match path_split.next() { - Some(id) => { - if util::CHANNEL_ID_REGEX.is_match(id).unwrap_or_default() { - Ok(UrlTarget::Channel { id: id.to_owned() }) - } else if util::ALBUM_ID_REGEX.is_match(id).unwrap_or_default() { - Ok(UrlTarget::Album { id: id.to_owned() }) - } else { - Err(Error::Other("invalid url: no browse id".into())) - } - } - None => Err(Error::Other("invalid url: invalid browse id".into())), - }, // Channel vanity URL or youtu.be shortlink Some(mut id) => { if id == "c" || id == "user" { diff --git a/src/model/mod.rs b/src/model/mod.rs index a77328e..b5ed216 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -68,7 +68,9 @@ impl UrlTarget { format!("{}/playlist?list={}", yt_host, id) } UrlTarget::Album { id } => { - format!("https://music.youtube.com/browse/{}", id) + // The official album URLs use the playlist ID + // This looks weird, but it works + format!("{}/channel/{}", yt_host, id) } } } diff --git a/tests/youtube.rs b/tests/youtube.rs index efce4e2..b119402 100644 --- a/tests/youtube.rs +++ b/tests/youtube.rs @@ -1189,8 +1189,6 @@ async fn search_suggestion_empty() { // Both a video ID and a channel name + video time param => returns video #[case("https://piped.mha.fi/dQw4w9WgXcQ?t=0", UrlTarget::Video {id: "dQw4w9WgXcQ".to_owned(), start_time: 0})] #[case("https://music.youtube.com/playlist?list=OLAK5uy_k0yFrZlFRgCf3rLPza-lkRmCrtLPbK9pE", UrlTarget::Album {id: "MPREb_GyH43gCvdM5".to_owned()})] -#[case("https://music.youtube.com/browse/MPREb_GyH43gCvdM5", UrlTarget::Album {id: "MPREb_GyH43gCvdM5".to_owned()})] -#[case("https://music.youtube.com/browse/UC5I2hjZYiW9gZPVkvzM8_Cw", UrlTarget::Channel {id: "UC5I2hjZYiW9gZPVkvzM8_Cw".to_owned()})] #[tokio::test] async fn resolve_url(#[case] url: &str, #[case] expect: UrlTarget) { let rp = RustyPipe::builder().strict().build(); @@ -1748,13 +1746,6 @@ async fn music_search_playlists_community() { assert!(!playlist.from_ytm); } -/// The YouTube Music search sometimes shows genre radio items. They should be skipped. -#[tokio::test] -async fn music_search_genre_radio() { - let rp = RustyPipe::builder().strict().build(); - rp.query().music_search("pop radio").await.unwrap(); -} - //#TESTUTIL /// Assert equality within 10% margin