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 reqwest::{Client, ClientBuilder};
use rustypipe::{
client::{ClientType, RustyPipe},
client::RustyPipe,
model::{UrlTarget, VideoId, YouTubeItem},
param::{search_filter, ChannelVideoTab, Country, Language, StreamFilter},
};
@ -81,8 +81,6 @@ enum Commands {
/// Get the player
#[clap(long)]
player: bool,
#[clap(long)]
player_type: Option<PlayerType>,
},
/// Search YouTube
Search {
@ -191,14 +189,6 @@ enum MusicSearchCategory {
PlaylistsCommunity,
}
#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
enum PlayerType {
Desktop,
Tv,
Android,
Ios,
}
impl From<SearchItemType> for search_filter::ItemType {
fn from(value: SearchItemType) -> Self {
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)]
async fn download_single_video(
video_id: &str,
@ -561,7 +540,6 @@ async fn main() {
comments,
lyrics,
player,
player_type,
} => {
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();
print_data(&details, format, pretty);
} else if player {
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();
let player = rp.query().player(&id).await.unwrap();
print_data(&player, format, pretty);
} else {
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_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<Regex> =
@ -1185,7 +1189,7 @@ impl RustyPipeQuery {
.inner
.http
.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::REFERER, YOUTUBE_HOME_URL)
@ -1200,7 +1204,7 @@ impl RustyPipeQuery {
.inner
.http
.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::REFERER, YOUTUBE_MUSIC_HOME_URL)
@ -1208,14 +1212,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}?{DISABLE_PRETTY_PRINT_PARAMETER}"
"{YOUTUBEI_V1_URL}{endpoint}?key={DESKTOP_API_KEY}{DISABLE_PRETTY_PRINT_PARAMETER}"
))
.header(header::ORIGIN, YOUTUBE_HOME_URL)
.header(header::REFERER, YOUTUBE_HOME_URL)
@ -1226,7 +1230,7 @@ impl RustyPipeQuery {
.inner
.http
.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::USER_AGENT,
@ -1241,7 +1245,7 @@ impl RustyPipeQuery {
.inner
.http
.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::USER_AGENT,

View file

@ -306,14 +306,19 @@ impl MapResponse<Lyrics> for response::MusicLyrics {
) -> Result<MapResult<Lyrics>, ExtractionError> {
let lyrics = self
.contents
.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")))?;
.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")),
})?;
Ok(MapResult {
c: Lyrics {
@ -328,39 +333,36 @@ impl MapResponse<Lyrics> for response::MusicLyrics {
impl MapResponse<MusicRelated> for response::MusicRelated {
fn map_response(
self,
id: &str,
_id: &str,
lang: Language,
_deobf: Option<&crate::deobfuscate::DeobfData>,
_vdata: Option<&str>,
) -> Result<MapResult<MusicRelated>, ExtractionError> {
let contents = self
.contents
.into_res()
.map_err(|msg| ExtractionError::NotFound {
id: id.to_owned(),
msg: msg.into(),
})?;
// Find artist
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 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 mut mapper_tracks = MusicListMapper::new(lang);
let mut mapper = match artist_id {
@ -368,7 +370,7 @@ impl MapResponse<MusicRelated> for response::MusicRelated {
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)) =
sections.next()
{

View file

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

View file

@ -7,7 +7,7 @@ use super::AlertRenderer;
use super::ContentsRenderer;
use super::{
music_item::{ItemSection, PlaylistPanelRenderer},
ContentRenderer,
ContentRenderer, SectionList,
};
/// 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: ListOrMessage<LyricsSection>,
pub contents: LyricsContents,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) enum ListOrMessage<T> {
SectionListRenderer(ContentsRenderer<T>),
MessageRenderer(AlertRenderer),
pub(crate) struct LyricsContents {
pub message_renderer: Option<AlertRenderer>,
pub section_list_renderer: Option<ContentsRenderer<LyricsSection>>,
}
#[derive(Debug, Deserialize)]
@ -137,14 +137,5 @@ pub(crate) struct LyricsRenderer {
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct MusicRelated {
pub contents: ListOrMessage<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),
}
}
pub contents: SectionList<ItemSection>,
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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