use ntex::web; use crate::util::requester::Requester; const BASE_URL: &str = "https://www.tube8.com"; #[derive(Debug, Clone)] pub struct Tube8Proxy {} impl Tube8Proxy { pub fn new() -> Self { Tube8Proxy {} } fn html_headers() -> Vec<(String, String)> { vec![ ( "User-Agent".to_string(), "Mozilla/5.0 (X11; Linux x86_64; rv:125.0) Gecko/20100101 Firefox/125.0" .to_string(), ), ( "Accept".to_string(), "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8".to_string(), ), ("Accept-Language".to_string(), "en-US,en;q=0.5".to_string()), ("Referer".to_string(), format!("{BASE_URL}/")), ] } fn api_headers(referer: &str) -> Vec<(String, String)> { vec![ ( "User-Agent".to_string(), "Mozilla/5.0 (X11; Linux x86_64; rv:125.0) Gecko/20100101 Firefox/125.0" .to_string(), ), ("Accept".to_string(), "application/json, text/javascript, */*; q=0.01".to_string()), ("Referer".to_string(), referer.to_string()), ("X-Requested-With".to_string(), "XMLHttpRequest".to_string()), ] } // Extract the first /media/hls/?s=... URL from a video page. // The page embeds it as: "videoUrl":"https:\/\/www.tube8.com\/media\/hls\/?s=TOKEN" fn extract_hls_endpoint(html: &str) -> Option { let needle = r#""format":"hls","videoUrl":""#; let start = html.find(needle)? + needle.len(); let rest = &html[start..]; let end = rest.find('"')?; let raw = &rest[..end]; // JSON-escaped forward slashes → real URL Some(raw.replace(r"\/", "/")) } // Parse the JSON quality array returned by /media/hls/?s=... // Returns the highest-quality HLS master playlist URL. fn best_hls_url(json: &str) -> Option { let parsed: serde_json::Value = serde_json::from_str(json).ok()?; let arr = parsed.as_array()?; // Prefer highest numeric quality; fall back to defaultQuality let mut best_quality: i64 = -1; let mut best_url: Option = None; let mut default_url: Option = None; for entry in arr { let url = entry .get("videoUrl") .and_then(|v| v.as_str()) .map(|v| v.replace(r"\/", "/")) .filter(|v| !v.is_empty())?; if entry .get("defaultQuality") .and_then(|v| v.as_bool()) .unwrap_or(false) && default_url.is_none() { default_url = Some(url.clone()); } if let Some(q) = entry .get("quality") .and_then(|v| v.as_str()) .and_then(|v| v.parse::().ok()) { if q > best_quality { best_quality = q; best_url = Some(url); } } } best_url.or(default_url) } pub async fn get_video_url( &self, video_id: String, requester: web::types::State, ) -> String { let video_id = video_id.trim_matches('/').trim(); if video_id.is_empty() { return String::new(); } let page_url = format!("{BASE_URL}/porn-video/{video_id}/"); let mut req = requester.get_ref().clone(); // Step 1: fetch video page to get the signed /media/hls/ endpoint let html = match req .get_with_headers(&page_url, Self::html_headers(), None) .await { Ok(v) => v, Err(_) => return String::new(), }; let hls_endpoint = match Self::extract_hls_endpoint(&html) { Some(url) => url, None => return String::new(), }; // Step 2: call the signed endpoint to get quality options let json = match req .get_with_headers(&hls_endpoint, Self::api_headers(&page_url), None) .await { Ok(v) => v, Err(_) => return String::new(), }; Self::best_hls_url(&json).unwrap_or_default() } } #[cfg(test)] mod tests { use super::Tube8Proxy; #[test] fn extracts_hls_endpoint_from_page() { let html = r#" mediaDefinition: [{"format":"hls","videoUrl":"https:\/\/www.tube8.com\/media\/hls\/?s=eyJTOKEN","remote":true}, {"format":"mp4","videoUrl":"https:\/\/www.tube8.com\/media\/mp4\/?s=eyJTOKEN","remote":true}], "#; let url = Tube8Proxy::extract_hls_endpoint(html).expect("should extract"); assert_eq!(url, "https://www.tube8.com/media/hls/?s=eyJTOKEN"); } #[test] fn picks_best_hls_quality() { let json = r#"[ {"defaultQuality":true,"format":"hls","quality":"480","videoUrl":"https://cdn.example/480/master.m3u8"}, {"defaultQuality":false,"format":"hls","quality":"720","videoUrl":"https://cdn.example/720/master.m3u8"}, {"defaultQuality":false,"format":"hls","quality":"1080","videoUrl":"https://cdn.example/1080/master.m3u8"}, {"defaultQuality":false,"format":"hls","quality":"240","videoUrl":"https://cdn.example/240/master.m3u8"} ]"#; let url = Tube8Proxy::best_hls_url(json).expect("should parse"); assert_eq!(url, "https://cdn.example/1080/master.m3u8"); } #[test] fn falls_back_to_default_quality_when_no_numeric() { let json = r#"[ {"defaultQuality":true,"format":"hls","videoUrl":"https://cdn.example/default/master.m3u8"}, {"defaultQuality":false,"format":"hls","videoUrl":"https://cdn.example/other/master.m3u8"} ]"#; let url = Tube8Proxy::best_hls_url(json).expect("should parse"); assert_eq!(url, "https://cdn.example/default/master.m3u8"); } }