Compare commits
	
		
			No commits in common. "9b6c952fd3eaf0771636b137cd0d82cec3ebb08e" and "220e335314329a9994045dc2755c58b943e3ec1f" have entirely different histories.
		
	
	
		
			
				9b6c952fd3
			
			...
			
				220e335314
			
		
	
		
					 4 changed files with 23 additions and 58 deletions
				
			
		| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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" {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue