diff --git a/src/providers/xhamster.rs b/src/providers/xhamster.rs index eb092f3..cc124f0 100644 --- a/src/providers/xhamster.rs +++ b/src/providers/xhamster.rs @@ -1,6 +1,8 @@ use crate::DbPool; use crate::api::ClientVersion; -use crate::providers::{Provider, report_provider_error, requester_or_default}; +use crate::providers::{ + Provider, probe_first_archive, report_provider_error, requester_or_default, slug_dashed, +}; use crate::status::*; use crate::util::cache::VideoCache; use crate::util::parse_abbreviated_number; @@ -94,6 +96,7 @@ enum Target { Search(String), Category(String), Channel(String), + Pornstar(String), } #[derive(Debug, Clone)] @@ -208,6 +211,7 @@ impl XhamsterProvider { } Target::Category(slug) => format!("{BASE_URL}/categories/{slug}"), Target::Channel(slug) => format!("{BASE_URL}/channels/{slug}"), + Target::Pornstar(slug) => format!("{BASE_URL}/pornstars/{slug}"), }; if page <= 1 { base @@ -385,6 +389,7 @@ impl XhamsterProvider { match kind.trim().to_ascii_lowercase().as_str() { "cat" | "category" => return Target::Category(slug), "channel" | "channels" => return Target::Channel(slug), + "pornstar" | "pornstars" | "star" | "model" => return Target::Pornstar(slug), _ => {} } } @@ -401,6 +406,23 @@ impl XhamsterProvider { Target::Search(trimmed.to_string()) } + /// A free-form query that names a pornstar or a channel goes to that + /// archive (probed on the site; unknown names 404), else keyword search. + async fn resolve_keyword(query: &str, options: &ServerOptions) -> Option { + let slug = slug_dashed(query); + if slug.is_empty() { + return None; + } + let candidates = [Target::Pornstar(slug.clone()), Target::Channel(slug)]; + let urls: Vec = candidates.iter().map(|t| Self::target_url(t, 1)).collect(); + let mut requester = requester_or_default(options, CHANNEL_ID, "resolve_keyword"); + let (index, _) = probe_first_archive(&mut requester, &urls, &format!("{BASE_URL}/"), |body| { + body.contains("video-thumb-info__name") + }) + .await?; + candidates.into_iter().nth(index) + } + fn resolve_sort_target(sort: &str) -> Target { match sort.trim().to_ascii_lowercase().as_str() { "popular" | "viewed" | "most_viewed" | "mostviewed" => Target::MostViewed, @@ -472,10 +494,15 @@ impl Provider for XhamsterProvider { let page = page.parse::().unwrap_or(1).max(1); let per_page = per_page.parse::().unwrap_or(10).clamp(1, 60); - let target = match query { + let mut target = match query { Some(q) if !q.trim().is_empty() => self.resolve_query_target(q.trim()), _ => self.resolve_option_target(&options, &sort), }; + if let Target::Search(query) = &target { + if let Some(found) = Self::resolve_keyword(query, &options).await { + target = found; + } + } match self .fetch_target(cache, target, page, per_page, options)