fix: accept music album playlist response without header

This commit is contained in:
ThetaDev 2022-11-30 21:11:33 +01:00
parent 8097873fe1
commit 38bc12f695
3 changed files with 74 additions and 37 deletions

View file

@ -2,7 +2,7 @@ use std::borrow::Cow;
use crate::{ use crate::{
error::{Error, ExtractionError}, error::{Error, ExtractionError},
model::{AlbumId, ChannelId, MusicAlbum, MusicPlaylist, Paginator}, model::{AlbumId, ChannelId, MusicAlbum, MusicPlaylist, Paginator, TrackItem},
serializer::MapResult, serializer::MapResult,
util::{self, TryRemove}, util::{self, TryRemove},
}; };
@ -65,8 +65,6 @@ impl MapResponse<MusicPlaylist> for response::MusicPlaylist {
) -> Result<MapResult<MusicPlaylist>, ExtractionError> { ) -> Result<MapResult<MusicPlaylist>, ExtractionError> {
// dbg!(&self); // dbg!(&self);
let header = self.header.music_detail_header_renderer;
let mut content = self.contents.single_column_browse_results_renderer.contents; let mut content = self.contents.single_column_browse_results_renderer.contents;
let mut music_contents = content let mut music_contents = content
.try_swap_remove(0) .try_swap_remove(0)
@ -85,31 +83,15 @@ impl MapResponse<MusicPlaylist> for response::MusicPlaylist {
"no sectionListRenderer content", "no sectionListRenderer content",
)))?; )))?;
let playlist_id = shelf if let Some(playlist_id) = shelf.playlist_id {
.playlist_id if playlist_id != id {
.ok_or(ExtractionError::InvalidData(Cow::Borrowed( return Err(ExtractionError::WrongResult(format!(
"no playlist id", "got wrong playlist id {}, expected {}",
)))?; playlist_id, id
)));
if playlist_id != id { }
return Err(ExtractionError::WrongResult(format!(
"got wrong playlist id {}, expected {}",
playlist_id, id
)));
} }
let from_ytm = header
.subtitle
.0
.iter()
.any(|c| c.as_str() == util::YT_MUSIC_NAME);
let channel = header
.subtitle
.0
.into_iter()
.find_map(|c| ChannelId::try_from(c).ok());
let mut mapper = MusicListMapper::new(lang); let mut mapper = MusicListMapper::new(lang);
mapper.map_response(shelf.contents); mapper.map_response(shelf.contents);
let map_res = mapper.conv_items(); let map_res = mapper.conv_items();
@ -120,10 +102,12 @@ impl MapResponse<MusicPlaylist> for response::MusicPlaylist {
.map(|cont| cont.next_continuation_data.continuation); .map(|cont| cont.next_continuation_data.continuation);
let track_count = match ctoken { let track_count = match ctoken {
Some(_) => header Some(_) => self.header.as_ref().and_then(|h| {
.second_subtitle h.music_detail_header_renderer
.first() .second_subtitle
.and_then(|txt| util::parse_numeric::<u64>(txt).ok()), .first()
.and_then(|txt| util::parse_numeric::<u64>(txt).ok())
}),
None => Some(map_res.c.len() as u64), None => Some(map_res.c.len() as u64),
}; };
@ -132,13 +116,63 @@ impl MapResponse<MusicPlaylist> for response::MusicPlaylist {
.try_swap_remove(0) .try_swap_remove(0)
.map(|c| c.next_continuation_data.continuation); .map(|c| c.next_continuation_data.continuation);
let (from_ytm, channel, name, thumbnail, description) = match self.header {
Some(header) => {
let h = header.music_detail_header_renderer;
let from_ytm = h
.subtitle
.0
.iter()
.any(|c| c.as_str() == util::YT_MUSIC_NAME);
let channel = h
.subtitle
.0
.into_iter()
.find_map(|c| ChannelId::try_from(c).ok());
(
from_ytm,
channel,
h.title,
h.thumbnail.into(),
h.description,
)
}
None => {
// Album playlists fetched via the playlist method dont include a header
let (album, cover) = map_res
.c
.first()
.and_then(|t: &TrackItem| {
t.album.as_ref().map(|a| (a.clone(), t.cover.clone()))
})
.ok_or(ExtractionError::InvalidData(Cow::Borrowed(
"playlist without header or album items",
)))?;
if !map_res.c.iter().all(|t| {
t.album
.as_ref()
.map(|a| a.id == album.id)
.unwrap_or_default()
}) {
return Err(ExtractionError::InvalidData(Cow::Borrowed(
"album playlist containing items from different albums",
)));
}
(true, None, album.name, cover, None)
}
};
Ok(MapResult { Ok(MapResult {
c: MusicPlaylist { c: MusicPlaylist {
id: playlist_id, id: id.to_owned(),
name: header.title, name,
thumbnail: header.thumbnail.into(), thumbnail,
channel, channel,
description: header.description, description,
track_count, track_count,
from_ytm, from_ytm,
tracks: Paginator::new_ext( tracks: Paginator::new_ext(
@ -170,7 +204,10 @@ impl MapResponse<MusicAlbum> for response::MusicPlaylist {
) -> Result<MapResult<MusicAlbum>, ExtractionError> { ) -> Result<MapResult<MusicAlbum>, ExtractionError> {
// dbg!(&self); // dbg!(&self);
let header = self.header.music_detail_header_renderer; let header = self
.header
.ok_or(ExtractionError::InvalidData(Cow::Borrowed("no header")))?
.music_detail_header_renderer;
let mut content = self.contents.single_column_browse_results_renderer.contents; let mut content = self.contents.single_column_browse_results_renderer.contents;
let sections = content let sections = content

View file

@ -11,7 +11,7 @@ use super::{ContentsRenderer, Tab};
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct MusicPlaylist { pub(crate) struct MusicPlaylist {
pub contents: Contents, pub contents: Contents,
pub header: Header, pub header: Option<Header>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]

View file

@ -1031,7 +1031,7 @@ pub struct ArtistId {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive] #[non_exhaustive]
pub struct AlbumItem { pub struct AlbumItem {
/// Unique YouTube album ID (e.g. `OLAK5uy_nZpcQys48R0aNb046hV-n1OAHGE4reftQ`) /// Unique YouTube album ID (e.g. `MPREb_T5s950Swfdy`)
pub id: String, pub id: String,
/// Album name /// Album name
pub name: String, pub name: String,