use crate::{youtube, InnerTubeRequestFields, YouTubeError};
use hyper_tls::HttpsConnector;
use hyper_util::client::legacy::Client;
use hyper_util::client::legacy::connect::HttpConnector;
use hyper::body::Bytes;
use http_body_util::{BodyExt, Full};
use hyper::{Method, Request, StatusCode};
use prost::Message;
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
pub struct UpdateHideUserStatusRequest<'a> {
pub client: &'a mut Client<HttpsConnector<HttpConnector>, Full<Bytes>>,
pub ip: &'a str,
pub fields: InnerTubeRequestFields<'a>,
pub channel_id: String,
pub hide_user: bool,
}
impl<'a> AsMut<InnerTubeRequestFields<'a>> for UpdateHideUserStatusRequest<'a> {
fn as_mut(&mut self) -> &mut InnerTubeRequestFields<'a> {
&mut self.fields
}
}
impl<'a> UpdateHideUserStatusRequest<'a> {
pub async fn send(self) -> Result<(), YouTubeError> {
let user_id = if self.channel_id.len() >= 2 {
self.channel_id[2..].to_string()
} else {
return Err(YouTubeError::Other(Box::new(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Channel ID must be at least 2 characters long"
))));
};
let hide_user_action = youtube::HideUserAction {
user_id,
options: if self.hide_user {
youtube::HideUserOptions::HideUser as i32
} else {
youtube::HideUserOptions::UnhideUser as i32
},
};
let mut action_bytes = Vec::new();
hide_user_action.encode(&mut action_bytes)
.map_err(|e| YouTubeError::Other(Box::new(e)))?;
let action_b64 = BASE64.encode(action_bytes);
let request = youtube::FlagRequest {
context: Some(youtube::Context {
client: Some(youtube::Client {
client_name: 1,
client_version: "2.20240614.01.00".to_string()
})
}),
action: action_b64,
};
let mut payload = Vec::new();
request.encode(&mut payload)
.map_err(|e| YouTubeError::Other(Box::new(e)))?;
let mut req_builder = Request::builder()
.method(Method::POST)
.uri(format!("https://{}/youtubei/v1/flag/flag", self.ip))
.header("Host", "www.youtube.com")
.header("Cookie", self.fields.cookie.unwrap())
.header("Authorization", self.fields.authorization.unwrap())
.header("Origin", "https://www.youtube.com")
.header("Content-Type", "application/x-protobuf")
.header("X-Goog-Encode-Response-If-Executable", "base64");
if let Some(token) = self.fields.authorization {
req_builder = req_builder.header("Authorization", token);
}
if let Some(cookie) = self.fields.cookie {
req_builder = req_builder.header("Cookie", cookie);
}
let req = req_builder
.body(Full::new(Bytes::from(payload)))
.map_err(|e| YouTubeError::Other(Box::new(e)))?;
let resp = self.client.request(req).await?;
match resp.status() {
StatusCode::NOT_FOUND => Err(YouTubeError::NotFound),
StatusCode::TOO_MANY_REQUESTS => Err(YouTubeError::Ratelimited),
StatusCode::UNAUTHORIZED => Err(YouTubeError::Unauthorized),
StatusCode::INTERNAL_SERVER_ERROR => Err(YouTubeError::InternalServerError),
StatusCode::OK => Ok(()),
status => {
let body_bytes = resp.into_body().collect().await?.to_bytes();
let body_str = String::from_utf8_lossy(&body_bytes);
eprintln!("Unknown status code {}: {}", status.as_u16(), body_str);
Err(YouTubeError::UnknownStatusCode(status))
}
}
}
}