From b52fd7349ba129c3fbe7d814b2f57e10bee93423 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Wed, 14 Sep 2022 23:55:44 +0200 Subject: [PATCH] refactored client API (query builder) use VecLogError for player streams --- cli/Cargo.toml | 2 +- src/client2/mod.rs | 295 +++++++++++++++++++------------ src/client2/player.rs | 55 ++++-- src/client2/playlist.rs | 22 ++- src/client2/response/channel.rs | 7 +- src/client2/response/mod.rs | 10 +- src/client2/response/player.rs | 18 +- src/client2/response/playlist.rs | 23 +-- src/deobfuscate.rs | 8 +- src/report.rs | 10 +- src/serializer/mod.rs | 4 +- 11 files changed, 277 insertions(+), 177 deletions(-) diff --git a/cli/Cargo.toml b/cli/Cargo.toml index b2a3462..23b8ed6 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] rustypipe = {path = "../", default_features = false, features = ["rustls-tls-native-roots"]} reqwest = {version = "0.11.11", default_features = false} -tokio = {version = "1.20.0", features = ["rt-multi-thread"]} +tokio = {version = "1.20.0", features = ["macros", "rt-multi-thread"]} indicatif = "0.17.0" futures = "0.3.21" anyhow = "1.0" diff --git a/src/client2/mod.rs b/src/client2/mod.rs index 356e59a..f7ee912 100644 --- a/src/client2/mod.rs +++ b/src/client2/mod.rs @@ -154,13 +154,12 @@ static CLIENT_VERSION_REGEXES: Lazy<[Regex; 1]> = #[derive(Clone)] pub struct RustyPipe { inner: Arc, - opts: RustyPipeOpts, } struct RustyPipeRef { http: Client, - storage: Option>, - reporter: Option>, + storage: Option>, + reporter: Option>, user_agent: String, consent_cookie: String, cache: Mutex, @@ -174,6 +173,12 @@ struct RustyPipeOpts { strict: bool, } +#[derive(Clone)] +pub struct RustyPipeQuery { + client: RustyPipe, + opts: RustyPipeOpts, +} + impl Default for RustyPipe { fn default() -> Self { Self::new( @@ -244,8 +249,8 @@ impl From for CacheEntry { impl RustyPipe { /// Create a new RustyPipe instance pub fn new( - storage: Option>, - reporter: Option>, + storage: Option>, + reporter: Option>, user_agent: Option, ) -> Self { let user_agent = user_agent.unwrap_or(DEFAULT_UA.to_owned()); @@ -287,7 +292,6 @@ impl RustyPipe { ), cache: Mutex::new(cache), }), - opts: RustyPipeOpts::default(), } } @@ -300,9 +304,35 @@ impl RustyPipe { Some(Box::new(crate::report::YamlFileReporter::default())), None, ) - .strict(true) } + pub fn query(&self) -> RustyPipeQuery { + RustyPipeQuery { + client: self.clone(), + opts: RustyPipeOpts { + lang: Language::En, + country: Country::Us, + report: false, + strict: false, + }, + } + } + + #[cfg(test)] + pub fn test_query(&self) -> RustyPipeQuery { + RustyPipeQuery { + client: self.clone(), + opts: RustyPipeOpts { + lang: Language::En, + country: Country::Us, + report: false, + strict: true, + }, + } + } +} + +impl RustyPipeQuery { /// Set the language parameter used when accessing the YouTube API /// This will change multilanguage video titles, descriptions and textual dates pub fn lang(mut self, lang: Language) -> Self { @@ -431,6 +461,7 @@ impl RustyPipe { ) -> RequestBuilder { match ctype { ClientType::Desktop => self + .client .inner .http .request( @@ -442,10 +473,14 @@ impl RustyPipe { ) .header(header::ORIGIN, "https://www.youtube.com") .header(header::REFERER, "https://www.youtube.com") - .header(header::COOKIE, self.inner.consent_cookie.to_owned()) + .header(header::COOKIE, self.client.inner.consent_cookie.to_owned()) .header("X-YouTube-Client-Name", "1") - .header("X-YouTube-Client-Version", self.get_desktop_client_version().await), + .header( + "X-YouTube-Client-Version", + self.get_desktop_client_version().await, + ), ClientType::DesktopMusic => self + .client .inner .http .request( @@ -460,10 +495,14 @@ impl RustyPipe { ) .header(header::ORIGIN, "https://music.youtube.com") .header(header::REFERER, "https://music.youtube.com") - .header(header::COOKIE, self.inner.consent_cookie.to_owned()) + .header(header::COOKIE, self.client.inner.consent_cookie.to_owned()) .header("X-YouTube-Client-Name", "67") - .header("X-YouTube-Client-Version", self.get_music_client_version().await), + .header( + "X-YouTube-Client-Version", + self.get_music_client_version().await, + ), ClientType::TvHtml5Embed => self + .client .inner .http .request( @@ -478,6 +517,7 @@ impl RustyPipe { .header("X-YouTube-Client-Name", "1") .header("X-YouTube-Client-Version", TVHTML5_CLIENT_VERSION), ClientType::Android => self + .client .inner .http .request( @@ -499,6 +539,7 @@ impl RustyPipe { ) .header("X-Goog-Api-Format-Version", "2"), ClientType::Ios => self + .client .inner .http .request( @@ -545,13 +586,13 @@ impl RustyPipe { let request_url = request.url().to_string(); let request_headers = request.headers().to_owned(); - let response = self.inner.http.execute(request).await?; + let response = self.client.inner.http.execute(request).await?; let status = response.status(); let resp_str = response.text().await?; let create_report = |level: Level, error: Option, msgs: Vec| { - if let Some(reporter) = &self.inner.reporter { + if let Some(reporter) = &self.client.inner.reporter { let report = Report { package: "rustypipe".to_owned(), version: "0.1.0".to_owned(), @@ -636,11 +677,16 @@ impl RustyPipe { } async fn get_desktop_client_version(&self) -> String { - let mut cache = self.inner.cache.lock().await; + let mut cache = self.client.inner.cache.lock().await; match cache.desktop_client.get() { Some(cdata) => cdata.version.to_owned(), - None => match self.extract_desktop_client_version().await { + None => match extract_desktop_client_version( + self.client.inner.http.clone(), + self.client.inner.consent_cookie.to_owned(), + ) + .await + { Ok(version) => { cache.desktop_client = CacheEntry::from(ClientData { version: version.to_owned(), @@ -657,11 +703,16 @@ impl RustyPipe { } async fn get_music_client_version(&self) -> String { - let mut cache = self.inner.cache.lock().await; + let mut cache = self.client.inner.cache.lock().await; match cache.music_client.get() { Some(cdata) => cdata.version.to_owned(), - None => match self.extract_music_client_version().await { + None => match extract_music_client_version( + self.client.inner.http.clone(), + self.client.inner.consent_cookie.to_owned(), + ) + .await + { Ok(version) => { cache.music_client = CacheEntry::from(ClientData { version: version.to_owned(), @@ -678,109 +729,21 @@ impl RustyPipe { } async fn get_deobf(&self) -> Result { - let mut cache = self.inner.cache.lock().await; - let deobf = Deobfuscator::new(self.inner.http.clone()).await?; - cache.deobf = CacheEntry::from(deobf.get_data()); - self.write_cache(&cache); - Ok(deobf) - } + let mut cache = self.client.inner.cache.lock().await; - async fn extract_desktop_client_version(&self) -> Result { - let from_swjs = async { - let swjs = self - .exec_request_text( - self.inner - .http - .get("https://www.youtube.com/sw.js") - .header(header::ORIGIN, "https://www.youtube.com") - .header(header::REFERER, "https://www.youtube.com") - .header(header::COOKIE, self.inner.consent_cookie.to_owned()) - .build() - .unwrap(), - ) - .await - .context("Failed to download sw.js")?; - - util::get_cg_from_regexes(CLIENT_VERSION_REGEXES.iter(), &swjs, 1) - .ok_or(anyhow!("Could not find desktop client version in sw.js")) - }; - - let from_html = async { - let html = self - .exec_request_text( - self.inner - .http - .get("https://www.youtube.com/results?search_query=") - .build() - .unwrap(), - ) - .await - .context("Failed to get YT Desktop page")?; - - util::get_cg_from_regexes(CLIENT_VERSION_REGEXES.iter(), &html, 1).ok_or(anyhow!( - "Could not find desktop client version on html page" - )) - }; - - match from_swjs.await { - Ok(client_version) => Ok(client_version), - Err(_) => from_html.await, + match cache.deobf.get() { + Some(deobf) => Ok(Deobfuscator::from(deobf.to_owned())), + None => { + let deobf = Deobfuscator::new(self.client.inner.http.clone()).await?; + cache.deobf = CacheEntry::from(deobf.get_data()); + self.write_cache(&cache); + Ok(deobf) + } } } - async fn extract_music_client_version(&self) -> Result { - let from_swjs = async { - let swjs = self - .exec_request_text( - self.inner - .http - .get("https://music.youtube.com/sw.js") - .header(header::ORIGIN, "https://music.youtube.com") - .header(header::REFERER, "https://music.youtube.com") - .header(header::COOKIE, self.inner.consent_cookie.to_owned()) - .build() - .unwrap(), - ) - .await - .context("Failed to download sw.js")?; - - util::get_cg_from_regexes(CLIENT_VERSION_REGEXES.iter(), &swjs, 1) - .ok_or(anyhow!("Could not find desktop client version in sw.js")) - }; - - let from_html = async { - let html = self - .exec_request_text( - self.inner - .http - .get("https://music.youtube.com") - .build() - .unwrap(), - ) - .await - .context("Failed to get YT Desktop page")?; - - util::get_cg_from_regexes(CLIENT_VERSION_REGEXES.iter(), &html, 1).ok_or(anyhow!( - "Could not find desktop client version on html page" - )) - }; - - match from_swjs.await { - Ok(client_version) => Ok(client_version), - Err(_) => from_html.await, - } - } - - async fn exec_request(&self, request: Request) -> Result { - Ok(self.inner.http.execute(request).await?.error_for_status()?) - } - - async fn exec_request_text(&self, request: Request) -> Result { - Ok(self.exec_request(request).await?.text().await?) - } - fn write_cache(&self, cache: &CacheData) { - if let Some(storage) = &self.inner.storage { + if let Some(storage) = &self.client.inner.storage { match serde_json::to_string(cache) { Ok(data) => storage.write(&data), Err(e) => error!("Could not serialize cache. Error: {}", e), @@ -789,6 +752,90 @@ impl RustyPipe { } } +async fn extract_desktop_client_version(http: Client, consent_cookie: String) -> Result { + let from_swjs = async { + let swjs = exec_request_text( + http.clone(), + http.get("https://www.youtube.com/sw.js") + .header(header::ORIGIN, "https://www.youtube.com") + .header(header::REFERER, "https://www.youtube.com") + .header(header::COOKIE, consent_cookie) + .build() + .unwrap(), + ) + .await + .context("Failed to download sw.js")?; + + util::get_cg_from_regexes(CLIENT_VERSION_REGEXES.iter(), &swjs, 1) + .ok_or(anyhow!("Could not find desktop client version in sw.js")) + }; + + let from_html = async { + let html = exec_request_text( + http.clone(), + http.get("https://www.youtube.com/results?search_query=") + .build() + .unwrap(), + ) + .await + .context("Failed to get YT Desktop page")?; + + util::get_cg_from_regexes(CLIENT_VERSION_REGEXES.iter(), &html, 1).ok_or(anyhow!( + "Could not find desktop client version on html page" + )) + }; + + match from_swjs.await { + Ok(client_version) => Ok(client_version), + Err(_) => from_html.await, + } +} + +async fn extract_music_client_version(http: Client, consent_cookie: String) -> Result { + let from_swjs = async { + let swjs = exec_request_text( + http.clone(), + http.get("https://music.youtube.com/sw.js") + .header(header::ORIGIN, "https://music.youtube.com") + .header(header::REFERER, "https://music.youtube.com") + .header(header::COOKIE, consent_cookie) + .build() + .unwrap(), + ) + .await + .context("Failed to download sw.js")?; + + util::get_cg_from_regexes(CLIENT_VERSION_REGEXES.iter(), &swjs, 1) + .ok_or(anyhow!("Could not find desktop client version in sw.js")) + }; + + let from_html = async { + let html = exec_request_text( + http.clone(), + http.get("https://music.youtube.com").build().unwrap(), + ) + .await + .context("Failed to get YT Desktop page")?; + + util::get_cg_from_regexes(CLIENT_VERSION_REGEXES.iter(), &html, 1).ok_or(anyhow!( + "Could not find desktop client version on html page" + )) + }; + + match from_swjs.await { + Ok(client_version) => Ok(client_version), + Err(_) => from_html.await, + } +} + +async fn exec_request(http: Client, request: Request) -> Result { + Ok(http.execute(request).await?.error_for_status()?) +} + +async fn exec_request_text(http: Client, request: Request) -> Result { + Ok(exec_request(http, request).await?.text().await?) +} + trait MapResponse { fn map_response( self, @@ -812,3 +859,21 @@ where self.c.fmt(f) } } + +impl Default for MapResult +where + T: Default, +{ + fn default() -> Self { + Self { + c: Default::default(), + warnings: Vec::new(), + } + } +} + +#[cfg(test)] +#[cfg(feature = "yaml")] +mod tests { + // use super::*; +} diff --git a/src/client2/player.rs b/src/client2/player.rs index 402b8ba..27571c9 100644 --- a/src/client2/player.rs +++ b/src/client2/player.rs @@ -21,7 +21,7 @@ use crate::{ use super::{ response::{self, player}, - ClientType, ContextYT, MapResponse, MapResult, RustyPipe, + ClientType, ContextYT, MapResponse, MapResult, RustyPipeQuery, }; #[derive(Clone, Debug, Serialize)] @@ -57,13 +57,21 @@ struct QContentPlaybackContext { referer: String, } -impl RustyPipe { - pub async fn get_player(&self, video_id: &str, client_type: ClientType) -> Result { - let (context, deobf) = tokio::join!(self.get_context(client_type, false), self.get_deobf()); - // let context = self.get_context(client_type, false).await; - // let deobf = self.get_deobf().await; +impl RustyPipeQuery { + pub async fn get_player(self, video_id: &str, client_type: ClientType) -> Result { + // let (context, deobf) = tokio::join!(self.get_context(client_type, false), self.get_deobf()); + // let deobf = deobf?; - let deobf = deobf?; + let q1 = self.clone(); + let t_context = tokio::spawn(async move { q1.get_context(client_type, false).await }); + let q2 = self.clone(); + let t_deobf = tokio::spawn(async move { q2.get_deobf().await }); + // let context = t_context.await.unwrap(); + // let deobf = t_deobf.await.unwrap()?; + + let (context, deobf) = tokio::join!(t_context, t_deobf); + let context = context.unwrap(); + let deobf = deobf.unwrap()?; let request_body = if client_type.is_web() { QPlayer { @@ -187,8 +195,11 @@ impl MapResponse for response::Player { is_family_safe, }; - let mut formats = streaming_data.formats; - formats.append(&mut streaming_data.adaptive_formats); + let mut formats = streaming_data.formats.c; + formats.append(&mut streaming_data.adaptive_formats.c); + + warnings.append(&mut streaming_data.formats.warnings); + warnings.append(&mut streaming_data.adaptive_formats.warnings); let mut last_nsig: [String; 2] = ["".to_owned(), "".to_owned()]; @@ -579,7 +590,11 @@ fn get_audio_codec(codecs: Vec<&str>) -> AudioCodec { mod tests { use std::{fs::File, io::BufReader, path::Path}; - use crate::{deobfuscate::DeobfData, client2::CLIENT_TYPES, report::TestFileReporter}; + use crate::{ + client2::{RustyPipe, CLIENT_TYPES}, + deobfuscate::DeobfData, + report::TestFileReporter, + }; use super::*; use rstest::rstest; @@ -606,8 +621,12 @@ mod tests { } let reporter = TestFileReporter::new(json_path); - let rp = RustyPipe::new(None, Some(Box::new(reporter)), None).report(true); - rp.get_player(video_id, client_type).await.unwrap(); + let rp = RustyPipe::new(None, Some(Box::new(reporter)), None); + rp.test_query() + .report(true) + .get_player(video_id, client_type) + .await + .unwrap(); } } @@ -623,7 +642,11 @@ mod tests { continue; } - let player_data = rp.get_player(id, ClientType::Desktop).await.unwrap(); + let player_data = rp + .test_query() + .get_player(id, ClientType::Desktop) + .await + .unwrap(); let file = File::create(json_path).unwrap(); serde_json::to_writer_pretty(file, &player_data).unwrap(); } @@ -685,7 +708,11 @@ mod tests { #[test_log::test(tokio::test)] async fn t_get_player(#[case] client_type: ClientType) { let rp = RustyPipe::new_test(); - let player_data = rp.get_player("n4tK7LYFxI0", client_type).await.unwrap(); + let player_data = rp + .test_query() + .get_player("n4tK7LYFxI0", client_type) + .await + .unwrap(); // dbg!(&player_data); diff --git a/src/client2/playlist.rs b/src/client2/playlist.rs index 8e3214a..2fd0ade 100644 --- a/src/client2/playlist.rs +++ b/src/client2/playlist.rs @@ -9,7 +9,7 @@ use crate::{ timeago, util, }; -use super::{response, ClientType, ContextYT, MapResponse, MapResult, RustyPipe}; +use super::{response, ClientType, ContextYT, MapResponse, MapResult, RustyPipeQuery}; #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] @@ -25,8 +25,8 @@ struct QPlaylistCont { continuation: String, } -impl RustyPipe { - pub async fn get_playlist(&self, playlist_id: &str) -> Result { +impl RustyPipeQuery { + pub async fn get_playlist(self, playlist_id: &str) -> Result { let context = self.get_context(ClientType::Desktop, true).await; let request_body = QPlaylist { context, @@ -44,7 +44,7 @@ impl RustyPipe { .await } - pub async fn get_playlist_cont(&self, playlist: &mut Playlist) -> Result<()> { + pub async fn get_playlist_cont(self, playlist: &mut Playlist) -> Result<()> { match &playlist.ctoken { Some(ctoken) => { let context = self.get_context(ClientType::Desktop, true).await; @@ -308,7 +308,7 @@ mod tests { use rstest::rstest; - use crate::report::TestFileReporter; + use crate::{client2::RustyPipe, report::TestFileReporter}; use super::*; @@ -349,7 +349,7 @@ mod tests { #[case] channel: Option, ) { let rp = RustyPipe::new_test(); - let playlist = rp.get_playlist(id).await.unwrap(); + let playlist = rp.test_query().get_playlist(id).await.unwrap(); assert_eq!(playlist.id, id); assert_eq!(playlist.name, name); @@ -380,8 +380,8 @@ mod tests { } let reporter = TestFileReporter::new(json_path); - let rp = RustyPipe::new(None, Some(Box::new(reporter)), None).report(true); - rp.get_playlist(id).await.unwrap(); + let rp = RustyPipe::new(None, Some(Box::new(reporter)), None); + rp.test_query().report(true).get_playlist(id).await.unwrap(); } } @@ -412,12 +412,16 @@ mod tests { async fn t_playlist_cont() { let rp = RustyPipe::new_test(); let mut playlist = rp + .test_query() .get_playlist("PLbZIPy20-1pN7mqjckepWF78ndb6ci_qi") .await .unwrap(); while playlist.ctoken.is_some() { - rp.get_playlist_cont(&mut playlist).await.unwrap(); + rp.test_query() + .get_playlist_cont(&mut playlist) + .await + .unwrap(); } assert!(playlist.videos.len() > 100); diff --git a/src/client2/response/channel.rs b/src/client2/response/channel.rs index ce7a6db..b4796c3 100644 --- a/src/client2/response/channel.rs +++ b/src/client2/response/channel.rs @@ -4,6 +4,7 @@ use serde_with::VecSkipError; use super::TimeOverlay; use super::{ContentRenderer, ContentsRenderer, Thumbnails, VideoListItem}; +use crate::serializer::text::Text; #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] @@ -63,11 +64,11 @@ pub struct GridRenderer { pub struct ChannelVideo { pub video_id: String, pub thumbnail: Thumbnails, - #[serde_as(as = "crate::serializer::text::Text")] + #[serde_as(as = "Text")] pub title: String, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub published_time_text: Option, - #[serde_as(as = "crate::serializer::text::Text")] + #[serde_as(as = "Text")] pub view_count_text: String, #[serde_as(as = "VecSkipError<_>")] pub thumbnail_overlays: Vec, diff --git a/src/client2/response/mod.rs b/src/client2/response/mod.rs index 09597c8..65641b8 100644 --- a/src/client2/response/mod.rs +++ b/src/client2/response/mod.rs @@ -16,7 +16,7 @@ pub use video::VideoRecommendations; use serde::Deserialize; use serde_with::{serde_as, DefaultOnError, VecSkipError}; -use crate::serializer::text::TextLink; +use crate::serializer::text::{Text, TextLink, TextLinks}; #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] @@ -94,10 +94,10 @@ pub struct VideoOwner { #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VideoOwnerRenderer { - #[serde_as(as = "crate::serializer::text::TextLink")] + #[serde_as(as = "TextLink")] pub title: TextLink, pub thumbnail: Thumbnails, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub subscriber_count_text: Option, #[serde(default)] #[serde_as(as = "VecSkipError<_>")] @@ -133,7 +133,7 @@ pub struct TimeOverlay { #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TimeOverlayRenderer { - #[serde_as(as = "crate::serializer::text::Text")] + #[serde_as(as = "Text")] pub text: String, #[serde(default)] #[serde_as(deserialize_as = "DefaultOnError")] @@ -199,7 +199,7 @@ pub struct MusicColumn { #[serde_as] #[derive(Clone, Debug, Deserialize)] pub struct MusicColumnRenderer { - #[serde_as(as = "crate::serializer::text::TextLinks")] + #[serde_as(as = "TextLinks")] pub text: Vec, } diff --git a/src/client2/response/player.rs b/src/client2/response/player.rs index 36b20f6..eb6a1b7 100644 --- a/src/client2/response/player.rs +++ b/src/client2/response/player.rs @@ -3,9 +3,11 @@ use std::ops::Range; use chrono::NaiveDate; use serde::Deserialize; use serde_with::serde_as; -use serde_with::{json::JsonString, DefaultOnError, VecSkipError}; +use serde_with::{json::JsonString, DefaultOnError}; use super::Thumbnails; +use crate::client2::MapResult; +use crate::serializer::{text::Text, VecLogError}; #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] @@ -45,11 +47,11 @@ pub struct StreamingData { #[serde_as(as = "JsonString")] pub expires_in_seconds: u32, #[serde(default)] - #[serde_as(as = "VecSkipError<_>")] - pub formats: Vec, + #[serde_as(as = "VecLogError<_>")] + pub formats: MapResult>, #[serde(default)] - #[serde_as(as = "VecSkipError<_>")] - pub adaptive_formats: Vec, + #[serde_as(as = "VecLogError<_>")] + pub adaptive_formats: MapResult>, /// Only on livestreams pub dash_manifest_url: Option, /// Only on livestreams @@ -73,9 +75,9 @@ pub struct Format { pub width: Option, pub height: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub index_range: Option>, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub init_range: Option>, #[serde_as(as = "JsonString")] @@ -188,7 +190,7 @@ pub struct PlayerCaptionsTracklistRenderer { #[serde(rename_all = "camelCase")] pub struct CaptionTrack { pub base_url: String, - #[serde_as(as = "crate::serializer::text::Text")] + #[serde_as(as = "Text")] pub name: String, pub language_code: String, } diff --git a/src/client2/response/playlist.rs b/src/client2/response/playlist.rs index 2ab8357..8b9118b 100644 --- a/src/client2/response/playlist.rs +++ b/src/client2/response/playlist.rs @@ -3,7 +3,8 @@ use serde_with::serde_as; use serde_with::{json::JsonString, DefaultOnError, VecSkipError}; use crate::client2::MapResult; -use crate::serializer::text::TextLink; +use crate::serializer::text::{Text, TextLink}; +use crate::serializer::VecLogError; use super::{ContentRenderer, ContentsRenderer, Thumbnails, ThumbnailsWrap, VideoListItem}; @@ -57,7 +58,7 @@ pub struct PlaylistVideoListRenderer { #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PlaylistVideoList { - #[serde_as(as = "crate::serializer::VecLogError<_>")] + #[serde_as(as = "VecLogError<_>")] pub contents: MapResult>>, } @@ -67,10 +68,10 @@ pub struct PlaylistVideoList { pub struct PlaylistVideo { pub video_id: String, pub thumbnail: Thumbnails, - #[serde_as(as = "crate::serializer::text::Text")] + #[serde_as(as = "Text")] pub title: String, #[serde(rename = "shortBylineText")] - #[serde_as(as = "crate::serializer::text::TextLink")] + #[serde_as(as = "TextLink")] pub channel: TextLink, #[serde_as(as = "JsonString")] pub length_seconds: u32, @@ -87,14 +88,14 @@ pub struct Header { #[serde(rename_all = "camelCase")] pub struct HeaderRenderer { pub playlist_id: String, - #[serde_as(as = "crate::serializer::text::Text")] + #[serde_as(as = "Text")] pub title: String, #[serde(default)] - #[serde_as(as = "DefaultOnError>")] + #[serde_as(as = "DefaultOnError>")] pub description_text: Option, - #[serde_as(as = "crate::serializer::text::Text")] + #[serde_as(as = "Text")] pub num_videos_text: String, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub owner_text: Option, // Alternative layout @@ -119,7 +120,7 @@ pub struct Byline { #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct BylineRenderer { - #[serde_as(as = "crate::serializer::text::Text")] + #[serde_as(as = "Text")] pub text: String, } @@ -151,7 +152,7 @@ pub struct SidebarPrimaryInfoRenderer { // - `"495", " videos"` // - `"3,310,996 views"` // - `"Last updated on ", "Aug 7, 2022"` - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub stats: Vec, } @@ -173,7 +174,7 @@ pub struct OnResponseReceivedAction { #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AppendAction { - #[serde_as(as = "crate::serializer::VecLogError<_>")] + #[serde_as(as = "VecLogError<_>")] pub continuation_items: MapResult>>, pub target_id: String, } diff --git a/src/deobfuscate.rs b/src/deobfuscate.rs index e85d012..2428421 100644 --- a/src/deobfuscate.rs +++ b/src/deobfuscate.rs @@ -3,7 +3,7 @@ use fancy_regex::Regex; use log::debug; use once_cell::sync::Lazy; use reqwest::Client; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; use std::result::Result::Ok; use crate::util; @@ -43,7 +43,7 @@ impl Deobfuscator { sig_fn, sts, }, - }) + }) } pub fn deobfuscate_sig(&self, sig: &str) -> Result { @@ -480,9 +480,7 @@ c[36](c[8],c[32]),c[20](c[25],c[10]),c[2](c[22],c[8]),c[32](c[20],c[16]),c[32](c #[test(tokio::test)] async fn t_update() { let client = Client::new(); - let deobf = Deobfuscator::new(client) - .await - .unwrap(); + let deobf = Deobfuscator::new(client).await.unwrap(); let deobf_sig = deobf.deobfuscate_sig("GOqGOqGOq0QJ8wRAIgaryQHfplJ9xJSKFywyaSMHuuwZYsoMTAvRvfm51qIGECIA5061zWeyfMPX9hEl_U6f9J0tr7GTJMKyPf5XNrJb5fb5i").unwrap(); println!("{}", deobf_sig); diff --git a/src/report.rs b/src/report.rs index 85939d9..2c6df25 100644 --- a/src/report.rs +++ b/src/report.rs @@ -86,7 +86,7 @@ impl JsonFileReporter { impl Default for JsonFileReporter { fn default() -> Self { Self { - path: Path::new("RustyPipeReports").to_path_buf(), + path: Path::new("rustypipe_reports").to_path_buf(), } } } @@ -98,12 +98,12 @@ impl Reporter for JsonFileReporter { } } -#[cfg(feature="yaml")] +#[cfg(feature = "yaml")] pub struct YamlFileReporter { path: PathBuf, } -#[cfg(feature="yaml")] +#[cfg(feature = "yaml")] impl YamlFileReporter { pub fn new>(path: P) -> Self { Self { @@ -118,7 +118,7 @@ impl YamlFileReporter { } } -#[cfg(feature="yaml")] +#[cfg(feature = "yaml")] impl Default for YamlFileReporter { fn default() -> Self { Self { @@ -127,7 +127,7 @@ impl Default for YamlFileReporter { } } -#[cfg(feature="yaml")] +#[cfg(feature = "yaml")] impl Reporter for YamlFileReporter { fn report(&self, report: &Report) { self._report(report) diff --git a/src/serializer/mod.rs b/src/serializer/mod.rs index fad2f1e..045160a 100644 --- a/src/serializer/mod.rs +++ b/src/serializer/mod.rs @@ -1,5 +1,7 @@ -pub mod range; pub mod text; + +mod range; mod vec_log_err; +pub use range::Range; pub use vec_log_err::VecLogError;