feat: add support for new artist discography page

This commit is contained in:
ThetaDev 2023-05-14 03:05:24 +02:00
parent bf80db8a9a
commit c8e2d342c6
25 changed files with 73368 additions and 91615 deletions

View file

@ -1,6 +1,5 @@
use std::{borrow::Cow, rc::Rc};
use std::borrow::Cow;
use futures::{stream, StreamExt};
use once_cell::sync::Lazy;
use regex::Regex;
@ -13,7 +12,7 @@ use crate::{
use super::{
response::{self, music_item::MusicListMapper, url_endpoint::PageType},
ClientType, MapResponse, QBrowse, QBrowseParams, RustyPipeQuery,
ClientType, MapResponse, QBrowse, RustyPipeQuery,
};
impl RustyPipeQuery {
@ -26,99 +25,57 @@ impl RustyPipeQuery {
all_albums: bool,
) -> Result<MusicArtist, Error> {
let artist_id = artist_id.as_ref();
let visitor_data = if all_albums {
Some(self.get_visitor_data().await?)
} else {
None
};
let res = self._music_artist(artist_id, visitor_data.as_deref()).await;
let res = self._music_artist(artist_id, all_albums).await;
if let Err(Error::Extraction(ExtractionError::Redirect(id))) = res {
log::debug!("music artist {} redirects to {}", artist_id, &id);
self._music_artist(&id, visitor_data.as_deref()).await
self._music_artist(&id, all_albums).await
} else {
res
}
}
async fn _music_artist(
&self,
artist_id: &str,
all_albums_vdata: Option<&str>,
) -> Result<MusicArtist, Error> {
match all_albums_vdata {
Some(visitor_data) => {
let context = self
.get_context(ClientType::DesktopMusic, true, Some(visitor_data))
.await;
let request_body = QBrowse {
context,
browse_id: artist_id,
};
async fn _music_artist(&self, artist_id: &str, all_albums: bool) -> Result<MusicArtist, Error> {
let context = self.get_context(ClientType::DesktopMusic, true, None).await;
let request_body = QBrowse {
context,
browse_id: artist_id,
};
let (mut artist, album_page_params) = self
.execute_request::<response::MusicArtist, _, _>(
ClientType::DesktopMusic,
"music_artist",
artist_id,
"browse",
&request_body,
)
.await?;
let visitor_data = Rc::new(visitor_data);
let album_page_results = stream::iter(album_page_params)
.map(|params| {
let visitor_data = visitor_data.clone();
async move {
self.music_artist_album_page(artist_id, &params, &visitor_data)
.await
}
})
.buffer_unordered(2)
.collect::<Vec<_>>()
.await;
for res in album_page_results {
let mut res = res?;
artist.albums.append(&mut res);
}
Ok(artist)
}
None => {
let context = self.get_context(ClientType::DesktopMusic, true, None).await;
let request_body = QBrowse {
context,
browse_id: artist_id,
};
self.execute_request::<response::MusicArtist, _, _>(
if all_albums {
let (mut artist, can_fetch_more) = self
.execute_request::<response::MusicArtist, _, _>(
ClientType::DesktopMusic,
"music_artist",
artist_id,
"browse",
&request_body,
)
.await
.await?;
if can_fetch_more {
artist.albums = self.music_artist_albums(artist_id).await?;
}
Ok(artist)
} else {
self.execute_request::<response::MusicArtist, _, _>(
ClientType::DesktopMusic,
"music_artist",
artist_id,
"browse",
&request_body,
)
.await
}
}
async fn music_artist_album_page(
&self,
artist_id: &str,
params: &str,
visitor_data: &str,
) -> Result<Vec<AlbumItem>, Error> {
let context = self
.get_context(ClientType::DesktopMusic, true, Some(visitor_data))
.await;
let request_body = QBrowseParams {
/// Get a list of all albums of a YouTube Music artist
pub async fn music_artist_albums(&self, artist_id: &str) -> Result<Vec<AlbumItem>, Error> {
let context = self.get_context(ClientType::DesktopMusic, true, None).await;
let request_body = QBrowse {
context,
browse_id: artist_id,
params,
browse_id: &format!("{}{}", util::ARTIST_DISCOGRAPHY_PREFIX, artist_id),
};
self.execute_request::<response::MusicArtistAlbums, _, _>(
@ -147,13 +104,13 @@ impl MapResponse<MusicArtist> for response::MusicArtist {
}
}
impl MapResponse<(MusicArtist, Vec<String>)> for response::MusicArtist {
impl MapResponse<(MusicArtist, bool)> for response::MusicArtist {
fn map_response(
self,
id: &str,
lang: crate::param::Language,
_deobf: Option<&crate::deobfuscate::DeobfData>,
) -> Result<MapResult<(MusicArtist, Vec<String>)>, ExtractionError> {
) -> Result<MapResult<(MusicArtist, bool)>, ExtractionError> {
map_artist_page(self, id, lang, true)
}
}
@ -163,7 +120,7 @@ fn map_artist_page(
id: &str,
lang: crate::param::Language,
skip_extendables: bool,
) -> Result<MapResult<(MusicArtist, Vec<String>)>, ExtractionError> {
) -> Result<MapResult<(MusicArtist, bool)>, ExtractionError> {
// dbg!(&res);
let header = res.header.music_immersive_header_renderer;
@ -203,7 +160,7 @@ fn map_artist_page(
let mut tracks_playlist_id = None;
let mut videos_playlist_id = None;
let mut album_page_params = Vec::new();
let mut can_fetch_more = false;
for section in sections {
match section {
@ -242,6 +199,11 @@ fn map_artist_page(
videos_playlist_id = Some(bep.browse_id);
}
}
// Albums
PageType::ArtistDiscography => {
can_fetch_more = true;
extendable_albums = true;
}
// Albums or playlists
PageType::Artist => {
// Peek at the first item to determine type
@ -250,7 +212,7 @@ fn map_artist_page(
be.browse_endpoint_context_supported_configs.as_ref().map(|config| {
config.browse_endpoint_context_music_config.page_type
})}) {
album_page_params.push(bep.params);
can_fetch_more = true;
extendable_albums = true;
}
}
@ -318,7 +280,7 @@ fn map_artist_page(
videos_playlist_id,
radio_id,
},
album_page_params,
can_fetch_more,
),
warnings: mapped.warnings,
})
@ -333,6 +295,10 @@ impl MapResponse<Vec<AlbumItem>> for response::MusicArtistAlbums {
) -> Result<MapResult<Vec<AlbumItem>>, ExtractionError> {
// dbg!(&self);
let Some(header) = self.header else {
return Err(ExtractionError::NotFound { id: id.into(), msg: "no header".into() });
};
let grids = self
.contents
.single_column_browse_results_renderer
@ -349,7 +315,7 @@ impl MapResponse<Vec<AlbumItem>> for response::MusicArtistAlbums {
lang,
ArtistId {
id: Some(id.to_owned()),
name: self.header.music_header_renderer.title,
name: header.music_header_renderer.title,
},
);
@ -380,7 +346,6 @@ mod tests {
#[rstest]
#[case::default("default", "UClmXPfaYhXOYsNn_QUyheWQ")]
#[case::no_more_albums("no_more_albums", "UC_vmjW5e1xEHhYjY2a0kK1A")]
#[case::only_singles("only_singles", "UCfwCE5VhPMGxNPFxtVv7lRw")]
#[case::no_artist("no_artist", "UCh8gHdtzO2tXd593_bjErWg")]
#[case::only_more_singles("only_more_singles", "UC0aXrjVxG5pZr99v77wZdPQ")]
@ -388,30 +353,27 @@ mod tests {
let json_path = path!(*TESTFILES / "music_artist" / format!("artist_{name}.json"));
let json_file = File::open(json_path).unwrap();
let mut album_page_paths = Vec::new();
for i in 1..=2 {
let json_path = path!(*TESTFILES / "music_artist" / format!("artist_{name}_{i}.json"));
if !json_path.exists() {
break;
}
album_page_paths.push(json_path);
let mut album_page_path = None;
let json_path = path!(*TESTFILES / "music_artist" / format!("artist_{name}_1.json"));
if json_path.exists() {
album_page_path = Some(json_path);
}
let resp: response::MusicArtist =
serde_json::from_reader(BufReader::new(json_file)).unwrap();
let map_res: MapResult<(MusicArtist, Vec<String>)> =
let map_res: MapResult<(MusicArtist, bool)> =
resp.map_response(id, Language::En, None).unwrap();
let (mut artist, album_page_params) = map_res.c;
let (mut artist, can_fetch_more) = map_res.c;
assert!(
map_res.warnings.is_empty(),
"deserialization/mapping warnings: {:?}",
map_res.warnings
);
assert_eq!(album_page_params.len(), album_page_paths.len());
assert_eq!(can_fetch_more, album_page_path.is_some());
for json_path in album_page_paths {
let json_file = File::open(json_path).unwrap();
if let Some(album_page_path) = album_page_path {
let json_file = File::open(album_page_path).unwrap();
let resp: response::MusicArtistAlbums =
serde_json::from_reader(BufReader::new(json_file)).unwrap();
let mut map_res: MapResult<Vec<AlbumItem>> =

View file

@ -73,9 +73,12 @@ pub(crate) struct ShareEntityEndpoint {
}
/// Response model for YouTube Music artist album page
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct MusicArtistAlbums {
pub header: SimpleHeader,
#[serde(default)]
#[serde_as(as = "DefaultOnError")]
pub header: Option<SimpleHeader>,
pub contents: SingleColumnBrowseResult<Tab<SectionList<Grid>>>,
}

View file

@ -1,7 +1,7 @@
use serde::Deserialize;
use serde_with::{serde_as, DefaultOnError};
use crate::model::UrlTarget;
use crate::{model::UrlTarget, util};
/// navigation/resolve_url response model
#[derive(Debug, Deserialize)]
@ -152,6 +152,8 @@ pub(crate) enum PageType {
alias = "MUSIC_PAGE_TYPE_AUDIOBOOK_ARTIST"
)]
Artist,
#[serde(rename = "MUSIC_PAGE_TYPE_ARTIST_DISCOGRAPHY")]
ArtistDiscography,
#[serde(rename = "MUSIC_PAGE_TYPE_ALBUM", alias = "MUSIC_PAGE_TYPE_AUDIOBOOK")]
Album,
#[serde(
@ -169,6 +171,9 @@ impl PageType {
pub(crate) fn to_url_target(self, id: String) -> Option<UrlTarget> {
match self {
PageType::Artist | PageType::Channel => Some(UrlTarget::Channel { id }),
PageType::ArtistDiscography => id
.strip_prefix(util::ARTIST_DISCOGRAPHY_PREFIX)
.map(|id| UrlTarget::Channel { id: id.to_owned() }),
PageType::Album => Some(UrlTarget::Album { id }),
PageType::Playlist => Some(UrlTarget::Playlist { id }),
PageType::Unknown => None,
@ -192,7 +197,7 @@ impl From<PageType> for MusicPageType {
PageType::Artist => MusicPageType::Artist,
PageType::Album => MusicPageType::Album,
PageType::Playlist => MusicPageType::Playlist,
PageType::Channel => MusicPageType::None,
PageType::Channel | PageType::ArtistDiscography => MusicPageType::None,
PageType::Unknown => MusicPageType::Unknown,
}
}

View file

@ -7,27 +7,27 @@ MusicArtist(
name: "Doobydobap",
header_image: [
Thumbnail(
url: "https://yt3.ggpht.com/BvnAqgiursrXpmS9AgDLtkOSTQfOG_Dqn0KzY5hcwO9XrHTEQTVgaflI913f9KRp7d0U2qBp=w540-h225-p-l90-rj",
url: "https://yt3.googleusercontent.com/BvnAqgiursrXpmS9AgDLtkOSTQfOG_Dqn0KzY5hcwO9XrHTEQTVgaflI913f9KRp7d0U2qBp=w540-h225-p-l90-rj",
width: 540,
height: 225,
),
Thumbnail(
url: "https://yt3.ggpht.com/BvnAqgiursrXpmS9AgDLtkOSTQfOG_Dqn0KzY5hcwO9XrHTEQTVgaflI913f9KRp7d0U2qBp=w816-h340-p-l90-rj",
url: "https://yt3.googleusercontent.com/BvnAqgiursrXpmS9AgDLtkOSTQfOG_Dqn0KzY5hcwO9XrHTEQTVgaflI913f9KRp7d0U2qBp=w816-h340-p-l90-rj",
width: 816,
height: 340,
),
Thumbnail(
url: "https://yt3.ggpht.com/BvnAqgiursrXpmS9AgDLtkOSTQfOG_Dqn0KzY5hcwO9XrHTEQTVgaflI913f9KRp7d0U2qBp=w1440-h600-p-l90-rj",
url: "https://yt3.googleusercontent.com/BvnAqgiursrXpmS9AgDLtkOSTQfOG_Dqn0KzY5hcwO9XrHTEQTVgaflI913f9KRp7d0U2qBp=w1440-h600-p-l90-rj",
width: 1440,
height: 600,
),
Thumbnail(
url: "https://yt3.ggpht.com/BvnAqgiursrXpmS9AgDLtkOSTQfOG_Dqn0KzY5hcwO9XrHTEQTVgaflI913f9KRp7d0U2qBp=w1920-h800-p-l90-rj",
url: "https://yt3.googleusercontent.com/BvnAqgiursrXpmS9AgDLtkOSTQfOG_Dqn0KzY5hcwO9XrHTEQTVgaflI913f9KRp7d0U2qBp=w1920-h800-p-l90-rj",
width: 1920,
height: 800,
),
Thumbnail(
url: "https://yt3.ggpht.com/BvnAqgiursrXpmS9AgDLtkOSTQfOG_Dqn0KzY5hcwO9XrHTEQTVgaflI913f9KRp7d0U2qBp=w2436-h1015-p-l90-rj",
url: "https://yt3.googleusercontent.com/BvnAqgiursrXpmS9AgDLtkOSTQfOG_Dqn0KzY5hcwO9XrHTEQTVgaflI913f9KRp7d0U2qBp=w2436-h1015-p-l90-rj",
width: 2436,
height: 1015,
),
@ -39,16 +39,82 @@ MusicArtist(
albums: [],
playlists: [
MusicPlaylistItem(
id: "PLwkM1QxaP342hjju64dtqG5wKqx2hNgjr",
name: "After Hours & Doob Gourmand",
id: "PLwkM1QxaP341MxmqdPrw_3ffjqLofZXOj",
name: "🌟 best of... doobyvlog",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/38Gd6TdmNVs/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3lbh9XVK3zCkDHMxgOnDokkqE3kwg",
url: "https://i.ytimg.com/vi/0onVbAuBGWI/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3mUFLfu6rAMobj83G1P7wFtrbRuqg",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/38Gd6TdmNVs/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3k4lcTkiScLDPpmxBPfEub3xft7iQ",
url: "https://i.ytimg.com/vi/0onVbAuBGWI/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3nU_px4mXhplu4zYKhseBKdYUJh0g",
width: 800,
height: 450,
),
],
channel: Some(ChannelId(
id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap",
)),
track_count: None,
from_ytm: false,
),
MusicPlaylistItem(
id: "PLwkM1QxaP343YqeP6g5VPGsgJdO1_SV4I",
name: "love & relationships",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/PXsK9-CFoH4/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3kaG7qOCwpNsPCgSCEoxkeeFW2SUg",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/PXsK9-CFoH4/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3ktLL_rN7UujT_48Np6bSaK0Un1pA",
width: 800,
height: 450,
),
],
channel: Some(ChannelId(
id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap",
)),
track_count: None,
from_ytm: false,
),
MusicPlaylistItem(
id: "PLwkM1QxaP340xbkARIPpiD1aHuzJVuZUg",
name: "🏠 opening a restaurant",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/7ebSD1ezP-M/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3n-xqleEhGCII_Qfza-POZT66c3Tg",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/7ebSD1ezP-M/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3ntBQTkAyTuRhx6HNxBEJtc4RpgWQ",
width: 800,
height: 450,
),
],
channel: Some(ChannelId(
id: "UCh8gHdtzO2tXd593_bjErWg",
name: "Doobydobap",
)),
track_count: None,
from_ytm: false,
),
MusicPlaylistItem(
id: "PLwkM1QxaP342hjju64dtqG5wKqx2hNgjr",
name: "🍽\u{fe0f} after hours & doob gourmand",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/2tg0MNZwUSQ/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3kqvMhctjtDMJ4u84hAp2h5UXT-QA",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/2tg0MNZwUSQ/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3nxfWmvCHZgvWYolc_mlJEWD1TkZQ",
width: 800,
height: 450,
),
@ -62,15 +128,15 @@ MusicArtist(
),
MusicPlaylistItem(
id: "PLwkM1QxaP342v1hhoB3XLiruSQOzmdmBt",
name: "doobyvlog",
name: "📹 doobyvlog",
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/CN-u8_2ixOU/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3kCdNu1hqEubXzcLcci_xhWSI9q-Q",
url: "https://i.ytimg.com/vi/7ebSD1ezP-M/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3n-xqleEhGCII_Qfza-POZT66c3Tg",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/CN-u8_2ixOU/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3lN1-hs5ItQ_DJXBp-aUQQ-DLjMVg",
url: "https://i.ytimg.com/vi/7ebSD1ezP-M/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3ntBQTkAyTuRhx6HNxBEJtc4RpgWQ",
width: 800,
height: 450,
),

View file

@ -34,7 +34,7 @@ MusicArtist(
],
description: Some("Choi Jin-ri, better known by her stage name Sulli, was a South Korean actress and singer. She first made her debut as a child actress, appearing as a supporting cast member on the SBS historical drama Ballad of Seodong. Following this, she earned a number of guest roles, appearing in the television series Love Needs a Miracle and Drama City, and the film Vacation. She then subsequently appeared in the independent films Punch Lady and BA:BO, the former being her first time cast in a substantial dramatic role.\nAfter signing a record deal with SM Entertainment, Sulli rose to prominence as a member of the girl group f(x) formed in 2009. The group achieved both critical and commercial success, with four Korean number-one singles and international recognition after becoming the first K-pop act to perform at SXSW. Concurrently with her music career, Sulli returned to acting by starring in the SBS romantic comedy series, To the Beautiful You, a Korean adaptation of the shōjo manga Hana-Kimi where her performance was positively received and earned her two SBS Drama Awards and a nomination at the 49th Paeksang Arts Awards.\n\nFrom Wikipedia (https://en.wikipedia.org/wiki/Sulli) under Creative Commons Attribution CC-BY-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0/legalcode)"),
wikipedia_url: Some("https://en.wikipedia.org/wiki/Sulli"),
subscriber_count: Some(74400),
subscriber_count: Some(80800),
tracks: [
TrackItem(
id: "BGcUVJXViqQ",
@ -156,7 +156,7 @@ MusicArtist(
],
artist_id: Some("UCfwCE5VhPMGxNPFxtVv7lRw"),
album: None,
view_count: Some(19000000),
view_count: Some(20000000),
is_video: true,
track_nr: None,
by_va: false,
@ -185,7 +185,7 @@ MusicArtist(
],
artist_id: Some("UClGBYGUZmpzUaHgeb9gOBww"),
album: None,
view_count: Some(206000),
view_count: Some(211000),
is_video: true,
track_nr: None,
by_va: false,
@ -209,7 +209,7 @@ MusicArtist(
artists: [
ArtistId(
id: Some("UCfaO3pZL5XOr8BvNZkrKeVA"),
name: "iKissesByMaki",
name: "ramuditas",
),
],
artist_id: Some("UCfaO3pZL5XOr8BvNZkrKeVA"),
@ -243,152 +243,36 @@ MusicArtist(
],
artist_id: Some("UCgVWicpO5Jn3VfxqgIU6cpA"),
album: None,
view_count: Some(3600),
view_count: Some(15000),
is_video: true,
track_nr: None,
by_va: false,
),
TrackItem(
id: "94q_2Zsq2os",
name: "Sulli - On The Moon (sped up)",
id: "N217ZuMQnfY",
name: "음악캠프 - Dorothy - Picnic, 도로시 - 소풍, Music Camp 20040417",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/94q_2Zsq2os/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3kf1HgaRExXYDCNGVqeqe-TKxJ2aQ",
url: "https://i.ytimg.com/vi/N217ZuMQnfY/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3kBmlxLhMmsi9VLaMVosj6Ie4aAyw",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/94q_2Zsq2os/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3mbke4_Zc9nkj4NvVMlYpA7aSrkzQ",
url: "https://i.ytimg.com/vi/N217ZuMQnfY/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3mHxVVuDdgr8HxoJjgtvYwaxtVbzA",
width: 800,
height: 450,
),
],
artists: [
ArtistId(
id: Some("UCgVWicpO5Jn3VfxqgIU6cpA"),
name: "Jpn Sch",
id: Some("UCe52oeb7Xv_KaJsEzcKXJJg"),
name: "MBCkpop",
),
],
artist_id: Some("UCgVWicpO5Jn3VfxqgIU6cpA"),
artist_id: Some("UCe52oeb7Xv_KaJsEzcKXJJg"),
album: None,
view_count: Some(2900),
is_video: true,
track_nr: None,
by_va: false,
),
TrackItem(
id: "fBce3VihpIQ",
name: "sulli - goblin (sped up)",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/fBce3VihpIQ/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3nVd442JLu3mxKbipFglRgRift5cA",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/fBce3VihpIQ/sddefault.jpg?sqp=-oaymwEWCKoDEPABIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3nxWW7AAdAGbEGuDXOVFgFaLW3XbA",
width: 426,
height: 240,
),
],
artists: [
ArtistId(
id: Some("UCwZLmi2q2ReEJvt-8bgVwwg"),
name: "e p i l o g u e",
),
],
artist_id: Some("UCwZLmi2q2ReEJvt-8bgVwwg"),
album: None,
view_count: Some(11000),
is_video: true,
track_nr: None,
by_va: false,
),
TrackItem(
id: "0-HXdJc-zDQ",
name: "Sulli - Dorothy (sped up)",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/0-HXdJc-zDQ/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3l_Cj2X9UftBVID7UoJ708Dd6UGUA",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/0-HXdJc-zDQ/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3n2XskAJVpE4eQ3okG60Lf6dPPFqQ",
width: 800,
height: 450,
),
],
artists: [
ArtistId(
id: Some("UCgVWicpO5Jn3VfxqgIU6cpA"),
name: "Jpn Sch",
),
],
artist_id: Some("UCgVWicpO5Jn3VfxqgIU6cpA"),
album: None,
view_count: Some(2200),
is_video: true,
track_nr: None,
by_va: false,
),
TrackItem(
id: "Bae4Fv7GlMY",
name: "Sulli\'nin goblin M/V\'sindeki korkutucu detaylar!😱💥",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/Bae4Fv7GlMY/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3nXyTG8uM24-GkmqaUvx2gQqjJO-g",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Bae4Fv7GlMY/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3m2mg6beaz7Lg-zRFebE5jHHbiiJw",
width: 800,
height: 450,
),
],
artists: [
ArtistId(
id: Some("UCuoa8Ie9kA1-AKIjNhsIGxQ"),
name: "JenDia` #Emrlsoolarbenim",
),
],
artist_id: Some("UCuoa8Ie9kA1-AKIjNhsIGxQ"),
album: None,
view_count: Some(6900),
is_video: true,
track_nr: None,
by_va: false,
),
TrackItem(
id: "Rq_JkcROjsI",
name: "𝗀𝗈𝖻𝗅𝗂𝗇 // 𝗌𝗎𝗅𝗅𝗂𝗌𝗅𝗈𝗐𝖾𝖽 + 𝗋𝖾𝗏𝖾𝗋𝖻",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/Rq_JkcROjsI/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3ntvxfQE8ErdwpxdksKA_Gtcza3Rw",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Rq_JkcROjsI/sddefault.jpg?sqp=-oaymwEWCKoDEPABIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3kyJ7LFsJWz4Ac44uL8GPxsRldOng",
width: 426,
height: 240,
),
],
artists: [
ArtistId(
id: Some("UCMPqKiPdiSoi8eCW5Dou1IQ"),
name: "ᴜᴋɪʏᴏ",
),
],
artist_id: Some("UCMPqKiPdiSoi8eCW5Dou1IQ"),
album: None,
view_count: Some(23000),
view_count: Some(1200),
is_video: true,
track_nr: None,
by_va: false,
@ -417,7 +301,123 @@ MusicArtist(
],
artist_id: Some("UCFFvwAcyQhpeQfuAgBN1XZw"),
album: None,
view_count: Some(9900),
view_count: Some(12000),
is_video: true,
track_nr: None,
by_va: false,
),
TrackItem(
id: "v5KZ5dalhzU",
name: "intp kpop songs (kpop mbti series pt.9)",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/v5KZ5dalhzU/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3nMdCDQ1v-pG312TsgWId2TsMo1zw",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/v5KZ5dalhzU/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3mS7zCcBbRf8t29XGtChWkxd3owMg",
width: 800,
height: 450,
),
],
artists: [
ArtistId(
id: Some("UC_xEL8cbkItBH00KrGz9fbQ"),
name: "orbitiny사샤",
),
],
artist_id: Some("UC_xEL8cbkItBH00KrGz9fbQ"),
album: None,
view_count: Some(7400),
is_video: true,
track_nr: None,
by_va: false,
),
TrackItem(
id: "vaSSdzgDNw0",
name: "Goblin~ Sulli English Cover",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/vaSSdzgDNw0/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3lbkZLY174thtujXlmmPqngKOtxvQ",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/vaSSdzgDNw0/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3k5nsO0ap7vTsm8JoihXd9opC9QwA",
width: 800,
height: 450,
),
],
artists: [
ArtistId(
id: Some("UCaFqztcJss3HrXNurzQJyqQ"),
name: "Bee",
),
],
artist_id: Some("UCaFqztcJss3HrXNurzQJyqQ"),
album: None,
view_count: Some(1400),
is_video: true,
track_nr: None,
by_va: false,
),
TrackItem(
id: "Rq_JkcROjsI",
name: "𝗀𝗈𝖻𝗅𝗂𝗇 // 𝗌𝗎𝗅𝗅𝗂𝗌𝗅𝗈𝗐𝖾𝖽 + 𝗋𝖾𝗏𝖾𝗋𝖻",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/Rq_JkcROjsI/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3ntvxfQE8ErdwpxdksKA_Gtcza3Rw",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Rq_JkcROjsI/sddefault.jpg?sqp=-oaymwEWCKoDEPABIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3kyJ7LFsJWz4Ac44uL8GPxsRldOng",
width: 426,
height: 240,
),
],
artists: [
ArtistId(
id: Some("UCMPqKiPdiSoi8eCW5Dou1IQ"),
name: "ᴜᴋɪʏᴏ",
),
],
artist_id: Some("UCMPqKiPdiSoi8eCW5Dou1IQ"),
album: None,
view_count: Some(25000),
is_video: true,
track_nr: None,
by_va: false,
),
TrackItem(
id: "5VNZWTzJFso",
name: "Dorothy - Picnic, 도로시 - 소풍, Music Camp 20040529",
duration: None,
cover: [
Thumbnail(
url: "https://i.ytimg.com/vi/5VNZWTzJFso/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3niVh6jZ_8X2utHrUst0PgILeoY4g",
width: 400,
height: 225,
),
Thumbnail(
url: "https://i.ytimg.com/vi/5VNZWTzJFso/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3nKv3B3uev0Pg01kUNF_F4GHO39qg",
width: 800,
height: 450,
),
],
artists: [
ArtistId(
id: Some("UCe52oeb7Xv_KaJsEzcKXJJg"),
name: "MBCkpop",
),
],
artist_id: Some("UCe52oeb7Xv_KaJsEzcKXJJg"),
album: None,
view_count: Some(3700),
is_video: true,
track_nr: None,
by_va: false,
@ -454,89 +454,21 @@ MusicArtist(
playlists: [],
similar_artists: [
ArtistItem(
id: "UCHmZYTfdTyVKQEJicLiXEOg",
name: "Red Velvet",
id: "UCpUChkP-KE20GRsyfjU83_g",
name: "CHUNG HA",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/eGQ0NAW1LeeaD0V0pe5HFg2ENDqn-T4qJfi4zBrLuoMHmEzYEHzT64p95_45UdXWTB6WTMskbzfXNw=w226-h226-p-l90-rj",
url: "https://lh3.googleusercontent.com/gfAf1lvXJt_ExLMV0FqnWrTGyAiFpRZW7zeb1ooahA542nBqjLTzcxdhFQtvVWvR0_jzcKOccjIyiFnP=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/eGQ0NAW1LeeaD0V0pe5HFg2ENDqn-T4qJfi4zBrLuoMHmEzYEHzT64p95_45UdXWTB6WTMskbzfXNw=w544-h544-p-l90-rj",
url: "https://lh3.googleusercontent.com/gfAf1lvXJt_ExLMV0FqnWrTGyAiFpRZW7zeb1ooahA542nBqjLTzcxdhFQtvVWvR0_jzcKOccjIyiFnP=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(5200000),
),
ArtistItem(
id: "UCvAUJvMNPy1LLVKpFiu-X9w",
name: "SEULGI",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/Gs5_FbRLCCPLjzgZgJ3Rzz9LJ-U4J825o2dTS-JRnEADJEt9qzMENRA52qRdVx8qavapNYsZDJX4jQ=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/Gs5_FbRLCCPLjzgZgJ3Rzz9LJ-U4J825o2dTS-JRnEADJEt9qzMENRA52qRdVx8qavapNYsZDJX4jQ=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(32100),
),
ArtistItem(
id: "UCyoD3vxUcLLdU1JQ0MMet-Q",
name: "Red Velvet - IRENE & SEULGI",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/jwZVdeVkLWjvoi5aXAKbT_ipid5krOyDCo9zGlMLm4uysvbtMwnYWHHemKmCLjM7DcsxNHfouD3caA=w226-h226-p-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/jwZVdeVkLWjvoi5aXAKbT_ipid5krOyDCo9zGlMLm4uysvbtMwnYWHHemKmCLjM7DcsxNHfouD3caA=w544-h544-p-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(34700),
),
ArtistItem(
id: "UCod8XS1t8Y9JADHv0AWDMLw",
name: "Seohyun",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/0uAuF9rHyOlwm18Q0c6DqjLwRzZWqGFwMLWCh5g802HYpy0AVa6VWs1jNmqXhcB-GAMw2fehnrF0P4s=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/0uAuF9rHyOlwm18Q0c6DqjLwRzZWqGFwMLWCh5g802HYpy0AVa6VWs1jNmqXhcB-GAMw2fehnrF0P4s=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(24400),
),
ArtistItem(
id: "UCOCo09j3eIVdj3HkbJ9c40Q",
name: "HYOYEON ",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/N46gjgCRMewGNJBym4Xqa10-GXxo97bcFf_MJeiAxZl2mQpbJn1MWGzzgmYBUG4XUIUlHpkHHrAZlOc=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/N46gjgCRMewGNJBym4Xqa10-GXxo97bcFf_MJeiAxZl2mQpbJn1MWGzzgmYBUG4XUIUlHpkHHrAZlOc=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(133000),
subscriber_count: Some(1470000),
),
ArtistItem(
id: "UCmeskqhmPRuteGVH4yCXT0A",
@ -553,24 +485,7 @@ MusicArtist(
height: 544,
),
],
subscriber_count: Some(1480000),
),
ArtistItem(
id: "UCF9KUOyCpRNq67dDKlB0tbw",
name: "HA:TFELT",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/9FoZXRMcJp_iIMTqQJ4NHEYumcdwNYN1JfUC2aXUILrRFzbAypG9Yq1r6mMRQzVfDxZ_4OADbruAn5Q=w226-h226-p-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/9FoZXRMcJp_iIMTqQJ4NHEYumcdwNYN1JfUC2aXUILrRFzbAypG9Yq1r6mMRQzVfDxZ_4OADbruAn5Q=w544-h544-p-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(18300),
subscriber_count: Some(1490000),
),
ArtistItem(
id: "UCVXeNwNQs07XQ8d1HtvuxVg",
@ -587,41 +502,126 @@ MusicArtist(
height: 544,
),
],
subscriber_count: Some(26900),
subscriber_count: Some(31400),
),
ArtistItem(
id: "UCHHz6g3igy0BFUfCSiC_6aw",
name: "HyunA",
id: "UCvAUJvMNPy1LLVKpFiu-X9w",
name: "SEULGI",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/L8EVj7a6sEbbj2oszOtVxQo_pJ6Mm1pIyVWcDXPyaEDynkHBa0yRMsxmfkwKBfPJSGLynsIM-CGjmw=w226-h226-p-l90-rj",
url: "https://lh3.googleusercontent.com/NT9GLSF-1zyAb76sixVb9SfMuGef0Dh5aiKODfWt_wWvtGYBnAPs_DcRtOErLw36zYZb4xbsQEzM8ek=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/L8EVj7a6sEbbj2oszOtVxQo_pJ6Mm1pIyVWcDXPyaEDynkHBa0yRMsxmfkwKBfPJSGLynsIM-CGjmw=w544-h544-p-l90-rj",
url: "https://lh3.googleusercontent.com/NT9GLSF-1zyAb76sixVb9SfMuGef0Dh5aiKODfWt_wWvtGYBnAPs_DcRtOErLw36zYZb4xbsQEzM8ek=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(3130000),
subscriber_count: Some(41100),
),
ArtistItem(
id: "UCveaR1hvVMrEQaKR6Xl8NDw",
name: "f(x)",
id: "UC277cpXD9BLeaGgh3yDP40w",
name: "블랙스완 BLACKSWAN",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/VQcMudR55EKlNeZCdhQKLy1dh_nojH8YAqwjXPAl515U1hFham1IRGWhcPWITm1X3yUHSQBHUYSWlh0=w226-h226-p-l90-rj",
url: "https://lh3.googleusercontent.com/a-/ACB-R5RmPcZVcm8zNXAbprhgqATOJMU6RNbj3OuQyERLHA=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/VQcMudR55EKlNeZCdhQKLy1dh_nojH8YAqwjXPAl515U1hFham1IRGWhcPWITm1X3yUHSQBHUYSWlh0=w544-h544-p-l90-rj",
url: "https://lh3.googleusercontent.com/a-/ACB-R5RmPcZVcm8zNXAbprhgqATOJMU6RNbj3OuQyERLHA=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(474000),
subscriber_count: Some(603000),
),
ArtistItem(
id: "UCrt7YUoF7m7PvV_Dt9xWqJQ",
name: "LUNA",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/MISR863mLnf03T-2YBEvxYFQPIvPYJ_clea7H5gZhdsN30i8ufw_2NnvOvVxQTOK7-lx_sRW8Q7A5uM=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/MISR863mLnf03T-2YBEvxYFQPIvPYJ_clea7H5gZhdsN30i8ufw_2NnvOvVxQTOK7-lx_sRW8Q7A5uM=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(4510),
),
ArtistItem(
id: "UCBj8m2FEsjfovt0Fxq0HEJg",
name: "Ladies\' Code",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/JC2ZAjp-IUVIStNvPpxgaJknX_WkUZmV1XqLQvJSr-_Kg9_bVfUJi_3DYztbI1ObFA6LejJimJfKdfjp=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/JC2ZAjp-IUVIStNvPpxgaJknX_WkUZmV1XqLQvJSr-_Kg9_bVfUJi_3DYztbI1ObFA6LejJimJfKdfjp=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(11400),
),
ArtistItem(
id: "UCdhWlMXp2dQBaG5rZS_aSkQ",
name: "DAWN",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/a-/ACB-R5QVBnhjI6XKoLtxuTf6LuijMMtJb7e5S2IlOeqo2Q=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/a-/ACB-R5QVBnhjI6XKoLtxuTf6LuijMMtJb7e5S2IlOeqo2Q=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(802000),
),
ArtistItem(
id: "UCWT2ZfW7d8YI-HinHEVhyCA",
name: "(G)I-DLE",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/uZQSZdDnDJCajJtE6Ig9tqgdO7-uogJpdk9TM0p7iEBmnAQXaSGqYET-W-SHTY-NL9UQ2sdOVtIhd54=w226-h226-p-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/uZQSZdDnDJCajJtE6Ig9tqgdO7-uogJpdk9TM0p7iEBmnAQXaSGqYET-W-SHTY-NL9UQ2sdOVtIhd54=w544-h544-p-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(5310000),
),
ArtistItem(
id: "UCOCo09j3eIVdj3HkbJ9c40Q",
name: "HYO",
avatar: [
Thumbnail(
url: "https://lh3.googleusercontent.com/olRNTvkhjENW-kLcKRg9LQDmWwS3V0KHhTtw4Y8ETFpFLNxlI371_EDh22CtTw0UKgm7YpbGJUUf6nEt=w226-h226-p-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/olRNTvkhjENW-kLcKRg9LQDmWwS3V0KHhTtw4Y8ETFpFLNxlI371_EDh22CtTw0UKgm7YpbGJUUf6nEt=w544-h544-p-l90-rj",
width: 544,
height: 544,
),
],
subscriber_count: Some(141000),
),
],
tracks_playlist_id: None,

