sxyporn cdn racing
This commit is contained in:
@@ -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());
|
||||
|
||||
@@ -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: "<sum1>-<host>-<sum2>" 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<String> {
|
||||
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<String> = 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<String> {
|
||||
let mut tmp: Vec<String> = 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::<u32>().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>) -> String {
|
||||
if candidate_urls.is_empty() {
|
||||
return String::new();
|
||||
}
|
||||
|
||||
let (tx, mut rx) = tokio::sync::mpsc::channel::<String>(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<Requester>,
|
||||
) -> 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::<Vec<&str>>()[1]
|
||||
.split("\":\"")
|
||||
.collect::<Vec<&str>>()[1]
|
||||
.split("\"}")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.replace("\\", "");
|
||||
// println!("src: {}", data_string);
|
||||
let mut tmp = data_string
|
||||
.split("/")
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
// 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::<u32>().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<String> = 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<String> = decoded
|
||||
.split('|')
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
|
||||
race_cdn_urls(urls).await
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user