diff --git a/src/client/music_playlist.rs b/src/client/music_playlist.rs index e672975..6db4c7e 100644 --- a/src/client/music_playlist.rs +++ b/src/client/music_playlist.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use crate::{ error::{Error, ExtractionError}, - model::{AlbumId, ChannelId, MusicAlbum, MusicPlaylist, Paginator}, + model::{AlbumId, ChannelId, MusicAlbum, MusicPlaylist, Paginator, TrackItem}, serializer::MapResult, util::{self, TryRemove}, }; @@ -65,8 +65,6 @@ impl MapResponse for response::MusicPlaylist { ) -> Result, ExtractionError> { // dbg!(&self); - let header = self.header.music_detail_header_renderer; - let mut content = self.contents.single_column_browse_results_renderer.contents; let mut music_contents = content .try_swap_remove(0) @@ -85,31 +83,15 @@ impl MapResponse for response::MusicPlaylist { "no sectionListRenderer content", )))?; - let playlist_id = shelf - .playlist_id - .ok_or(ExtractionError::InvalidData(Cow::Borrowed( - "no playlist id", - )))?; - - if playlist_id != id { - return Err(ExtractionError::WrongResult(format!( - "got wrong playlist id {}, expected {}", - playlist_id, id - ))); + if let Some(playlist_id) = shelf.playlist_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); mapper.map_response(shelf.contents); let map_res = mapper.conv_items(); @@ -120,10 +102,12 @@ impl MapResponse for response::MusicPlaylist { .map(|cont| cont.next_continuation_data.continuation); let track_count = match ctoken { - Some(_) => header - .second_subtitle - .first() - .and_then(|txt| util::parse_numeric::(txt).ok()), + Some(_) => self.header.as_ref().and_then(|h| { + h.music_detail_header_renderer + .second_subtitle + .first() + .and_then(|txt| util::parse_numeric::(txt).ok()) + }), None => Some(map_res.c.len() as u64), }; @@ -132,13 +116,63 @@ impl MapResponse for response::MusicPlaylist { .try_swap_remove(0) .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 { c: MusicPlaylist { - id: playlist_id, - name: header.title, - thumbnail: header.thumbnail.into(), + id: id.to_owned(), + name, + thumbnail, channel, - description: header.description, + description, track_count, from_ytm, tracks: Paginator::new_ext( @@ -170,7 +204,10 @@ impl MapResponse for response::MusicPlaylist { ) -> Result, ExtractionError> { // 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 sections = content diff --git a/src/client/response/music_playlist.rs b/src/client/response/music_playlist.rs index 6e2fb2e..6b8aa49 100644 --- a/src/client/response/music_playlist.rs +++ b/src/client/response/music_playlist.rs @@ -11,7 +11,7 @@ use super::{ContentsRenderer, Tab}; #[serde(rename_all = "camelCase")] pub(crate) struct MusicPlaylist { pub contents: Contents, - pub header: Header, + pub header: Option
, } #[derive(Debug, Deserialize)] diff --git a/src/model/mod.rs b/src/model/mod.rs index a037b19..2c8fbfd 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -1031,7 +1031,7 @@ pub struct ArtistId { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[non_exhaustive] pub struct AlbumItem { - /// Unique YouTube album ID (e.g. `OLAK5uy_nZpcQys48R0aNb046hV-n1OAHGE4reftQ`) + /// Unique YouTube album ID (e.g. `MPREb_T5s950Swfdy`) pub id: String, /// Album name pub name: String,