From a8fb337fae9cb0112e0152f9a0a19ebae49c2a4d Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Tue, 16 Apr 2024 15:18:29 +0200 Subject: [PATCH 1/5] fix: remove Innertube API keys, update android player params --- src/client/mod.rs | 18 +++++++----------- src/client/player.rs | 4 ++-- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index 0203c3d..b64ecbb 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -192,19 +192,15 @@ const YOUTUBE_MUSIC_V1_URL: &str = "https://music.youtube.com/youtubei/v1/"; const YOUTUBE_HOME_URL: &str = "https://www.youtube.com/"; const YOUTUBE_MUSIC_HOME_URL: &str = "https://music.youtube.com/"; -const DISABLE_PRETTY_PRINT_PARAMETER: &str = "&prettyPrint=false"; +const DISABLE_PRETTY_PRINT_PARAMETER: &str = "prettyPrint=false"; // Desktop client const DESKTOP_CLIENT_VERSION: &str = "2.20230126.00.00"; -const DESKTOP_API_KEY: &str = "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8"; const TVHTML5_CLIENT_VERSION: &str = "2.0"; -const DESKTOP_MUSIC_API_KEY: &str = "AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30"; const DESKTOP_MUSIC_CLIENT_VERSION: &str = "1.20230123.01.01"; // Mobile client const MOBILE_CLIENT_VERSION: &str = "18.03.33"; -const ANDROID_API_KEY: &str = "AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w"; -const IOS_API_KEY: &str = "AIzaSyB-63vPrdThhKuerbB2N_l7Kwwcxj6yUAc"; const IOS_DEVICE_MODEL: &str = "iPhone14,5"; static CLIENT_VERSION_REGEX: Lazy = @@ -1189,7 +1185,7 @@ impl RustyPipeQuery { .inner .http .post(format!( - "{YOUTUBEI_V1_URL}{endpoint}?key={DESKTOP_API_KEY}{DISABLE_PRETTY_PRINT_PARAMETER}" + "{YOUTUBEI_V1_URL}{endpoint}?{DISABLE_PRETTY_PRINT_PARAMETER}" )) .header(header::ORIGIN, YOUTUBE_HOME_URL) .header(header::REFERER, YOUTUBE_HOME_URL) @@ -1204,7 +1200,7 @@ impl RustyPipeQuery { .inner .http .post(format!( - "{YOUTUBE_MUSIC_V1_URL}{endpoint}?key={DESKTOP_MUSIC_API_KEY}{DISABLE_PRETTY_PRINT_PARAMETER}" + "{YOUTUBE_MUSIC_V1_URL}{endpoint}?{DISABLE_PRETTY_PRINT_PARAMETER}" )) .header(header::ORIGIN, YOUTUBE_MUSIC_HOME_URL) .header(header::REFERER, YOUTUBE_MUSIC_HOME_URL) @@ -1212,14 +1208,14 @@ impl RustyPipeQuery { .header("X-YouTube-Client-Name", "67") .header( "X-YouTube-Client-Version", - self.client.get_music_client_version().await + self.client.get_music_client_version().await, ), ClientType::TvHtml5Embed => self .client .inner .http .post(format!( - "{YOUTUBEI_V1_URL}{endpoint}?key={DESKTOP_API_KEY}{DISABLE_PRETTY_PRINT_PARAMETER}" + "{YOUTUBEI_V1_URL}{endpoint}?{DISABLE_PRETTY_PRINT_PARAMETER}" )) .header(header::ORIGIN, YOUTUBE_HOME_URL) .header(header::REFERER, YOUTUBE_HOME_URL) @@ -1230,7 +1226,7 @@ impl RustyPipeQuery { .inner .http .post(format!( - "{YOUTUBEI_V1_GAPIS_URL}{endpoint}?key={ANDROID_API_KEY}{DISABLE_PRETTY_PRINT_PARAMETER}" + "{YOUTUBEI_V1_GAPIS_URL}{endpoint}?{DISABLE_PRETTY_PRINT_PARAMETER}" )) .header( header::USER_AGENT, @@ -1245,7 +1241,7 @@ impl RustyPipeQuery { .inner .http .post(format!( - "{YOUTUBEI_V1_GAPIS_URL}{endpoint}?key={IOS_API_KEY}{DISABLE_PRETTY_PRINT_PARAMETER}" + "{YOUTUBEI_V1_GAPIS_URL}{endpoint}?{DISABLE_PRETTY_PRINT_PARAMETER}" )) .header( header::USER_AGENT, diff --git a/src/client/player.rs b/src/client/player.rs index 1dceb7d..ba1bee8 100644 --- a/src/client/player.rs +++ b/src/client/player.rs @@ -128,8 +128,8 @@ impl RustyPipeQuery { video_id, content_check_ok: true, racy_check_ok: true, - // Source: https://github.com/TeamNewPipe/NewPipeExtractor/pull/1084 - params: Some("CgIQBg").filter(|_| client_type == ClientType::Android), + // Source: https://github.com/TeamNewPipe/NewPipeExtractor/pull/1168 + params: Some("CgIIAQ%3D%3D").filter(|_| client_type == ClientType::Android), } }; From 77ee9237783cc2183d52e383baefa7fe117ba027 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Thu, 18 Apr 2024 17:37:50 +0200 Subject: [PATCH 2/5] test: update channel ID for L. R. Eswari --- tests/snapshots/youtube__music_album_no_artist.snap | 4 ++-- tests/snapshots/youtube__music_album_no_artist_intl.snap | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/snapshots/youtube__music_album_no_artist.snap b/tests/snapshots/youtube__music_album_no_artist.snap index eea1c14..1b4773b 100644 --- a/tests/snapshots/youtube__music_album_no_artist.snap +++ b/tests/snapshots/youtube__music_album_no_artist.snap @@ -63,11 +63,11 @@ MusicAlbum( cover: [], artists: [ ArtistId( - id: Some("UCUhWwvF6gIPWTYlYb4-icLA"), + id: Some("UC_KQPMiRQl3CFAIKTVfCHwA"), name: "L.r. Eswari", ), ], - artist_id: Some("UCUhWwvF6gIPWTYlYb4-icLA"), + artist_id: Some("UC_KQPMiRQl3CFAIKTVfCHwA"), album: Some(AlbumId( id: "MPREb_bqWA6mAZFWS", name: "Pedha Rasi Peddamma Katha", diff --git a/tests/snapshots/youtube__music_album_no_artist_intl.snap b/tests/snapshots/youtube__music_album_no_artist_intl.snap index a8cefcf..779f0aa 100644 --- a/tests/snapshots/youtube__music_album_no_artist_intl.snap +++ b/tests/snapshots/youtube__music_album_no_artist_intl.snap @@ -63,11 +63,11 @@ MusicAlbum( cover: [], artists: [ ArtistId( - id: Some("UCUhWwvF6gIPWTYlYb4-icLA"), + id: Some("UC_KQPMiRQl3CFAIKTVfCHwA"), name: "[name]", ), ], - artist_id: Some("UCUhWwvF6gIPWTYlYb4-icLA"), + artist_id: Some("UC_KQPMiRQl3CFAIKTVfCHwA"), album: Some(AlbumId( id: "MPREb_bqWA6mAZFWS", name: "[name]", From 8fbd6b95b6f01108b46f53fe60a56b0c561e40c1 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Thu, 18 Apr 2024 19:50:06 +0200 Subject: [PATCH 3/5] fix: parsing error when no `music_related` content available --- src/client/music_details.rs | 76 ++++++++++++++-------------- src/client/response/music_details.rs | 21 +++++--- 2 files changed, 52 insertions(+), 45 deletions(-) diff --git a/src/client/music_details.rs b/src/client/music_details.rs index 36d5ddd..0459389 100644 --- a/src/client/music_details.rs +++ b/src/client/music_details.rs @@ -306,19 +306,14 @@ impl MapResponse for response::MusicLyrics { ) -> Result, ExtractionError> { let lyrics = self .contents - .section_list_renderer - .and_then(|sl| { - sl.contents - .into_iter() - .find_map(|item| item.music_description_shelf_renderer) - }) - .ok_or(match self.contents.message_renderer { - Some(msg) => ExtractionError::NotFound { - id: id.to_owned(), - msg: msg.text.into(), - }, - None => ExtractionError::InvalidData(Cow::Borrowed("no content")), - })?; + .into_res() + .map_err(|msg| ExtractionError::NotFound { + id: id.to_owned(), + msg: msg.into(), + })? + .into_iter() + .find_map(|item| item.music_description_shelf_renderer) + .ok_or(ExtractionError::InvalidData(Cow::Borrowed("no content")))?; Ok(MapResult { c: Lyrics { @@ -333,36 +328,39 @@ impl MapResponse for response::MusicLyrics { impl MapResponse for response::MusicRelated { fn map_response( self, - _id: &str, + id: &str, lang: Language, _deobf: Option<&crate::deobfuscate::DeobfData>, _vdata: Option<&str>, ) -> Result, ExtractionError> { + let contents = self + .contents + .into_res() + .map_err(|msg| ExtractionError::NotFound { + id: id.to_owned(), + msg: msg.into(), + })?; + // Find artist - let artist_id = self - .contents - .section_list_renderer - .contents - .iter() - .find_map(|section| match section { - response::music_item::ItemSection::MusicCarouselShelfRenderer(shelf) => { - shelf.header.as_ref().and_then(|h| { - h.music_carousel_shelf_basic_header_renderer - .title - .0 - .iter() - .find_map(|c| { - let artist = ArtistId::from(c.clone()); - if artist.id.is_some() { - Some(artist) - } else { - None - } - }) - }) - } - _ => None, - }); + let artist_id = contents.iter().find_map(|section| match section { + response::music_item::ItemSection::MusicCarouselShelfRenderer(shelf) => { + shelf.header.as_ref().and_then(|h| { + h.music_carousel_shelf_basic_header_renderer + .title + .0 + .iter() + .find_map(|c| { + let artist = ArtistId::from(c.clone()); + if artist.id.is_some() { + Some(artist) + } else { + None + } + }) + }) + } + _ => None, + }); let mut mapper_tracks = MusicListMapper::new(lang); let mut mapper = match artist_id { @@ -370,7 +368,7 @@ impl MapResponse for response::MusicRelated { None => MusicListMapper::new(lang), }; - let mut sections = self.contents.section_list_renderer.contents.into_iter(); + let mut sections = contents.into_iter(); if let Some(response::music_item::ItemSection::MusicCarouselShelfRenderer(shelf)) = sections.next() { diff --git a/src/client/response/music_details.rs b/src/client/response/music_details.rs index 387f041..4254b00 100644 --- a/src/client/response/music_details.rs +++ b/src/client/response/music_details.rs @@ -7,7 +7,7 @@ use super::AlertRenderer; use super::ContentsRenderer; use super::{ music_item::{ItemSection, PlaylistPanelRenderer}, - ContentRenderer, SectionList, + ContentRenderer, }; /// Response model for YouTube Music track details @@ -108,14 +108,14 @@ pub(crate) struct PlaylistPanel { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub(crate) struct MusicLyrics { - pub contents: LyricsContents, + pub contents: ListOrMessage, } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] -pub(crate) struct LyricsContents { - pub message_renderer: Option, - pub section_list_renderer: Option>, +pub(crate) enum ListOrMessage { + SectionListRenderer(ContentsRenderer), + MessageRenderer(AlertRenderer), } #[derive(Debug, Deserialize)] @@ -137,5 +137,14 @@ pub(crate) struct LyricsRenderer { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub(crate) struct MusicRelated { - pub contents: SectionList, + pub contents: ListOrMessage, +} + +impl ListOrMessage { + pub fn into_res(self) -> Result, String> { + match self { + ListOrMessage::SectionListRenderer(c) => Ok(c.contents), + ListOrMessage::MessageRenderer(msg) => Err(msg.text), + } + } } From 16e0e28c4866bb69d8e4c06eef94176f329a1c27 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Fri, 26 Apr 2024 16:09:13 +0200 Subject: [PATCH 4/5] feat: CLI: setting player type --- cli/src/main.rs | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index cd8bd4c..138e915 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -8,7 +8,7 @@ use futures::stream::{self, StreamExt}; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use reqwest::{Client, ClientBuilder}; use rustypipe::{ - client::RustyPipe, + client::{ClientType, RustyPipe}, model::{UrlTarget, VideoId, YouTubeItem}, param::{search_filter, ChannelVideoTab, Country, Language, StreamFilter}, }; @@ -81,6 +81,8 @@ enum Commands { /// Get the player #[clap(long)] player: bool, + #[clap(long)] + player_type: Option, }, /// Search YouTube Search { @@ -189,6 +191,14 @@ enum MusicSearchCategory { PlaylistsCommunity, } +#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)] +enum PlayerType { + Desktop, + Tv, + Android, + Ios, +} + impl From for search_filter::ItemType { fn from(value: SearchItemType) -> Self { match value { @@ -231,6 +241,17 @@ impl From for search_filter::Order { } } +impl From for ClientType { + fn from(value: PlayerType) -> Self { + match value { + PlayerType::Desktop => Self::Desktop, + PlayerType::Tv => Self::TvHtml5Embed, + PlayerType::Android => Self::Android, + PlayerType::Ios => Self::Ios, + } + } +} + #[allow(clippy::too_many_arguments)] async fn download_single_video( video_id: &str, @@ -540,6 +561,7 @@ async fn main() { comments, lyrics, player, + player_type, } => { let target = rp.query().resolve_string(&id, false).await.unwrap(); @@ -558,7 +580,12 @@ async fn main() { let details = rp.query().music_details(&id).await.unwrap(); print_data(&details, format, pretty); } else if player { - let player = rp.query().player(&id).await.unwrap(); + let player = if let Some(player_type) = player_type { + rp.query().player_from_client(&id, player_type.into()).await + } else { + rp.query().player(&id).await + } + .unwrap(); print_data(&player, format, pretty); } else { let mut details = rp.query().video_details(&id).await.unwrap(); From b4a6658e33c8cbe7f6463edee2c6ab824032508b Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Thu, 2 May 2024 14:14:22 +0200 Subject: [PATCH 5/5] test: update track durations --- tests/snapshots/youtube__music_album_ep.snap | 10 +++++----- tests/snapshots/youtube__music_album_ep_intl.snap | 10 +++++----- .../youtube__music_album_version_no_artist.snap | 8 ++++---- .../youtube__music_album_version_no_artist_intl.snap | 8 ++++---- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/snapshots/youtube__music_album_ep.snap b/tests/snapshots/youtube__music_album_ep.snap index 10e5e03..03a55ab 100644 --- a/tests/snapshots/youtube__music_album_ep.snap +++ b/tests/snapshots/youtube__music_album_ep.snap @@ -22,7 +22,7 @@ MusicAlbum( TrackItem( id: "aGd3VKSOTxY", name: "Ich wache auf", - duration: Some(221), + duration: Some(222), cover: [], artists: [ ArtistId( @@ -43,7 +43,7 @@ MusicAlbum( TrackItem( id: "Jz-26iiDuYs", name: "Waldbrand", - duration: Some(208), + duration: Some(209), cover: [], artists: [ ArtistId( @@ -64,7 +64,7 @@ MusicAlbum( TrackItem( id: "Bu26uFtpt58", name: "Verlernt", - duration: Some(223), + duration: Some(224), cover: [], artists: [ ArtistId( @@ -85,7 +85,7 @@ MusicAlbum( TrackItem( id: "RgwNqqiVqdY", name: "In Farbe", - duration: Some(221), + duration: Some(222), cover: [], artists: [ ArtistId( @@ -106,7 +106,7 @@ MusicAlbum( TrackItem( id: "2TuOh30XbCI", name: "Stadt im Hinterland", - duration: Some(197), + duration: Some(198), cover: [], artists: [ ArtistId( diff --git a/tests/snapshots/youtube__music_album_ep_intl.snap b/tests/snapshots/youtube__music_album_ep_intl.snap index c48bcd4..83775be 100644 --- a/tests/snapshots/youtube__music_album_ep_intl.snap +++ b/tests/snapshots/youtube__music_album_ep_intl.snap @@ -22,7 +22,7 @@ MusicAlbum( TrackItem( id: "aGd3VKSOTxY", name: "[name]", - duration: Some(221), + duration: Some(222), cover: [], artists: [ ArtistId( @@ -43,7 +43,7 @@ MusicAlbum( TrackItem( id: "Jz-26iiDuYs", name: "[name]", - duration: Some(208), + duration: Some(209), cover: [], artists: [ ArtistId( @@ -64,7 +64,7 @@ MusicAlbum( TrackItem( id: "Bu26uFtpt58", name: "[name]", - duration: Some(223), + duration: Some(224), cover: [], artists: [ ArtistId( @@ -85,7 +85,7 @@ MusicAlbum( TrackItem( id: "RgwNqqiVqdY", name: "[name]", - duration: Some(221), + duration: Some(222), cover: [], artists: [ ArtistId( @@ -106,7 +106,7 @@ MusicAlbum( TrackItem( id: "2TuOh30XbCI", name: "[name]", - duration: Some(197), + duration: Some(198), cover: [], artists: [ ArtistId( diff --git a/tests/snapshots/youtube__music_album_version_no_artist.snap b/tests/snapshots/youtube__music_album_version_no_artist.snap index f1fb92f..b7a3813 100644 --- a/tests/snapshots/youtube__music_album_version_no_artist.snap +++ b/tests/snapshots/youtube__music_album_version_no_artist.snap @@ -26,7 +26,7 @@ MusicAlbum( TrackItem( id: "AKJ3IJZKPWc", name: "Oh Javaraala", - duration: Some(228), + duration: Some(229), cover: [], artists: [ ArtistId( @@ -51,7 +51,7 @@ MusicAlbum( TrackItem( id: "WnpZuHNB33E", name: "Siva Manoranjani", - duration: Some(266), + duration: Some(267), cover: [], artists: [ ArtistId( @@ -72,7 +72,7 @@ MusicAlbum( TrackItem( id: "pRqoDGXg1-I", name: "Gulabi Buggalunna", - duration: Some(154), + duration: Some(155), cover: [], artists: [ ArtistId( @@ -93,7 +93,7 @@ MusicAlbum( TrackItem( id: "20vIKLJxjBY", name: "Kuluku Nadakula", - duration: Some(178), + duration: Some(179), cover: [], artists: [ ArtistId( diff --git a/tests/snapshots/youtube__music_album_version_no_artist_intl.snap b/tests/snapshots/youtube__music_album_version_no_artist_intl.snap index da0a781..4cc82ef 100644 --- a/tests/snapshots/youtube__music_album_version_no_artist_intl.snap +++ b/tests/snapshots/youtube__music_album_version_no_artist_intl.snap @@ -26,7 +26,7 @@ MusicAlbum( TrackItem( id: "AKJ3IJZKPWc", name: "[name]", - duration: Some(228), + duration: Some(229), cover: [], artists: [ ArtistId( @@ -51,7 +51,7 @@ MusicAlbum( TrackItem( id: "WnpZuHNB33E", name: "[name]", - duration: Some(266), + duration: Some(267), cover: [], artists: [ ArtistId( @@ -72,7 +72,7 @@ MusicAlbum( TrackItem( id: "pRqoDGXg1-I", name: "[name]", - duration: Some(154), + duration: Some(155), cover: [], artists: [ ArtistId( @@ -93,7 +93,7 @@ MusicAlbum( TrackItem( id: "20vIKLJxjBY", name: "[name]", - duration: Some(178), + duration: Some(179), cover: [], artists: [ ArtistId(