fix: support MusicCardShelfRenderer (music search results)
This commit is contained in:
parent
0164fac2e0
commit
b7ecd1e4a3
7 changed files with 9516 additions and 3 deletions
|
|
@ -31,6 +31,8 @@ pub(crate) enum ItemSection {
|
|||
None,
|
||||
}
|
||||
|
||||
/// MusicShelf represents the standard, vertical list of music items
|
||||
/// (used in search results, playlist, album).
|
||||
#[serde_as]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
|
@ -49,6 +51,8 @@ pub(crate) struct MusicShelf {
|
|||
pub bottom_endpoint: Option<BrowseEndpointWrap>,
|
||||
}
|
||||
|
||||
/// MusicCarouselShelf represents a horizontal list of music items displayed with
|
||||
/// large covers.
|
||||
#[serde_as]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
|
@ -58,6 +62,24 @@ pub(crate) struct MusicCarouselShelf {
|
|||
pub contents: MapResult<Vec<MusicResponseItem>>,
|
||||
}
|
||||
|
||||
/// MusicCardShelf is used to display the top search result. It contains
|
||||
/// one main item and optionally a list of sub-items (like an artist + top tracks).
|
||||
#[serde_as]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct MusicCardShelf {
|
||||
#[serde_as(as = "Text")]
|
||||
pub title: String,
|
||||
pub on_tap: NavigationEndpoint,
|
||||
#[serde(default)]
|
||||
pub subtitle: TextComponents,
|
||||
#[serde(default)]
|
||||
pub thumbnail: MusicThumbnailRenderer,
|
||||
#[serde(default)]
|
||||
#[serde_as(as = "VecLogError<_>")]
|
||||
pub contents: MapResult<Vec<MusicResponseItem>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) enum MusicResponseItem {
|
||||
|
|
@ -888,6 +910,120 @@ impl MusicListMapper {
|
|||
etype
|
||||
}
|
||||
|
||||
pub fn map_card(&mut self, card: MusicCardShelf) -> Option<MusicItemType> {
|
||||
/*
|
||||
"Artist" " • " "<subscriber count>"
|
||||
"Album" " • " "<artist>"
|
||||
"Song" " • " "<artist>" " • " "<album>" " • " "<duration>"
|
||||
"Video" " • " "<artist>" " • " "<view count>" " • " "<duration>"
|
||||
"Playlist" " • " "<author>" " • " "<track count>" (guessed)
|
||||
*/
|
||||
let mut subtitle_parts = card.subtitle.split(util::DOT_SEPARATOR).into_iter();
|
||||
let subtitle_p1 = subtitle_parts.next();
|
||||
let subtitle_p2 = subtitle_parts.next();
|
||||
let subtitle_p3 = subtitle_parts.next();
|
||||
let subtitle_p4 = subtitle_parts.next();
|
||||
|
||||
let item_type = match card.on_tap.music_page() {
|
||||
Some((page_type, id)) => match page_type {
|
||||
MusicPageType::Artist => {
|
||||
let subscriber_count = subtitle_p2
|
||||
.and_then(|p| util::parse_large_numstr(p.first_str(), self.lang));
|
||||
|
||||
self.items.push(MusicItem::Artist(ArtistItem {
|
||||
id,
|
||||
name: card.title,
|
||||
avatar: card.thumbnail.into(),
|
||||
subscriber_count,
|
||||
}));
|
||||
Some(MusicItemType::Artist)
|
||||
}
|
||||
MusicPageType::Album => {
|
||||
let (artists, by_va) = map_artists(subtitle_p2);
|
||||
let album_type = subtitle_p1
|
||||
.map(|p| map_album_type(p.first_str(), self.lang))
|
||||
.unwrap_or_default();
|
||||
|
||||
self.items.push(MusicItem::Album(AlbumItem {
|
||||
id,
|
||||
name: card.title,
|
||||
cover: card.thumbnail.into(),
|
||||
artist_id: artists.first().and_then(|a| a.id.to_owned()),
|
||||
artists,
|
||||
album_type,
|
||||
year: subtitle_p3.and_then(|y| util::parse_numeric(y.first_str()).ok()),
|
||||
by_va,
|
||||
}));
|
||||
Some(MusicItemType::Album)
|
||||
}
|
||||
MusicPageType::Track { is_video } => {
|
||||
let (artists, by_va) = map_artists(subtitle_p2);
|
||||
let duration =
|
||||
subtitle_p4.and_then(|p| util::parse_video_length(p.first_str()));
|
||||
let (album, view_count) = if is_video {
|
||||
(
|
||||
None,
|
||||
subtitle_p3
|
||||
.and_then(|p| util::parse_large_numstr(p.first_str(), self.lang)),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
subtitle_p3.and_then(|p| {
|
||||
p.0.into_iter().find_map(|c| AlbumId::try_from(c).ok())
|
||||
}),
|
||||
None,
|
||||
)
|
||||
};
|
||||
|
||||
self.items.push(MusicItem::Track(TrackItem {
|
||||
id,
|
||||
name: card.title,
|
||||
duration,
|
||||
cover: card.thumbnail.into(),
|
||||
artist_id: artists.first().and_then(|a| a.id.to_owned()),
|
||||
artists,
|
||||
album,
|
||||
view_count,
|
||||
is_video,
|
||||
track_nr: None,
|
||||
by_va,
|
||||
}));
|
||||
Some(MusicItemType::Track)
|
||||
}
|
||||
MusicPageType::Playlist => {
|
||||
let from_ytm = subtitle_p2
|
||||
.as_ref()
|
||||
.map(|p| p.first_str() == util::YT_MUSIC_NAME)
|
||||
.unwrap_or(true);
|
||||
let channel = subtitle_p2
|
||||
.and_then(|p| p.0.into_iter().find_map(|c| ChannelId::try_from(c).ok()));
|
||||
let track_count =
|
||||
subtitle_p3.and_then(|p| util::parse_numeric(p.first_str()).ok());
|
||||
|
||||
self.items.push(MusicItem::Playlist(MusicPlaylistItem {
|
||||
id,
|
||||
name: card.title,
|
||||
thumbnail: card.thumbnail.into(),
|
||||
channel,
|
||||
track_count,
|
||||
from_ytm,
|
||||
}));
|
||||
Some(MusicItemType::Playlist)
|
||||
}
|
||||
MusicPageType::None => None,
|
||||
},
|
||||
None => {
|
||||
self.warnings
|
||||
.push("could not determine item type".to_owned());
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
self.map_response(card.contents);
|
||||
|
||||
item_type
|
||||
}
|
||||
|
||||
pub fn add_item(&mut self, item: MusicItem) {
|
||||
self.items.push(item);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use serde_with::{rust::deserialize_ignore_any, serde_as, VecSkipError};
|
|||
use crate::serializer::text::Text;
|
||||
|
||||
use super::{
|
||||
music_item::{ListMusicItem, MusicShelf},
|
||||
music_item::{ListMusicItem, MusicCardShelf, MusicShelf},
|
||||
ContentsRenderer, SectionList, Tab,
|
||||
};
|
||||
|
||||
|
|
@ -37,6 +37,7 @@ pub(crate) struct Contents {
|
|||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) enum ItemSection {
|
||||
MusicShelfRenderer(MusicShelf),
|
||||
MusicCardShelfRenderer(MusicCardShelf),
|
||||
ItemSectionRenderer {
|
||||
#[serde_as(as = "VecSkipError<_>")]
|
||||
contents: Vec<ShowingResultsFor>,
|
||||
|
|
|
|||
Reference in a new issue