feat: add http_client method to RustyPipe and user_agent method to RustyPipeQuery
For downloading, the http client as well as the user agent used by RustyPipe should be available.
This commit is contained in:
parent
1934592ab8
commit
c51a273513
1 changed files with 50 additions and 17 deletions
|
|
@ -225,6 +225,7 @@ struct RustyPipeRef {
|
||||||
n_http_retries: u32,
|
n_http_retries: u32,
|
||||||
cache: CacheHolder,
|
cache: CacheHolder,
|
||||||
default_opts: RustyPipeOpts,
|
default_opts: RustyPipeOpts,
|
||||||
|
user_agent: Cow<'static, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
@ -455,8 +456,13 @@ impl RustyPipeBuilder {
|
||||||
|
|
||||||
/// Create a new, configured RustyPipe instance using a Reqwest client builder.
|
/// Create a new, configured RustyPipe instance using a Reqwest client builder.
|
||||||
pub fn build_with_client(self, mut client_builder: ClientBuilder) -> Result<RustyPipe, Error> {
|
pub fn build_with_client(self, mut client_builder: ClientBuilder) -> Result<RustyPipe, Error> {
|
||||||
|
let user_agent = self
|
||||||
|
.user_agent
|
||||||
|
.map(Cow::Owned)
|
||||||
|
.unwrap_or(Cow::Borrowed(DEFAULT_UA));
|
||||||
|
|
||||||
client_builder = client_builder
|
client_builder = client_builder
|
||||||
.user_agent(self.user_agent.unwrap_or_else(|| DEFAULT_UA.to_owned()))
|
.user_agent(user_agent.as_ref())
|
||||||
.gzip(true)
|
.gzip(true)
|
||||||
.brotli(true)
|
.brotli(true)
|
||||||
.redirect(reqwest::redirect::Policy::none());
|
.redirect(reqwest::redirect::Policy::none());
|
||||||
|
|
@ -503,6 +509,7 @@ impl RustyPipeBuilder {
|
||||||
deobf: RwLock::new(cdata.deobf),
|
deobf: RwLock::new(cdata.deobf),
|
||||||
},
|
},
|
||||||
default_opts: self.default_opts,
|
default_opts: self.default_opts,
|
||||||
|
user_agent,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -710,6 +717,14 @@ impl RustyPipe {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the internal HTTP client
|
||||||
|
///
|
||||||
|
/// Can be used for downloading videos or custom YT requests.
|
||||||
|
#[must_use]
|
||||||
|
pub fn http_client(&self) -> &Client {
|
||||||
|
&self.inner.http
|
||||||
|
}
|
||||||
|
|
||||||
/// Execute the given http request.
|
/// Execute the given http request.
|
||||||
async fn http_request(&self, request: &Request) -> Result<Response, reqwest::Error> {
|
async fn http_request(&self, request: &Request) -> Result<Response, reqwest::Error> {
|
||||||
let mut last_resp = None;
|
let mut last_resp = None;
|
||||||
|
|
@ -963,7 +978,14 @@ impl RustyPipe {
|
||||||
/// visitor data is extracted from the html page.
|
/// visitor data is extracted from the html page.
|
||||||
async fn get_visitor_data(&self) -> Result<String, Error> {
|
async fn get_visitor_data(&self) -> Result<String, Error> {
|
||||||
tracing::debug!("getting YT visitor data");
|
tracing::debug!("getting YT visitor data");
|
||||||
let resp = self.inner.http.get(YOUTUBE_MUSIC_HOME_URL).send().await?;
|
let resp = self
|
||||||
|
.inner
|
||||||
|
.http
|
||||||
|
.get(YOUTUBE_MUSIC_HOME_URL)
|
||||||
|
.header(header::ORIGIN, YOUTUBE_MUSIC_HOME_URL)
|
||||||
|
.header(header::REFERER, YOUTUBE_MUSIC_HOME_URL)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
let vdata = resp
|
let vdata = resp
|
||||||
.headers()
|
.headers()
|
||||||
|
|
@ -972,7 +994,10 @@ impl RustyPipe {
|
||||||
.find_map(|c| {
|
.find_map(|c| {
|
||||||
if let Ok(cookie) = c.to_str() {
|
if let Ok(cookie) = c.to_str() {
|
||||||
if let Some(after) = cookie.strip_prefix("__Secure-YEC=") {
|
if let Some(after) = cookie.strip_prefix("__Secure-YEC=") {
|
||||||
return after.split_once(';').map(|s| s.0.to_owned());
|
return after
|
||||||
|
.split_once(';')
|
||||||
|
.map(|s| s.0.to_owned())
|
||||||
|
.filter(|s| !s.is_empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
|
@ -1065,6 +1090,27 @@ impl RustyPipeQuery {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the user agent for the given client type
|
||||||
|
///
|
||||||
|
/// This can be used for additional HTTP requests (e.g. downloading/streaming)
|
||||||
|
pub fn user_agent(&self, ctype: ClientType) -> Cow<'_, str> {
|
||||||
|
match ctype {
|
||||||
|
ClientType::Desktop | ClientType::DesktopMusic | ClientType::TvHtml5Embed => {
|
||||||
|
Cow::Borrowed(&self.client.inner.user_agent)
|
||||||
|
}
|
||||||
|
ClientType::Android => format!(
|
||||||
|
"com.google.android.youtube/{} (Linux; U; Android 12; {}) gzip",
|
||||||
|
MOBILE_CLIENT_VERSION, self.opts.country
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
ClientType::Ios => format!(
|
||||||
|
"com.google.ios.youtube/{} ({}; U; CPU iOS 15_4 like Mac OS X; {})",
|
||||||
|
MOBILE_CLIENT_VERSION, IOS_DEVICE_MODEL, self.opts.country
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new context object, which is included in every request to
|
/// Create a new context object, which is included in every request to
|
||||||
/// the YouTube API and contains language, country and device parameters.
|
/// the YouTube API and contains language, country and device parameters.
|
||||||
///
|
///
|
||||||
|
|
@ -1227,13 +1273,6 @@ impl RustyPipeQuery {
|
||||||
.post(format!(
|
.post(format!(
|
||||||
"{YOUTUBEI_V1_GAPIS_URL}{endpoint}?{DISABLE_PRETTY_PRINT_PARAMETER}"
|
"{YOUTUBEI_V1_GAPIS_URL}{endpoint}?{DISABLE_PRETTY_PRINT_PARAMETER}"
|
||||||
))
|
))
|
||||||
.header(
|
|
||||||
header::USER_AGENT,
|
|
||||||
format!(
|
|
||||||
"com.google.android.youtube/{} (Linux; U; Android 12; {}) gzip",
|
|
||||||
MOBILE_CLIENT_VERSION, self.opts.country
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.header("X-Goog-Api-Format-Version", "2"),
|
.header("X-Goog-Api-Format-Version", "2"),
|
||||||
ClientType::Ios => self
|
ClientType::Ios => self
|
||||||
.client
|
.client
|
||||||
|
|
@ -1242,15 +1281,9 @@ impl RustyPipeQuery {
|
||||||
.post(format!(
|
.post(format!(
|
||||||
"{YOUTUBEI_V1_GAPIS_URL}{endpoint}?{DISABLE_PRETTY_PRINT_PARAMETER}"
|
"{YOUTUBEI_V1_GAPIS_URL}{endpoint}?{DISABLE_PRETTY_PRINT_PARAMETER}"
|
||||||
))
|
))
|
||||||
.header(
|
|
||||||
header::USER_AGENT,
|
|
||||||
format!(
|
|
||||||
"com.google.ios.youtube/{} ({}; U; CPU iOS 15_4 like Mac OS X; {})",
|
|
||||||
MOBILE_CLIENT_VERSION, IOS_DEVICE_MODEL, self.opts.country
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.header("X-Goog-Api-Format-Version", "2"),
|
.header("X-Goog-Api-Format-Version", "2"),
|
||||||
};
|
};
|
||||||
|
r = r.header(header::USER_AGENT, self.user_agent(ctype).as_ref());
|
||||||
if let Some(vdata) = self.opts.visitor_data.as_deref().or(visitor_data) {
|
if let Some(vdata) = self.opts.visitor_data.as_deref().or(visitor_data) {
|
||||||
r = r.header("X-Goog-EOM-Visitor-Id", vdata);
|
r = r.header("X-Goog-EOM-Visitor-Id", vdata);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Reference in a new issue