Move redirecting-proxy formats to video.url for verified app clients; document the rule

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUYdCh2nCVsQYgJAT7gwSi
This commit is contained in:
Simon
2026-10-19 07:48:34 +00:00
parent f900987434
commit 6c69b46395
3 changed files with 153 additions and 0 deletions

View File

@@ -20,6 +20,40 @@ use crate::proxies::thaiporntv::ThaipornTvProxy;
use crate::proxies::*;
use crate::util::requester::Requester;
/// Proxies that answer with a `302` to the real media instead of streaming it.
/// The Hot Tub app cannot follow a redirect inside `formats[].url`, so for app
/// clients a format pointing at one of these is moved to `video.url` (see
/// `move_redirect_formats_to_url`). Keep in sync with the `proxy2redirect`
/// routes below; `redirect_proxy_list_matches_routes` enforces it.
pub const REDIRECT_PROXIES: &[&str] = &[
"archivebate",
"clapdat",
"doodstream",
"lulustream",
"sxyprn",
"javtiful",
"spankbang",
"porndish",
"hqporner",
"heavyfetish",
"vjav",
"pornhd3x",
"vidara",
"pimpbunny",
"thaiporntv",
"tube8",
"supjav",
];
/// True for a `/proxy/<redirect proxy>/...` URL (absolute or host-relative).
pub fn is_redirect_proxy_url(url: &str) -> bool {
let Some(rest) = url.split_once("/proxy/").map(|(_, rest)| rest) else {
return false;
};
let name = rest.split('/').next().unwrap_or_default();
REDIRECT_PROXIES.contains(&name)
}
pub fn config(cfg: &mut web::ServiceConfig) {
cfg.service(
web::resource("/archivebate/{endpoint}*")
@@ -239,3 +273,39 @@ fn get_proxy(proxy: &str) -> Option<AnyProxy> {
_ => None,
}
}
#[cfg(test)]
mod redirect_proxy_tests {
use super::*;
#[test]
fn recognises_redirect_proxy_urls() {
assert!(is_redirect_proxy_url("http://h/proxy/archivebate/archivebate.com/watch/1"));
assert!(is_redirect_proxy_url("/proxy/hqporner/x"));
assert!(!is_redirect_proxy_url("http://h/proxy/noodlemagazine/x"));
assert!(!is_redirect_proxy_url("https://cdn.example/video.mp4"));
}
#[test]
fn redirect_proxy_list_matches_routes() {
let source = include_str!("proxy.rs");
let config_body = source
.split("pub fn config(")
.nth(1)
.and_then(|rest| rest.split("async fn proxy2redirect(").next())
.expect("config body");
let mut from_routes: Vec<String> = Vec::new();
for block in config_body.split("web::resource(\"/").skip(1) {
let name = block.split("/{endpoint}").next().unwrap_or_default();
let until_next = block.split("web::resource(").next().unwrap_or(block);
if until_next.contains("proxy2redirect") {
from_routes.push(name.to_string());
}
}
let mut listed: Vec<String> = REDIRECT_PROXIES.iter().map(|s| s.to_string()).collect();
from_routes.sort();
from_routes.dedup();
listed.sort();
assert_eq!(listed, from_routes);
}
}