This commit is contained in:
Simon
2026-04-05 20:31:38 +00:00
parent 9773590f64
commit 7b464fe796
10 changed files with 815 additions and 565 deletions

View File

@@ -14,6 +14,8 @@ use diesel::r2d2;
use error_chain::error_chain;
use futures::future::join_all;
use htmlentity::entity::{ICodedDataTrait, decode};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::vec;
use wreq::Version;
@@ -41,15 +43,58 @@ error_chain! {
#[derive(Debug, Clone)]
pub struct MissavProvider {
url: String,
tag_map: Arc<RwLock<HashMap<String, String>>>,
}
impl MissavProvider {
pub fn new() -> Self {
MissavProvider {
url: "https://missav.ws".to_string(),
tag_map: Arc::new(RwLock::new(HashMap::new())),
}
}
fn normalize_key(value: &str) -> String {
value
.trim()
.to_ascii_lowercase()
.replace(['_', '-'], " ")
.split_whitespace()
.collect::<Vec<_>>()
.join(" ")
}
fn humanize_slug(value: &str) -> String {
value
.trim_matches('/')
.replace('-', " ")
.split_whitespace()
.collect::<Vec<_>>()
.join(" ")
}
fn insert_tag_mapping(&self, key: &str, path_or_url: &str) {
let normalized = Self::normalize_key(key);
if normalized.is_empty() || path_or_url.trim().is_empty() {
return;
}
if let Ok(mut map) = self.tag_map.write() {
map.insert(normalized, path_or_url.trim().to_string());
}
}
fn resolve_query_url(&self, query: &str, page: u8, sort: &str) -> Option<String> {
let normalized = Self::normalize_key(query);
let mapped = self.tag_map.read().ok()?.get(&normalized)?.clone();
let separator = if mapped.contains('?') { "&" } else { "?" };
let mut url = format!("{mapped}{separator}page={page}");
if !sort.is_empty() {
url.push_str("&sort=");
url.push_str(sort);
}
Some(url)
}
fn build_channel(&self, _clientversion: ClientVersion) -> Channel {
Channel {
id: "missav".to_string(),
@@ -248,10 +293,13 @@ impl MissavProvider {
if !sort.is_empty() {
sort = format!("&sort={}", sort);
}
let url_str = format!(
let mut url_str = format!(
"{}/{}/search/{}?page={}{}",
self.url, language, search_string, page, sort
);
if let Some(mapped_url) = self.resolve_query_url(query, page, &sort.replace("&sort=", "")) {
url_str = mapped_url;
}
if let Some((time, items)) = cache.get(&url_str) {
if time.elapsed().unwrap_or_default().as_secs() < 3600 {
@@ -386,19 +434,54 @@ impl MissavProvider {
// 3. Extract Tags (Generic approach to avoid repetitive code)
let mut tags = vec![];
for (label, prefix) in [
("Actress:", "@actress"),
("Actor:", "@actor"),
("Maker:", "@maker"),
("Genre:", "@genre"),
for (label, route_kind) in [
("Actress:", "actress"),
("Actor:", "actor"),
("Maker:", "maker"),
("Genre:", "genre"),
] {
let marker = format!("<span>{}</span>", label);
if let Some(section) = extract(&vid, &marker, "</div>") {
for part in section.split("class=\"text-nord13 font-medium\">").skip(1) {
if let Some(val) = part.split('<').next() {
let clean = val.trim();
if !clean.is_empty() {
tags.push(format!("{}:{}", prefix, clean));
for anchor in section.split("<a ").skip(1) {
let href = anchor
.split("href=\"")
.nth(1)
.and_then(|value| value.split('"').next())
.unwrap_or_default()
.to_string();
let title = anchor
.split("class=\"text-nord13 font-medium\">")
.nth(1)
.and_then(|value| value.split('<').next())
.map(str::trim)
.unwrap_or_default()
.to_string();
if !title.is_empty() {
tags.push(title.clone());
if !href.is_empty() {
let full_url = if href.starts_with("http://") || href.starts_with("https://") {
href.clone()
} else {
format!("{}{}", self.url, href)
};
self.insert_tag_mapping(&title, &full_url);
let slug = href
.trim_matches('/')
.rsplit('/')
.next()
.unwrap_or_default()
.to_string();
if !slug.is_empty() {
self.insert_tag_mapping(&slug, &full_url);
self.insert_tag_mapping(
&format!("{route_kind}:{}", slug),
&full_url,
);
self.insert_tag_mapping(
&format!("{route_kind}:{}", Self::humanize_slug(&slug)),
&full_url,
);
}
}
}
}