feat: allow searching for YTM users
This commit is contained in:
parent
577370b06d
commit
50010b7b08
12 changed files with 8224 additions and 3336 deletions
|
|
@ -269,6 +269,7 @@ enum MusicSearchCategory {
|
||||||
Albums,
|
Albums,
|
||||||
PlaylistsYtm,
|
PlaylistsYtm,
|
||||||
PlaylistsCommunity,
|
PlaylistsCommunity,
|
||||||
|
Users,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
|
#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
|
||||||
|
|
@ -348,8 +349,13 @@ fn print_data<T: Serialize>(data: &T, format: Format, pretty: bool) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_entities(items: &[impl YtEntity]) {
|
fn print_entities(items: &[impl YtEntity], with_type: bool) {
|
||||||
for e in items {
|
for e in items {
|
||||||
|
if with_type {
|
||||||
|
if let Some(t) = e.music_item_type() {
|
||||||
|
anstream::print!("{: >8} ", format!("{t:?}").dimmed());
|
||||||
|
}
|
||||||
|
}
|
||||||
anstream::print!("[{}] {}", e.id(), e.name().bold());
|
anstream::print!("[{}] {}", e.id(), e.name().bold());
|
||||||
if let Some(n) = e.channel_name() {
|
if let Some(n) = e.channel_name() {
|
||||||
anstream::print!(" - {}", n.cyan());
|
anstream::print!(" - {}", n.cyan());
|
||||||
|
|
@ -399,6 +405,7 @@ fn print_music_search<T: Serialize + YtEntity>(
|
||||||
data: &MusicSearchResult<T>,
|
data: &MusicSearchResult<T>,
|
||||||
format: Option<Format>,
|
format: Option<Format>,
|
||||||
pretty: bool,
|
pretty: bool,
|
||||||
|
with_type: bool,
|
||||||
) {
|
) {
|
||||||
match format {
|
match format {
|
||||||
Some(format) => print_data(data, format, pretty),
|
Some(format) => print_data(data, format, pretty),
|
||||||
|
|
@ -406,7 +413,7 @@ fn print_music_search<T: Serialize + YtEntity>(
|
||||||
if let Some(corr) = &data.corrected_query {
|
if let Some(corr) = &data.corrected_query {
|
||||||
anstream::println!("Did you mean `{}`?", corr.magenta());
|
anstream::println!("Did you mean `{}`?", corr.magenta());
|
||||||
}
|
}
|
||||||
print_entities(&data.items.items);
|
print_entities(&data.items.items, with_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -788,7 +795,7 @@ async fn run() -> anyhow::Result<()> {
|
||||||
print_description(Some(details.description.to_plaintext()));
|
print_description(Some(details.description.to_plaintext()));
|
||||||
if !details.recommended.is_empty() {
|
if !details.recommended.is_empty() {
|
||||||
print_h2("Recommended");
|
print_h2("Recommended");
|
||||||
print_entities(&details.recommended.items);
|
print_entities(&details.recommended.items, false);
|
||||||
}
|
}
|
||||||
let comment_list = comments.map(|c| match c {
|
let comment_list = comments.map(|c| match c {
|
||||||
CommentsOrder::Top => &details.top_comments.items,
|
CommentsOrder::Top => &details.top_comments.items,
|
||||||
|
|
@ -872,11 +879,11 @@ async fn run() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
if !artist.playlists.is_empty() {
|
if !artist.playlists.is_empty() {
|
||||||
print_h2("Playlists");
|
print_h2("Playlists");
|
||||||
print_entities(&artist.playlists);
|
print_entities(&artist.playlists, false);
|
||||||
}
|
}
|
||||||
if !artist.similar_artists.is_empty() {
|
if !artist.similar_artists.is_empty() {
|
||||||
print_h2("Similar artists");
|
print_h2("Similar artists");
|
||||||
print_entities(&artist.similar_artists);
|
print_entities(&artist.similar_artists, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -903,7 +910,7 @@ async fn run() -> anyhow::Result<()> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
print_entities(&rss.videos);
|
print_entities(&rss.videos, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -944,7 +951,7 @@ async fn run() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
print_description(Some(channel.description));
|
print_description(Some(channel.description));
|
||||||
println!();
|
println!();
|
||||||
print_entities(&channel.content.items);
|
print_entities(&channel.content.items, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -973,7 +980,7 @@ async fn run() -> anyhow::Result<()> {
|
||||||
anstream::println!("{} {}", "Videos:".blue(), vids);
|
anstream::println!("{} {}", "Videos:".blue(), vids);
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
print_entities(&channel.content.items);
|
print_entities(&channel.content.items, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1077,7 +1084,7 @@ async fn run() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
print_description(playlist.description.map(|d| d.to_plaintext()));
|
print_description(playlist.description.map(|d| d.to_plaintext()));
|
||||||
println!();
|
println!();
|
||||||
print_entities(&playlist.videos.items);
|
print_entities(&playlist.videos.items, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1145,7 +1152,7 @@ async fn run() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
print_description(Some(channel.description));
|
print_description(Some(channel.description));
|
||||||
println!();
|
println!();
|
||||||
print_entities(&channel.content.items);
|
print_entities(&channel.content.items, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1167,34 +1174,34 @@ async fn run() -> anyhow::Result<()> {
|
||||||
if let Some(corr) = res.corrected_query {
|
if let Some(corr) = res.corrected_query {
|
||||||
anstream::println!("Did you mean `{}`?", corr.magenta());
|
anstream::println!("Did you mean `{}`?", corr.magenta());
|
||||||
}
|
}
|
||||||
print_entities(&res.items.items);
|
print_entities(&res.items.items, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(MusicSearchCategory::All) => {
|
Some(MusicSearchCategory::All) => {
|
||||||
let res = rp.query().music_search_main(&query).await?;
|
let res = rp.query().music_search_main(&query).await?;
|
||||||
print_music_search(&res, format, pretty);
|
print_music_search(&res, format, pretty, true);
|
||||||
}
|
}
|
||||||
Some(MusicSearchCategory::Tracks) => {
|
Some(MusicSearchCategory::Tracks) => {
|
||||||
let mut res = rp.query().music_search_tracks(&query).await?;
|
let mut res = rp.query().music_search_tracks(&query).await?;
|
||||||
res.items.extend_limit(rp.query(), limit).await?;
|
res.items.extend_limit(rp.query(), limit).await?;
|
||||||
print_music_search(&res, format, pretty);
|
print_music_search(&res, format, pretty, false);
|
||||||
}
|
}
|
||||||
Some(MusicSearchCategory::Videos) => {
|
Some(MusicSearchCategory::Videos) => {
|
||||||
let mut res = rp.query().music_search_videos(&query).await?;
|
let mut res = rp.query().music_search_videos(&query).await?;
|
||||||
res.items.extend_limit(rp.query(), limit).await?;
|
res.items.extend_limit(rp.query(), limit).await?;
|
||||||
print_music_search(&res, format, pretty);
|
print_music_search(&res, format, pretty, false);
|
||||||
}
|
}
|
||||||
Some(MusicSearchCategory::Artists) => {
|
Some(MusicSearchCategory::Artists) => {
|
||||||
let mut res = rp.query().music_search_artists(&query).await?;
|
let mut res = rp.query().music_search_artists(&query).await?;
|
||||||
res.items.extend_limit(rp.query(), limit).await?;
|
res.items.extend_limit(rp.query(), limit).await?;
|
||||||
print_music_search(&res, format, pretty);
|
print_music_search(&res, format, pretty, false);
|
||||||
}
|
}
|
||||||
Some(MusicSearchCategory::Albums) => {
|
Some(MusicSearchCategory::Albums) => {
|
||||||
let mut res = rp.query().music_search_albums(&query).await?;
|
let mut res = rp.query().music_search_albums(&query).await?;
|
||||||
res.items.extend_limit(rp.query(), limit).await?;
|
res.items.extend_limit(rp.query(), limit).await?;
|
||||||
print_music_search(&res, format, pretty);
|
print_music_search(&res, format, pretty, false);
|
||||||
}
|
}
|
||||||
Some(MusicSearchCategory::PlaylistsYtm | MusicSearchCategory::PlaylistsCommunity) => {
|
Some(MusicSearchCategory::PlaylistsYtm | MusicSearchCategory::PlaylistsCommunity) => {
|
||||||
let mut res = rp
|
let mut res = rp
|
||||||
|
|
@ -1205,7 +1212,12 @@ async fn run() -> anyhow::Result<()> {
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
res.items.extend_limit(rp.query(), limit).await?;
|
res.items.extend_limit(rp.query(), limit).await?;
|
||||||
print_music_search(&res, format, pretty);
|
print_music_search(&res, format, pretty, false);
|
||||||
|
}
|
||||||
|
Some(MusicSearchCategory::Users) => {
|
||||||
|
let mut res = rp.query().music_search_users(&query).await?;
|
||||||
|
res.items.extend_limit(rp.query(), limit).await?;
|
||||||
|
print_music_search(&res, format, pretty, false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Commands::Vdata => {
|
Commands::Vdata => {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
paginator::{ContinuationEndpoint, Paginator},
|
paginator::{ContinuationEndpoint, Paginator},
|
||||||
traits::FromYtItem,
|
traits::FromYtItem,
|
||||||
AlbumItem, ArtistItem, MusicItem, MusicPlaylistItem, MusicSearchResult,
|
AlbumItem, ArtistItem, MusicItem, MusicPlaylistItem, MusicSearchResult,
|
||||||
MusicSearchSuggestion, TrackItem,
|
MusicSearchSuggestion, TrackItem, UserItem,
|
||||||
},
|
},
|
||||||
param::search_filter::MusicSearchFilter,
|
param::search_filter::MusicSearchFilter,
|
||||||
serializer::MapResult,
|
serializer::MapResult,
|
||||||
|
|
@ -121,6 +121,15 @@ impl RustyPipeQuery {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Search YouTube Music users
|
||||||
|
pub async fn music_search_users<S: AsRef<str>>(
|
||||||
|
&self,
|
||||||
|
query: S,
|
||||||
|
) -> Result<MusicSearchResult<UserItem>, Error> {
|
||||||
|
self.music_search(query, Some(MusicSearchFilter::Users))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
/// Get YouTube Music search suggestions
|
/// Get YouTube Music search suggestions
|
||||||
#[tracing::instrument(skip(self), level = "error")]
|
#[tracing::instrument(skip(self), level = "error")]
|
||||||
pub async fn music_search_suggestion<S: AsRef<str> + Debug>(
|
pub async fn music_search_suggestion<S: AsRef<str> + Debug>(
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use serde_with::{rust::deserialize_ignore_any, serde_as, DefaultOnError, VecSkip
|
||||||
use crate::{
|
use crate::{
|
||||||
model::{
|
model::{
|
||||||
self, traits::FromYtItem, AlbumId, AlbumItem, AlbumType, ArtistId, ArtistItem, ChannelId,
|
self, traits::FromYtItem, AlbumId, AlbumItem, AlbumType, ArtistId, ArtistItem, ChannelId,
|
||||||
MusicItem, MusicItemType, MusicPlaylistItem, TrackItem,
|
MusicItem, MusicItemType, MusicPlaylistItem, TrackItem, UserItem,
|
||||||
},
|
},
|
||||||
param::Language,
|
param::Language,
|
||||||
serializer::{
|
serializer::{
|
||||||
|
|
@ -535,7 +535,7 @@ impl MusicListMapper {
|
||||||
etype
|
etype
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map a ListMusicItem (album/playlist tile)
|
/// Map a ListMusicItem (album/playlist item, search result)
|
||||||
fn map_list_item(&mut self, item: ListMusicItem) -> Result<Option<MusicItemType>, String> {
|
fn map_list_item(&mut self, item: ListMusicItem) -> Result<Option<MusicItemType>, String> {
|
||||||
let mut columns = item.flex_columns.into_iter();
|
let mut columns = item.flex_columns.into_iter();
|
||||||
let c1 = columns.next();
|
let c1 = columns.next();
|
||||||
|
|
@ -858,6 +858,19 @@ impl MusicListMapper {
|
||||||
}));
|
}));
|
||||||
Ok(Some(MusicItemType::Playlist))
|
Ok(Some(MusicItemType::Playlist))
|
||||||
}
|
}
|
||||||
|
MusicPageType::User => {
|
||||||
|
// Part 1 may be the "Profile" label
|
||||||
|
let handle = map_channel_handle(subtitle_p2.as_ref())
|
||||||
|
.or_else(|| map_channel_handle(subtitle_p1.as_ref()));
|
||||||
|
|
||||||
|
self.items.push(MusicItem::User(UserItem {
|
||||||
|
id,
|
||||||
|
name: title,
|
||||||
|
handle,
|
||||||
|
avatar: item.thumbnail.into(),
|
||||||
|
}));
|
||||||
|
Ok(Some(MusicItemType::User))
|
||||||
|
}
|
||||||
MusicPageType::None => {
|
MusicPageType::None => {
|
||||||
// There may be broken YT channels from the artist search. They can be skipped.
|
// There may be broken YT channels from the artist search. They can be skipped.
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
|
@ -1009,7 +1022,7 @@ impl MusicListMapper {
|
||||||
}));
|
}));
|
||||||
Ok(Some(MusicItemType::Playlist))
|
Ok(Some(MusicItemType::Playlist))
|
||||||
}
|
}
|
||||||
MusicPageType::None => Ok(None),
|
MusicPageType::None | MusicPageType::User => Ok(None),
|
||||||
},
|
},
|
||||||
None => Err("could not determine item type".to_owned()),
|
None => Err("could not determine item type".to_owned()),
|
||||||
}
|
}
|
||||||
|
|
@ -1144,6 +1157,19 @@ impl MusicListMapper {
|
||||||
}));
|
}));
|
||||||
Some(MusicItemType::Playlist)
|
Some(MusicItemType::Playlist)
|
||||||
}
|
}
|
||||||
|
MusicPageType::User => {
|
||||||
|
// Part 1 may be the "Profile" label
|
||||||
|
let handle = map_channel_handle(subtitle_p2.as_ref())
|
||||||
|
.or_else(|| map_channel_handle(subtitle_p1.as_ref()));
|
||||||
|
|
||||||
|
self.items.push(MusicItem::User(UserItem {
|
||||||
|
id: music_page.id,
|
||||||
|
name: card.title,
|
||||||
|
handle,
|
||||||
|
avatar: card.thumbnail.into(),
|
||||||
|
}));
|
||||||
|
Some(MusicItemType::User)
|
||||||
|
}
|
||||||
MusicPageType::None => None,
|
MusicPageType::None => None,
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -1206,6 +1232,7 @@ impl MusicListMapper {
|
||||||
MusicItem::Album(album) => albums.push(album),
|
MusicItem::Album(album) => albums.push(album),
|
||||||
MusicItem::Artist(artist) => artists.push(artist),
|
MusicItem::Artist(artist) => artists.push(artist),
|
||||||
MusicItem::Playlist(playlist) => playlists.push(playlist),
|
MusicItem::Playlist(playlist) => playlists.push(playlist),
|
||||||
|
MusicItem::User(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1256,6 +1283,12 @@ fn map_artist_id_fallback(
|
||||||
.or_else(|| fallback_artist.and_then(|a| a.id.clone()))
|
.or_else(|| fallback_artist.and_then(|a| a.id.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn map_channel_handle(st: Option<&TextComponents>) -> Option<String> {
|
||||||
|
st.map(|t| t.first_str())
|
||||||
|
.filter(|t| t.starts_with('@'))
|
||||||
|
.map(str::to_owned)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn map_artist_id(entries: Vec<MusicItemMenuEntry>) -> Option<String> {
|
pub(crate) fn map_artist_id(entries: Vec<MusicItemMenuEntry>) -> Option<String> {
|
||||||
entries.into_iter().find_map(|i| {
|
entries.into_iter().find_map(|i| {
|
||||||
if let NavigationEndpoint::Browse {
|
if let NavigationEndpoint::Browse {
|
||||||
|
|
|
||||||
|
|
@ -227,6 +227,7 @@ pub(crate) enum MusicPageType {
|
||||||
Album,
|
Album,
|
||||||
Playlist,
|
Playlist,
|
||||||
Track { vtype: MusicVideoType },
|
Track { vtype: MusicVideoType },
|
||||||
|
User,
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -236,10 +237,11 @@ impl From<PageType> for MusicPageType {
|
||||||
PageType::Artist => MusicPageType::Artist,
|
PageType::Artist => MusicPageType::Artist,
|
||||||
PageType::Album => MusicPageType::Album,
|
PageType::Album => MusicPageType::Album,
|
||||||
PageType::Playlist | PageType::Podcast => MusicPageType::Playlist,
|
PageType::Playlist | PageType::Podcast => MusicPageType::Playlist,
|
||||||
PageType::Channel | PageType::Unknown => MusicPageType::None,
|
PageType::Channel => MusicPageType::User,
|
||||||
PageType::Episode => MusicPageType::Track {
|
PageType::Episode => MusicPageType::Track {
|
||||||
vtype: MusicVideoType::Episode,
|
vtype: MusicVideoType::Episode,
|
||||||
},
|
},
|
||||||
|
PageType::Unknown => MusicPageType::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ expression: map_res.c
|
||||||
---
|
---
|
||||||
MusicSearchResult(
|
MusicSearchResult(
|
||||||
items: Paginator(
|
items: Paginator(
|
||||||
count: Some(16),
|
count: Some(28),
|
||||||
items: [
|
items: [
|
||||||
Track(TrackItem(
|
Track(TrackItem(
|
||||||
id: "ZeerrnuLi5E",
|
id: "ZeerrnuLi5E",
|
||||||
|
|
@ -25,7 +25,79 @@ MusicSearchResult(
|
||||||
],
|
],
|
||||||
artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
|
artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
|
||||||
album: None,
|
album: None,
|
||||||
view_count: Some(235000000),
|
view_count: Some(273000000),
|
||||||
|
is_video: true,
|
||||||
|
track_nr: None,
|
||||||
|
by_va: false,
|
||||||
|
)),
|
||||||
|
Track(TrackItem(
|
||||||
|
id: "NU611fxGyPU",
|
||||||
|
name: "Black Mamba",
|
||||||
|
duration: Some(175),
|
||||||
|
cover: [
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://i.ytimg.com/vi/NU611fxGyPU/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3md93InOvanuHclIZe1FpSmEVWGKw",
|
||||||
|
width: 400,
|
||||||
|
height: 225,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artists: [
|
||||||
|
ArtistId(
|
||||||
|
id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
|
||||||
|
name: "aespa",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
|
||||||
|
album: None,
|
||||||
|
view_count: Some(43000000),
|
||||||
|
is_video: true,
|
||||||
|
track_nr: None,
|
||||||
|
by_va: false,
|
||||||
|
)),
|
||||||
|
Track(TrackItem(
|
||||||
|
id: "Yi2nsnpw5h0",
|
||||||
|
name: "aespa - Black Mamba (Official Instrumental)",
|
||||||
|
duration: Some(175),
|
||||||
|
cover: [
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://i.ytimg.com/vi/Yi2nsnpw5h0/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3meMnbqX2Gi5z5lD0G6PeDxcp-zpA",
|
||||||
|
width: 400,
|
||||||
|
height: 225,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artists: [
|
||||||
|
ArtistId(
|
||||||
|
id: Some("UCx5Dw_5guQcKu_lMGCh-IuQ"),
|
||||||
|
name: "aesthetic inst.",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artist_id: Some("UCx5Dw_5guQcKu_lMGCh-IuQ"),
|
||||||
|
album: None,
|
||||||
|
view_count: Some(1500000),
|
||||||
|
is_video: true,
|
||||||
|
track_nr: None,
|
||||||
|
by_va: false,
|
||||||
|
)),
|
||||||
|
Track(TrackItem(
|
||||||
|
id: "2Qefh0W_H88",
|
||||||
|
name: "aespa - black mamba (𝒔𝒍𝒐𝒘𝒆𝒅 𝒏 𝒓𝒆𝒗𝒆𝒓𝒃)",
|
||||||
|
duration: Some(209),
|
||||||
|
cover: [
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://i.ytimg.com/vi/2Qefh0W_H88/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3knLw9_f0ukxeV-S6vS5_JOTXnaWQ",
|
||||||
|
width: 400,
|
||||||
|
height: 225,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artists: [
|
||||||
|
ArtistId(
|
||||||
|
id: Some("UCrGYENbzwtva2X16bAPhTbA"),
|
||||||
|
name: "i n s o m n i o",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artist_id: Some("UCrGYENbzwtva2X16bAPhTbA"),
|
||||||
|
album: None,
|
||||||
|
view_count: Some(1500000),
|
||||||
is_video: true,
|
is_video: true,
|
||||||
track_nr: None,
|
track_nr: None,
|
||||||
by_va: false,
|
by_va: false,
|
||||||
|
|
@ -57,110 +129,82 @@ MusicSearchResult(
|
||||||
id: "MPREb_OpHWHwyNOuY",
|
id: "MPREb_OpHWHwyNOuY",
|
||||||
name: "Black Mamba",
|
name: "Black Mamba",
|
||||||
)),
|
)),
|
||||||
view_count: None,
|
view_count: Some(544000000),
|
||||||
is_video: false,
|
is_video: false,
|
||||||
track_nr: None,
|
track_nr: None,
|
||||||
by_va: false,
|
by_va: false,
|
||||||
)),
|
)),
|
||||||
Track(TrackItem(
|
Track(TrackItem(
|
||||||
id: "cATe8Toht70",
|
id: "PpKu3UsHYrk",
|
||||||
name: "Black Mamba",
|
name: "Ghetto Millionnaire",
|
||||||
duration: Some(74),
|
duration: Some(263),
|
||||||
cover: [
|
cover: [
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://lh3.googleusercontent.com/ZesxRmV1_bDW89z70eojCd6DofYPbzbgGaXSIRP3UjmE4nIAkOuWc8pXaozR4AwrzPQublDCKrg6vcxHOg=w60-h60-l90-rj",
|
url: "https://lh3.googleusercontent.com/p6AWfbIdksK7FGWMlutdCV0t449Nd_odfNnT9G80KDajqmXklX4H-nymvTADsn1JhEnRDaPSfbw_hmlKdg=w60-h60-l90-rj",
|
||||||
width: 60,
|
width: 60,
|
||||||
height: 60,
|
height: 60,
|
||||||
),
|
),
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://lh3.googleusercontent.com/ZesxRmV1_bDW89z70eojCd6DofYPbzbgGaXSIRP3UjmE4nIAkOuWc8pXaozR4AwrzPQublDCKrg6vcxHOg=w120-h120-l90-rj",
|
url: "https://lh3.googleusercontent.com/p6AWfbIdksK7FGWMlutdCV0t449Nd_odfNnT9G80KDajqmXklX4H-nymvTADsn1JhEnRDaPSfbw_hmlKdg=w120-h120-l90-rj",
|
||||||
width: 120,
|
width: 120,
|
||||||
height: 120,
|
height: 120,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
artists: [
|
artists: [
|
||||||
ArtistId(
|
ArtistId(
|
||||||
id: Some("UCfCNL5oajlQBAlyjWv1ChVw"),
|
id: Some("UCxX9tNcQgCBuU56ezupriqg"),
|
||||||
name: "Hans Zimmer",
|
name: "Black Mamba",
|
||||||
),
|
|
||||||
ArtistId(
|
|
||||||
id: Some("UCvTXGTZf9EvuCAwZOkoR2iQ"),
|
|
||||||
name: "Lorne Balfe",
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
artist_id: Some("UCfCNL5oajlQBAlyjWv1ChVw"),
|
artist_id: Some("UCxX9tNcQgCBuU56ezupriqg"),
|
||||||
album: Some(AlbumId(
|
album: Some(AlbumId(
|
||||||
id: "MPREb_UmDOhLpDsc0",
|
id: "MPREb_miyMs44ZpHc",
|
||||||
name: "Megamind (Music from the Motion Picture)",
|
name: "Ghetto Millionnaire",
|
||||||
)),
|
)),
|
||||||
view_count: None,
|
view_count: Some(1200000),
|
||||||
is_video: false,
|
is_video: false,
|
||||||
track_nr: None,
|
track_nr: None,
|
||||||
by_va: false,
|
by_va: false,
|
||||||
)),
|
)),
|
||||||
Track(TrackItem(
|
Track(TrackItem(
|
||||||
id: "WwNKyoizf8k",
|
id: "jynOfK8JB0E",
|
||||||
name: "BLACK MAMBA",
|
name: "It Ain\'t You",
|
||||||
duration: Some(182),
|
duration: Some(268),
|
||||||
cover: [
|
cover: [
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://lh3.googleusercontent.com/gall0XXuwoV_SYR3S6EgtOGaBC3YOR5wOpQxCyqgxC3Xht3Jc95Y-sFg-sGAcQl946MfurGY_xSv0YBT=w60-h60-l90-rj",
|
url: "https://lh3.googleusercontent.com/ByZsPc5CHoZwtn-cl7e_nbhiVkWxoFJ2RHkNUvLTiowT8228-aVd6r2XT08Z8a32Qa7d-0-Go44sxkdf=w60-h60-l90-rj",
|
||||||
width: 60,
|
width: 60,
|
||||||
height: 60,
|
height: 60,
|
||||||
),
|
),
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://lh3.googleusercontent.com/gall0XXuwoV_SYR3S6EgtOGaBC3YOR5wOpQxCyqgxC3Xht3Jc95Y-sFg-sGAcQl946MfurGY_xSv0YBT=w120-h120-l90-rj",
|
url: "https://lh3.googleusercontent.com/ByZsPc5CHoZwtn-cl7e_nbhiVkWxoFJ2RHkNUvLTiowT8228-aVd6r2XT08Z8a32Qa7d-0-Go44sxkdf=w120-h120-l90-rj",
|
||||||
width: 120,
|
width: 120,
|
||||||
height: 120,
|
height: 120,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
artists: [
|
artists: [
|
||||||
ArtistId(
|
ArtistId(
|
||||||
id: Some("UCz6yr3CgFGrrrPDa2asbWMQ"),
|
id: Some("UCaDT20-B3U8h-tPg_VMvntw"),
|
||||||
name: "Bayamon PR Tribe",
|
name: "The Black Mamba",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
artist_id: Some("UCz6yr3CgFGrrrPDa2asbWMQ"),
|
artist_id: Some("UCaDT20-B3U8h-tPg_VMvntw"),
|
||||||
album: Some(AlbumId(
|
album: Some(AlbumId(
|
||||||
id: "MPREb_RV0PGHyGfkp",
|
id: "MPREb_hXasyBrDJm7",
|
||||||
name: "LISTEN ME",
|
name: "The Black Mamba",
|
||||||
)),
|
)),
|
||||||
view_count: None,
|
view_count: Some(1300000),
|
||||||
is_video: false,
|
is_video: false,
|
||||||
track_nr: None,
|
track_nr: None,
|
||||||
by_va: false,
|
by_va: false,
|
||||||
)),
|
)),
|
||||||
Track(TrackItem(
|
Track(TrackItem(
|
||||||
id: "yQUU29NwNF4",
|
id: "pgjQkcYD-rQ",
|
||||||
name: "aespa(에스파) - Black Mamba @인기가요 inkigayo 20201122",
|
name: "Black Mamba (Techwear ver. Dance Practice)",
|
||||||
duration: Some(213),
|
duration: Some(198),
|
||||||
cover: [
|
cover: [
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://i.ytimg.com/vi/yQUU29NwNF4/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3k0HD8CTPlz4YU0hvy1GqKSf2HKUQ",
|
url: "https://i.ytimg.com/vi/pgjQkcYD-rQ/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3k8ndHhyzqiuzAXoLwsrk-I7IKt5Q",
|
||||||
width: 400,
|
|
||||||
height: 225,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
artists: [
|
|
||||||
ArtistId(
|
|
||||||
id: Some("UCS_hnpJLQTvBkqALgapi_4g"),
|
|
||||||
name: "스브스케이팝 X INKIGAYO",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
artist_id: Some("UCS_hnpJLQTvBkqALgapi_4g"),
|
|
||||||
album: None,
|
|
||||||
view_count: Some(10000000),
|
|
||||||
is_video: true,
|
|
||||||
track_nr: None,
|
|
||||||
by_va: false,
|
|
||||||
)),
|
|
||||||
Track(TrackItem(
|
|
||||||
id: "Ky5RT5oGg0w",
|
|
||||||
name: "Black Mamba",
|
|
||||||
duration: Some(287),
|
|
||||||
cover: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://i.ytimg.com/vi/Ky5RT5oGg0w/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3mB-DDgCruC-dhPM0v66ckiZJQnJg",
|
|
||||||
width: 400,
|
width: 400,
|
||||||
height: 225,
|
height: 225,
|
||||||
),
|
),
|
||||||
|
|
@ -173,18 +217,18 @@ MusicSearchResult(
|
||||||
],
|
],
|
||||||
artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
|
artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
|
||||||
album: None,
|
album: None,
|
||||||
view_count: Some(18000000),
|
view_count: Some(9600000),
|
||||||
is_video: true,
|
is_video: true,
|
||||||
track_nr: None,
|
track_nr: None,
|
||||||
by_va: false,
|
by_va: false,
|
||||||
)),
|
)),
|
||||||
Track(TrackItem(
|
Track(TrackItem(
|
||||||
id: "dz9bieeSVRw",
|
id: "w2GXdb-pHo8",
|
||||||
name: "aespa - Black Mamba (Music Bank) | KBS WORLD TV 201127",
|
name: "(Hot Debut) aespa - Black Mamba (Music Bank) | KBS WORLD TV 201120",
|
||||||
duration: Some(192),
|
duration: None,
|
||||||
cover: [
|
cover: [
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://i.ytimg.com/vi/dz9bieeSVRw/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3lsJGKTqJhnt-ckrJtBLlvSp46Y5g",
|
url: "https://i.ytimg.com/vi/w2GXdb-pHo8/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3l5kwjfr-rdoZvgzcLk38ilMou95g",
|
||||||
width: 400,
|
width: 400,
|
||||||
height: 225,
|
height: 225,
|
||||||
),
|
),
|
||||||
|
|
@ -197,135 +241,223 @@ MusicSearchResult(
|
||||||
],
|
],
|
||||||
artist_id: Some("UC5BMQOsAB8hKUyHu9KI6yig"),
|
artist_id: Some("UC5BMQOsAB8hKUyHu9KI6yig"),
|
||||||
album: None,
|
album: None,
|
||||||
view_count: Some(3200000),
|
view_count: None,
|
||||||
|
is_video: true,
|
||||||
|
track_nr: None,
|
||||||
|
by_va: false,
|
||||||
|
)),
|
||||||
|
Track(TrackItem(
|
||||||
|
id: "JepNreB58TA",
|
||||||
|
name: "aespa (에스파) - Black Mamba | Sydney - SYNK: Parallel Line | 4K60 직캠 Fancam Front Row",
|
||||||
|
duration: Some(170),
|
||||||
|
cover: [
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://i.ytimg.com/vi/JepNreB58TA/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3lhDLwngEone0tYZ0omfA6rs6Nj2w",
|
||||||
|
width: 400,
|
||||||
|
height: 225,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artists: [
|
||||||
|
ArtistId(
|
||||||
|
id: Some("UCAOoElZAQnI0zN91qvzezCw"),
|
||||||
|
name: "yentaxi",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artist_id: Some("UCAOoElZAQnI0zN91qvzezCw"),
|
||||||
|
album: None,
|
||||||
|
view_count: Some(213000),
|
||||||
is_video: true,
|
is_video: true,
|
||||||
track_nr: None,
|
track_nr: None,
|
||||||
by_va: false,
|
by_va: false,
|
||||||
)),
|
)),
|
||||||
Album(AlbumItem(
|
Album(AlbumItem(
|
||||||
id: "MPREb_OpHWHwyNOuY",
|
id: "MPREb_rR0VQ4fTxPM",
|
||||||
|
name: "black mamba",
|
||||||
|
cover: [
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://lh3.googleusercontent.com/9H5D-h9AQdUQsPlq7emEOm4R6atXeOVsQl9CNFfKAXocK9UWVemlewjCc665YE_CJFJPQzm4euGmHDvl=w60-h60-l90-rj",
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://lh3.googleusercontent.com/9H5D-h9AQdUQsPlq7emEOm4R6atXeOVsQl9CNFfKAXocK9UWVemlewjCc665YE_CJFJPQzm4euGmHDvl=w120-h120-l90-rj",
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://lh3.googleusercontent.com/9H5D-h9AQdUQsPlq7emEOm4R6atXeOVsQl9CNFfKAXocK9UWVemlewjCc665YE_CJFJPQzm4euGmHDvl=w226-h226-l90-rj",
|
||||||
|
width: 226,
|
||||||
|
height: 226,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://lh3.googleusercontent.com/9H5D-h9AQdUQsPlq7emEOm4R6atXeOVsQl9CNFfKAXocK9UWVemlewjCc665YE_CJFJPQzm4euGmHDvl=w544-h544-l90-rj",
|
||||||
|
width: 544,
|
||||||
|
height: 544,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artists: [
|
||||||
|
ArtistId(
|
||||||
|
id: Some("UClSZ2io808U-NOICSbjvwEg"),
|
||||||
|
name: "ff phonk",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artist_id: Some("UClSZ2io808U-NOICSbjvwEg"),
|
||||||
|
album_type: Single,
|
||||||
|
year: Some(2024),
|
||||||
|
by_va: false,
|
||||||
|
)),
|
||||||
|
Album(AlbumItem(
|
||||||
|
id: "MPREb_ZXbDKPXnct4",
|
||||||
|
name: "Mi Back",
|
||||||
|
cover: [
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://lh3.googleusercontent.com/N1vPbX8Qwykpsx_bCguQKz4D6REvCvHSvgqpGKwN8Z1GIuZHblZalXlKZn-4IMe5Gxv3uSmNDRiagss3XA=w60-h60-l90-rj",
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://lh3.googleusercontent.com/N1vPbX8Qwykpsx_bCguQKz4D6REvCvHSvgqpGKwN8Z1GIuZHblZalXlKZn-4IMe5Gxv3uSmNDRiagss3XA=w120-h120-l90-rj",
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://lh3.googleusercontent.com/N1vPbX8Qwykpsx_bCguQKz4D6REvCvHSvgqpGKwN8Z1GIuZHblZalXlKZn-4IMe5Gxv3uSmNDRiagss3XA=w226-h226-l90-rj",
|
||||||
|
width: 226,
|
||||||
|
height: 226,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://lh3.googleusercontent.com/N1vPbX8Qwykpsx_bCguQKz4D6REvCvHSvgqpGKwN8Z1GIuZHblZalXlKZn-4IMe5Gxv3uSmNDRiagss3XA=w544-h544-l90-rj",
|
||||||
|
width: 544,
|
||||||
|
height: 544,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artists: [
|
||||||
|
ArtistId(
|
||||||
|
id: Some("UCzKrKM1QQQyw8uZ_NcNldGQ"),
|
||||||
|
name: "Black Mamba & eLgozzy",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artist_id: Some("UCzKrKM1QQQyw8uZ_NcNldGQ"),
|
||||||
|
album_type: Single,
|
||||||
|
year: Some(2024),
|
||||||
|
by_va: false,
|
||||||
|
)),
|
||||||
|
Album(AlbumItem(
|
||||||
|
id: "MPREb_LGXKt36T0rM",
|
||||||
name: "Black Mamba",
|
name: "Black Mamba",
|
||||||
cover: [
|
cover: [
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://lh3.googleusercontent.com/MOL4_Ula9hocErkX2xK_7mISFiWvQz51vReT14KCHF9wsqCEH6sO8iilFFelWMn7JOYIk2WFa-gMmw2uvw=w60-h60-l90-rj",
|
url: "https://lh3.googleusercontent.com/eusGkrg73YceOgTNl2na4Ywi2pKSdeIVCNYuebdd5nJ20Yw_L4wBTKKR0_Qj4W0-in32dKal-GYKNUGB=w60-h60-l90-rj",
|
||||||
width: 60,
|
width: 60,
|
||||||
height: 60,
|
height: 60,
|
||||||
),
|
),
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://lh3.googleusercontent.com/MOL4_Ula9hocErkX2xK_7mISFiWvQz51vReT14KCHF9wsqCEH6sO8iilFFelWMn7JOYIk2WFa-gMmw2uvw=w120-h120-l90-rj",
|
url: "https://lh3.googleusercontent.com/eusGkrg73YceOgTNl2na4Ywi2pKSdeIVCNYuebdd5nJ20Yw_L4wBTKKR0_Qj4W0-in32dKal-GYKNUGB=w120-h120-l90-rj",
|
||||||
width: 120,
|
width: 120,
|
||||||
height: 120,
|
height: 120,
|
||||||
),
|
),
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://lh3.googleusercontent.com/MOL4_Ula9hocErkX2xK_7mISFiWvQz51vReT14KCHF9wsqCEH6sO8iilFFelWMn7JOYIk2WFa-gMmw2uvw=w226-h226-l90-rj",
|
url: "https://lh3.googleusercontent.com/eusGkrg73YceOgTNl2na4Ywi2pKSdeIVCNYuebdd5nJ20Yw_L4wBTKKR0_Qj4W0-in32dKal-GYKNUGB=w226-h226-l90-rj",
|
||||||
width: 226,
|
width: 226,
|
||||||
height: 226,
|
height: 226,
|
||||||
),
|
),
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://lh3.googleusercontent.com/MOL4_Ula9hocErkX2xK_7mISFiWvQz51vReT14KCHF9wsqCEH6sO8iilFFelWMn7JOYIk2WFa-gMmw2uvw=w544-h544-l90-rj",
|
url: "https://lh3.googleusercontent.com/eusGkrg73YceOgTNl2na4Ywi2pKSdeIVCNYuebdd5nJ20Yw_L4wBTKKR0_Qj4W0-in32dKal-GYKNUGB=w544-h544-l90-rj",
|
||||||
width: 544,
|
width: 544,
|
||||||
height: 544,
|
height: 544,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
artists: [
|
artists: [
|
||||||
ArtistId(
|
ArtistId(
|
||||||
id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
|
id: Some("UC7R_cJYLxanyOw9KsfOzu7Q"),
|
||||||
name: "aespa",
|
name: "MOGI (IL)",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
|
artist_id: Some("UC7R_cJYLxanyOw9KsfOzu7Q"),
|
||||||
album_type: Single,
|
album_type: Single,
|
||||||
year: Some(2020),
|
year: Some(2024),
|
||||||
by_va: false,
|
by_va: false,
|
||||||
)),
|
)),
|
||||||
Album(AlbumItem(
|
Playlist(MusicPlaylistItem(
|
||||||
id: "MPREb_pvdHyqvGjbI",
|
id: "PLnAcDMIXVUFqVONj6hrtTb5jfumRjA6NF",
|
||||||
name: "Girls - The 2nd Mini Album",
|
name: "Black Mamba Mixtape",
|
||||||
cover: [
|
thumbnail: [
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://lh3.googleusercontent.com/JYOTl7neLJLMUEVjdg_qIqz7XjUZB2AQAx_sRDlNVd5jSYiv1xA0v68ZN8Kn0KKf1fSfQnTaeakGeQgI=w60-h60-l90-rj",
|
url: "https://i.ytimg.com/vi/S3xisVb4Nt0/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3kHkhtnq5pAgdX7sVqd7699sdwzPw",
|
||||||
width: 60,
|
width: 400,
|
||||||
height: 60,
|
height: 225,
|
||||||
),
|
),
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://lh3.googleusercontent.com/JYOTl7neLJLMUEVjdg_qIqz7XjUZB2AQAx_sRDlNVd5jSYiv1xA0v68ZN8Kn0KKf1fSfQnTaeakGeQgI=w120-h120-l90-rj",
|
url: "https://i.ytimg.com/vi/S3xisVb4Nt0/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3kgdAd7dmEUsPEwH_QG1yjtHLfxNA",
|
||||||
width: 120,
|
width: 800,
|
||||||
height: 120,
|
height: 450,
|
||||||
),
|
),
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://lh3.googleusercontent.com/JYOTl7neLJLMUEVjdg_qIqz7XjUZB2AQAx_sRDlNVd5jSYiv1xA0v68ZN8Kn0KKf1fSfQnTaeakGeQgI=w226-h226-l90-rj",
|
url: "https://i.ytimg.com/vi/S3xisVb4Nt0/hq720.jpg?sqp=-oaymwEXCNUGEOADIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3mLtxWlynBlNSCHsBbxPLxGUhcijQ",
|
||||||
width: 226,
|
width: 853,
|
||||||
height: 226,
|
height: 480,
|
||||||
),
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://lh3.googleusercontent.com/JYOTl7neLJLMUEVjdg_qIqz7XjUZB2AQAx_sRDlNVd5jSYiv1xA0v68ZN8Kn0KKf1fSfQnTaeakGeQgI=w544-h544-l90-rj",
|
|
||||||
width: 544,
|
|
||||||
height: 544,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
artists: [
|
channel: Some(ChannelId(
|
||||||
ArtistId(
|
id: "UCulZuGBZLHEu_9natGq9Q7g",
|
||||||
id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
|
name: "Jay South Music",
|
||||||
name: "aespa",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
artist_id: Some("UCEdZAdnnKqbaHOlv8nM6OtA"),
|
|
||||||
album_type: Album,
|
|
||||||
year: Some(2022),
|
|
||||||
by_va: false,
|
|
||||||
)),
|
)),
|
||||||
Album(AlbumItem(
|
track_count: None,
|
||||||
id: "MPREb_CznUTKnATw6",
|
from_ytm: false,
|
||||||
name: "Black Mamba (feat. Foolio)",
|
|
||||||
cover: [
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://lh3.googleusercontent.com/3ut0tvS5LYcfHjLwrYPSYNbraALbFb9ov28b2GXHB8ABaMGWILUko_BJa1jpsSVrELE_B8so3NtYMVfb1g=w60-h60-l90-rj",
|
|
||||||
width: 60,
|
|
||||||
height: 60,
|
|
||||||
),
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://lh3.googleusercontent.com/3ut0tvS5LYcfHjLwrYPSYNbraALbFb9ov28b2GXHB8ABaMGWILUko_BJa1jpsSVrELE_B8so3NtYMVfb1g=w120-h120-l90-rj",
|
|
||||||
width: 120,
|
|
||||||
height: 120,
|
|
||||||
),
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://lh3.googleusercontent.com/3ut0tvS5LYcfHjLwrYPSYNbraALbFb9ov28b2GXHB8ABaMGWILUko_BJa1jpsSVrELE_B8so3NtYMVfb1g=w226-h226-l90-rj",
|
|
||||||
width: 226,
|
|
||||||
height: 226,
|
|
||||||
),
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://lh3.googleusercontent.com/3ut0tvS5LYcfHjLwrYPSYNbraALbFb9ov28b2GXHB8ABaMGWILUko_BJa1jpsSVrELE_B8so3NtYMVfb1g=w544-h544-l90-rj",
|
|
||||||
width: 544,
|
|
||||||
height: 544,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
artists: [
|
|
||||||
ArtistId(
|
|
||||||
id: Some("UCZK5n7V2-iPHfUXLV2tDvzw"),
|
|
||||||
name: "Cojack",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
artist_id: Some("UCZK5n7V2-iPHfUXLV2tDvzw"),
|
|
||||||
album_type: Single,
|
|
||||||
year: Some(2020),
|
|
||||||
by_va: false,
|
|
||||||
)),
|
)),
|
||||||
Artist(ArtistItem(
|
Playlist(MusicPlaylistItem(
|
||||||
id: "UCEdZAdnnKqbaHOlv8nM6OtA",
|
id: "PL38uS170Dxaatridfyyj-fqjSOlwg7h5R",
|
||||||
name: "aespa",
|
name: "Black Mamba Man",
|
||||||
avatar: [
|
thumbnail: [
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://lh3.googleusercontent.com/gV8Sbt3iKraNm_H9ZaH3oh6ERRdN0Dj6qHmTLPiQQ4WS8uGNN09HlpujMJOWwei_z5yC9Th1cZXyOQ=w60-h60-p-l90-rj",
|
url: "https://i.ytimg.com/vi/1jbpmnC_ox0/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3kZHx-HBttEizSkMOftu5xGb7CYYQ",
|
||||||
width: 60,
|
width: 400,
|
||||||
height: 60,
|
height: 225,
|
||||||
),
|
),
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://lh3.googleusercontent.com/gV8Sbt3iKraNm_H9ZaH3oh6ERRdN0Dj6qHmTLPiQQ4WS8uGNN09HlpujMJOWwei_z5yC9Th1cZXyOQ=w120-h120-p-l90-rj",
|
url: "https://i.ytimg.com/vi/1jbpmnC_ox0/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3kYUMfl3cLxq6_oUdli4dyGLPrJRA",
|
||||||
width: 120,
|
width: 800,
|
||||||
height: 120,
|
height: 450,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://i.ytimg.com/vi/1jbpmnC_ox0/hq720.jpg?sqp=-oaymwEXCNUGEOADIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3n_rKN8FnNasX2FNzN8_OtVyGmvBQ",
|
||||||
|
width: 853,
|
||||||
|
height: 480,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
subscriber_count: Some(4120000),
|
channel: Some(ChannelId(
|
||||||
|
id: "UCNgek3KQIVQsT_2O2puCWRA",
|
||||||
|
name: "one day",
|
||||||
|
)),
|
||||||
|
track_count: None,
|
||||||
|
from_ytm: false,
|
||||||
|
)),
|
||||||
|
Playlist(MusicPlaylistItem(
|
||||||
|
id: "PLEl8NhnoNOpbZ0HkkChu3xEhKShx_vtya",
|
||||||
|
name: "The Black Mamba",
|
||||||
|
thumbnail: [
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://yt3.ggpht.com/NtKO3BdJFpImVJNOTFMS2f1F6rK6ivWHVP3jGsYoERpvvBr7oXb7eWSwhIZBYRCEtW_Qvyib9KH2=s192",
|
||||||
|
width: 192,
|
||||||
|
height: 192,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://yt3.ggpht.com/NtKO3BdJFpImVJNOTFMS2f1F6rK6ivWHVP3jGsYoERpvvBr7oXb7eWSwhIZBYRCEtW_Qvyib9KH2=s576",
|
||||||
|
width: 576,
|
||||||
|
height: 576,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://yt3.ggpht.com/NtKO3BdJFpImVJNOTFMS2f1F6rK6ivWHVP3jGsYoERpvvBr7oXb7eWSwhIZBYRCEtW_Qvyib9KH2=s1200",
|
||||||
|
width: 1200,
|
||||||
|
height: 1200,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
channel: Some(ChannelId(
|
||||||
|
id: "UCCE3DsIpCrNWQMICx-zMNew",
|
||||||
|
name: "MoveAMente",
|
||||||
|
)),
|
||||||
|
track_count: None,
|
||||||
|
from_ytm: false,
|
||||||
)),
|
)),
|
||||||
Artist(ArtistItem(
|
Artist(ArtistItem(
|
||||||
id: "UCaDT20-B3U8h-tPg_VMvntw",
|
id: "UCaDT20-B3U8h-tPg_VMvntw",
|
||||||
|
|
@ -342,19 +474,36 @@ MusicSearchResult(
|
||||||
height: 120,
|
height: 120,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
subscriber_count: Some(2640),
|
subscriber_count: Some(2890),
|
||||||
|
)),
|
||||||
|
Artist(ArtistItem(
|
||||||
|
id: "UCRpi1gBlax4sK3dNNxIxxFg",
|
||||||
|
name: "Black Mamba Official",
|
||||||
|
avatar: [
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://lh3.googleusercontent.com/DBaVgQwyirgH4_Rg6w7jQBTP1fyHl5dNMK91dLZD5q2lFLoijOK3Or53rVnNgYfTQwDIofcLrG2QtxKQoQ=w60-h60-l90-rj",
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://lh3.googleusercontent.com/DBaVgQwyirgH4_Rg6w7jQBTP1fyHl5dNMK91dLZD5q2lFLoijOK3Or53rVnNgYfTQwDIofcLrG2QtxKQoQ=w120-h120-l90-rj",
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
subscriber_count: Some(140),
|
||||||
)),
|
)),
|
||||||
Artist(ArtistItem(
|
Artist(ArtistItem(
|
||||||
id: "UCLcwLJIGBDDvbfq8JERV6Ag",
|
id: "UCLcwLJIGBDDvbfq8JERV6Ag",
|
||||||
name: "Black Mamba",
|
name: "Black Mamba",
|
||||||
avatar: [
|
avatar: [
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://lh3.googleusercontent.com/qPN6oDatmKgTxytO4b8ScN1qGGMBpsF2_vH9OG1sSDn8Hew28J8vy9y4WNWOJYvSCyHbghIs_B5aGgkJ=w60-h60-l90-rj",
|
url: "https://lh3.googleusercontent.com/qorCs0oXX4VRdkGM6T6pG9IEugjWfeA9hWoGSzkH427PkRcMi5cJR6Vy4m_FTw-Bhmnj-sAHH54i7PI2=w60-h60-l90-rj",
|
||||||
width: 60,
|
width: 60,
|
||||||
height: 60,
|
height: 60,
|
||||||
),
|
),
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://lh3.googleusercontent.com/qPN6oDatmKgTxytO4b8ScN1qGGMBpsF2_vH9OG1sSDn8Hew28J8vy9y4WNWOJYvSCyHbghIs_B5aGgkJ=w120-h120-l90-rj",
|
url: "https://lh3.googleusercontent.com/qorCs0oXX4VRdkGM6T6pG9IEugjWfeA9hWoGSzkH427PkRcMi5cJR6Vy4m_FTw-Bhmnj-sAHH54i7PI2=w120-h120-l90-rj",
|
||||||
width: 120,
|
width: 120,
|
||||||
height: 120,
|
height: 120,
|
||||||
),
|
),
|
||||||
|
|
@ -362,86 +511,205 @@ MusicSearchResult(
|
||||||
subscriber_count: Some(9),
|
subscriber_count: Some(9),
|
||||||
)),
|
)),
|
||||||
Playlist(MusicPlaylistItem(
|
Playlist(MusicPlaylistItem(
|
||||||
id: "PLk76iSbFqNJsu_Gozn9SkEXxQ7t-bpXid",
|
id: "PLF1nPSf9c6AdlQDKMe5gW0ztSNR4GPrcl",
|
||||||
name: "IRMA MIRTILLA Black Mamba",
|
name: "The Mamba + Maddy Show",
|
||||||
thumbnail: [
|
thumbnail: [
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://i.ytimg.com/vi/md19pon3B9o/sddefault.jpg?sqp=-oaymwEWCJADEOEBIAQqCghqEJQEGHgg6AJIWg&rs=AMzJL3kR84wE4E_UufGzATfZhAsFWEieaA",
|
url: "https://i.ytimg.com/pl_c/PLF1nPSf9c6AdlQDKMe5gW0ztSNR4GPrcl/studio_square_thumbnail.jpg?sqp=CM3xtbkG-oaymwEICDwQPCAASFqi85f_AwYI3OWhtAY&rs=AMzJL3m9ePMyYiazdHhl0bve79YoANXNHA",
|
||||||
width: 400,
|
width: 60,
|
||||||
height: 225,
|
height: 60,
|
||||||
),
|
),
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://i.ytimg.com/vi/md19pon3B9o/hq720.jpg?sqp=-oaymwEXCKAGEMIDIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3nxumiGKYWYiiTokZB8M6rwtK5mRw",
|
url: "https://i.ytimg.com/pl_c/PLF1nPSf9c6AdlQDKMe5gW0ztSNR4GPrcl/studio_square_thumbnail.jpg?sqp=CM3xtbkG-oaymwEICHgQeCAASFqi85f_AwYI3OWhtAY&rs=AMzJL3kBrm54WfDV1202bhc_7NnFtuR2QA",
|
||||||
width: 800,
|
width: 120,
|
||||||
height: 450,
|
height: 120,
|
||||||
),
|
),
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://i.ytimg.com/vi/md19pon3B9o/hq720.jpg?sqp=-oaymwEXCNUGEOADIAQqCwjVARCqCBh4INgESFo&rs=AMzJL3mEU1yvpIHQXYgVnCyXx8Rlzilg6Q",
|
url: "https://i.ytimg.com/pl_c/PLF1nPSf9c6AdlQDKMe5gW0ztSNR4GPrcl/studio_square_thumbnail.jpg?sqp=CM3xtbkG-oaymwEKCOIBEOIBIABIWqLzl_8DBgjc5aG0Bg&rs=AMzJL3moPZlY6pebVzsucNM0hFJg6E1iOA",
|
||||||
width: 853,
|
width: 226,
|
||||||
height: 480,
|
height: 226,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://i.ytimg.com/pl_c/PLF1nPSf9c6AdlQDKMe5gW0ztSNR4GPrcl/studio_square_thumbnail.jpg?sqp=CM3xtbkG-oaymwEKCKAEEKAEIABIWqLzl_8DBgjc5aG0Bg&rs=AMzJL3kK8BcSgB8AnNLvrIlo25u5ldoy9A",
|
||||||
|
width: 544,
|
||||||
|
height: 544,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
channel: Some(ChannelId(
|
channel: None,
|
||||||
id: "UCtZaFx5MXZHIh7VTItJK1lQ",
|
|
||||||
name: "Lajos Fülöp",
|
|
||||||
)),
|
|
||||||
track_count: None,
|
track_count: None,
|
||||||
from_ytm: false,
|
from_ytm: false,
|
||||||
)),
|
)),
|
||||||
Playlist(MusicPlaylistItem(
|
Playlist(MusicPlaylistItem(
|
||||||
id: "PLIL9Q2jz6euDEJZKHd4QaG4iic944_vKY",
|
id: "PL4OEJAvKcBHAe32md9b1c9kEUNi6Ifbqu",
|
||||||
name: "Black Mamba",
|
name: "Tooth & Claw Podcast",
|
||||||
thumbnail: [
|
thumbnail: [
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://yt3.ggpht.com/jsvBK6isPIQ0ERSc1xV6PoaYxbYZqCzqr90lHZNEfUcQL2lP0oNzrdimX8KIBchE6X8myc58zwyS=s192",
|
url: "https://i.ytimg.com/vi/s7sLjdWb-D4/hqdefault.jpg?sqp=-oaymwExCI4CEI4CIAQqCggAEOADGC0guwJIWvKriqkDFZoCEgg0EDgYFyABLQAAoEE1zcxMPw&rs=AMzJL3m2chIFdDJTYKB7dW_xtWdZcsZg3A",
|
||||||
width: 192,
|
width: 270,
|
||||||
height: 192,
|
height: 270,
|
||||||
),
|
),
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://yt3.ggpht.com/jsvBK6isPIQ0ERSc1xV6PoaYxbYZqCzqr90lHZNEfUcQL2lP0oNzrdimX8KIBchE6X8myc58zwyS=s576",
|
url: "https://i.ytimg.com/vi/s7sLjdWb-D4/hq720.jpg?sqp=-oaymwElCNAFENAFIAZIWvKriqkDFZoCEgg0EDgYFyABLQAAoEE1zcxMPw&rs=AMzJL3klUEptMBmqrEkpdCBn4cnZ_dnaXw",
|
||||||
width: 576,
|
width: 720,
|
||||||
height: 576,
|
height: 720,
|
||||||
),
|
|
||||||
Thumbnail(
|
|
||||||
url: "https://yt3.ggpht.com/jsvBK6isPIQ0ERSc1xV6PoaYxbYZqCzqr90lHZNEfUcQL2lP0oNzrdimX8KIBchE6X8myc58zwyS=s1200",
|
|
||||||
width: 1200,
|
|
||||||
height: 1200,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
channel: Some(ChannelId(
|
channel: None,
|
||||||
id: "UCwFT0vvkbtbohtzVbwx7WjQ",
|
|
||||||
name: "Toshihiko KOMINAMI",
|
|
||||||
)),
|
|
||||||
track_count: None,
|
track_count: None,
|
||||||
from_ytm: false,
|
from_ytm: false,
|
||||||
)),
|
)),
|
||||||
Playlist(MusicPlaylistItem(
|
Playlist(MusicPlaylistItem(
|
||||||
id: "PLinm7-cvTdN7RqadpfNrncUGqkdyKNpn6",
|
id: "PLQ0daRB_QJ_ZkIhLnp_nPeURDM4vQM5yg",
|
||||||
name: "Black Mamba",
|
name: "You Should Know Podcast",
|
||||||
thumbnail: [
|
thumbnail: [
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://yt3.ggpht.com/hj6EywHSUD3UEnRQPHaEjHPC1VRi9UcsrkW8zGiOaXhRGlyNikLw6Iv0VnHTSuo2MlVBiQaskqo=s192",
|
url: "https://yt3.googleusercontent.com/n4-LrAD8Piik4s3N4OKu3gmotbTnjlJ30twT8IrUVMoNvSHBrCiFppALvovh52qVhvWifoR7jA=w60-c-h60-k-c0x00ffffff-no-l90-rj",
|
||||||
width: 192,
|
width: 60,
|
||||||
height: 192,
|
height: 60,
|
||||||
),
|
),
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://yt3.ggpht.com/hj6EywHSUD3UEnRQPHaEjHPC1VRi9UcsrkW8zGiOaXhRGlyNikLw6Iv0VnHTSuo2MlVBiQaskqo=s576",
|
url: "https://yt3.googleusercontent.com/n4-LrAD8Piik4s3N4OKu3gmotbTnjlJ30twT8IrUVMoNvSHBrCiFppALvovh52qVhvWifoR7jA=w120-c-h120-k-c0x00ffffff-no-l90-rj",
|
||||||
width: 576,
|
width: 120,
|
||||||
height: 576,
|
height: 120,
|
||||||
),
|
),
|
||||||
Thumbnail(
|
Thumbnail(
|
||||||
url: "https://yt3.ggpht.com/hj6EywHSUD3UEnRQPHaEjHPC1VRi9UcsrkW8zGiOaXhRGlyNikLw6Iv0VnHTSuo2MlVBiQaskqo=s1200",
|
url: "https://yt3.googleusercontent.com/n4-LrAD8Piik4s3N4OKu3gmotbTnjlJ30twT8IrUVMoNvSHBrCiFppALvovh52qVhvWifoR7jA=w226-c-h226-k-c0x00ffffff-no-l90-rj",
|
||||||
width: 1200,
|
width: 226,
|
||||||
height: 1200,
|
height: 226,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://yt3.googleusercontent.com/n4-LrAD8Piik4s3N4OKu3gmotbTnjlJ30twT8IrUVMoNvSHBrCiFppALvovh52qVhvWifoR7jA=w544-c-h544-k-c0x00ffffff-no-l90-rj",
|
||||||
|
width: 544,
|
||||||
|
height: 544,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
channel: Some(ChannelId(
|
channel: None,
|
||||||
id: "UCEdZAdnnKqbaHOlv8nM6OtA",
|
|
||||||
name: "aespa",
|
|
||||||
)),
|
|
||||||
track_count: None,
|
track_count: None,
|
||||||
from_ytm: false,
|
from_ytm: false,
|
||||||
)),
|
)),
|
||||||
|
Track(TrackItem(
|
||||||
|
id: "xd-9D3GzUpo",
|
||||||
|
name: "MAMBA MENTALITY - Kobe Bryant Motivational Speech",
|
||||||
|
duration: None,
|
||||||
|
cover: [
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://i.ytimg.com/vi/xd-9D3GzUpo/hqdefault.jpg?sqp=-oaymwEWCOADEI4CIAQqCggAEOADGC0guwJIWg&rs=AMzJL3k3TxrniLSRkQR1LMtpKFsrd-x-Vg",
|
||||||
|
width: 480,
|
||||||
|
height: 270,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artists: [
|
||||||
|
ArtistId(
|
||||||
|
id: None,
|
||||||
|
name: "Discipline Motivation - Best Motivational Speeches By Motiversity",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artist_id: None,
|
||||||
|
album: None,
|
||||||
|
view_count: None,
|
||||||
|
is_video: true,
|
||||||
|
track_nr: None,
|
||||||
|
by_va: false,
|
||||||
|
)),
|
||||||
|
Track(TrackItem(
|
||||||
|
id: "GE0UAdxPTc0",
|
||||||
|
name: "THE MAMBA MENTALITY - Kobe Bryant Motivational Speech Compilation",
|
||||||
|
duration: None,
|
||||||
|
cover: [
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://i.ytimg.com/vi/GE0UAdxPTc0/hqdefault.jpg?sqp=-oaymwEWCOADEI4CIAQqCggAEOADGC0guwJIWg&rs=AMzJL3mI3Lbo29pKfU9Qpv3lLY04Fi0yLg",
|
||||||
|
width: 480,
|
||||||
|
height: 270,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artists: [
|
||||||
|
ArtistId(
|
||||||
|
id: None,
|
||||||
|
name: "Motivation Daily by Motiversity",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artist_id: None,
|
||||||
|
album: None,
|
||||||
|
view_count: None,
|
||||||
|
is_video: true,
|
||||||
|
track_nr: None,
|
||||||
|
by_va: false,
|
||||||
|
)),
|
||||||
|
Track(TrackItem(
|
||||||
|
id: "4gi9y3sTrXE",
|
||||||
|
name: "Mamba Mentality - Kobe Bryant (Motivational Video)",
|
||||||
|
duration: None,
|
||||||
|
cover: [
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://i.ytimg.com/vi/4gi9y3sTrXE/hqdefault.jpg?sqp=-oaymwEWCOADEI4CIAQqCggAEOADGC0guwJIWg&rs=AMzJL3mi8Id0rBphHeAUx35-u2iDbL2liQ",
|
||||||
|
width: 480,
|
||||||
|
height: 270,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artists: [
|
||||||
|
ArtistId(
|
||||||
|
id: None,
|
||||||
|
name: "Powerful Motivational Speech (Chispa Motivation)",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
artist_id: None,
|
||||||
|
album: None,
|
||||||
|
view_count: None,
|
||||||
|
is_video: true,
|
||||||
|
track_nr: None,
|
||||||
|
by_va: false,
|
||||||
|
)),
|
||||||
|
User(UserItem(
|
||||||
|
id: "UCOeTBeQwhOSvNcaZhxM1PUg",
|
||||||
|
name: "Black Mamba",
|
||||||
|
handle: Some("@blackmambagyn"),
|
||||||
|
avatar: [
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://yt3.googleusercontent.com/WR8-SnEMVJ-FRQxo0M_nsOO5ceDql9vWSZ8Os4pyrPd6gLE_cLm3K68F6Ozh38gKoYke9FSL1g=w60-c-h60-k-c0x00ffffff-no-l90-rj",
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://yt3.googleusercontent.com/WR8-SnEMVJ-FRQxo0M_nsOO5ceDql9vWSZ8Os4pyrPd6gLE_cLm3K68F6Ozh38gKoYke9FSL1g=w120-c-h120-k-c0x00ffffff-no-l90-rj",
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
User(UserItem(
|
||||||
|
id: "UCpxiesQUPBb1H-rCNR9vU-w",
|
||||||
|
name: "BLACK MAMBA",
|
||||||
|
handle: Some("@BLACKMAMBA-lv4xw"),
|
||||||
|
avatar: [
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://yt3.googleusercontent.com/TunC2xLBq7LNTJHQYyRKFqyUp6QNUl0ZNDo3axPBiDIWMDAsaOvHPi6cHqzdONhLXcFOPCU2FA=w60-c-h60-k-c0x00ffffff-no-l90-rj",
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://yt3.googleusercontent.com/TunC2xLBq7LNTJHQYyRKFqyUp6QNUl0ZNDo3axPBiDIWMDAsaOvHPi6cHqzdONhLXcFOPCU2FA=w120-c-h120-k-c0x00ffffff-no-l90-rj",
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
User(UserItem(
|
||||||
|
id: "UC04-OP5K9gQ_0x39dERUBQw",
|
||||||
|
name: "Black Mamba",
|
||||||
|
handle: Some("@blackmamba1294"),
|
||||||
|
avatar: [
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://yt3.googleusercontent.com/ytc/AIdro_n4zT7h4GQ1HWesFb706jhqzxvNRZlV3oAmF4ug-OU=w60-c-h60-k-c0x00ffffff-no-l90-rj",
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://yt3.googleusercontent.com/ytc/AIdro_n4zT7h4GQ1HWesFb706jhqzxvNRZlV3oAmF4ug-OU=w120-c-h120-k-c0x00ffffff-no-l90-rj",
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
],
|
],
|
||||||
ctoken: None,
|
ctoken: None,
|
||||||
endpoint: music_search,
|
endpoint: music_search,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ expression: map_res.c
|
||||||
---
|
---
|
||||||
MusicSearchResult(
|
MusicSearchResult(
|
||||||
items: Paginator(
|
items: Paginator(
|
||||||
count: Some(24),
|
count: Some(27),
|
||||||
items: [
|
items: [
|
||||||
Playlist(MusicPlaylistItem(
|
Playlist(MusicPlaylistItem(
|
||||||
id: "RDATficG9wIHJhZGlv",
|
id: "RDATficG9wIHJhZGlv",
|
||||||
|
|
@ -650,6 +650,57 @@ MusicSearchResult(
|
||||||
track_nr: None,
|
track_nr: None,
|
||||||
by_va: false,
|
by_va: false,
|
||||||
)),
|
)),
|
||||||
|
User(UserItem(
|
||||||
|
id: "UCdQrWgvHD9f-caMSn3SS-WQ",
|
||||||
|
name: "Pop Hist Radio",
|
||||||
|
handle: Some("@PopHistRadio"),
|
||||||
|
avatar: [
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://yt3.googleusercontent.com/2Py1-HUmfdmgxE3PEL-EdCE4lgGktuklnIOPdBy0N1_51Ne65bW5gJnxcc-eJB9vxhbU4-JQCg=w60-c-h60-k-c0x00ffffff-no-l90-rj",
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://yt3.googleusercontent.com/2Py1-HUmfdmgxE3PEL-EdCE4lgGktuklnIOPdBy0N1_51Ne65bW5gJnxcc-eJB9vxhbU4-JQCg=w120-c-h120-k-c0x00ffffff-no-l90-rj",
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
User(UserItem(
|
||||||
|
id: "UC-j25R4eGB_pjsaRxF5WOoQ",
|
||||||
|
name: "HMD RADIO POP",
|
||||||
|
handle: Some("@hmdradiopop5581"),
|
||||||
|
avatar: [
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://yt3.googleusercontent.com/ytc/AIdro_kuTfeNrt95jHhzzMfQoqFSGWo2cCfUsZ_OHH5zpeM=w60-c-h60-k-c0x00ffffff-no-l90-rj",
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://yt3.googleusercontent.com/ytc/AIdro_kuTfeNrt95jHhzzMfQoqFSGWo2cCfUsZ_OHH5zpeM=w120-c-h120-k-c0x00ffffff-no-l90-rj",
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
User(UserItem(
|
||||||
|
id: "UCRkEipin-M9fQ12UWxc9UGQ",
|
||||||
|
name: "MUSIC RADIO",
|
||||||
|
handle: Some("@musicradio8514"),
|
||||||
|
avatar: [
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://yt3.googleusercontent.com/evLHWABaoAFyXkfnSH95NZ7Fj96AmPW4iHp7pQckNn48PBmKeIvrkgnMtSdwQCOuqLx3tpQIuA=w60-c-h60-k-c0x00ffffff-no-l90-rj",
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
),
|
||||||
|
Thumbnail(
|
||||||
|
url: "https://yt3.googleusercontent.com/evLHWABaoAFyXkfnSH95NZ7Fj96AmPW4iHp7pQckNn48PBmKeIvrkgnMtSdwQCOuqLx3tpQIuA=w120-c-h120-k-c0x00ffffff-no-l90-rj",
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
],
|
],
|
||||||
ctoken: None,
|
ctoken: None,
|
||||||
endpoint: music_search,
|
endpoint: music_search,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use super::{
|
use super::{
|
||||||
AlbumItem, ArtistId, ArtistItem, Channel, ChannelId, ChannelItem, ChannelRssVideo, ChannelTag,
|
AlbumItem, ArtistId, ArtistItem, Channel, ChannelId, ChannelItem, ChannelRssVideo, ChannelTag,
|
||||||
MusicArtist, MusicItem, MusicPlaylistItem, PlaylistItem, TrackItem, VideoId, VideoItem,
|
MusicArtist, MusicItem, MusicPlaylistItem, PlaylistItem, TrackItem, UserItem, VideoId,
|
||||||
YouTubeItem,
|
VideoItem, YouTubeItem,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Trait for casting generic YouTube/YouTube music items to a specific kind.
|
/// Trait for casting generic YouTube/YouTube music items to a specific kind.
|
||||||
|
|
@ -139,6 +139,21 @@ impl From<MusicPlaylistItem> for MusicItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromYtItem for UserItem {
|
||||||
|
fn from_ytm_item(item: MusicItem) -> Option<Self> {
|
||||||
|
match item {
|
||||||
|
MusicItem::User(user) => Some(user),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UserItem> for MusicItem {
|
||||||
|
fn from(value: UserItem) -> Self {
|
||||||
|
Self::User(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> From<Channel<T>> for ChannelTag {
|
impl<T> From<Channel<T>> for ChannelTag {
|
||||||
fn from(channel: Channel<T>) -> Self {
|
fn from(channel: Channel<T>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
||||||
|
|
@ -964,6 +964,19 @@ pub struct ArtistItem {
|
||||||
pub subscriber_count: Option<u64>,
|
pub subscriber_count: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// YouTube Music user item
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
pub struct UserItem {
|
||||||
|
/// Unique YouTube user ID
|
||||||
|
pub id: String,
|
||||||
|
/// User name
|
||||||
|
pub name: String,
|
||||||
|
/// YouTube channel handle (e.g. `@EEVblog`)
|
||||||
|
pub handle: Option<String>,
|
||||||
|
/// User avatar/profile picture
|
||||||
|
pub avatar: Vec<Thumbnail>,
|
||||||
|
}
|
||||||
|
|
||||||
/// YouTube Music artist identifier
|
/// YouTube Music artist identifier
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
|
@ -1140,6 +1153,7 @@ pub enum MusicItem {
|
||||||
Album(AlbumItem),
|
Album(AlbumItem),
|
||||||
Artist(ArtistItem),
|
Artist(ArtistItem),
|
||||||
Playlist(MusicPlaylistItem),
|
Playlist(MusicPlaylistItem),
|
||||||
|
User(UserItem),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// YouTube Music item type
|
/// YouTube Music item type
|
||||||
|
|
@ -1150,6 +1164,7 @@ pub enum MusicItemType {
|
||||||
Album,
|
Album,
|
||||||
Artist,
|
Artist,
|
||||||
Playlist,
|
Playlist,
|
||||||
|
User,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// YouTube Music search result
|
/// YouTube Music search result
|
||||||
|
|
|
||||||
|
|
@ -143,10 +143,12 @@ pub trait YtEntity {
|
||||||
///
|
///
|
||||||
/// `None` if the entity does not belong to a channel
|
/// `None` if the entity does not belong to a channel
|
||||||
fn channel_name(&self) -> Option<&str>;
|
fn channel_name(&self) -> Option<&str>;
|
||||||
|
/// YTM item type
|
||||||
|
fn music_item_type(&self) -> Option<MusicItemType>;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! yt_entity {
|
macro_rules! yt_entity {
|
||||||
($entity_type:ty) => {
|
($entity_type:ty, $music_item_type:expr) => {
|
||||||
impl YtEntity for $entity_type {
|
impl YtEntity for $entity_type {
|
||||||
fn id(&self) -> &str {
|
fn id(&self) -> &str {
|
||||||
&self.id
|
&self.id
|
||||||
|
|
@ -163,12 +165,16 @@ macro_rules! yt_entity {
|
||||||
fn channel_name(&self) -> Option<&str> {
|
fn channel_name(&self) -> Option<&str> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn music_item_type(&self) -> Option<MusicItemType> {
|
||||||
|
$music_item_type
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! yt_entity_owner {
|
macro_rules! yt_entity_owner {
|
||||||
($entity_type:ty) => {
|
($entity_type:ty, $music_item_type:expr) => {
|
||||||
impl YtEntity for $entity_type {
|
impl YtEntity for $entity_type {
|
||||||
fn id(&self) -> &str {
|
fn id(&self) -> &str {
|
||||||
&self.id
|
&self.id
|
||||||
|
|
@ -185,12 +191,16 @@ macro_rules! yt_entity_owner {
|
||||||
fn channel_name(&self) -> Option<&str> {
|
fn channel_name(&self) -> Option<&str> {
|
||||||
Some(&self.channel.name)
|
Some(&self.channel.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn music_item_type(&self) -> Option<MusicItemType> {
|
||||||
|
Some($music_item_type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! yt_entity_owner_opt {
|
macro_rules! yt_entity_owner_opt {
|
||||||
($entity_type:ty) => {
|
($entity_type:ty, $music_item_type:expr) => {
|
||||||
impl YtEntity for $entity_type {
|
impl YtEntity for $entity_type {
|
||||||
fn id(&self) -> &str {
|
fn id(&self) -> &str {
|
||||||
&self.id
|
&self.id
|
||||||
|
|
@ -207,12 +217,16 @@ macro_rules! yt_entity_owner_opt {
|
||||||
fn channel_name(&self) -> Option<&str> {
|
fn channel_name(&self) -> Option<&str> {
|
||||||
self.channel.as_ref().map(|c| c.name.as_str())
|
self.channel.as_ref().map(|c| c.name.as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn music_item_type(&self) -> Option<MusicItemType> {
|
||||||
|
Some($music_item_type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! yt_entity_owner_music {
|
macro_rules! yt_entity_owner_music {
|
||||||
($entity_type:ty) => {
|
($entity_type:ty, $music_item_type:expr) => {
|
||||||
impl YtEntity for $entity_type {
|
impl YtEntity for $entity_type {
|
||||||
fn id(&self) -> &str {
|
fn id(&self) -> &str {
|
||||||
&self.id
|
&self.id
|
||||||
|
|
@ -233,6 +247,10 @@ macro_rules! yt_entity_owner_music {
|
||||||
self.artists.first().map(|a| a.name.as_str())
|
self.artists.first().map(|a| a.name.as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn music_item_type(&self) -> Option<MusicItemType> {
|
||||||
|
Some($music_item_type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -253,6 +271,10 @@ impl<T> YtEntity for Channel<T> {
|
||||||
fn channel_name(&self) -> Option<&str> {
|
fn channel_name(&self) -> Option<&str> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn music_item_type(&self) -> Option<MusicItemType> {
|
||||||
|
Some(MusicItemType::User)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl YtEntity for YouTubeItem {
|
impl YtEntity for YouTubeItem {
|
||||||
|
|
@ -287,6 +309,14 @@ impl YtEntity for YouTubeItem {
|
||||||
YouTubeItem::Channel(_) => None,
|
YouTubeItem::Channel(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn music_item_type(&self) -> Option<MusicItemType> {
|
||||||
|
Some(match self {
|
||||||
|
YouTubeItem::Video(_) => MusicItemType::Track,
|
||||||
|
YouTubeItem::Playlist(_) => MusicItemType::Playlist,
|
||||||
|
YouTubeItem::Channel(_) => MusicItemType::User,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl YtEntity for MusicItem {
|
impl YtEntity for MusicItem {
|
||||||
|
|
@ -296,6 +326,7 @@ impl YtEntity for MusicItem {
|
||||||
MusicItem::Album(b) => &b.id,
|
MusicItem::Album(b) => &b.id,
|
||||||
MusicItem::Artist(a) => &a.id,
|
MusicItem::Artist(a) => &a.id,
|
||||||
MusicItem::Playlist(p) => &p.id,
|
MusicItem::Playlist(p) => &p.id,
|
||||||
|
MusicItem::User(u) => &u.id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -305,6 +336,7 @@ impl YtEntity for MusicItem {
|
||||||
MusicItem::Album(b) => &b.name,
|
MusicItem::Album(b) => &b.name,
|
||||||
MusicItem::Artist(a) => &a.name,
|
MusicItem::Artist(a) => &a.name,
|
||||||
MusicItem::Playlist(p) => &p.name,
|
MusicItem::Playlist(p) => &p.name,
|
||||||
|
MusicItem::User(u) => &u.name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -312,7 +344,7 @@ impl YtEntity for MusicItem {
|
||||||
match self {
|
match self {
|
||||||
MusicItem::Track(t) => t.channel_id(),
|
MusicItem::Track(t) => t.channel_id(),
|
||||||
MusicItem::Album(b) => b.channel_id(),
|
MusicItem::Album(b) => b.channel_id(),
|
||||||
MusicItem::Artist(_) => None,
|
MusicItem::Artist(_) | MusicItem::User(_) => None,
|
||||||
MusicItem::Playlist(p) => p.channel_id(),
|
MusicItem::Playlist(p) => p.channel_id(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -321,29 +353,40 @@ impl YtEntity for MusicItem {
|
||||||
match self {
|
match self {
|
||||||
MusicItem::Track(t) => t.channel_name(),
|
MusicItem::Track(t) => t.channel_name(),
|
||||||
MusicItem::Album(b) => b.channel_name(),
|
MusicItem::Album(b) => b.channel_name(),
|
||||||
MusicItem::Artist(_) => None,
|
MusicItem::Artist(_) | MusicItem::User(_) => None,
|
||||||
MusicItem::Playlist(p) => p.channel_id(),
|
MusicItem::Playlist(p) => p.channel_name(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn music_item_type(&self) -> Option<MusicItemType> {
|
||||||
|
Some(match self {
|
||||||
|
MusicItem::Track(_) => MusicItemType::Track,
|
||||||
|
MusicItem::Album(_) => MusicItemType::Album,
|
||||||
|
MusicItem::Artist(_) => MusicItemType::Artist,
|
||||||
|
MusicItem::Playlist(_) => MusicItemType::Playlist,
|
||||||
|
MusicItem::User(_) => MusicItemType::User,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
yt_entity_owner_opt! {Playlist}
|
yt_entity_owner_opt! {Playlist, MusicItemType::Playlist}
|
||||||
yt_entity! {ChannelId}
|
yt_entity! {ChannelId, Some(MusicItemType::User)}
|
||||||
yt_entity_owner! {VideoDetails}
|
yt_entity_owner! {VideoDetails, MusicItemType::Track}
|
||||||
yt_entity! {ChannelTag}
|
yt_entity! {ChannelTag, Some(MusicItemType::User)}
|
||||||
yt_entity! {ChannelRss}
|
yt_entity! {ChannelRss, Some(MusicItemType::User)}
|
||||||
yt_entity! {ChannelRssVideo}
|
yt_entity! {ChannelRssVideo, Some(MusicItemType::Track)}
|
||||||
yt_entity_owner_opt! {VideoItem}
|
yt_entity_owner_opt! {VideoItem, MusicItemType::Track}
|
||||||
yt_entity! {ChannelItem}
|
yt_entity! {ChannelItem, Some(MusicItemType::User)}
|
||||||
yt_entity_owner_opt! {PlaylistItem}
|
yt_entity_owner_opt! {PlaylistItem, MusicItemType::Playlist}
|
||||||
yt_entity! {VideoId}
|
yt_entity! {VideoId, Some(MusicItemType::Track)}
|
||||||
yt_entity_owner_music! {TrackItem}
|
yt_entity_owner_music! {TrackItem, MusicItemType::Track}
|
||||||
yt_entity! {ArtistItem}
|
yt_entity! {ArtistItem, Some(MusicItemType::Artist)}
|
||||||
yt_entity_owner_music! {AlbumItem}
|
yt_entity_owner_music! {AlbumItem, MusicItemType::Album}
|
||||||
yt_entity_owner_opt! {MusicPlaylistItem}
|
yt_entity_owner_opt! {MusicPlaylistItem, MusicItemType::Playlist}
|
||||||
yt_entity! {AlbumId}
|
yt_entity! {AlbumId, Some(MusicItemType::Album)}
|
||||||
yt_entity_owner_opt! {MusicPlaylist}
|
yt_entity_owner_opt! {MusicPlaylist, MusicItemType::Playlist}
|
||||||
yt_entity_owner_music! {MusicAlbum}
|
yt_entity_owner_music! {MusicAlbum, MusicItemType::Album}
|
||||||
yt_entity! {MusicArtist}
|
yt_entity! {MusicArtist, Some(MusicItemType::Artist)}
|
||||||
yt_entity! {MusicGenreItem}
|
yt_entity! {UserItem, Some(MusicItemType::User)}
|
||||||
yt_entity! {MusicGenre}
|
yt_entity! {MusicGenreItem, None}
|
||||||
|
yt_entity! {MusicGenre, None}
|
||||||
|
|
|
||||||
|
|
@ -236,17 +236,20 @@ pub enum MusicSearchFilter {
|
||||||
YtmPlaylists,
|
YtmPlaylists,
|
||||||
/// Playlists created by YouTube users
|
/// Playlists created by YouTube users
|
||||||
CommunityPlaylists,
|
CommunityPlaylists,
|
||||||
|
/// Users
|
||||||
|
Users,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MusicSearchFilter {
|
impl MusicSearchFilter {
|
||||||
pub(crate) fn params(self) -> &'static str {
|
pub(crate) fn params(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
MusicSearchFilter::Tracks => "EgWKAQIIAWoMEAMQBBAJEA4QChAF",
|
MusicSearchFilter::Tracks => "EgWKAQIIAWoQEAMQBBAJEAoQBRAREBAQFQ%3D%3D",
|
||||||
MusicSearchFilter::Videos => "EgWKAQIQAWoMEAMQBBAJEA4QChAF",
|
MusicSearchFilter::Videos => "EgWKAQIQAWoQEAMQBBAJEAoQBRAREBAQFQ%3D%3D",
|
||||||
MusicSearchFilter::Albums => "EgWKAQIYAWoMEAMQBBAJEA4QChAF",
|
MusicSearchFilter::Albums => "EgWKAQIYAWoQEAMQBBAJEAoQBRAREBAQFQ%3D%3D",
|
||||||
MusicSearchFilter::Artists => "EgWKAQIgAWoMEAMQBBAJEA4QChAF",
|
MusicSearchFilter::Artists => "EgWKAQIgAWoQEAMQBBAJEAoQBRAREBAQFQ%3D%3D",
|
||||||
MusicSearchFilter::YtmPlaylists => "EgeKAQQoADgBagwQAxAEEAkQDhAKEAU%3D",
|
MusicSearchFilter::YtmPlaylists => "EgeKAQQoADgBahIQAxAEEAkQDhAKEAUQERAQEBU%3D",
|
||||||
MusicSearchFilter::CommunityPlaylists => "EgeKAQQoAEABagwQAxAEEAkQDhAKEAU%3D",
|
MusicSearchFilter::CommunityPlaylists => "EgeKAQQoAEABahAQAxAEEAkQChAFEBEQEBAV",
|
||||||
|
MusicSearchFilter::Users => "EgWKAQJYAWoQEAMQBBAJEAoQBRAREBAQFQ%3D%3D",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1902,6 +1902,7 @@ fn check_search_result(items: &[MusicItem]) {
|
||||||
let mut has_albums = false;
|
let mut has_albums = false;
|
||||||
let mut has_artists = false;
|
let mut has_artists = false;
|
||||||
let mut has_playlists = false;
|
let mut has_playlists = false;
|
||||||
|
let mut has_users = false;
|
||||||
|
|
||||||
for itm in items {
|
for itm in items {
|
||||||
match itm {
|
match itm {
|
||||||
|
|
@ -1915,6 +1916,7 @@ fn check_search_result(items: &[MusicItem]) {
|
||||||
MusicItem::Album(_) => has_albums = true,
|
MusicItem::Album(_) => has_albums = true,
|
||||||
MusicItem::Artist(_) => has_artists = true,
|
MusicItem::Artist(_) => has_artists = true,
|
||||||
MusicItem::Playlist(_) => has_playlists = true,
|
MusicItem::Playlist(_) => has_playlists = true,
|
||||||
|
MusicItem::User(_) => has_users = true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1923,6 +1925,7 @@ fn check_search_result(items: &[MusicItem]) {
|
||||||
assert!(has_albums, "no albums");
|
assert!(has_albums, "no albums");
|
||||||
assert!(has_artists, "no artists");
|
assert!(has_artists, "no artists");
|
||||||
assert!(has_playlists, "no playlists");
|
assert!(has_playlists, "no playlists");
|
||||||
|
assert!(has_users, "no users");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
|
|
@ -2204,6 +2207,30 @@ async fn music_search_playlists_community(rp: RustyPipe) {
|
||||||
assert!(!playlist.from_ytm);
|
assert!(!playlist.from_ytm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn music_search_users(rp: RustyPipe) {
|
||||||
|
let res = rp
|
||||||
|
.query()
|
||||||
|
.music_search_users("amyprincesspink")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(res.corrected_query, None);
|
||||||
|
let user = res
|
||||||
|
.items
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.find(|u| u.id == "UC-CeCRHc8D47hh8P_9MR5Vg")
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
panic!("could not find user, got {:#?}", &res.items.items);
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(user.name, "amyprincesspink");
|
||||||
|
assert_eq!(user.handle.as_deref().unwrap(), "@amyprincesspink");
|
||||||
|
assert!(!user.avatar.is_empty(), "got no avatar");
|
||||||
|
}
|
||||||
|
|
||||||
/// The YouTube Music search sometimes shows genre radio items. They should be skipped.
|
/// The YouTube Music search sometimes shows genre radio items. They should be skipped.
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|
|
||||||
Reference in a new issue