1use crate::Error;
3use crate::client::{Client, QueryResponse};
4use crate::error::Result;
5use crate::parse::ProcessedResult;
6use crate::query::{GetQuery, PostQuery};
7use crate::utils::constants::{YTM_API_URL, YTM_PARAMS, YTM_PARAMS_KEY};
8pub use browser::BrowserToken;
9use chrono::Utc;
10pub use oauth::{OAuthToken, OAuthTokenGenerator};
11use reqwest::Url;
12use serde_json::json;
13use std::borrow::Cow;
14use std::marker::PhantomData;
15
16pub mod browser;
17pub mod noauth;
18pub mod oauth;
19
20pub trait AuthToken: Sized {
24 fn headers(&self) -> Result<impl IntoIterator<Item = (&str, Cow<'_, str>)>>;
25 fn client_version(&self) -> Cow<'_, str>;
26 fn deserialize_response<Q>(raw: RawResult<Q, Self>) -> Result<ProcessedResult<Q>>;
27}
28
29#[derive(PartialEq, Debug)]
33pub struct RawResult<'a, Q, A>
34where
35 A: AuthToken,
36{
37 token: PhantomData<A>,
40 pub query: &'a Q,
42 pub json: String,
44}
45
46impl<'a, Q, A: AuthToken> RawResult<'a, Q, A> {
47 pub(crate) fn from_raw(json: String, query: &'a Q) -> Self {
48 Self {
49 query,
50 token: PhantomData,
51 json,
52 }
53 }
54 pub fn destructure_json(self) -> String {
55 self.json
56 }
57 pub fn process(self) -> Result<ProcessedResult<'a, Q>> {
58 A::deserialize_response(self)
59 }
60}
61
62pub(crate) async fn raw_query_post<'a, A: AuthToken, Q: PostQuery>(
63 q: &'a Q,
64 tok: &A,
65 c: &Client,
66) -> Result<RawResult<'a, Q, A>> {
67 let url = format!("{YTM_API_URL}{}{YTM_PARAMS}{YTM_PARAMS_KEY}", q.path());
68 let mut body = json!({
69 "context" : {
70 "client" : {
71 "clientName" : "WEB_REMIX",
72 "clientVersion" : tok.client_version(),
73 "user" : {},
74 },
75 },
76 });
77 if let Some(body) = body.as_object_mut() {
78 body.append(&mut q.header());
79 } else {
80 unreachable!("Body created in this function as an object")
81 };
82 let QueryResponse { text, .. } = c
83 .post_json_query(url, tok.headers()?, &body, &q.params())
84 .await?;
85 Ok(RawResult::from_raw(text, q))
86}
87
88pub(crate) async fn raw_query_get<'a, Q: GetQuery, A: AuthToken>(
89 tok: &A,
90 client: &Client,
91 query: &'a Q,
92) -> Result<RawResult<'a, Q, A>> {
93 let url = Url::parse_with_params(query.url(), query.params())
94 .map_err(|e| Error::web(format!("{e}")))?;
95 let result = client
96 .get_query(url, tok.headers()?, &query.params())
97 .await?;
98 let result = RawResult::from_raw(result.text, query);
99 Ok(result)
100}
101
102pub trait LoggedIn: AuthToken {}
107
108impl LoggedIn for BrowserToken {}
109impl LoggedIn for OAuthToken {}
110
111fn fallback_client_version(time: &chrono::DateTime<Utc>) -> String {
114 format!("1.{}.01.00", time.format("%Y%m%d"))
115}