pornhub: probe pornstar/model/channel for unknown free-form queries
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
use crate::DbPool;
|
||||
use crate::api::ClientVersion;
|
||||
use crate::providers::{Provider, proxy_aylo_thumb, report_provider_error, requester_or_default};
|
||||
use crate::providers::{
|
||||
Provider, probe_first_archive, proxy_aylo_thumb, report_provider_error, requester_or_default,
|
||||
slug_dashed,
|
||||
};
|
||||
use crate::status::*;
|
||||
use crate::util::cache::VideoCache;
|
||||
use crate::util::parse_abbreviated_number;
|
||||
@@ -45,6 +48,8 @@ error_chain! {
|
||||
pub struct PornhubProvider {
|
||||
url: String,
|
||||
tag_map: Arc<RwLock<HashMap<String, TagInfo>>>,
|
||||
/// Queries already probed on the site that are neither a pornstar, model nor channel.
|
||||
probe_misses: Arc<RwLock<std::collections::HashSet<String>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@@ -91,6 +96,7 @@ impl PornhubProvider {
|
||||
let provider = Self {
|
||||
url: BASE_URL.to_string(),
|
||||
tag_map: Arc::new(RwLock::new(HashMap::new())),
|
||||
probe_misses: Arc::new(RwLock::new(std::collections::HashSet::new())),
|
||||
};
|
||||
provider.spawn_initial_load();
|
||||
provider
|
||||
@@ -588,6 +594,43 @@ impl PornhubProvider {
|
||||
values.push(normalized.to_string());
|
||||
}
|
||||
|
||||
/// A free-form query that is not in the loaded catalogs may still name a
|
||||
/// pornstar, model or channel. Probe the site (unknown names 404 or
|
||||
/// redirect away) and remember the hit in `tag_map` so later pages and
|
||||
/// queries resolve without another request.
|
||||
async fn learn_query_target(&self, query: &str, options: &ServerOptions) {
|
||||
let key = query.trim().to_ascii_lowercase();
|
||||
let slug = slug_dashed(query);
|
||||
if slug.is_empty() || self.probe_misses.read().unwrap().contains(&key) {
|
||||
return;
|
||||
}
|
||||
let kinds = [
|
||||
QueryTargetKind::Pornstar,
|
||||
QueryTargetKind::Model,
|
||||
QueryTargetKind::Channel,
|
||||
];
|
||||
let urls: Vec<String> = kinds
|
||||
.iter()
|
||||
.map(|kind| format!("{}/{}/{slug}/videos", self.url, kind.path_segment()))
|
||||
.collect();
|
||||
let mut requester = requester_or_default(options, CHANNEL_ID, "learn_query_target");
|
||||
let hit = probe_first_archive(&mut requester, &urls, &format!("{}/", self.url), |body| {
|
||||
body.contains("pcVideoListItem")
|
||||
})
|
||||
.await;
|
||||
match hit {
|
||||
Some((index, _)) => {
|
||||
self.tag_map.write().unwrap().insert(
|
||||
key,
|
||||
TagInfo { kind: kinds[index], slug, title: query.trim().to_string() },
|
||||
);
|
||||
}
|
||||
None => {
|
||||
self.probe_misses.write().unwrap().insert(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_listing(
|
||||
&self,
|
||||
cache: VideoCache,
|
||||
@@ -596,6 +639,11 @@ impl PornhubProvider {
|
||||
query: Option<&str>,
|
||||
options: ServerOptions,
|
||||
) -> Result<Vec<VideoItem>> {
|
||||
if let Some(q) = query.map(str::trim).filter(|q| !q.is_empty()) {
|
||||
if self.parse_query_target(q).is_none() {
|
||||
self.learn_query_target(q, &options).await;
|
||||
}
|
||||
}
|
||||
if query.is_some() && self.tag_map.read().unwrap().is_empty() {
|
||||
let _ = Self::load_tags(&self.url, Arc::clone(&self.tag_map)).await;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user