From 79446bed1c7d90b923af7e29e5a9eacd072ce1f3 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sat, 4 Feb 2023 16:15:35 +0100 Subject: [PATCH 1/3] fix: keep JS context when deobfuscating --- src/client/mod.rs | 13 +++-- src/client/player.rs | 29 +++++----- src/deobfuscate.rs | 134 +++++++++++++++++++++++-------------------- tests/youtube.rs | 1 + 4 files changed, 94 insertions(+), 83 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index aca22fe..6ec5717 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -708,15 +708,16 @@ impl RustyPipe { /// Instantiate a new deobfuscator from either cached or extracted YouTube JavaScript code. async fn get_deobf(&self) -> Result { // Write lock here to prevent concurrent tasks from fetching the same data - let mut deobf = self.inner.cache.deobf.write().await; + let mut deobf_data = self.inner.cache.deobf.write().await; - match deobf.get() { - Some(deobf) => Ok(Deobfuscator::from(deobf.to_owned())), + match deobf_data.get() { + Some(deobf_data) => Ok(Deobfuscator::new(deobf_data.clone())?), None => { log::debug!("getting deobfuscator"); - let new_deobf = Deobfuscator::new(self.inner.http.clone()).await?; - *deobf = CacheEntry::from(new_deobf.get_data()); - drop(deobf); + let data = DeobfData::download(self.inner.http.clone()).await?; + let new_deobf = Deobfuscator::new(data.clone())?; + *deobf_data = CacheEntry::from(data); + drop(deobf_data); self.store_cache().await; Ok(new_deobf) } diff --git a/src/client/player.rs b/src/client/player.rs index c91a21b..c95b6c0 100644 --- a/src/client/player.rs +++ b/src/client/player.rs @@ -612,19 +612,20 @@ mod tests { use std::{fs::File, io::BufReader}; use path_macro::path; - use rstest::rstest; + use rstest::{fixture, rstest}; use super::*; use crate::deobfuscate::DeobfData; - static DEOBFUSCATOR: Lazy = Lazy::new(|| { - Deobfuscator::from(DeobfData { - js_url: "https://www.youtube.com/s/player/c8b8a173/player_ias.vflset/en_US/base.js".to_owned(), - sig_fn: "var oB={B4:function(a){a.reverse()},xm:function(a,b){a.splice(0,b)},dC:function(a,b){var c=a[0];a[0]=a[b%a.length];a[b%a.length]=c}};var Vva=function(a){a=a.split(\"\");oB.dC(a,42);oB.xm(a,3);oB.dC(a,48);oB.B4(a,68);return a.join(\"\")};function deobfuscate(a){return Vva(a);}".to_owned(), - nsig_fn: "Ska=function(a){var b=a.split(\"\"),c=[-1505243983,function(d,e){e=(e%d.length+d.length)%d.length;d.splice(-e).reverse().forEach(function(f){d.unshift(f)})},\n-1692381986,function(d,e){e=(e%d.length+d.length)%d.length;var f=d[0];d[0]=d[e];d[e]=f},\n-262444939,\"unshift\",function(d){for(var e=d.length;e;)d.push(d.splice(--e,1)[0])},\n1201502951,-546377604,-504264123,-1978377336,1042456724,function(d,e){for(e=(e%d.length+d.length)%d.length;e--;)d.unshift(d.pop())},\n711986897,406699922,-1842537993,-1678108293,1803491779,1671716087,12778705,-718839990,null,null,-1617525823,342523552,-1338406651,-399705108,-696713950,b,function(d,e){e=(e%d.length+d.length)%d.length;d.splice(0,1,d.splice(e,1,d[0])[0])},\nfunction(d,e){e=(e%d.length+d.length)%d.length;d.splice(e,1)},\n-980602034,356396192,null,-1617525823,function(d,e,f){var h=f.length;d.forEach(function(l,m,n){this.push(n[m]=f[(f.indexOf(l)-f.indexOf(this[m])+m+h--)%f.length])},e.split(\"\"))},\n-1029864222,-641353250,-1681901809,-1391247867,1707415199,-1957855835,b,function(){for(var d=64,e=[];++d-e.length-32;)switch(d){case 58:d=96;continue;case 91:d=44;break;case 65:d=47;continue;case 46:d=153;case 123:d-=58;default:e.push(String.fromCharCode(d))}return e},\n-1936558978,-1505243983,function(d){d.reverse()},\n1296889058,-1813915420,-943019300,function(d,e,f){var h=f.length;d.forEach(function(l,m,n){this.push(n[m]=f[(f.indexOf(l)-f.indexOf(this[m])+m+h--)%f.length])},e.split(\"\"))},\n\"join\",b,-2061642263];c[21]=c;c[22]=c;c[33]=c;try{c[3](c[33],c[9]),c[29](c[22],c[25]),c[29](c[22],c[19]),c[29](c[33],c[17]),c[29](c[21],c[2]),c[29](c[42],c[10]),c[1](c[52],c[40]),c[12](c[28],c[8]),c[29](c[21],c[45]),c[1](c[21],c[48]),c[44](c[26]),c[39](c[5],c[2]),c[31](c[53],c[16]),c[30](c[29],c[8]),c[51](c[29],c[6],c[44]()),c[4](c[43],c[1]),c[2](c[23],c[42]),c[2](c[0],c[46]),c[38](c[14],c[52]),c[32](c[5]),c[26](c[29],c[46]),c[26](c[5],c[13]),c[28](c[1],c[37]),c[26](c[31],c[13]),c[26](c[1],c[34]),\nc[46](c[1],c[32],c[40]()),c[26](c[50],c[44]),c[17](c[50],c[51]),c[0](c[3],c[24]),c[32](c[13]),c[43](c[3],c[51]),c[0](c[34],c[17]),c[16](c[45],c[53]),c[29](c[44],c[13]),c[42](c[1],c[50]),c[47](c[22],c[53]),c[37](c[22]),c[13](c[52],c[21]),c[6](c[43],c[34]),c[6](c[31],c[46])}catch(d){return\"enhanced_except_gZYB_un-_w8_\"+a}return b.join(\"\")};function deobfuscate(a){return Ska(a);}".to_owned(), - sts: "19201".to_owned(), - }) - }); + #[fixture] + fn deobf() -> Deobfuscator { + Deobfuscator::new(DeobfData { + js_url: "https://www.youtube.com/s/player/c8b8a173/player_ias.vflset/en_US/base.js".to_owned(), + sig_fn: "var oB={B4:function(a){a.reverse()},xm:function(a,b){a.splice(0,b)},dC:function(a,b){var c=a[0];a[0]=a[b%a.length];a[b%a.length]=c}};var Vva=function(a){a=a.split(\"\");oB.dC(a,42);oB.xm(a,3);oB.dC(a,48);oB.B4(a,68);return a.join(\"\")};var deobf_sig=Vva;".to_owned(), + nsig_fn: "Ska=function(a){var b=a.split(\"\"),c=[-1505243983,function(d,e){e=(e%d.length+d.length)%d.length;d.splice(-e).reverse().forEach(function(f){d.unshift(f)})},\n-1692381986,function(d,e){e=(e%d.length+d.length)%d.length;var f=d[0];d[0]=d[e];d[e]=f},\n-262444939,\"unshift\",function(d){for(var e=d.length;e;)d.push(d.splice(--e,1)[0])},\n1201502951,-546377604,-504264123,-1978377336,1042456724,function(d,e){for(e=(e%d.length+d.length)%d.length;e--;)d.unshift(d.pop())},\n711986897,406699922,-1842537993,-1678108293,1803491779,1671716087,12778705,-718839990,null,null,-1617525823,342523552,-1338406651,-399705108,-696713950,b,function(d,e){e=(e%d.length+d.length)%d.length;d.splice(0,1,d.splice(e,1,d[0])[0])},\nfunction(d,e){e=(e%d.length+d.length)%d.length;d.splice(e,1)},\n-980602034,356396192,null,-1617525823,function(d,e,f){var h=f.length;d.forEach(function(l,m,n){this.push(n[m]=f[(f.indexOf(l)-f.indexOf(this[m])+m+h--)%f.length])},e.split(\"\"))},\n-1029864222,-641353250,-1681901809,-1391247867,1707415199,-1957855835,b,function(){for(var d=64,e=[];++d-e.length-32;)switch(d){case 58:d=96;continue;case 91:d=44;break;case 65:d=47;continue;case 46:d=153;case 123:d-=58;default:e.push(String.fromCharCode(d))}return e},\n-1936558978,-1505243983,function(d){d.reverse()},\n1296889058,-1813915420,-943019300,function(d,e,f){var h=f.length;d.forEach(function(l,m,n){this.push(n[m]=f[(f.indexOf(l)-f.indexOf(this[m])+m+h--)%f.length])},e.split(\"\"))},\n\"join\",b,-2061642263];c[21]=c;c[22]=c;c[33]=c;try{c[3](c[33],c[9]),c[29](c[22],c[25]),c[29](c[22],c[19]),c[29](c[33],c[17]),c[29](c[21],c[2]),c[29](c[42],c[10]),c[1](c[52],c[40]),c[12](c[28],c[8]),c[29](c[21],c[45]),c[1](c[21],c[48]),c[44](c[26]),c[39](c[5],c[2]),c[31](c[53],c[16]),c[30](c[29],c[8]),c[51](c[29],c[6],c[44]()),c[4](c[43],c[1]),c[2](c[23],c[42]),c[2](c[0],c[46]),c[38](c[14],c[52]),c[32](c[5]),c[26](c[29],c[46]),c[26](c[5],c[13]),c[28](c[1],c[37]),c[26](c[31],c[13]),c[26](c[1],c[34]),\nc[46](c[1],c[32],c[40]()),c[26](c[50],c[44]),c[17](c[50],c[51]),c[0](c[3],c[24]),c[32](c[13]),c[43](c[3],c[51]),c[0](c[34],c[17]),c[16](c[45],c[53]),c[29](c[44],c[13]),c[42](c[1],c[50]),c[47](c[22],c[53]),c[37](c[22]),c[13](c[52],c[21]),c[6](c[43],c[34]),c[6](c[31],c[46])}catch(d){return\"enhanced_except_gZYB_un-_w8_\"+a}return b.join(\"\")};var deobf_nsig=Ska;".to_owned(), + sts: "19201".to_owned(), + }).unwrap() + } #[rstest] #[case::desktop("desktop")] @@ -632,13 +633,13 @@ mod tests { #[case::tv_html5_embed("tvhtml5embed")] #[case::android("android")] #[case::ios("ios")] - fn map_player_data(#[case] name: &str) { + fn map_player_data(#[case] name: &str, deobf: Deobfuscator) { let json_path = path!("testfiles" / "player" / format!("{name}_video.json")); let json_file = File::open(json_path).unwrap(); let resp: response::Player = serde_json::from_reader(BufReader::new(json_file)).unwrap(); let map_res = resp - .map_response("pPvd8UxmSbQ", Language::En, Some(&DEOBFUSCATOR)) + .map_response("pPvd8UxmSbQ", Language::En, Some(&deobf)) .unwrap(); assert!( @@ -660,14 +661,14 @@ mod tests { }); } - #[test] - fn cipher_to_url() { + #[rstest] + fn cipher_to_url(deobf: Deobfuscator) { let signature_cipher = "s=w%3DAe%3DA6aDNQLkViKS7LOm9QtxZJHKwb53riq9qEFw-ecBWJCAiA%3DcEg0tn3dty9jEHszfzh4Ud__bg9CEHVx4ix-7dKsIPAhIQRw8JQ0qOA&sp=sig&url=https://rr5---sn-h0jelnez.googlevideo.com/videoplayback%3Fexpire%3D1659376413%26ei%3Dvb7nYvH5BMK8gAfBj7ToBQ%26ip%3D2003%253Ade%253Aaf06%253A6300%253Ac750%253A1b77%253Ac74a%253A80e3%26id%3Do-AB_BABwrXZJN428ZwDxq5ScPn2AbcGODnRlTVhCQ3mj2%26itag%3D251%26source%3Dyoutube%26requiressl%3Dyes%26mh%3DhH%26mm%3D31%252C26%26mn%3Dsn-h0jelnez%252Csn-4g5ednsl%26ms%3Dau%252Conr%26mv%3Dm%26mvi%3D5%26pl%3D37%26initcwndbps%3D1588750%26spc%3DlT-Khi831z8dTejFIRCvCEwx_6romtM%26vprv%3D1%26mime%3Daudio%252Fwebm%26ns%3Db_Mq_qlTFcSGlG9RpwpM9xQH%26gir%3Dyes%26clen%3D3781277%26dur%3D229.301%26lmt%3D1655510291473933%26mt%3D1659354538%26fvip%3D5%26keepalive%3Dyes%26fexp%3D24001373%252C24007246%26c%3DWEB%26rbqsm%3Dfr%26txp%3D4532434%26n%3Dd2g6G2hVqWIXxedQ%26sparams%3Dexpire%252Cei%252Cip%252Cid%252Citag%252Csource%252Crequiressl%252Cspc%252Cvprv%252Cmime%252Cns%252Cgir%252Cclen%252Cdur%252Clmt%26lsparams%3Dmh%252Cmm%252Cmn%252Cms%252Cmv%252Cmvi%252Cpl%252Cinitcwndbps%26lsig%3DAG3C_xAwRQIgCKCGJ1iu4wlaGXy3jcJyU3inh9dr1FIfqYOZEG_MdmACIQCbungkQYFk7EhD6K2YvLaHFMjKOFWjw001_tLb0lPDtg%253D%253D"; let mut last_nsig: [String; 2] = ["".to_owned(), "".to_owned()]; let map_res = map_url( &None, &Some(signature_cipher.to_owned()), - &DEOBFUSCATOR, + &deobf, &mut last_nsig, ); let (url, throttled) = map_res.c.unwrap(); diff --git a/src/deobfuscate.rs b/src/deobfuscate.rs index de92555..26f65b4 100644 --- a/src/deobfuscate.rs +++ b/src/deobfuscate.rs @@ -3,7 +3,6 @@ use once_cell::sync::Lazy; use regex::Regex; use reqwest::Client; use serde::{Deserialize, Serialize}; -use std::result::Result::Ok; use crate::{error::DeobfError, util}; @@ -11,6 +10,7 @@ type Result = core::result::Result; pub struct Deobfuscator { data: DeobfData, + ctx: quick_js::Context, } #[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -21,8 +21,8 @@ pub struct DeobfData { pub sts: String, } -impl Deobfuscator { - pub async fn new(http: Client) -> Result { +impl DeobfData { + pub async fn download(http: Client) -> Result { let js_url = get_player_js_url(&http).await?; let player_js = get_response(&http, &js_url).await?; @@ -33,21 +33,46 @@ impl Deobfuscator { let sts = get_sts(&player_js)?; Ok(Self { - data: DeobfData { - js_url, - nsig_fn, - sig_fn, - sts, - }, + js_url, + sig_fn, + nsig_fn, + sts, }) } +} + +impl Deobfuscator { + pub fn new(data: DeobfData) -> Result { + let ctx = + quick_js::Context::new().or(Err(DeobfError::Other("could not create QuickJS rt")))?; + ctx.eval(&data.sig_fn)?; + ctx.eval(&data.nsig_fn)?; + + Ok(Self { data, ctx }) + } pub fn deobfuscate_sig(&self, sig: &str) -> Result { - deobfuscate_sig(sig, &self.data.sig_fn) + let res = self.ctx.call_function(DEOBF_SIG_FUNC_NAME, vec![sig])?; + + res.as_str().map_or( + Err(DeobfError::Other("sig deobfuscation func returned null")), + |res| { + log::debug!("deobfuscated sig"); + Ok(res.to_owned()) + }, + ) } pub fn deobfuscate_nsig(&self, nsig: &str) -> Result { - deobfuscate_nsig(nsig, &self.data.nsig_fn) + let res = self.ctx.call_function(DEOBF_NSIG_FUNC_NAME, vec![nsig])?; + + res.as_str().map_or( + Err(DeobfError::Other("nsig deobfuscation func returned null")), + |res| { + log::debug!("deobfuscated nsig"); + Ok(res.to_owned()) + }, + ) } pub fn get_sts(&self) -> String { @@ -59,13 +84,8 @@ impl Deobfuscator { } } -impl From for Deobfuscator { - fn from(data: DeobfData) -> Self { - Self { data } - } -} - -const DEOBFUSCATION_FUNC_NAME: &str = "deobfuscate"; +const DEOBF_SIG_FUNC_NAME: &str = "deobf_sig"; +const DEOBF_NSIG_FUNC_NAME: &str = "deobf_nsig"; fn get_sig_fn_name(player_js: &str) -> Result { static FUNCTION_REGEXES: Lazy<[FancyRegex; 6]> = Lazy::new(|| { @@ -83,8 +103,8 @@ fn get_sig_fn_name(player_js: &str) -> Result { .ok_or(DeobfError::Extraction("deobf function name")) } -fn caller_function(fn_name: &str) -> String { - format!("var {DEOBFUSCATION_FUNC_NAME}={fn_name};") +fn caller_function(mapped_name: &str, fn_name: &str) -> String { + format!("var {mapped_name}={fn_name};") } fn get_sig_fn(player_js: &str) -> Result { @@ -125,19 +145,9 @@ fn get_sig_fn(player_js: &str) -> Result { .unwrap() .as_str(); - Ok(helper_object.to_owned() + &deobfuscate_function + &caller_function(&dfunc_name)) -} - -fn deobfuscate_sig(sig: &str, sig_fn: &str) -> Result { - let context = - quick_js::Context::new().or(Err(DeobfError::Other("could not create QuickJS rt")))?; - context.eval(sig_fn)?; - let res = context.call_function(DEOBFUSCATION_FUNC_NAME, vec![sig])?; - - res.as_str().map_or( - Err(DeobfError::Other("sig deobfuscation func returned null")), - |res| Ok(res.to_owned()), - ) + Ok(helper_object.to_owned() + + &deobfuscate_function + + &caller_function(DEOBF_SIG_FUNC_NAME, &dfunc_name)) } fn get_nsig_fn_name(player_js: &str) -> Result { @@ -240,19 +250,7 @@ fn get_nsig_fn(player_js: &str) -> Result { let offset = player_js.find(&function_base).unwrap_or_default(); extract_js_fn(&player_js[offset..], &function_name) - .map(|s| s + ";" + &caller_function(&function_name)) -} - -fn deobfuscate_nsig(sig: &str, nsig_fn: &str) -> Result { - let context = - quick_js::Context::new().or(Err(DeobfError::Other("could not create QuickJS rt")))?; - context.eval(nsig_fn)?; - let res = context.call_function(DEOBFUSCATION_FUNC_NAME, vec![sig])?; - - res.as_str().map_or( - Err(DeobfError::Other("nsig deobfuscation func returned null")), - |res| Ok(res.to_owned()), - ) + .map(|s| s + ";" + &caller_function(DEOBF_NSIG_FUNC_NAME, &function_name)) } async fn get_player_js_url(http: &Client) -> Result { @@ -300,14 +298,15 @@ fn get_sts(player_js: &str) -> Result { mod tests { use super::*; use path_macro::path; - use test_log::test; + use rstest::{fixture, rstest}; static TEST_JS: Lazy = Lazy::new(|| { let js_path = path!("testfiles" / "deobf" / "dummy_player.js"); std::fs::read_to_string(js_path).unwrap() }); - const N_DEOBF_FUNC: &str = r#"Vo=function(a){var b=a.split(""),c=[function(d,e,f){var h=f.length;d.forEach(function(l,m,n){this.push(n[m]=f[(f.indexOf(l)-f.indexOf(this[m])+m+h--)%f.length])},e.split(""))}, + const SIG_DEOBF_FUNC: &str = r#"var qB={w8:function(a){a.reverse()},EC:function(a,b){var c=a[0];a[0]=a[b%a.length];a[b%a.length]=c},Np:function(a,b){a.splice(0,b)}};var Rva=function(a){a=a.split("");qB.Np(a,3);qB.w8(a,41);qB.EC(a,55);qB.Np(a,3);qB.w8(a,33);qB.Np(a,3);qB.EC(a,48);qB.EC(a,17);qB.EC(a,43);return a.join("")};var deobf_sig=Rva;"#; + const NSIG_DEOBF_FUNC: &str = r#"Vo=function(a){var b=a.split(""),c=[function(d,e,f){var h=f.length;d.forEach(function(l,m,n){this.push(n[m]=f[(f.indexOf(l)-f.indexOf(this[m])+m+h--)%f.length])},e.split(""))}, 928409064,-595856984,1403221911,653089124,-168714481,-1883008765,158931990,1346921902,361518508,1403221911,-362174697,-233641452,function(){for(var d=64,e=[];++d-e.length-32;){switch(d){case 91:d=44;continue;case 123:d=65;break;case 65:d-=18;continue;case 58:d=96;continue;case 46:d=95}e.push(String.fromCharCode(d))}return e}, b,158931990,791141857,-907319795,-1776185924,1595027902,-829736173,function(d,e){e=(e%d.length+d.length)%d.length;d.splice(0,1,d.splice(e,1,d[0])[0])}, -1274951142,function(){for(var d=64,e=[];++d-e.length-32;){switch(d){case 91:d=44;continue;case 123:d=65;break;case 65:d-=18;continue;case 58:d=96;continue;case 46:d=95}e.push(String.fromCharCode(d))}return e}, @@ -319,7 +318,18 @@ null,497372841,-1912651541,function(d,e){d.push(e)}, function(d,e){e=(e%d.length+d.length)%d.length;d.splice(-e).reverse().forEach(function(f){d.unshift(f)})}, function(d,e){e=(e%d.length+d.length)%d.length;var f=d[0];d[0]=d[e];d[e]=f}]; c[30]=c;c[40]=c;c[46]=c;try{c[43](c[34]),c[45](c[40],c[47]),c[46](c[51],c[33]),c[16](c[47],c[36]),c[38](c[31],c[49]),c[16](c[11],c[39]),c[0](c[11]),c[35](c[0],c[30]),c[35](c[4],c[17]),c[34](c[48],c[7],c[11]()),c[35](c[4],c[23]),c[35](c[4],c[9]),c[5](c[48],c[28]),c[36](c[46],c[16]),c[4](c[41],c[1]),c[4](c[16],c[28]),c[3](c[40],c[17]),c[9](c[8],c[23]),c[45](c[30],c[4]),c[50](c[3],c[28]),c[36](c[51],c[23]),c[14](c[0],c[24]),c[14](c[35],c[1]),c[20](c[51],c[41]),c[15](c[8],c[0]),c[31](c[35]),c[29](c[26]), -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[47],c[49]),c[1](c[44],c[28]),c[39](c[16]),c[32](c[42],c[22]),c[46](c[14],c[48]),c[26](c[29],c[10]),c[46](c[9],c[3]),c[32](c[45])}catch(d){return"enhanced_except_85UBjOr-_w8_"+a}return b.join("")};var deobfuscate=Vo;"#; +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[47],c[49]),c[1](c[44],c[28]),c[39](c[16]),c[32](c[42],c[22]),c[46](c[14],c[48]),c[26](c[29],c[10]),c[46](c[9],c[3]),c[32](c[45])}catch(d){return"enhanced_except_85UBjOr-_w8_"+a}return b.join("")};var deobf_nsig=Vo;"#; + + #[fixture] + fn deobf() -> Deobfuscator { + Deobfuscator::new(DeobfData { + js_url: String::default(), + sig_fn: SIG_DEOBF_FUNC.to_owned(), + nsig_fn: NSIG_DEOBF_FUNC.to_owned(), + sts: String::default(), + }) + .unwrap() + } #[test] fn t_get_sig_fn_name() { @@ -330,17 +340,13 @@ 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] fn t_get_sig_fn() { let dcode = get_sig_fn(&TEST_JS).unwrap(); - assert_eq!( - dcode, - r#"var qB={w8:function(a){a.reverse()},EC:function(a,b){var c=a[0];a[0]=a[b%a.length];a[b%a.length]=c},Np:function(a,b){a.splice(0,b)}};var Rva=function(a){a=a.split("");qB.Np(a,3);qB.w8(a,41);qB.EC(a,55);qB.Np(a,3);qB.w8(a,33);qB.Np(a,3);qB.EC(a,48);qB.EC(a,17);qB.EC(a,43);return a.join("")};var deobfuscate=Rva;"# - ); + assert_eq!(dcode, SIG_DEOBF_FUNC); } - #[test] - fn t_deobfuscate_sig() { - let dcode = get_sig_fn(&TEST_JS).unwrap(); - let deobf = deobfuscate_sig("GOqGOqGOq0QJ8wRAIgaryQHfplJ9xJSKFywyaSMHuuwZYsoMTAvRvfm51qIGECIA5061zWeyfMPX9hEl_U6f9J0tr7GTJMKyPf5XNrJb5fb5i", &dcode).unwrap(); - assert_eq!(deobf, "AOq0QJ8wRAIgaryQHmplJ9xJSKFywyaSMHuuwZYsoMTfvRviG51qIGECIA5061zWeyfMPX9hEl_U6f9J0tr7GTJMKyPf5XNrJb5f"); + #[rstest] + fn t_deobfuscate_sig(deobf: Deobfuscator) { + let dsig = deobf.deobfuscate_sig("GOqGOqGOq0QJ8wRAIgaryQHfplJ9xJSKFywyaSMHuuwZYsoMTAvRvfm51qIGECIA5061zWeyfMPX9hEl_U6f9J0tr7GTJMKyPf5XNrJb5fb5i").unwrap(); + assert_eq!(dsig, "AOq0QJ8wRAIgaryQHmplJ9xJSKFywyaSMHuuwZYsoMTfvRviG51qIGECIA5061zWeyfMPX9hEl_U6f9J0tr7GTJMKyPf5XNrJb5f"); } #[test] @@ -361,6 +367,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] fn t_extract_js_fn_eviljs() { + // Evil JavaScript code containing braces within strings and regular expressions let base_js = "Wka = function(d){var x = [/,,/,913,/(,)}/,\"abcdef}\\\"\",];var y = 10/2/1;return x[1][y];}//some={}random-padding+;"; let res = extract_js_fn(base_js, "Wka").unwrap(); assert_eq!( @@ -372,7 +379,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] fn t_get_nsig_fn() { let res = get_nsig_fn(&TEST_JS).unwrap(); - assert_eq!(res, N_DEOBF_FUNC); + assert_eq!(res, NSIG_DEOBF_FUNC); } #[test] @@ -381,9 +388,9 @@ 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 assert_eq!(res, "19187") } - #[test] - fn t_deobfuscate_nsig() { - let res = deobfuscate_nsig("BI_n4PxQ22is-KKajKUW", N_DEOBF_FUNC).unwrap(); + #[rstest] + fn t_deobfuscate_nsig(deobf: Deobfuscator) { + let res = deobf.deobfuscate_nsig("BI_n4PxQ22is-KKajKUW").unwrap(); assert_eq!(res, "nrkec0fwgTWolw"); } @@ -398,7 +405,8 @@ 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] fn t_update() { let client = Client::new(); - let deobf = tokio_test::block_on(Deobfuscator::new(client)).unwrap(); + let deobf_data = tokio_test::block_on(DeobfData::download(client)).unwrap(); + let deobf = Deobfuscator::new(deobf_data).unwrap(); let deobf_sig = deobf.deobfuscate_sig("GOqGOqGOq0QJ8wRAIgaryQHfplJ9xJSKFywyaSMHuuwZYsoMTAvRvfm51qIGECIA5061zWeyfMPX9hEl_U6f9J0tr7GTJMKyPf5XNrJb5fb5i").unwrap(); println!("{deobf_sig}"); diff --git a/tests/youtube.rs b/tests/youtube.rs index 9687079..9124465 100644 --- a/tests/youtube.rs +++ b/tests/youtube.rs @@ -28,6 +28,7 @@ use rustypipe::param::{ #[case::tv_html5_embed(ClientType::TvHtml5Embed)] #[case::android(ClientType::Android)] #[case::ios(ClientType::Ios)] +#[test_log::test] fn get_player_from_client(#[case] client_type: ClientType) { let rp = RustyPipe::builder().strict().build(); let player_data = From 19b6c98b20ba95e71e81f37b80d7f56f017fbe8e Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sat, 4 Feb 2023 16:33:41 +0100 Subject: [PATCH 2/3] tests: use fixture for RustyPipe instances --- tests/youtube.rs | 334 ++++++++++++++++++++--------------------------- 1 file changed, 140 insertions(+), 194 deletions(-) diff --git a/tests/youtube.rs b/tests/youtube.rs index 9124465..4eadd4c 100644 --- a/tests/youtube.rs +++ b/tests/youtube.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; use std::fmt::Display; -use rstest::rstest; +use rstest::{fixture, rstest}; use rustypipe::model::paginator::ContinuationEndpoint; use rustypipe::validate; use time::macros::date; @@ -29,8 +29,7 @@ use rustypipe::param::{ #[case::android(ClientType::Android)] #[case::ios(ClientType::Ios)] #[test_log::test] -fn get_player_from_client(#[case] client_type: ClientType) { - let rp = RustyPipe::builder().strict().build(); +fn get_player_from_client(#[case] client_type: ClientType, rp: RustyPipe) { let player_data = tokio_test::block_on(rp.query().player_from_client("n4tK7LYFxI0", client_type)).unwrap(); @@ -219,8 +218,8 @@ fn get_player( #[case] views: u64, #[case] is_live: bool, #[case] is_live_content: bool, + rp: RustyPipe, ) { - let rp = RustyPipe::builder().strict().build(); let player_data = tokio_test::block_on(rp.query().player(id)).unwrap(); let details = player_data.details; @@ -304,8 +303,7 @@ fn get_player( "extraction error: Video cant be played because of being private. Reason (from YT): " )] #[case::age_restricted("CUO8secmc0g", "extraction error: Video is age restricted")] -fn get_player_error(#[case] id: &str, #[case] msg: &str) { - let rp = RustyPipe::builder().strict().build(); +fn get_player_error(#[case] id: &str, #[case] msg: &str, rp: RustyPipe) { let err = tokio_test::block_on(rp.query().player(id)) .unwrap_err() .to_string(); @@ -346,8 +344,8 @@ fn get_playlist( #[case] is_long: bool, #[case] description: Option, #[case] channel: Option<(&str, &str)>, + rp: RustyPipe, ) { - let rp = RustyPipe::builder().strict().build(); let playlist = tokio_test::block_on(rp.query().playlist(id)).unwrap(); assert_eq!(playlist.id, id); @@ -366,9 +364,8 @@ fn get_playlist( assert!(!playlist.thumbnail.is_empty()); } -#[test] -fn playlist_cont() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn playlist_cont(rp: RustyPipe) { let mut playlist = tokio_test::block_on(rp.query().playlist("PLbZIPy20-1pN7mqjckepWF78ndb6ci_qi")).unwrap(); @@ -377,9 +374,8 @@ fn playlist_cont() { assert!(playlist.videos.count.unwrap() > 100); } -#[test] -fn playlist_cont2() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn playlist_cont2(rp: RustyPipe) { let mut playlist = tokio_test::block_on(rp.query().playlist("PLbZIPy20-1pN7mqjckepWF78ndb6ci_qi")).unwrap(); @@ -388,9 +384,8 @@ fn playlist_cont2() { assert!(playlist.videos.count.unwrap() > 100); } -#[test] -fn playlist_not_found() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn playlist_not_found(rp: RustyPipe) { let err = tokio_test::block_on(rp.query().playlist("PLbZIPy20-1pN7mqjckepWF78ndb6ci_qz")) .unwrap_err(); @@ -405,9 +400,8 @@ fn playlist_not_found() { //#VIDEO DETAILS -#[test] -fn get_video_details() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn get_video_details(rp: RustyPipe) { let details = tokio_test::block_on(rp.query().video_details("ZeerrnuLi5E")).unwrap(); // dbg!(&details); @@ -446,9 +440,8 @@ fn get_video_details() { assert!(!details.latest_comments.is_exhausted()); } -#[test] -fn get_video_details_music() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn get_video_details_music(rp: RustyPipe) { let details = tokio_test::block_on(rp.query().video_details("XuM2onMGvTI")).unwrap(); // dbg!(&details); @@ -488,9 +481,8 @@ fn get_video_details_music() { */ } -#[test] -fn get_video_details_ccommons() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn get_video_details_ccommons(rp: RustyPipe) { let details = tokio_test::block_on(rp.query().video_details("0rb9CfOvojk")).unwrap(); // dbg!(&details); @@ -532,9 +524,8 @@ fn get_video_details_ccommons() { assert!(!details.latest_comments.is_exhausted()); } -#[test] -fn get_video_details_chapters() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn get_video_details_chapters(rp: RustyPipe) { let details = tokio_test::block_on(rp.query().video_details("nFDBxBUfE74")).unwrap(); // dbg!(&details); @@ -656,9 +647,8 @@ fn get_video_details_chapters() { assert!(!details.latest_comments.is_exhausted()); } -#[test] -fn get_video_details_live() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn get_video_details_live(rp: RustyPipe) { let details = tokio_test::block_on(rp.query().video_details("86YLFOog4GM")).unwrap(); // dbg!(&details); @@ -702,9 +692,8 @@ fn get_video_details_live() { assert!(details.latest_comments.is_empty()); } -#[test] -fn get_video_details_agegate() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn get_video_details_agegate(rp: RustyPipe) { let details = tokio_test::block_on(rp.query().video_details("HRKu0cvrr_o")).unwrap(); // dbg!(&details); @@ -742,9 +731,8 @@ fn get_video_details_agegate() { assert!(details.recommended.items.is_empty()); } -#[test] -fn get_video_details_not_found() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn get_video_details_not_found(rp: RustyPipe) { let err = tokio_test::block_on(rp.query().video_details("abcdefgLi5X")).unwrap_err(); assert!( @@ -756,9 +744,8 @@ fn get_video_details_not_found() { ) } -#[test] -fn get_video_comments() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn get_video_comments(rp: RustyPipe) { let details = tokio_test::block_on(rp.query().video_details("ZeerrnuLi5E")).unwrap(); let top_comments = tokio_test::block_on(details.top_comments.next(rp.query())) @@ -781,9 +768,8 @@ fn get_video_comments() { //#CHANNEL -#[test] -fn channel_videos() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn channel_videos(rp: RustyPipe) { let channel = tokio_test::block_on(rp.query().channel_videos("UC2DjFE7Xf11URZqWBigcVOQ")).unwrap(); @@ -804,9 +790,8 @@ fn channel_videos() { assert_next(channel.content, rp.query(), 15, 2); } -#[test] -fn channel_shorts() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn channel_shorts(rp: RustyPipe) { let channel = tokio_test::block_on(rp.query().channel_shorts("UCh8gHdtzO2tXd593_bjErWg")).unwrap(); @@ -835,9 +820,8 @@ fn channel_shorts() { assert_next(channel.content, rp.query(), 15, 1); } -#[test] -fn channel_livestreams() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn channel_livestreams(rp: RustyPipe) { let channel = tokio_test::block_on(rp.query().channel_livestreams("UC2DjFE7Xf11URZqWBigcVOQ")).unwrap(); @@ -852,9 +836,8 @@ fn channel_livestreams() { assert_next(channel.content, rp.query(), 5, 1); } -#[test] -fn channel_playlists() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn channel_playlists(rp: RustyPipe) { let channel = tokio_test::block_on(rp.query().channel_playlists("UC2DjFE7Xf11URZqWBigcVOQ")).unwrap(); @@ -868,9 +851,8 @@ fn channel_playlists() { assert_next(channel.content, rp.query(), 15, 1); } -#[test] -fn channel_info() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn channel_info(rp: RustyPipe) { let channel = tokio_test::block_on(rp.query().channel_info("UC2DjFE7Xf11URZqWBigcVOQ")).unwrap(); @@ -905,9 +887,8 @@ fn channel_info() { "###); } -#[test] -fn channel_search() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn channel_search(rp: RustyPipe) { let channel = tokio_test::block_on( rp.query() .channel_search("UC2DjFE7Xf11URZqWBigcVOQ", "test"), @@ -952,9 +933,8 @@ fn channel_more( #[case] name: &str, #[case] has_videos: bool, #[case] has_playlists: bool, + rp: RustyPipe, ) { - let rp = RustyPipe::builder().strict().build(); - fn assert_channel(channel: &Channel, id: &str, name: &str) { assert_eq!(channel.id, id); assert_eq!(channel.name, name); @@ -985,8 +965,7 @@ fn channel_more( #[case::movies("UCuJcl0Ju-gPDoksRjK1ya-w")] #[case::sports("UCEgdi0XIXXZ-qJOFPf4JSKw")] #[case::learning("UCtFRv9O2AHqOZjjynzrv-xg")] -fn channel_not_found(#[case] id: &str) { - let rp = RustyPipe::builder().strict().build(); +fn channel_not_found(#[case] id: &str, rp: RustyPipe) { let err = tokio_test::block_on(rp.query().channel_videos(&id)).unwrap_err(); assert!( @@ -1005,9 +984,8 @@ mod channel_rss { use super::*; use time::macros::datetime; - #[test] - fn get_channel_rss() { - let rp = RustyPipe::builder().strict().build(); + #[rstest] + fn get_channel_rss(rp: RustyPipe) { let channel = tokio_test::block_on(rp.query().channel_rss("UCHnyfMqiRRG1u-2MsSQLbXA")).unwrap(); @@ -1018,9 +996,8 @@ mod channel_rss { assert!(!channel.videos.is_empty()); } - #[test] - fn get_channel_rss_not_found() { - let rp = RustyPipe::builder().strict().build(); + #[rstest] + fn get_channel_rss_not_found(rp: RustyPipe) { let err = tokio_test::block_on(rp.query().channel_rss("UCHnyfMqiRRG1u-2MsSQLbXZ")).unwrap_err(); @@ -1037,9 +1014,8 @@ mod channel_rss { //#SEARCH -#[test] -fn search() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn search(rp: RustyPipe) { let result = tokio_test::block_on(rp.query().search("doobydoobap")).unwrap(); assert!( @@ -1056,8 +1032,7 @@ fn search() { #[case::video(search_filter::ItemType::Video)] #[case::channel(search_filter::ItemType::Channel)] #[case::playlist(search_filter::ItemType::Playlist)] -fn search_filter_item_type(#[case] item_type: search_filter::ItemType) { - let rp = RustyPipe::builder().strict().build(); +fn search_filter_item_type(#[case] item_type: search_filter::ItemType, rp: RustyPipe) { let mut result = tokio_test::block_on( rp.query() .search_filter("with no videos", &SearchFilter::new().item_type(item_type)), @@ -1080,9 +1055,8 @@ fn search_filter_item_type(#[case] item_type: search_filter::ItemType) { }); } -#[test] -fn search_empty() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn search_empty(rp: RustyPipe) { let result = tokio_test::block_on( rp.query().search_filter( "test", @@ -1096,17 +1070,15 @@ fn search_empty() { assert!(result.items.is_empty()); } -#[test] -fn search_suggestion() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn search_suggestion(rp: RustyPipe) { let result = tokio_test::block_on(rp.query().search_suggestion("hunger ga")).unwrap(); assert!(result.contains(&"hunger games".to_owned())); } -#[test] -fn search_suggestion_empty() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn search_suggestion_empty(rp: RustyPipe) { let result = tokio_test::block_on(rp.query().search_suggestion("fjew327%4ifjelwfvnewg49")).unwrap(); @@ -1137,8 +1109,7 @@ fn search_suggestion_empty() { #[case("https://music.youtube.com/playlist?list=OLAK5uy_k0yFrZlFRgCf3rLPza-lkRmCrtLPbK9pE", UrlTarget::Album {id: "MPREb_GyH43gCvdM5".to_owned()})] #[case("https://music.youtube.com/browse/MPREb_GyH43gCvdM5", UrlTarget::Album {id: "MPREb_GyH43gCvdM5".to_owned()})] #[case("https://music.youtube.com/browse/UC5I2hjZYiW9gZPVkvzM8_Cw", UrlTarget::Channel {id: "UC5I2hjZYiW9gZPVkvzM8_Cw".to_owned()})] -fn resolve_url(#[case] url: &str, #[case] expect: UrlTarget) { - let rp = RustyPipe::builder().strict().build(); +fn resolve_url(#[case] url: &str, #[case] expect: UrlTarget, rp: RustyPipe) { let target = tokio_test::block_on(rp.query().resolve_url(url, true)).unwrap(); assert_eq!(target, expect); } @@ -1155,15 +1126,13 @@ fn resolve_url(#[case] url: &str, #[case] expect: UrlTarget) { #[case("RDCLAK5uy_kFQXdnqMaQCVx2wpUM4ZfbsGCDibZtkJk", UrlTarget::Playlist {id: "RDCLAK5uy_kFQXdnqMaQCVx2wpUM4ZfbsGCDibZtkJk".to_owned()})] #[case("OLAK5uy_k0yFrZlFRgCf3rLPza-lkRmCrtLPbK9pE", UrlTarget::Album {id: "MPREb_GyH43gCvdM5".to_owned()})] #[case("MPREb_GyH43gCvdM5", UrlTarget::Album {id: "MPREb_GyH43gCvdM5".to_owned()})] -fn resolve_string(#[case] string: &str, #[case] expect: UrlTarget) { - let rp = RustyPipe::builder().strict().build(); +fn resolve_string(#[case] string: &str, #[case] expect: UrlTarget, rp: RustyPipe) { let target = tokio_test::block_on(rp.query().resolve_string(string, true)).unwrap(); assert_eq!(target, expect); } -#[test] -fn resolve_channel_not_found() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn resolve_channel_not_found(rp: RustyPipe) { let err = tokio_test::block_on( rp.query() .resolve_url("https://www.youtube.com/feeqegnhq3rkwghjq43ruih43io3", true), @@ -1178,9 +1147,8 @@ fn resolve_channel_not_found() { //#TRENDS -#[test] -fn startpage() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn startpage(rp: RustyPipe) { let startpage = tokio_test::block_on(rp.query().startpage()).unwrap(); // The startpage requires visitor data to fetch continuations @@ -1189,9 +1157,8 @@ fn startpage() { assert_next(startpage, rp.query(), 12, 2); } -#[test] -fn trending() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn trending(rp: RustyPipe) { let result = tokio_test::block_on(rp.query().trending()).unwrap(); assert_gte(result.len(), 50, "items"); @@ -1231,8 +1198,8 @@ fn music_playlist( #[case] description: Option, #[case] channel: Option<(&str, &str)>, #[case] from_ytm: bool, + rp: RustyPipe, ) { - let rp = RustyPipe::builder().strict().build(); let playlist = tokio_test::block_on(rp.query().music_playlist(id)).unwrap(); assert_eq!(playlist.id, id); @@ -1252,9 +1219,8 @@ fn music_playlist( assert_eq!(playlist.from_ytm, from_ytm); } -#[test] -fn music_playlist_cont() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_playlist_cont(rp: RustyPipe) { let mut playlist = tokio_test::block_on( rp.query() .music_playlist("PLbZIPy20-1pN7mqjckepWF78ndb6ci_qi"), @@ -1267,9 +1233,8 @@ fn music_playlist_cont() { assert_gte(playlist.tracks.count.unwrap(), 100, "track count"); } -#[test] -fn music_playlist_related() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_playlist_related(rp: RustyPipe) { let mut playlist = tokio_test::block_on( rp.query() .music_playlist("PLbZIPy20-1pN7mqjckepWF78ndb6ci_qi"), @@ -1285,9 +1250,8 @@ fn music_playlist_related() { ); } -#[test] -fn music_playlist_not_found() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_playlist_not_found(rp: RustyPipe) { let err = tokio_test::block_on( rp.query() .music_playlist("PLbZIPy20-1pN7mqjckepWF78ndb6ci_qz"), @@ -1314,8 +1278,7 @@ fn music_playlist_not_found() { #[case::no_year("no_year", "MPREb_F3Af9UZZVxX")] #[case::version_no_artist("version_no_artist", "MPREb_h8ltx5oKvyY")] #[case::no_artist("no_artist", "MPREb_bqWA6mAZFWS")] -fn music_album(#[case] name: &str, #[case] id: &str) { - let rp = RustyPipe::builder().strict().build(); +fn music_album(#[case] name: &str, #[case] id: &str, rp: RustyPipe) { let album = tokio_test::block_on(rp.query().music_album(id)).unwrap(); assert!(!album.cover.is_empty(), "got no cover"); @@ -1325,9 +1288,8 @@ fn music_album(#[case] name: &str, #[case] id: &str) { ); } -#[test] -fn music_album_not_found() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_album_not_found(rp: RustyPipe) { let err = tokio_test::block_on(rp.query().music_album("MPREb_nlBWQROfvjoz")).unwrap_err(); assert!( @@ -1354,9 +1316,8 @@ fn music_artist( #[case] all_albums: bool, #[case] min_tracks: usize, #[case] min_playlists: usize, + rp: RustyPipe, ) { - let rp = RustyPipe::builder().strict().build(); - let mut artist = tokio_test::block_on(rp.query().music_artist(id, all_albums)).unwrap(); assert_gte(artist.tracks.len(), min_tracks, "tracks"); @@ -1401,9 +1362,8 @@ fn music_artist( }); } -#[test] -fn music_artist_not_found() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_artist_not_found(rp: RustyPipe) { let err = tokio_test::block_on(rp.query().music_artist("UC7cl4MmM6ZZ2TcFyMk_b4pq", false)) .unwrap_err(); @@ -1419,8 +1379,7 @@ fn music_artist_not_found() { #[rstest] #[case::default(false)] #[case::typo(true)] -fn music_search(#[case] typo: bool) { - let rp = RustyPipe::builder().strict().build(); +fn music_search(#[case] typo: bool, rp: RustyPipe) { let res = tokio_test::block_on(rp.query().music_search(match typo { false => "black mamba aespa", true => "blck mamba aespa", @@ -1458,9 +1417,8 @@ fn music_search(#[case] typo: bool) { assert_eq!(track.track_nr, None); } -#[test] -fn music_search_tracks() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_search_tracks(rp: RustyPipe) { let res = tokio_test::block_on(rp.query().music_search_tracks("black mamba")).unwrap(); let track = &res @@ -1492,9 +1450,8 @@ fn music_search_tracks() { assert_next(res.items, rp.query(), 15, 2); } -#[test] -fn music_search_videos() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_search_videos(rp: RustyPipe) { let res = tokio_test::block_on(rp.query().music_search_videos("black mamba")).unwrap(); let track = &res @@ -1585,8 +1542,8 @@ fn music_search_albums( #[case] artist_id: &str, #[case] year: u16, #[case] album_type: AlbumType, + rp: RustyPipe, ) { - let rp = RustyPipe::builder().strict().build(); let res = tokio_test::block_on(rp.query().music_search_albums(query)).unwrap(); let album = &res.items.items.iter().find(|a| a.id == id).unwrap(); @@ -1607,9 +1564,8 @@ fn music_search_albums( assert_next(res.items, rp.query(), 15, 1); } -#[test] -fn music_search_artists() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_search_artists(rp: RustyPipe) { let res = tokio_test::block_on(rp.query().music_search_artists("namika")).unwrap(); let artist = res @@ -1628,9 +1584,8 @@ fn music_search_artists() { assert_eq!(res.corrected_query, None); } -#[test] -fn music_search_artists_cont() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_search_artists_cont(rp: RustyPipe) { let res = tokio_test::block_on(rp.query().music_search_artists("band")).unwrap(); assert_eq!(res.corrected_query, None); @@ -1640,8 +1595,7 @@ fn music_search_artists_cont() { #[rstest] #[case::ytm(false)] #[case::default(true)] -fn music_search_playlists(#[case] with_community: bool) { - let rp = RustyPipe::builder().strict().build(); +fn music_search_playlists(#[case] with_community: bool, rp: RustyPipe) { let res = if with_community { tokio_test::block_on(rp.query().music_search_playlists("easy pop")).unwrap() } else { @@ -1663,9 +1617,8 @@ fn music_search_playlists(#[case] with_community: bool) { assert!(playlist.from_ytm); } -#[test] -fn music_search_playlists_community() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_search_playlists_community(rp: RustyPipe) { let res = tokio_test::block_on( rp.query() .music_search_playlists_filter("Best Pop Music Videos - Top Pop Hits Playlist", true), @@ -1694,9 +1647,8 @@ fn music_search_playlists_community() { } /// The YouTube Music search sometimes shows genre radio items. They should be skipped. -#[test] -fn music_search_genre_radio() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_search_genre_radio(rp: RustyPipe) { tokio_test::block_on(rp.query().music_search("pop radio")).unwrap(); } @@ -1707,8 +1659,8 @@ fn music_search_suggestion( #[case] query: &str, #[case] term: Option<&str>, #[case] artist: Option<&str>, + rp: RustyPipe, ) { - let rp = RustyPipe::builder().strict().build(); let suggestion = tokio_test::block_on(rp.query().music_search_suggestion(query)).unwrap(); match term { @@ -1733,8 +1685,7 @@ fn music_search_suggestion( #[rstest] #[case::mv("mv", "ZeerrnuLi5E")] #[case::track("track", "7nigXQS1Xb0")] -fn music_details(#[case] name: &str, #[case] id: &str) { - let rp = RustyPipe::builder().strict().build(); +fn music_details(#[case] name: &str, #[case] id: &str, rp: RustyPipe) { let track = tokio_test::block_on(rp.query().music_details(id)).unwrap(); assert!(!track.track.cover.is_empty(), "got no cover"); @@ -1752,17 +1703,15 @@ fn music_details(#[case] name: &str, #[case] id: &str) { ); } -#[test] -fn music_lyrics() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_lyrics(rp: RustyPipe) { let track = tokio_test::block_on(rp.query().music_details("NO8Arj4yeww")).unwrap(); let lyrics = tokio_test::block_on(rp.query().music_lyrics(&track.lyrics_id.unwrap())).unwrap(); insta::assert_ron_snapshot!(lyrics); } -#[test] -fn music_lyrics_not_found() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_lyrics_not_found(rp: RustyPipe) { let track = tokio_test::block_on(rp.query().music_details("ekXI8qrbe1s")).unwrap(); let err = tokio_test::block_on(rp.query().music_lyrics(&track.lyrics_id.unwrap())).unwrap_err(); @@ -1778,8 +1727,7 @@ fn music_lyrics_not_found() { #[rstest] #[case::a("7nigXQS1Xb0", true)] #[case::b("4t3SUDZCBaQ", false)] -fn music_related(#[case] id: &str, #[case] full: bool) { - let rp = RustyPipe::builder().strict().build(); +fn music_related(#[case] id: &str, #[case] full: bool, rp: RustyPipe) { let track = tokio_test::block_on(rp.query().music_details(id)).unwrap(); let related = tokio_test::block_on(rp.query().music_related(&track.related_id.unwrap())).unwrap(); @@ -1876,9 +1824,8 @@ fn music_related(#[case] id: &str, #[case] full: bool) { } } -#[test] -fn music_details_not_found() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_details_not_found(rp: RustyPipe) { let err = tokio_test::block_on(rp.query().music_details("7nigXQS1XbZ")).unwrap_err(); assert!( @@ -1890,16 +1837,14 @@ fn music_details_not_found() { ); } -#[test] -fn music_radio_track() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_radio_track(rp: RustyPipe) { let tracks = tokio_test::block_on(rp.query().music_radio_track("ZeerrnuLi5E")).unwrap(); assert_next_items(tracks, rp.query(), 20); } -#[test] -fn music_radio_track_not_found() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_radio_track_not_found(rp: RustyPipe) { let err = tokio_test::block_on(rp.query().music_radio_track("7nigXQS1XbZ")).unwrap_err(); assert!( @@ -1911,9 +1856,8 @@ fn music_radio_track_not_found() { ); } -#[test] -fn music_radio_playlist() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_radio_playlist(rp: RustyPipe) { let tracks = tokio_test::block_on( rp.query() .music_radio_playlist("PL5dDx681T4bR7ZF1IuWzOv1omlRbE7PiJ"), @@ -1922,9 +1866,8 @@ fn music_radio_playlist() { assert_next_items(tracks, rp.query(), 20); } -#[test] -fn music_radio_playlist_not_found() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_radio_playlist_not_found(rp: RustyPipe) { let res = tokio_test::block_on( rp.query() .music_radio_playlist("PL5dDx681T4bR7ZF1IuWzOv1omlZZZZZZZ"), @@ -1941,17 +1884,15 @@ fn music_radio_playlist_not_found() { } } -#[test] -fn music_radio_artist() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_radio_artist(rp: RustyPipe) { let tracks = tokio_test::block_on(rp.query().music_radio("RDEM_Ktu-TilkxtLvmc9wX1MLQ")).unwrap(); assert_next_items(tracks, rp.query(), 20); } -#[test] -fn music_radio_not_found() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_radio_not_found(rp: RustyPipe) { let err = tokio_test::block_on(rp.query().music_radio("RDEM_Ktu-TilkxtLvmc9wXZZZZ")).unwrap_err(); @@ -1975,8 +1916,12 @@ fn music_radio_not_found() { "PL4fGSI1pDJn69On1f-8NAvX_CYlx7QyZc", "PLrEnWoR732-DtKgaDdnPkezM_nDidBU9H" )] -fn music_charts(#[case] country: Country, #[case] plid_top: &str, #[case] plid_trend: &str) { - let rp = RustyPipe::builder().strict().build(); +fn music_charts( + #[case] country: Country, + #[case] plid_top: &str, + #[case] plid_trend: &str, + rp: RustyPipe, +) { let charts = tokio_test::block_on(rp.query().music_charts(Some(country))).unwrap(); assert_eq!(charts.top_playlist_id.unwrap(), plid_top); @@ -1992,9 +1937,8 @@ fn music_charts(#[case] country: Country, #[case] plid_top: &str, #[case] plid_t } } -#[test] -fn music_new_albums() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_new_albums(rp: RustyPipe) { let albums = tokio_test::block_on(rp.query().music_new_albums()).unwrap(); assert_gte(albums.len(), 10, "albums"); @@ -2005,9 +1949,8 @@ fn music_new_albums() { } } -#[test] -fn music_new_videos() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_new_videos(rp: RustyPipe) { let videos = tokio_test::block_on(rp.query().music_new_videos()).unwrap(); assert_gte(videos.len(), 5, "videos"); @@ -2020,9 +1963,8 @@ fn music_new_videos() { } } -#[test] -fn music_genres() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_genres(rp: RustyPipe) { let genres = tokio_test::block_on(rp.query().music_genres()).unwrap(); let chill = genres @@ -2048,8 +1990,7 @@ fn music_genres() { #[rstest] #[case::chill("ggMPOg1uX1JOQWZFeDByc2Jm", "Chill")] #[case::pop("ggMPOg1uX1lMbVZmbzl6NlJ3", "Pop")] -fn music_genre(#[case] id: &str, #[case] name: &str) { - let rp = RustyPipe::builder().strict().build(); +fn music_genre(#[case] id: &str, #[case] name: &str, rp: RustyPipe) { let genre = tokio_test::block_on(rp.query().music_genre(id)).unwrap(); fn check_music_genre(genre: MusicGenre, id: &str, name: &str) -> Vec<(String, String)> { @@ -2099,9 +2040,8 @@ fn music_genre(#[case] id: &str, #[case] name: &str) { } } -#[test] -fn music_genre_not_found() { - let rp = RustyPipe::builder().strict().build(); +#[rstest] +fn music_genre_not_found(rp: RustyPipe) { let err = tokio_test::block_on(rp.query().music_genre("ggMPOg1uX1JOQWZFeDByc2zz")).unwrap_err(); assert!( @@ -2119,10 +2059,7 @@ const VISITOR_DATA_SEARCH_CHANNEL_HANDLES: &str = "CgszYlc1Yk1WZGRCSSjrwOSbBg%3D #[test] fn ab3_search_channel_handles() { - let rp = RustyPipe::builder() - .strict() - .visitor_data(VISITOR_DATA_SEARCH_CHANNEL_HANDLES) - .build(); + let rp = rp_visitor_data(VISITOR_DATA_SEARCH_CHANNEL_HANDLES); tokio_test::block_on(rp.query().search_filter( "test", @@ -2136,10 +2073,7 @@ fn ab3_search_channel_handles() { #[rstest] #[case::desktop(ContinuationEndpoint::Browse)] #[case::music(ContinuationEndpoint::MusicBrowse)] -#[test] -fn invalid_ctoken(#[case] ep: ContinuationEndpoint) { - let rp = RustyPipe::builder().strict().build(); - +fn invalid_ctoken(#[case] ep: ContinuationEndpoint, rp: RustyPipe) { let e = tokio_test::block_on(rp.query().continuation::("Abcd", ep, None)) .unwrap_err(); @@ -2156,6 +2090,17 @@ fn invalid_ctoken(#[case] ep: ContinuationEndpoint) { //#TESTUTIL +/// Get a new RustyPipe instance +#[fixture] +fn rp() -> RustyPipe { + RustyPipe::builder().strict().build() +} + +/// Get a new RustyPipe instance with pre-set visitor data +fn rp_visitor_data(vdata: &str) -> RustyPipe { + RustyPipe::builder().strict().visitor_data(vdata).build() +} + /// Assert equality within 10% margin fn assert_approx(left: f64, right: f64) { if left != right { @@ -2167,6 +2112,7 @@ fn assert_approx(left: f64, right: f64) { } } +/// Assert that number A is greater than or equal to number B fn assert_gte(a: T, b: T, msg: &str) { assert!(a >= b, "expected {b} {msg}, got {a}"); } From d9e07b37e688f2bce099ea16fb78a90001704037 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Mon, 6 Feb 2023 19:54:52 +0100 Subject: [PATCH 3/3] fix: use quick-js-dtp crate --- Cargo.toml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e429901..22f4e6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "rustypipe" version = "0.1.0" edition = "2021" -authors = ["ThetaDev"] +authors = ["ThetaDev "] license = "GPL-3.0" description = "Client for the public YouTube / YouTube Music API (Innertube), inspired by NewPipe" keywords = ["youtube", "video", "music"] @@ -23,8 +23,9 @@ rustls-tls-webpki-roots = ["reqwest/rustls-tls-webpki-roots"] rustls-tls-native-roots = ["reqwest/rustls-tls-native-roots"] [dependencies] -# quick-js = "0.4.1" -quick-js = { path = "../quickjs-rs", default-features = false } +quick-js-dtp = { version = "0.4.1", default-features = false, features = [ + "patch-dateparser", +] } once_cell = "1.12.0" regex = "1.6.0" fancy-regex = "0.11.0"