supjav tags-fixes

This commit is contained in:
Simon
2026-04-03 18:17:14 +00:00
parent e680319541
commit c0717fdacf
2 changed files with 125 additions and 30 deletions

View File

@@ -13,7 +13,7 @@ use htmlentity::entity::{ICodedDataTrait, decode};
use regex::Regex;
use scraper::{ElementRef, Html, Selector};
use serde::Deserialize;
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::process::Command;
use std::sync::{Arc, RwLock};
use std::thread;
@@ -454,12 +454,54 @@ impl SupjavProvider {
Ok(url.to_string())
}
fn match_filter(options: &[FilterOption], query: &str) -> Option<String> {
let normalized_query = Self::normalize_title(query);
options
.iter()
.find(|value| Self::normalize_title(&value.title) == normalized_query)
.map(|value| value.id.clone())
fn extend_filter_lookup(lookup: &mut HashMap<String, String>, options: &[FilterOption]) {
for option in options {
for key in Self::filter_lookup_keys(option) {
lookup.entry(key).or_insert_with(|| option.id.clone());
}
}
}
fn resolve_filter_lookup(&self) -> HashMap<String, String> {
let mut lookup = HashMap::new();
if let Ok(uploaders) = self.uploaders.read() {
Self::extend_filter_lookup(&mut lookup, &uploaders);
}
if let Ok(stars) = self.stars.read() {
Self::extend_filter_lookup(&mut lookup, &stars);
}
Self::extend_filter_lookup(&mut lookup, &self.categories);
if let Ok(tags) = self.tags.read() {
Self::extend_filter_lookup(&mut lookup, &tags);
}
lookup
}
fn filter_lookup_keys(option: &FilterOption) -> Vec<String> {
let mut keys = vec![Self::normalize_title(&option.title)];
let slug = option
.id
.trim_end_matches('/')
.rsplit('/')
.next()
.unwrap_or_default()
.trim();
if !slug.is_empty() {
keys.push(Self::normalize_title(&slug.replace('-', " ")));
if let Some(base_slug) = slug.strip_suffix("-jav") {
if !base_slug.is_empty() {
keys.push(Self::normalize_title(&base_slug.replace('-', " ")));
}
}
}
keys.sort();
keys.dedup();
keys
}
fn resolve_option_target(&self, options: &ServerOptions) -> Option<String> {
@@ -481,23 +523,10 @@ impl SupjavProvider {
}
fn resolve_query_target(&self, query: &str) -> Option<String> {
if let Ok(uploaders) = self.uploaders.read() {
if let Some(target) = Self::match_filter(&uploaders, query) {
return self.normalize_archive_target(&target);
}
}
if let Ok(stars) = self.stars.read() {
if let Some(target) = Self::match_filter(&stars, query) {
return self.normalize_archive_target(&target);
}
}
if let Ok(tags) = self.tags.read() {
if let Some(target) = Self::match_filter(&tags, query) {
return self.normalize_archive_target(&target);
}
}
Self::match_filter(&self.categories, query)
.and_then(|target| self.normalize_archive_target(&target))
let normalized_query = Self::normalize_title(query);
self.resolve_filter_lookup()
.get(&normalized_query)
.and_then(|target| self.normalize_archive_target(target))
}
fn filters_need_loading(&self) -> bool {
@@ -1581,7 +1610,7 @@ print(json.dumps({
let items = self
.fetch_items_for_url(cache, url, sort, per_page_limit)
.await?;
if exact_target.is_some() || items.len() >= 5 || !items.is_empty() {
if !items.is_empty() {
return Ok(items);
}
@@ -1647,13 +1676,23 @@ mod tests {
fn test_provider() -> SupjavProvider {
SupjavProvider {
url: BASE_URL.to_string(),
categories: vec![FilterOption {
id: format!("{BASE_URL}/category/censored-jav"),
title: "Censored JAV".to_string(),
}],
categories: vec![
FilterOption {
id: format!("{BASE_URL}/category/censored-jav"),
title: "Censored JAV".to_string(),
},
FilterOption {
id: format!("{BASE_URL}/category/uncensored-jav"),
title: "Uncensored JAV".to_string(),
},
],
tags: Arc::new(RwLock::new(vec![FilterOption {
id: "/tag/creampie".to_string(),
title: "Creampie".to_string(),
},
FilterOption {
id: "/tag/uncensored-jav".to_string(),
title: "Uncensored JAV".to_string(),
}])),
uploaders: Arc::new(RwLock::new(vec![])),
stars: Arc::new(RwLock::new(vec![])),
@@ -1666,6 +1705,18 @@ mod tests {
assert_eq!(SupjavProvider::strip_count_suffix("Censored JAV"), "Censored JAV");
}
#[test]
fn filter_lookup_stores_title_and_slug_aliases() {
let option = FilterOption {
id: format!("{BASE_URL}/category/uncensored-jav"),
title: "Uncensored JAV".to_string(),
};
let keys = SupjavProvider::filter_lookup_keys(&option);
assert!(keys.iter().any(|value| value == "uncensored jav"));
assert!(keys.iter().any(|value| value == "uncensored"));
}
#[test]
fn builds_archive_page_url_with_query() {
assert_eq!(
@@ -1756,6 +1807,50 @@ mod tests {
);
}
#[test]
fn resolves_category_queries_with_or_without_jav_suffix() {
let provider = test_provider();
assert_eq!(
provider.resolve_query_target("Uncensored JAV").as_deref(),
Some("https://supjav.com/category/uncensored-jav")
);
assert_eq!(
provider.resolve_query_target("Uncensored").as_deref(),
Some("https://supjav.com/category/uncensored-jav")
);
}
#[test]
fn category_lookup_wins_over_same_named_tag() {
let provider = test_provider();
let lookup = provider.resolve_filter_lookup();
assert_eq!(
lookup.get("uncensored jav").map(String::as_str),
Some("https://supjav.com/category/uncensored-jav")
);
assert_eq!(
lookup.get("uncensored").map(String::as_str),
Some("https://supjav.com/category/uncensored-jav")
);
}
#[test]
fn item_query_matching_uses_full_query_text() {
let mut item = VideoItem::new(
"abc".to_string(),
"Sample".to_string(),
"https://supjav.com/sample".to_string(),
CHANNEL_ID.to_string(),
String::new(),
0,
);
item.tags = Some(vec!["Uncensored".to_string()]);
assert!(SupjavProvider::item_matches_query(&item, "Uncensored"));
assert!(!SupjavProvider::item_matches_query(&item, "Uncensored JAV"));
}
fn test_db_pool() -> DbPool {
let unique = SystemTime::now()
.duration_since(UNIX_EPOCH)