pornhub: probe pornstar/model/channel for unknown free-form queries
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
use crate::DbPool;
|
use crate::DbPool;
|
||||||
use crate::api::ClientVersion;
|
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::status::*;
|
||||||
use crate::util::cache::VideoCache;
|
use crate::util::cache::VideoCache;
|
||||||
use crate::util::parse_abbreviated_number;
|
use crate::util::parse_abbreviated_number;
|
||||||
@@ -45,6 +48,8 @@ error_chain! {
|
|||||||
pub struct PornhubProvider {
|
pub struct PornhubProvider {
|
||||||
url: String,
|
url: String,
|
||||||
tag_map: Arc<RwLock<HashMap<String, TagInfo>>>,
|
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)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
@@ -91,6 +96,7 @@ impl PornhubProvider {
|
|||||||
let provider = Self {
|
let provider = Self {
|
||||||
url: BASE_URL.to_string(),
|
url: BASE_URL.to_string(),
|
||||||
tag_map: Arc::new(RwLock::new(HashMap::new())),
|
tag_map: Arc::new(RwLock::new(HashMap::new())),
|
||||||
|
probe_misses: Arc::new(RwLock::new(std::collections::HashSet::new())),
|
||||||
};
|
};
|
||||||
provider.spawn_initial_load();
|
provider.spawn_initial_load();
|
||||||
provider
|
provider
|
||||||
@@ -588,6 +594,43 @@ impl PornhubProvider {
|
|||||||
values.push(normalized.to_string());
|
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(
|
async fn fetch_listing(
|
||||||
&self,
|
&self,
|
||||||
cache: VideoCache,
|
cache: VideoCache,
|
||||||
@@ -596,6 +639,11 @@ impl PornhubProvider {
|
|||||||
query: Option<&str>,
|
query: Option<&str>,
|
||||||
options: ServerOptions,
|
options: ServerOptions,
|
||||||
) -> Result<Vec<VideoItem>> {
|
) -> 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() {
|
if query.is_some() && self.tag_map.read().unwrap().is_empty() {
|
||||||
let _ = Self::load_tags(&self.url, Arc::clone(&self.tag_map)).await;
|
let _ = Self::load_tags(&self.url, Arc::clone(&self.tag_map)).await;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user