Compare commits

..

No commits in common. "b4a6658e33c8cbe7f6463edee2c6ab824032508b" and "6c41ef2fb2531e10a12c271e2d48504510a3b0bf" have entirely different histories.

11 changed files with 82 additions and 112 deletions

View file

@ -8,7 +8,7 @@ use futures::stream::{self, StreamExt};
use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use reqwest::{Client, ClientBuilder}; use reqwest::{Client, ClientBuilder};
use rustypipe::{ use rustypipe::{
client::{ClientType, RustyPipe}, client::RustyPipe,
model::{UrlTarget, VideoId, YouTubeItem}, model::{UrlTarget, VideoId, YouTubeItem},
param::{search_filter, ChannelVideoTab, Country, Language, StreamFilter}, param::{search_filter, ChannelVideoTab, Country, Language, StreamFilter},
}; };
@ -81,8 +81,6 @@ enum Commands {
/// Get the player /// Get the player
#[clap(long)] #[clap(long)]
player: bool, player: bool,
#[clap(long)]
player_type: Option<PlayerType>,
}, },
/// Search YouTube /// Search YouTube
Search { Search {
@ -191,14 +189,6 @@ enum MusicSearchCategory {
PlaylistsCommunity, PlaylistsCommunity,
} }
#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
enum PlayerType {
Desktop,
Tv,
Android,
Ios,
}
impl From<SearchItemType> for search_filter::ItemType { impl From<SearchItemType> for search_filter::ItemType {
fn from(value: SearchItemType) -> Self { fn from(value: SearchItemType) -> Self {
match value { match value {
@ -241,17 +231,6 @@ impl From<SearchOrder> for search_filter::Order {
} }
} }
impl From<PlayerType> 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)] #[allow(clippy::too_many_arguments)]
async fn download_single_video( async fn download_single_video(
video_id: &str, video_id: &str,
@ -561,7 +540,6 @@ async fn main() {
comments, comments,
lyrics, lyrics,
player, player,
player_type,
} => { } => {
let target = rp.query().resolve_string(&id, false).await.unwrap(); let target = rp.query().resolve_string(&id, false).await.unwrap();
@ -580,12 +558,7 @@ async fn main() {
let details = rp.query().music_details(&id).await.unwrap(); let details = rp.query().music_details(&id).await.unwrap();
print_data(&details, format, pretty); print_data(&details, format, pretty);
} else if player { } else if player {
let player = if let Some(player_type) = player_type { let player = rp.query().player(&id).await.unwrap();
rp.query().player_from_client(&id, player_type.into()).await
} else {
rp.query().player(&id).await
}
.unwrap();
print_data(&player, format, pretty); print_data(&player, format, pretty);
} else { } else {
let mut details = rp.query().video_details(&id).await.unwrap(); let mut details = rp.query().video_details(&id).await.unwrap();

View file

@ -192,15 +192,19 @@ const YOUTUBE_MUSIC_V1_URL: &str = "https://music.youtube.com/youtubei/v1/";
const YOUTUBE_HOME_URL: &str = "https://www.youtube.com/"; const YOUTUBE_HOME_URL: &str = "https://www.youtube.com/";
const YOUTUBE_MUSIC_HOME_URL: &str = "https://music.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 // Desktop client
const DESKTOP_CLIENT_VERSION: &str = "2.20230126.00.00"; 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 TVHTML5_CLIENT_VERSION: &str = "2.0";
const DESKTOP_MUSIC_API_KEY: &str = "AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30";
const DESKTOP_MUSIC_CLIENT_VERSION: &str = "1.20230123.01.01"; const DESKTOP_MUSIC_CLIENT_VERSION: &str = "1.20230123.01.01";
// Mobile client // Mobile client
const MOBILE_CLIENT_VERSION: &str = "18.03.33"; 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"; const IOS_DEVICE_MODEL: &str = "iPhone14,5";
static CLIENT_VERSION_REGEX: Lazy<Regex> = static CLIENT_VERSION_REGEX: Lazy<Regex> =
@ -1185,7 +1189,7 @@ impl RustyPipeQuery {
.inner .inner
.http .http
.post(format!( .post(format!(
"{YOUTUBEI_V1_URL}{endpoint}?{DISABLE_PRETTY_PRINT_PARAMETER}" "{YOUTUBEI_V1_URL}{endpoint}?key={DESKTOP_API_KEY}{DISABLE_PRETTY_PRINT_PARAMETER}"
)) ))
.header(header::ORIGIN, YOUTUBE_HOME_URL) .header(header::ORIGIN, YOUTUBE_HOME_URL)
.header(header::REFERER, YOUTUBE_HOME_URL) .header(header::REFERER, YOUTUBE_HOME_URL)
@ -1200,7 +1204,7 @@ impl RustyPipeQuery {
.inner .inner
.http .http
.post(format!( .post(format!(
"{YOUTUBE_MUSIC_V1_URL}{endpoint}?{DISABLE_PRETTY_PRINT_PARAMETER}" "{YOUTUBE_MUSIC_V1_URL}{endpoint}?key={DESKTOP_MUSIC_API_KEY}{DISABLE_PRETTY_PRINT_PARAMETER}"
)) ))
.header(header::ORIGIN, YOUTUBE_MUSIC_HOME_URL) .header(header::ORIGIN, YOUTUBE_MUSIC_HOME_URL)
.header(header::REFERER, YOUTUBE_MUSIC_HOME_URL) .header(header::REFERER, YOUTUBE_MUSIC_HOME_URL)
@ -1208,14 +1212,14 @@ impl RustyPipeQuery {
.header("X-YouTube-Client-Name", "67") .header("X-YouTube-Client-Name", "67")
.header( .header(
"X-YouTube-Client-Version", "X-YouTube-Client-Version",
self.client.get_music_client_version().await, self.client.get_music_client_version().await
), ),
ClientType::TvHtml5Embed => self ClientType::TvHtml5Embed => self
.client .client
.inner .inner
.http .http
.post(format!( .post(format!(
"{YOUTUBEI_V1_URL}{endpoint}?{DISABLE_PRETTY_PRINT_PARAMETER}" "{YOUTUBEI_V1_URL}{endpoint}?key={DESKTOP_API_KEY}{DISABLE_PRETTY_PRINT_PARAMETER}"
)) ))
.header(header::ORIGIN, YOUTUBE_HOME_URL) .header(header::ORIGIN, YOUTUBE_HOME_URL)
.header(header::REFERER, YOUTUBE_HOME_URL) .header(header::REFERER, YOUTUBE_HOME_URL)
@ -1226,7 +1230,7 @@ impl RustyPipeQuery {
.inner .inner
.http .http
.post(format!( .post(format!(
"{YOUTUBEI_V1_GAPIS_URL}{endpoint}?{DISABLE_PRETTY_PRINT_PARAMETER}" "{YOUTUBEI_V1_GAPIS_URL}{endpoint}?key={ANDROID_API_KEY}{DISABLE_PRETTY_PRINT_PARAMETER}"
)) ))
.header( .header(
header::USER_AGENT, header::USER_AGENT,
@ -1241,7 +1245,7 @@ impl RustyPipeQuery {
.inner .inner
.http .http
.post(format!( .post(format!(
"{YOUTUBEI_V1_GAPIS_URL}{endpoint}?{DISABLE_PRETTY_PRINT_PARAMETER}" "{YOUTUBEI_V1_GAPIS_URL}{endpoint}?key={IOS_API_KEY}{DISABLE_PRETTY_PRINT_PARAMETER}"
)) ))
.header( .header(
header::USER_AGENT, header::USER_AGENT,

View file

@ -306,14 +306,19 @@ impl MapResponse<Lyrics> for response::MusicLyrics {
) -> Result<MapResult<Lyrics>, ExtractionError> { ) -> Result<MapResult<Lyrics>, ExtractionError> {
let lyrics = self let lyrics = self
.contents .contents
.into_res() .section_list_renderer
.map_err(|msg| ExtractionError::NotFound { .and_then(|sl| {
id: id.to_owned(), sl.contents
msg: msg.into(),
})?
.into_iter() .into_iter()
.find_map(|item| item.music_description_shelf_renderer) .find_map(|item| item.music_description_shelf_renderer)
.ok_or(ExtractionError::InvalidData(Cow::Borrowed("no content")))?; })
.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")),
})?;
Ok(MapResult { Ok(MapResult {
c: Lyrics { c: Lyrics {
@ -328,21 +333,18 @@ impl MapResponse<Lyrics> for response::MusicLyrics {
impl MapResponse<MusicRelated> for response::MusicRelated { impl MapResponse<MusicRelated> for response::MusicRelated {
fn map_response( fn map_response(
self, self,
id: &str, _id: &str,
lang: Language, lang: Language,
_deobf: Option<&crate::deobfuscate::DeobfData>, _deobf: Option<&crate::deobfuscate::DeobfData>,
_vdata: Option<&str>, _vdata: Option<&str>,
) -> Result<MapResult<MusicRelated>, ExtractionError> { ) -> Result<MapResult<MusicRelated>, ExtractionError> {
let contents = self
.contents
.into_res()
.map_err(|msg| ExtractionError::NotFound {
id: id.to_owned(),
msg: msg.into(),
})?;
// Find artist // Find artist
let artist_id = contents.iter().find_map(|section| match section { let artist_id = self
.contents
.section_list_renderer
.contents
.iter()
.find_map(|section| match section {
response::music_item::ItemSection::MusicCarouselShelfRenderer(shelf) => { response::music_item::ItemSection::MusicCarouselShelfRenderer(shelf) => {
shelf.header.as_ref().and_then(|h| { shelf.header.as_ref().and_then(|h| {
h.music_carousel_shelf_basic_header_renderer h.music_carousel_shelf_basic_header_renderer
@ -368,7 +370,7 @@ impl MapResponse<MusicRelated> for response::MusicRelated {
None => MusicListMapper::new(lang), None => MusicListMapper::new(lang),
}; };
let mut sections = contents.into_iter(); let mut sections = self.contents.section_list_renderer.contents.into_iter();
if let Some(response::music_item::ItemSection::MusicCarouselShelfRenderer(shelf)) = if let Some(response::music_item::ItemSection::MusicCarouselShelfRenderer(shelf)) =
sections.next() sections.next()
{ {

View file

@ -128,8 +128,8 @@ impl RustyPipeQuery {
video_id, video_id,
content_check_ok: true, content_check_ok: true,
racy_check_ok: true, racy_check_ok: true,
// Source: https://github.com/TeamNewPipe/NewPipeExtractor/pull/1168 // Source: https://github.com/TeamNewPipe/NewPipeExtractor/pull/1084
params: Some("CgIIAQ%3D%3D").filter(|_| client_type == ClientType::Android), params: Some("CgIQBg").filter(|_| client_type == ClientType::Android),
} }
}; };

View file

@ -7,7 +7,7 @@ use super::AlertRenderer;
use super::ContentsRenderer; use super::ContentsRenderer;
use super::{ use super::{
music_item::{ItemSection, PlaylistPanelRenderer}, music_item::{ItemSection, PlaylistPanelRenderer},
ContentRenderer, ContentRenderer, SectionList,
}; };
/// Response model for YouTube Music track details /// Response model for YouTube Music track details
@ -108,14 +108,14 @@ pub(crate) struct PlaylistPanel {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct MusicLyrics { pub(crate) struct MusicLyrics {
pub contents: ListOrMessage<LyricsSection>, pub contents: LyricsContents,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) enum ListOrMessage<T> { pub(crate) struct LyricsContents {
SectionListRenderer(ContentsRenderer<T>), pub message_renderer: Option<AlertRenderer>,
MessageRenderer(AlertRenderer), pub section_list_renderer: Option<ContentsRenderer<LyricsSection>>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -137,14 +137,5 @@ pub(crate) struct LyricsRenderer {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub(crate) struct MusicRelated { pub(crate) struct MusicRelated {
pub contents: ListOrMessage<ItemSection>, pub contents: SectionList<ItemSection>,
}
impl<T> ListOrMessage<T> {
pub fn into_res(self) -> Result<Vec<T>, String> {
match self {
ListOrMessage::SectionListRenderer(c) => Ok(c.contents),
ListOrMessage::MessageRenderer(msg) => Err(msg.text),
}
}
} }

View file

@ -22,7 +22,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "aGd3VKSOTxY", id: "aGd3VKSOTxY",
name: "Ich wache auf", name: "Ich wache auf",
duration: Some(222), duration: Some(221),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(
@ -43,7 +43,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "Jz-26iiDuYs", id: "Jz-26iiDuYs",
name: "Waldbrand", name: "Waldbrand",
duration: Some(209), duration: Some(208),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(
@ -64,7 +64,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "Bu26uFtpt58", id: "Bu26uFtpt58",
name: "Verlernt", name: "Verlernt",
duration: Some(224), duration: Some(223),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(
@ -85,7 +85,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "RgwNqqiVqdY", id: "RgwNqqiVqdY",
name: "In Farbe", name: "In Farbe",
duration: Some(222), duration: Some(221),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(
@ -106,7 +106,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "2TuOh30XbCI", id: "2TuOh30XbCI",
name: "Stadt im Hinterland", name: "Stadt im Hinterland",
duration: Some(198), duration: Some(197),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(

View file

@ -22,7 +22,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "aGd3VKSOTxY", id: "aGd3VKSOTxY",
name: "[name]", name: "[name]",
duration: Some(222), duration: Some(221),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(
@ -43,7 +43,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "Jz-26iiDuYs", id: "Jz-26iiDuYs",
name: "[name]", name: "[name]",
duration: Some(209), duration: Some(208),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(
@ -64,7 +64,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "Bu26uFtpt58", id: "Bu26uFtpt58",
name: "[name]", name: "[name]",
duration: Some(224), duration: Some(223),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(
@ -85,7 +85,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "RgwNqqiVqdY", id: "RgwNqqiVqdY",
name: "[name]", name: "[name]",
duration: Some(222), duration: Some(221),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(
@ -106,7 +106,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "2TuOh30XbCI", id: "2TuOh30XbCI",
name: "[name]", name: "[name]",
duration: Some(198), duration: Some(197),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(

View file

@ -63,11 +63,11 @@ MusicAlbum(
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(
id: Some("UC_KQPMiRQl3CFAIKTVfCHwA"), id: Some("UCUhWwvF6gIPWTYlYb4-icLA"),
name: "L.r. Eswari", name: "L.r. Eswari",
), ),
], ],
artist_id: Some("UC_KQPMiRQl3CFAIKTVfCHwA"), artist_id: Some("UCUhWwvF6gIPWTYlYb4-icLA"),
album: Some(AlbumId( album: Some(AlbumId(
id: "MPREb_bqWA6mAZFWS", id: "MPREb_bqWA6mAZFWS",
name: "Pedha Rasi Peddamma Katha", name: "Pedha Rasi Peddamma Katha",

View file

@ -63,11 +63,11 @@ MusicAlbum(
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(
id: Some("UC_KQPMiRQl3CFAIKTVfCHwA"), id: Some("UCUhWwvF6gIPWTYlYb4-icLA"),
name: "[name]", name: "[name]",
), ),
], ],
artist_id: Some("UC_KQPMiRQl3CFAIKTVfCHwA"), artist_id: Some("UCUhWwvF6gIPWTYlYb4-icLA"),
album: Some(AlbumId( album: Some(AlbumId(
id: "MPREb_bqWA6mAZFWS", id: "MPREb_bqWA6mAZFWS",
name: "[name]", name: "[name]",

View file

@ -26,7 +26,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "AKJ3IJZKPWc", id: "AKJ3IJZKPWc",
name: "Oh Javaraala", name: "Oh Javaraala",
duration: Some(229), duration: Some(228),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(
@ -51,7 +51,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "WnpZuHNB33E", id: "WnpZuHNB33E",
name: "Siva Manoranjani", name: "Siva Manoranjani",
duration: Some(267), duration: Some(266),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(
@ -72,7 +72,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "pRqoDGXg1-I", id: "pRqoDGXg1-I",
name: "Gulabi Buggalunna", name: "Gulabi Buggalunna",
duration: Some(155), duration: Some(154),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(
@ -93,7 +93,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "20vIKLJxjBY", id: "20vIKLJxjBY",
name: "Kuluku Nadakula", name: "Kuluku Nadakula",
duration: Some(179), duration: Some(178),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(

View file

@ -26,7 +26,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "AKJ3IJZKPWc", id: "AKJ3IJZKPWc",
name: "[name]", name: "[name]",
duration: Some(229), duration: Some(228),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(
@ -51,7 +51,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "WnpZuHNB33E", id: "WnpZuHNB33E",
name: "[name]", name: "[name]",
duration: Some(267), duration: Some(266),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(
@ -72,7 +72,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "pRqoDGXg1-I", id: "pRqoDGXg1-I",
name: "[name]", name: "[name]",
duration: Some(155), duration: Some(154),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(
@ -93,7 +93,7 @@ MusicAlbum(
TrackItem( TrackItem(
id: "20vIKLJxjBY", id: "20vIKLJxjBY",
name: "[name]", name: "[name]",
duration: Some(179), duration: Some(178),
cover: [], cover: [],
artists: [ artists: [
ArtistId( ArtistId(