Rust client for the public YouTube / YouTube Music API (Innertube) (forked from https://codeberg.org/ThetaDev/rustypipe)
1

Configure Feed

Select the types of activity you want to include in your feed.

fix: handle music artist not found

ThetaDev (Mar 31, 2025, 6:11 PM +0200) daf3d035 187bf1c9

+48 -35
+19 -3
src/client/music_artist.rs
··· 154 154 ctx: &MapRespCtx<'_>, 155 155 skip_extendables: bool, 156 156 ) -> Result<MapResult<(MusicArtist, bool)>, ExtractionError> { 157 - let header = res.header.music_immersive_header_renderer; 157 + let contents = match res.contents { 158 + Some(c) => c, 159 + None => { 160 + if res.microformat.microformat_data_renderer.noindex { 161 + return Err(ExtractionError::NotFound { 162 + id: ctx.id.to_owned(), 163 + msg: "no contents".into(), 164 + }); 165 + } else { 166 + return Err(ExtractionError::InvalidData("no contents".into())); 167 + } 168 + } 169 + }; 170 + 171 + let header = res 172 + .header 173 + .ok_or(ExtractionError::InvalidData("no header".into()))? 174 + .music_immersive_header_renderer; 158 175 159 176 if let Some(share) = header.share_endpoint { 160 177 let pb = share.share_entity_endpoint.serialized_share_entity; ··· 171 188 } 172 189 } 173 190 174 - let sections = res 175 - .contents 191 + let sections = contents 176 192 .single_column_browse_results_renderer 177 193 .contents 178 194 .into_iter()
+4 -11
src/client/music_playlist.rs
··· 154 154 let contents = match self.contents { 155 155 Some(c) => c, 156 156 None => { 157 - if self 158 - .microformat 159 - .map(|m| m.microformat_data_renderer.noindex) 160 - .unwrap_or_default() 161 - { 157 + if self.microformat.microformat_data_renderer.noindex { 162 158 return Err(ExtractionError::NotFound { 163 159 id: ctx.id.to_owned(), 164 160 msg: "no contents".into(), ··· 359 355 let contents = match self.contents { 360 356 Some(c) => c, 361 357 None => { 362 - if self 363 - .microformat 364 - .map(|m| m.microformat_data_renderer.noindex) 365 - .unwrap_or_default() 366 - { 358 + if self.microformat.microformat_data_renderer.noindex { 367 359 return Err(ExtractionError::NotFound { 368 360 id: ctx.id.to_owned(), 369 361 msg: "no contents".into(), ··· 492 484 493 485 let playlist_id = self 494 486 .microformat 495 - .and_then(|mf| mf.microformat_data_renderer.url_canonical) 487 + .microformat_data_renderer 488 + .url_canonical 496 489 .and_then(|x| { 497 490 x.strip_prefix("https://music.youtube.com/playlist?list=") 498 491 .map(str::to_owned)
+6 -3
src/client/response/music_artist.rs
··· 5 5 6 6 use super::{ 7 7 music_item::{ 8 - Button, Grid, ItemSection, MusicThumbnailRenderer, SimpleHeader, SingleColumnBrowseResult, 8 + Button, Grid, ItemSection, MusicMicroformat, MusicThumbnailRenderer, SimpleHeader, 9 + SingleColumnBrowseResult, 9 10 }, 10 11 SectionList, Tab, 11 12 }; ··· 14 15 #[derive(Debug, Deserialize)] 15 16 #[serde(rename_all = "camelCase")] 16 17 pub(crate) struct MusicArtist { 17 - pub contents: SingleColumnBrowseResult<Tab<SectionList<ItemSection>>>, 18 - pub header: Header, 18 + pub contents: Option<SingleColumnBrowseResult<Tab<SectionList<ItemSection>>>>, 19 + pub header: Option<Header>, 20 + #[serde(default)] 21 + pub microformat: MusicMicroformat, 19 22 } 20 23 21 24 #[derive(Debug, Deserialize)]
+16
src/client/response/music_item.rs
··· 433 433 LiveBadgeRenderer {}, 434 434 } 435 435 436 + #[serde_as] 437 + #[derive(Default, Debug, Deserialize)] 438 + #[serde(rename_all = "camelCase")] 439 + pub(crate) struct MusicMicroformat { 440 + #[serde_as(as = "DefaultOnError")] 441 + pub microformat_data_renderer: MicroformatData, 442 + } 443 + 444 + #[derive(Default, Debug, Deserialize)] 445 + #[serde(rename_all = "camelCase")] 446 + pub(crate) struct MicroformatData { 447 + pub url_canonical: Option<String>, 448 + #[serde(default)] 449 + pub noindex: bool, 450 + } 451 + 436 452 /* 437 453 #MAPPER 438 454 */
+3 -18
src/client/response/music_playlist.rs
··· 5 5 6 6 use super::{ 7 7 music_item::{ 8 - Button, ItemSection, MusicContentsRenderer, MusicItemMenuEntry, MusicThumbnailRenderer, 8 + Button, ItemSection, MusicContentsRenderer, MusicItemMenuEntry, MusicMicroformat, 9 + MusicThumbnailRenderer, 9 10 }, 10 11 url_endpoint::OnTapWrap, 11 12 ContentsRenderer, SectionList, Tab, 12 13 }; 13 14 14 15 /// Response model for YouTube Music playlists and albums 15 - #[serde_as] 16 16 #[derive(Debug, Deserialize)] 17 17 #[serde(rename_all = "camelCase")] 18 18 pub(crate) struct MusicPlaylist { 19 19 pub contents: Option<Contents>, 20 20 pub header: Option<Header>, 21 21 #[serde(default)] 22 - #[serde_as(as = "DefaultOnError")] 23 - pub microformat: Option<Microformat>, 22 + pub microformat: MusicMicroformat, 24 23 } 25 24 26 25 #[serde_as] ··· 162 161 pub(crate) struct AvatarStackRendererContext { 163 162 pub command_context: Option<OnTapWrap>, 164 163 } 165 - 166 - #[derive(Debug, Deserialize)] 167 - #[serde(rename_all = "camelCase")] 168 - pub(crate) struct Microformat { 169 - pub microformat_data_renderer: MicroformatData, 170 - } 171 - 172 - #[derive(Debug, Deserialize)] 173 - #[serde(rename_all = "camelCase")] 174 - pub(crate) struct MicroformatData { 175 - pub url_canonical: Option<String>, 176 - #[serde(default)] 177 - pub noindex: bool, 178 - }