View file

@ -119,6 +119,14 @@ impl RustyPipeQuery {
Ok(UrlTarget::Channel { id: id.to_owned() })
} else if util::ALBUM_ID_REGEX.is_match(id) {
Ok(UrlTarget::Album { id: id.to_owned() })
} else if id
.strip_prefix(util::ARTIST_DISCOGRAPHY_PREFIX)
.map(|cid| util::CHANNEL_ID_REGEX.is_match(cid))
.unwrap_or_default()
{
Ok(UrlTarget::Channel {
id: id[4..].to_owned(),
})
} else {
Err(Error::Other("invalid url: no browse id".into()))
}
@ -261,6 +269,14 @@ impl RustyPipeQuery {
}
} else if util::ALBUM_ID_REGEX.is_match(s) {
Ok(UrlTarget::Album { id: s.to_owned() })
} else if s
.strip_prefix(util::ARTIST_DISCOGRAPHY_PREFIX)
.map(|cid| util::CHANNEL_ID_REGEX.is_match(cid))
.unwrap_or_default()
{
Ok(UrlTarget::Channel {
id: s[4..].to_owned(),
})
}
// Channel name only
else if util::VANITY_PATH_REGEX.is_match(s) {

View file

@ -37,6 +37,7 @@ pub static VANITY_PATH_REGEX: Lazy<Regex> = Lazy::new(|| {
pub const DOT_SEPARATOR: &str = "";
pub const VARIOUS_ARTISTS: &str = "Various Artists";
pub const PLAYLIST_ID_ALBUM_PREFIX: &str = "OLAK";
pub const ARTIST_DISCOGRAPHY_PREFIX: &str = "MPAD";
const CONTENT_PLAYBACK_NONCE_ALPHABET: &[u8; 64] =
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";