diff --git a/src/client/url_resolver.rs b/src/client/url_resolver.rs index 9ced71a..4446eb1 100644 --- a/src/client/url_resolver.rs +++ b/src/client/url_resolver.rs @@ -72,6 +72,19 @@ 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 b5ed216..a77328e 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -68,9 +68,7 @@ impl UrlTarget { format!("{}/playlist?list={}", yt_host, id) } UrlTarget::Album { id } => { - // The official album URLs use the playlist ID - // This looks weird, but it works - format!("{}/channel/{}", yt_host, id) + format!("https://music.youtube.com/browse/{}", id) } } } diff --git a/tests/youtube.rs b/tests/youtube.rs index 365fa9a..efce4e2 100644 --- a/tests/youtube.rs +++ b/tests/youtube.rs @@ -1189,6 +1189,8 @@ 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();