Compare commits

..

No commits in common. "9b6c952fd3eaf0771636b137cd0d82cec3ebb08e" and "220e335314329a9994045dc2755c58b943e3ec1f" have entirely different histories.

4 changed files with 23 additions and 58 deletions

View file

@ -345,9 +345,8 @@ impl MusicListMapper {
}
}
fn map_item(&mut self, item: MusicResponseItem) -> Result<Option<MusicEntityType>, String> {
fn map_item(&mut self, item: MusicResponseItem) -> Result<MusicEntityType, String> {
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

View file

@ -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" {

View file

@ -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)
}
}
}

View file

@ -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