From 59ea0495a69535b865629391c7a33388681ebd23 Mon Sep 17 00:00:00 2001 From: Simon Date: Sat, 17 Oct 2026 05:40:28 +0000 Subject: [PATCH] porn4fans: resolve model/tag names without prefix --- src/providers/porn4fans.rs | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/providers/porn4fans.rs b/src/providers/porn4fans.rs index d1f25df..6377f32 100644 --- a/src/providers/porn4fans.rs +++ b/src/providers/porn4fans.rs @@ -2,6 +2,7 @@ use crate::DbPool; use crate::api::ClientVersion; use crate::providers::{ Provider, report_provider_error, report_provider_error_background, requester_or_default, + probe_first_archive, }; use crate::status::*; use crate::util::cache::VideoCache; @@ -260,6 +261,34 @@ impl Porn4fansProvider { Target::Latest } + /// A free-form query that names a model or a tag goes to that archive + /// (probed on the site; unknown names 404), else keyword search. + async fn resolve_keyword(&self, query: &str, options: &ServerOptions) -> Option { + let slug = Self::slugify(query); + if slug.is_empty() { + return None; + } + let candidates = [ + Target::Model { slug: slug.clone() }, + Target::Tag { slug }, + ]; + let urls: Vec = candidates + .iter() + .map(|t| match t { + Target::Model { slug } => format!("{}/models/{slug}/", self.url), + Target::Tag { slug } => format!("{}/tags/{slug}/", self.url), + _ => String::new(), + }) + .collect(); + let mut requester = requester_or_default(options, CHANNEL_ID, "resolve_keyword"); + let (index, _) = + probe_first_archive(&mut requester, &urls, &format!("{}/", self.url), |body| { + body.contains("custom_list_videos") + }) + .await?; + candidates.into_iter().nth(index) + } + /// Build the async `get_block` URL plus the Referer to send with it. fn build_block_url(&self, target: &Target, sort: &str, page: u32) -> (String, String) { let sort_by = Self::sort_by(sort); @@ -788,7 +817,12 @@ impl Provider for Porn4fansProvider { options: ServerOptions, ) -> Vec { let page = page.parse::().unwrap_or(1).max(1); - let target = self.target_from_request(query.as_deref(), options.categories.as_deref()); + let mut target = self.target_from_request(query.as_deref(), options.categories.as_deref()); + if let Target::Search { query } = &target { + if let Some(found) = self.resolve_keyword(query, &options).await { + target = found; + } + } match self .get_videos_for_target(cache, &target, &sort, page, options)