From 9571c7031a9347f4714f006cadf50e5d496e8dae Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 26 Jun 2026 15:46:15 +0000 Subject: [PATCH] sxyporn cdn racing --- src/providers/sxyprn.rs | 30 ++++---- src/proxies/sxyprn.rs | 163 ++++++++++++++++++++++++++++++---------- 2 files changed, 137 insertions(+), 56 deletions(-) diff --git a/src/providers/sxyprn.rs b/src/providers/sxyprn.rs index 0056af4..6378fd4 100644 --- a/src/providers/sxyprn.rs +++ b/src/providers/sxyprn.rs @@ -476,23 +476,23 @@ impl SxyprnProvider { options.public_url_base.as_deref().unwrap_or(""), id ); - let mut video_url = sxyprn_url; + let video_url = sxyprn_url; - if let Some(dood_url) = title_links - .iter() - .find(|u| proxy_name_for_url(u).as_deref() == Some("doodstream")) - .map(|u| rewrite_hoster_url(options, u)) - { - video_url = dood_url; - } + // if let Some(dood_url) = title_links + // .iter() + // .find(|u| proxy_name_for_url(u).as_deref() == Some("doodstream")) + // .map(|u| rewrite_hoster_url(options, u)) + // { + // video_url = dood_url; + // } - if let Some(vidara_url) = title_links - .iter() - .find(|u| proxy_name_for_url(u).as_deref() == Some("vidara")) - .map(|u| rewrite_hoster_url(options, u)) - { - video_url = vidara_url; - } + // if let Some(vidara_url) = title_links + // .iter() + // .find(|u| proxy_name_for_url(u).as_deref() == Some("vidara")) + // .map(|u| rewrite_hoster_url(options, u)) + // { + // video_url = vidara_url; + // } let mut headers = std::collections::HashMap::new(); headers.insert("Referer".to_string(), "https://sxyprn.com/".to_string()); diff --git a/src/proxies/sxyprn.rs b/src/proxies/sxyprn.rs index 4d1c613..931c803 100644 --- a/src/proxies/sxyprn.rs +++ b/src/proxies/sxyprn.rs @@ -1,9 +1,9 @@ use base64::{Engine as _, engine::general_purpose}; use ntex::web; +use std::time::Duration; use crate::util::requester::Requester; -/// Extracts digits from a string and sums them. fn ssut51(arg: &str) -> u32 { arg.chars() .filter(|c| c.is_ascii_digit()) @@ -11,18 +11,96 @@ fn ssut51(arg: &str) -> u32 { .sum() } -/// Encodes a token: "--" using Base64 URL-safe variant. fn boo(sum1: u32, sum2: u32) -> String { let raw = format!("{}-{}-{}", sum1, "sxyprn.com", sum2); let encoded = general_purpose::STANDARD.encode(raw); - - // Replace + → -, / → _, = → . encoded .replace('+', "-") .replace('/', "_") .replace('=', ".") } +/// Extracts all CDN path values from the data-vnfo JSON attribute. +fn extract_all_cdn_paths(html: &str) -> Vec { + let json_str = match html.split("data-vnfo='").nth(1) { + Some(s) => s.split('\'').next().unwrap_or(""), + None => return vec![], + }; + + if let Ok(serde_json::Value::Object(map)) = serde_json::from_str(json_str) { + let paths: Vec = map + .values() + .filter_map(|v| v.as_str().map(|s| s.replace('\\', ""))) + .filter(|s| !s.is_empty()) + .collect(); + if !paths.is_empty() { + return paths; + } + } + + // Fallback: original single-value extraction + let first = json_str + .split("\":\"") + .nth(1) + .and_then(|s| s.split("\"}").next()) + .map(|s| s.replace('\\', "")) + .unwrap_or_default(); + if first.is_empty() { + vec![] + } else { + vec![first] + } +} + +/// Applies the sxyprn segment transformation to produce the pre-redirect CDN URL. +fn transform_cdn_path(path: &str) -> Option { + let mut tmp: Vec = path.split('/').map(|s| s.to_string()).collect(); + if tmp.len() < 8 { + return None; + } + let s6 = ssut51(&tmp[6]); + let s7 = ssut51(&tmp[7]); + tmp[1] = format!("{}8/{}", tmp[1], boo(s6, s7)); + tmp[5] = format!("{}", tmp[5].parse::().unwrap_or(0).saturating_sub(s6 + s7)); + Some(format!("https://sxyprn.com{}", tmp.join("/"))) +} + +/// Races all candidate CDN URLs concurrently via blocking curl HEAD requests. +/// Returns the final CDN URL from the first candidate that successfully redirects. +async fn race_cdn_urls(candidate_urls: Vec) -> String { + if candidate_urls.is_empty() { + return String::new(); + } + + let (tx, mut rx) = tokio::sync::mpsc::channel::(candidate_urls.len()); + + for cdn_url in candidate_urls { + let tx = tx.clone(); + tokio::spawn(async move { + let result = tokio::task::spawn_blocking(move || { + crate::util::get_redirect_location(&cdn_url) + .ok() + .flatten() + .map(|loc| format!("https:{}", loc)) + }) + .await + .ok() + .flatten(); + + if let Some(url) = result { + let _ = tx.send(url).await; + } + }); + } + drop(tx); + + tokio::time::timeout(Duration::from_secs(15), rx.recv()) + .await + .ok() + .flatten() + .unwrap_or_default() +} + #[derive(Debug, Clone)] pub struct SxyprnProxy {} @@ -36,45 +114,48 @@ impl SxyprnProxy { url: String, requester: web::types::State, ) -> String { - let mut requester = requester.get_ref().clone(); - let url = "https://sxyprn.com/".to_string() + &url; - // println!("Fetching URL: {}", url); - let text = requester.get(&url, None).await.unwrap_or("".to_string()); - if text.is_empty() { - return "".to_string(); + if let Some(encoded) = url.strip_prefix("race/") { + return self.resolve_race(encoded).await; } - let data_string = text.split("data-vnfo='").collect::>()[1] - .split("\":\"") - .collect::>()[1] - .split("\"}") - .collect::>()[0] - .replace("\\", ""); - // println!("src: {}", data_string); - let mut tmp = data_string - .split("/") - .map(|s| s.to_string()) - .collect::>(); - // println!("tmp: {:?}", tmp); - tmp[1] = format!( - "{}8/{}", - tmp[1], - boo(ssut51(tmp[6].as_str()), ssut51(tmp[7].as_str())) - ); - // println!("tmp[1]: {:?}", tmp[1]); - //preda - tmp[5] = format!( - "{}", - tmp[5].parse::().unwrap() - ssut51(tmp[6].as_str()) - ssut51(tmp[7].as_str()) - ); - // println!("tmp: {:?}", tmp); - let sxyprn_video_url = format!("https://sxyprn.com{}", tmp.join("/")); - // println!("sxyprn_video_url: {}", sxyprn_video_url); - match crate::util::get_redirect_location(&sxyprn_video_url) { - Ok(Some(loc)) => {return format!("https:{}", loc)}, - Ok(None) => println!("No redirect found for {}", sxyprn_video_url), - Err(e) => eprintln!("Request failed: {}", e), + let mut requester = requester.get_ref().clone(); + let full_url = format!("https://sxyprn.com/{}", url); + let text = requester.get(&full_url, None).await.unwrap_or_default(); + if text.is_empty() { + return String::new(); } - return "".to_string(); + + let cdn_paths = extract_all_cdn_paths(&text); + let candidate_urls: Vec = cdn_paths + .iter() + .filter_map(|p| transform_cdn_path(p)) + .collect(); + + if candidate_urls.is_empty() { + return String::new(); + } + + let joined = candidate_urls.join("|"); + let encoded = general_purpose::URL_SAFE_NO_PAD.encode(joined.as_bytes()); + format!("/proxy/sxyprn/race/{}", encoded) + } + + async fn resolve_race(&self, encoded: &str) -> String { + let bytes = match general_purpose::URL_SAFE_NO_PAD.decode(encoded.as_bytes()) { + Ok(b) => b, + Err(_) => return String::new(), + }; + let decoded = match String::from_utf8(bytes) { + Ok(s) => s, + Err(_) => return String::new(), + }; + + let urls: Vec = decoded + .split('|') + .filter(|s| !s.is_empty()) + .map(|s| s.to_string()) + .collect(); + + race_cdn_urls(urls).await } }