This commit is contained in:
Simon
2026-05-20 14:28:11 +00:00
committed by ForgeCode
parent 2a72e08d8a
commit 2ec9137df9
6 changed files with 792 additions and 0 deletions

173
src/proxies/tube8.rs Normal file
View File

@@ -0,0 +1,173 @@
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<String> {
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<String> {
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<String> = None;
let mut default_url: Option<String> = 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::<i64>().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<Requester>,
) -> 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");
}
}