From 55d687c361380bd3a7030634c4cacc34984a06c0 Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 6 Jul 2026 14:59:11 +0000 Subject: [PATCH] sxyprn fix? --- src/api.rs | 14 +++- src/providers/allpornstream.rs | 1 + src/providers/archivebate.rs | 1 + src/providers/hsex.rs | 1 + src/providers/jable.rs | 3 + src/providers/mod.rs | 1 + src/providers/noodlemagazine.rs | 1 + src/providers/pimpbunny.rs | 3 + src/providers/porndish.rs | 1 + src/providers/pornhd3x.rs | 4 + src/providers/redgifs.rs | 1 + src/providers/shooshtime.rs | 2 + src/providers/supjav.rs | 3 + src/providers/sxyprn.rs | 138 +++++++++++++++++++++++--------- src/providers/thaiporntv.rs | 1 + src/providers/tube8.rs | 1 + src/providers/vrporn.rs | 1 + src/proxies/sxyprn.rs | 66 ++++++++++++++- src/util/hoster_proxy.rs | 1 + src/videos.rs | 2 + 20 files changed, 203 insertions(+), 43 deletions(-) diff --git a/src/api.rs b/src/api.rs index 8bdec6b..7e63dd8 100644 --- a/src/api.rs +++ b/src/api.rs @@ -19,7 +19,7 @@ use std::process::Command; use tokio::task; use url::Url; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct ClientVersion { version: u32, subversion: u32, @@ -568,6 +568,7 @@ async fn videos_post( duration: Some(duration), sort: Some(sort.clone()), sexuality: Some(sexuality), + client_version: Some(clientversion.clone()), }; if let Some(query_url) = query.as_deref().and_then(normalize_query_url) { @@ -580,7 +581,9 @@ async fn videos_post( if let Some(mut video_items) = videos_from_ytdlp_query_url(&channel, &query_url, perPage as usize) { - if clientversion == ClientVersion::new(38, 0, "Hot%20Tub".to_string()) { + if clientversion == ClientVersion::new(38, 0, "Hot%20Tub".to_string()) + && clientversion.version == 38 + { video_items = video_items .into_iter() .filter_map(|video| { @@ -674,8 +677,10 @@ async fn videos_post( } } - // There is a bug in Hottub38 that makes the client error for a 403-url even though formats work fine - if clientversion == ClientVersion::new(38, 0, "Hot%20Tub".to_string()) { + // There is a bug in Hottub38 that makes the client error for a 403-url even though formats work fine. + // ClientVersion equality only compares the client name, so this also checks `.version` directly + // to make sure the workaround stays scoped to actual v38 clients. + if clientversion == ClientVersion::new(38, 0, "Hot%20Tub".to_string()) && clientversion.version == 38 { // filter out videos without preview for old clients video_items = video_items .into_iter() @@ -809,6 +814,7 @@ async fn uploaders_post( duration: None, sort: None, sexuality: None, + client_version: None, }; crate::flow_debug!( diff --git a/src/providers/allpornstream.rs b/src/providers/allpornstream.rs index de4990b..2a53625 100644 --- a/src/providers/allpornstream.rs +++ b/src/providers/allpornstream.rs @@ -471,6 +471,7 @@ mod tests { fn make_options() -> ServerOptions { ServerOptions { + client_version: None, featured: None, category: None, sites: None, diff --git a/src/providers/archivebate.rs b/src/providers/archivebate.rs index 76c468e..c1d6da6 100644 --- a/src/providers/archivebate.rs +++ b/src/providers/archivebate.rs @@ -148,6 +148,7 @@ impl ArchivebateProvider { runtime.block_on(async move { let options = ServerOptions { + client_version: None, featured: None, category: None, sites: None, diff --git a/src/providers/hsex.rs b/src/providers/hsex.rs index 538af49..1c38936 100644 --- a/src/providers/hsex.rs +++ b/src/providers/hsex.rs @@ -1343,6 +1343,7 @@ mod tests { async fn fetches_page_two_items() { let provider = provider(); let options = ServerOptions { + client_version: None, featured: None, category: None, sites: None, diff --git a/src/providers/jable.rs b/src/providers/jable.rs index 4ff95e9..707bb93 100644 --- a/src/providers/jable.rs +++ b/src/providers/jable.rs @@ -605,6 +605,7 @@ mod tests { #[test] fn resolve_lang_defaults_to_en() { let opts = ServerOptions { + client_version: None, language: None, sort: None, featured: None, category: None, sites: None, filter: None, public_url_base: None, requester: None, @@ -651,6 +652,7 @@ mod tests { #[test] fn picks_target_from_sort() { let opts = ServerOptions { + client_version: None, sort: Some("hot".to_string()), featured: None, category: None, @@ -674,6 +676,7 @@ mod tests { #[test] fn picks_tag_target_from_query_prefix() { let opts = ServerOptions { + client_version: None, sort: None, featured: None, category: None, diff --git a/src/providers/mod.rs b/src/providers/mod.rs index ee9c160..fc93f15 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -258,6 +258,7 @@ async fn run_provider_validation(provider_id: &str) -> Result<(), String> { .and_then(|value| value.to_u8()) .unwrap_or(VALIDATION_RESULTS_REQUIRED as u8); let options = ServerOptions { + client_version: Some(validation_client_version()), featured: request.featured.clone(), category: request.category.clone(), sites: request.sites.clone(), diff --git a/src/providers/noodlemagazine.rs b/src/providers/noodlemagazine.rs index f855e09..c3aa7ef 100644 --- a/src/providers/noodlemagazine.rs +++ b/src/providers/noodlemagazine.rs @@ -604,6 +604,7 @@ mod tests { fn options() -> ServerOptions { ServerOptions { + client_version: None, featured: None, category: None, sites: None, diff --git a/src/providers/pimpbunny.rs b/src/providers/pimpbunny.rs index 68397cd..d4f01fe 100644 --- a/src/providers/pimpbunny.rs +++ b/src/providers/pimpbunny.rs @@ -871,6 +871,7 @@ mod tests { fn rewrites_allowed_thumbs_to_proxy_urls() { let provider = test_provider(); let options = ServerOptions { + client_version: None, featured: None, category: None, sites: None, @@ -901,6 +902,7 @@ mod tests { fn rewrites_video_pages_to_redirect_proxy() { let provider = test_provider(); let options = ServerOptions { + client_version: None, featured: None, category: None, sites: None, @@ -931,6 +933,7 @@ mod tests { fn parses_listing_without_detail_requests() { let provider = test_provider(); let options = ServerOptions { + client_version: None, featured: None, category: None, sites: None, diff --git a/src/providers/porndish.rs b/src/providers/porndish.rs index 6aa6c83..476ea11 100644 --- a/src/providers/porndish.rs +++ b/src/providers/porndish.rs @@ -1430,6 +1430,7 @@ mod tests { let provider = PorndishProvider::new(); let mut requester = Requester::new(); let options = ServerOptions { + client_version: None, featured: None, category: None, sites: None, diff --git a/src/providers/pornhd3x.rs b/src/providers/pornhd3x.rs index bfedfdc..8fa1593 100644 --- a/src/providers/pornhd3x.rs +++ b/src/providers/pornhd3x.rs @@ -485,6 +485,7 @@ impl Pornhd3xProvider { async fn load_home_catalogs(&self) -> Result<()> { let mut requester = requester_or_default( &ServerOptions { + client_version: None, featured: None, category: None, sites: None, @@ -551,6 +552,7 @@ impl Pornhd3xProvider { async fn load_sitemap_catalogs(&self) -> Result<()> { let mut requester = requester_or_default( &ServerOptions { + client_version: None, featured: None, category: None, sites: None, @@ -609,6 +611,7 @@ impl Pornhd3xProvider { async fn load_recent_tags(&self) -> Result<()> { let mut requester = requester_or_default( &ServerOptions { + client_version: None, featured: None, category: None, sites: None, @@ -1333,6 +1336,7 @@ mod tests { fn builds_proxied_video_urls() { let provider = Pornhd3xProvider::new_for_tests(); let options = ServerOptions { + client_version: None, featured: None, category: None, sites: None, diff --git a/src/providers/redgifs.rs b/src/providers/redgifs.rs index ea60f32..4c9878e 100644 --- a/src/providers/redgifs.rs +++ b/src/providers/redgifs.rs @@ -607,6 +607,7 @@ mod tests { fn empty_options() -> ServerOptions { ServerOptions { + client_version: None, featured: None, category: None, sites: None, diff --git a/src/providers/shooshtime.rs b/src/providers/shooshtime.rs index 39cb867..f8556e4 100644 --- a/src/providers/shooshtime.rs +++ b/src/providers/shooshtime.rs @@ -1335,6 +1335,7 @@ mod tests { html, "https://shooshtime.com/videos/example/123/", &crate::videos::ServerOptions { + client_version: None, featured: None, category: None, sites: None, @@ -1363,6 +1364,7 @@ mod tests { fn builds_proxied_video_urls() { let provider = ShooshtimeProvider::new(); let options = crate::videos::ServerOptions { + client_version: None, featured: None, category: None, sites: None, diff --git a/src/providers/supjav.rs b/src/providers/supjav.rs index 5722ac6..482370d 100644 --- a/src/providers/supjav.rs +++ b/src/providers/supjav.rs @@ -1758,6 +1758,7 @@ mod tests { fn ignores_sort_filter_when_resolving_archive_target() { let provider = test_provider(); let options = ServerOptions { + client_version: None, featured: None, category: Some("all".to_string()), sites: None, @@ -1780,6 +1781,7 @@ mod tests { fn normalizes_relative_archive_targets() { let provider = test_provider(); let options = ServerOptions { + client_version: None, featured: None, category: Some("/category/censored-jav".to_string()), sites: None, @@ -1874,6 +1876,7 @@ mod tests { let cache = VideoCache::new().max_size(1_000).to_owned(); let pool = test_db_pool(); let options = ServerOptions { + client_version: None, featured: None, category: None, sites: None, diff --git a/src/providers/sxyprn.rs b/src/providers/sxyprn.rs index 6378fd4..5637cf9 100644 --- a/src/providers/sxyprn.rs +++ b/src/providers/sxyprn.rs @@ -5,7 +5,6 @@ use crate::status::*; use crate::util::cache::VideoCache; use crate::util::discord::format_error_chain; use crate::util::discord::send_discord_error_report; -use crate::util::hoster_proxy::{proxy_name_for_url, rewrite_hoster_url}; use crate::util::requester::Requester; use crate::util::time::parse_time_to_seconds; use crate::videos::ServerOptions; @@ -142,9 +141,24 @@ impl SxyprnProvider { sort_string ); - let old_items = match cache.get(&url_str) { + // App requests carry eagerly-resolved (and shorter-lived) CDN URLs in + // `formats`, so they're cached separately from the plain listing and + // expire sooner, since the resolved URLs are more likely to go stale. + let is_app_client = options + .client_version + .as_ref() + .map(|cv| *cv == ClientVersion::new(0, 0, "Hot%20Tub".to_string())) + .unwrap_or(false); + let cache_key = if is_app_client { + format!("{url_str}#app") + } else { + url_str.clone() + }; + let cache_ttl_secs: u64 = if is_app_client { 30 * 60 } else { 60 * 60 }; + + let old_items = match cache.get(&cache_key) { Some((time, items)) => { - if time.elapsed().unwrap_or_default().as_secs() < 60 * 60 { + if time.elapsed().unwrap_or_default().as_secs() < cache_ttl_secs { return Ok(items.clone()); } else { items.clone() @@ -192,8 +206,8 @@ impl SxyprnProvider { // .get_video_items_from_html(text.clone(), pool, requester) // .await; if !video_items.is_empty() { - cache.remove(&url_str); - cache.insert(url_str.clone(), video_items.clone()); + cache.remove(&cache_key); + cache.insert(cache_key.clone(), video_items.clone()); } else { return Ok(old_items); } @@ -225,10 +239,24 @@ impl SxyprnProvider { ((page as u32) - 1) * 20, sort_string ); - // Check our Video Cache. If the result is younger than 1 hour, we return it. - let old_items = match cache.get(&url_str) { + // App requests carry eagerly-resolved (and shorter-lived) CDN URLs in + // `formats`, so they're cached separately from the plain listing and + // expire sooner, since the resolved URLs are more likely to go stale. + let is_app_client = options + .client_version + .as_ref() + .map(|cv| *cv == ClientVersion::new(0, 0, "Hot%20Tub".to_string())) + .unwrap_or(false); + let cache_key = if is_app_client { + format!("{url_str}#app") + } else { + url_str.clone() + }; + let cache_ttl_secs: u64 = if is_app_client { 30 * 60 } else { 60 * 60 }; + // Check our Video Cache. If the result is younger than the TTL, we return it. + let old_items = match cache.get(&cache_key) { Some((time, items)) => { - if time.elapsed().unwrap_or_default().as_secs() < 60 * 60 { + if time.elapsed().unwrap_or_default().as_secs() < cache_ttl_secs { return Ok(items.clone()); } else { let _ = cache.check().await; @@ -277,8 +305,8 @@ impl SxyprnProvider { // .get_video_items_from_html(text.clone(), pool, requester) // .await; if !video_items.is_empty() { - cache.remove(&url_str); - cache.insert(url_str.clone(), video_items.clone()); + cache.remove(&cache_key); + cache.insert(cache_key.clone(), video_items.clone()); } else { return Ok(old_items); } @@ -289,13 +317,23 @@ impl SxyprnProvider { &self, html: String, _pool: DbPool, - _requester: Requester, + requester: Requester, options: &ServerOptions, ) -> Result> { if html.is_empty() { return Ok(vec![]); } + // The Hottub app can resolve directly-playable format URLs itself, so for + // app requests we serve the real sxyprn.com page as `url` and eagerly + // resolve every mirror CDN URL into `formats`. Other clients keep the + // lazy `/proxy/sxyprn/post/{id}` redirect and get no formats. + let is_app_client = options + .client_version + .as_ref() + .map(|cv| *cv == ClientVersion::new(0, 0, "Hot%20Tub".to_string())) + .unwrap_or(false); + // take content before "