114 lines
3.6 KiB
Rust
114 lines
3.6 KiB
Rust
use ntex::web;
|
|
use regex::Regex;
|
|
|
|
use crate::util::requester::Requester;
|
|
|
|
const BASE_URL: &str = "https://www.clapdat.com";
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ClapdatProxy {}
|
|
|
|
impl ClapdatProxy {
|
|
pub fn new() -> Self {
|
|
Self {}
|
|
}
|
|
|
|
fn normalize_detail_url(endpoint: &str) -> Option<String> {
|
|
let value = endpoint.trim().trim_start_matches('/');
|
|
if value.is_empty() {
|
|
return None;
|
|
}
|
|
|
|
let detail_url = if value.starts_with("http://") || value.starts_with("https://") {
|
|
value.to_string()
|
|
} else {
|
|
format!("https://{}", value)
|
|
};
|
|
|
|
let detail_url = detail_url.replacen("http://", "https://", 1);
|
|
let parsed = url::Url::parse(&detail_url).ok()?;
|
|
let host = parsed.host_str()?;
|
|
if !(host == "www.clapdat.com" || host == "clapdat.com") {
|
|
return None;
|
|
}
|
|
if !parsed.path().starts_with("/video/") {
|
|
return None;
|
|
}
|
|
|
|
Some(detail_url)
|
|
}
|
|
|
|
fn clapdat_decode(input: &str) -> Option<Vec<u8>> {
|
|
let compact = if input.len() > 209 {
|
|
format!("{}{}", &input[..19], &input[209..])
|
|
} else {
|
|
input.to_string()
|
|
};
|
|
|
|
let cleaned: String = compact
|
|
.chars()
|
|
.filter(|c| c.is_ascii_alphanumeric() || *c == '+' || *c == '/')
|
|
.collect();
|
|
if cleaned.is_empty() {
|
|
return None;
|
|
}
|
|
|
|
let mut padded = cleaned;
|
|
while padded.len() % 4 != 0 {
|
|
padded.push('=');
|
|
}
|
|
|
|
base64::Engine::decode(&base64::engine::general_purpose::STANDARD, padded.as_bytes()).ok()
|
|
}
|
|
|
|
fn extract_media_url(html: &str) -> Option<String> {
|
|
let domain_re = Regex::new(r#"file_domain:"([^"]+)""#).ok()?;
|
|
let file_re = Regex::new(r#"file:"([^"]+)""#).ok()?;
|
|
let domain = domain_re
|
|
.captures(html)
|
|
.and_then(|caps| caps.get(1).map(|m| m.as_str().trim().to_string()))?;
|
|
let encoded = file_re
|
|
.captures(html)
|
|
.and_then(|caps| caps.get(1).map(|m| m.as_str().trim().to_string()))?;
|
|
|
|
let decoded = Self::clapdat_decode(&encoded)?;
|
|
let path: String = decoded.into_iter().map(char::from).collect();
|
|
if path.is_empty() {
|
|
return None;
|
|
}
|
|
Some(format!("https://{}/{}", domain, path.trim_start_matches('/')))
|
|
}
|
|
}
|
|
|
|
impl crate::proxies::Proxy for ClapdatProxy {
|
|
async fn get_video_url(&self, url: String, requester: web::types::State<Requester>) -> String {
|
|
let Some(detail_url) = Self::normalize_detail_url(&url) else {
|
|
return String::new();
|
|
};
|
|
|
|
let mut requester = requester.get_ref().clone();
|
|
let headers = vec![
|
|
(
|
|
"accept".to_string(),
|
|
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8".to_string(),
|
|
),
|
|
("accept-language".to_string(), "en-US,en;q=0.8".to_string()),
|
|
(
|
|
"user-agent".to_string(),
|
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36".to_string(),
|
|
),
|
|
("referer".to_string(), BASE_URL.to_string()),
|
|
];
|
|
|
|
let html = requester
|
|
.get_with_headers(&detail_url, headers, Some(wreq::Version::HTTP_11))
|
|
.await
|
|
.unwrap_or_default();
|
|
if html.is_empty() {
|
|
return String::new();
|
|
}
|
|
|
|
Self::extract_media_url(&html).unwrap_or_default()
|
|
}
|
|
}
|