pub mod constants {
use const_format::concatcp;
pub const USER_AGENT: &str =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0";
pub const YTM_URL: &str = "https://music.youtube.com";
pub const YTM_API_URL: &str = "https://music.youtube.com/youtubei/v1/";
pub const YTM_PARAMS: &str = "?alt=json&prettyPrint=false";
pub const YTM_PARAMS_KEY: &str = "&key=AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30";
pub const OAUTH_SCOPE: &str = "https://www.googleapis.com/auth/youtube";
pub const OAUTH_CODE_URL: &str = "https://www.youtube.com/o/oauth2/device/code";
pub const OAUTH_TOKEN_URL: &str = "https://oauth2.googleapis.com/token";
pub const OAUTH_USER_AGENT: &str = concatcp!(USER_AGENT, " Cobalt/Version");
pub const OAUTH_GRANT_URL: &str = "http://oauth.net/grant_type/device/1.0";
pub const DEFAULT_X_GOOG_AUTHUSER: &str = "0";
}
use constants::YTM_URL;
use sha1::{Digest, Sha1};
use std::time::{SystemTime, UNIX_EPOCH};
pub fn hash_sapisid(sapisid: &str) -> String {
let elapsed = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("SystemTime::now() should always be ahead of UNIX_EPOCH")
.as_secs();
let mut hasher = Sha1::new();
hasher.update(format!("{elapsed} {sapisid} {YTM_URL}"));
let result = hasher.finalize();
let mut hex = String::new();
for b in result {
hex.push_str(&format!("{b:02x}"));
}
format!("{elapsed}_{hex}")
}
macro_rules! impl_youtube_id {
($t:ty) => {
impl<'a> YoutubeID<'a> for $t {
fn get_raw(&self) -> &str {
&self.0
}
fn from_raw<S: Into<Cow<'a, str>>>(raw_str: S) -> Self {
Self(raw_str.into())
}
}
impl<'a> From<&'a $t> for $t {
fn from(value: &'a $t) -> Self {
let core = &value.0;
Self(core.as_ref().into())
}
}
};
}
macro_rules! ab_warn {
() => {
#[cfg(feature = "ab-warning")]
eprintln!("WARNING: a/b test deprecated branch taken");
};
}
#[cfg(test)]
macro_rules! parse_test {
($in:expr_2021,$out:expr_2021,$query:expr_2021,$token:ty) => {
let source_path = std::path::Path::new($in);
let expected_path = std::path::Path::new($out);
let source = tokio::fs::read_to_string(source_path)
.await
.expect("Expect file read to pass during tests");
let expected = tokio::fs::read_to_string(expected_path)
.await
.expect("Expect file read to pass during tests");
let expected = expected.trim();
let output = crate::process_json::<_, $token>(source, $query).unwrap();
let output = format!("{:#?}", output);
pretty_assertions::assert_eq!(expected, output);
};
}
#[cfg(test)]
macro_rules! parse_test_value {
($in:expr_2021,$out:expr_2021,$query:expr_2021,$token:ty) => {
let source_path = std::path::Path::new($in);
let source = tokio::fs::read_to_string(source_path)
.await
.expect("Expect file read to pass during tests");
let parsed = crate::process_json::<_, $token>(source, $query).unwrap();
pretty_assertions::assert_eq!(parsed, $out);
};
}
#[cfg(test)]
macro_rules! parse_continuations_test {
($in:expr_2021,$out:expr_2021,$query:expr_2021,$token:ty) => {
let source_path = std::path::Path::new($in);
let expected_path = std::path::Path::new($out);
let source = tokio::fs::read_to_string(source_path)
.await
.expect("Expect file read to pass during tests");
let expected = tokio::fs::read_to_string(expected_path)
.await
.expect("Expect file read to pass during tests");
let expected = expected.trim();
let query = $query;
let continuations_query = crate::query::GetContinuationsQuery::new_mock_unchecked(&query);
let output = crate::process_json::<_, $token>(source, continuations_query).unwrap();
let output = format!("{:#?}", output);
pretty_assertions::assert_eq!(expected, output);
};
}
#[cfg(test)]
macro_rules! parse_with_matching_continuation_test {
($in_first:expr_2021,$in_cont:expr_2021,$out:expr_2021,$query:expr_2021,$token:ty) => {
let first_path = std::path::Path::new($in_first);
let continuation_path = std::path::Path::new($in_cont);
let expected_path = std::path::Path::new($out);
let source_first = tokio::fs::read_to_string(first_path)
.await
.expect("Expect file read to pass during tests");
let source_continuation = tokio::fs::read_to_string(continuation_path)
.await
.expect("Expect file read to pass during tests");
let expected = tokio::fs::read_to_string(expected_path)
.await
.expect("Expect file read to pass during tests");
let expected = expected.trim();
let query = $query;
let continuations_query = crate::query::GetContinuationsQuery::new_mock_unchecked(&query);
let output_first = crate::process_json::<_, $token>(source_first, $query).unwrap();
let output_first = format!("{:#?}", output_first);
let output_continuation =
crate::process_json::<_, $token>(source_continuation, continuations_query).unwrap();
let output_continuation = format!("{:#?}", output_continuation);
pretty_assertions::assert_eq!(expected, output_first);
pretty_assertions::assert_eq!(expected, output_continuation);
};
}