shooshtime
This commit is contained in:
@@ -35,6 +35,7 @@ pub mod paradisehill;
|
||||
pub mod porn00;
|
||||
pub mod porn4fans;
|
||||
pub mod pornzog;
|
||||
pub mod shooshtime;
|
||||
pub mod sxyprn;
|
||||
pub mod tnaflix;
|
||||
pub mod tokyomotion;
|
||||
@@ -133,6 +134,10 @@ pub static ALL_PROVIDERS: Lazy<HashMap<&'static str, DynProvider>> = Lazy::new(|
|
||||
"porn4fans",
|
||||
Arc::new(porn4fans::Porn4fansProvider::new()) as DynProvider,
|
||||
);
|
||||
m.insert(
|
||||
"shooshtime",
|
||||
Arc::new(shooshtime::ShooshtimeProvider::new()) as DynProvider,
|
||||
);
|
||||
m.insert(
|
||||
"pornzog",
|
||||
Arc::new(pornzog::PornzogProvider::new()) as DynProvider,
|
||||
|
||||
@@ -11,6 +11,7 @@ use error_chain::error_chain;
|
||||
use futures::future::join_all;
|
||||
use htmlentity::entity::{ICodedDataTrait, decode};
|
||||
use regex::Regex;
|
||||
use scraper::{Html, Selector};
|
||||
use std::collections::HashSet;
|
||||
|
||||
error_chain! {
|
||||
@@ -266,6 +267,36 @@ impl Porn4fansProvider {
|
||||
text.replace("\\/", "/").replace("&", "&")
|
||||
}
|
||||
|
||||
fn decode_html_text(text: &str) -> String {
|
||||
decode(text.as_bytes())
|
||||
.to_string()
|
||||
.unwrap_or_else(|_| text.to_string())
|
||||
.split_whitespace()
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
.trim()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn strip_tags(text: &str) -> String {
|
||||
Regex::new(r"(?is)<[^>]+>")
|
||||
.ok()
|
||||
.map(|regex| regex.replace_all(text, "").to_string())
|
||||
.unwrap_or_else(|| text.to_string())
|
||||
}
|
||||
|
||||
fn push_unique_tag(values: &mut Vec<String>, value: String) {
|
||||
let value = value.trim().to_string();
|
||||
if value.is_empty()
|
||||
|| values
|
||||
.iter()
|
||||
.any(|existing| existing.eq_ignore_ascii_case(&value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
values.push(value);
|
||||
}
|
||||
|
||||
fn extract_views(text: &str) -> Option<u32> {
|
||||
Regex::new(r"(?i)<svg[^>]+icon-eye[^>]*>.*?</svg>\s*<span>([^<]+)</span>")
|
||||
.ok()
|
||||
@@ -303,6 +334,34 @@ impl Porn4fansProvider {
|
||||
None
|
||||
}
|
||||
|
||||
fn collect_texts(document: &Html, selector: &str) -> Vec<String> {
|
||||
let Ok(selector) = Selector::parse(selector) else {
|
||||
return vec![];
|
||||
};
|
||||
let mut values = Vec::new();
|
||||
for element in document.select(&selector) {
|
||||
let raw_text = element.text().collect::<Vec<_>>().join(" ");
|
||||
let cleaned = Self::decode_html_text(&Self::strip_tags(&raw_text));
|
||||
Self::push_unique_tag(&mut values, cleaned);
|
||||
}
|
||||
|
||||
values
|
||||
}
|
||||
|
||||
fn extract_page_models_and_categories(text: &str) -> (Vec<String>, Vec<String>) {
|
||||
let document = Html::parse_document(text);
|
||||
|
||||
let models = Self::collect_texts(&document, ".player-models-list a[href*=\"/models/\"]");
|
||||
|
||||
let mut categories =
|
||||
Self::collect_texts(&document, ".categories-row a[href*=\"/categories/\"]");
|
||||
for value in Self::collect_texts(&document, ".tags-row a[href*=\"/tags/\"]") {
|
||||
Self::push_unique_tag(&mut categories, value);
|
||||
}
|
||||
|
||||
(models, categories)
|
||||
}
|
||||
|
||||
fn parse_video_cards_from_html(&self, html: &str) -> Vec<Porn4fansCard> {
|
||||
if html.trim().is_empty() {
|
||||
return vec![];
|
||||
@@ -375,9 +434,17 @@ impl Porn4fansProvider {
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|text| Self::extract_direct_video_url_from_page(&text))
|
||||
.ok();
|
||||
|
||||
let (direct_url, models, categories) = match direct_url {
|
||||
Some(text) => {
|
||||
let url = Self::extract_direct_video_url_from_page(&text)
|
||||
.unwrap_or_else(|| card.page_url.clone());
|
||||
let (models, categories) = Self::extract_page_models_and_categories(&text);
|
||||
(url, models, categories)
|
||||
}
|
||||
None => (card.page_url.clone(), vec![], vec![]),
|
||||
};
|
||||
|
||||
let mut item = VideoItem::new(
|
||||
card.id,
|
||||
@@ -393,6 +460,10 @@ impl Porn4fansProvider {
|
||||
if let Some(rating) = card.rating {
|
||||
item = item.rating(rating);
|
||||
}
|
||||
if let Some(model) = models.first() {
|
||||
item = item.uploader(model.clone());
|
||||
}
|
||||
item = item.tags(categories);
|
||||
item
|
||||
}
|
||||
|
||||
@@ -541,4 +612,33 @@ mod tests {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_models_and_categories_from_video_page() {
|
||||
let html = r#"
|
||||
<div class="player-models-list">
|
||||
<div class="player-model-item">
|
||||
<a href="/models/piper-rockelle/"><span class="player-model-name">Piper Rockelle</span></a>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="categories-row">
|
||||
<li class="visible"><a href="/categories/striptease/">Striptease</a></li>
|
||||
<li class="visible"><a href="/categories/teen/">Teen</a></li>
|
||||
</ul>
|
||||
<ul class="tags-row">
|
||||
<li class="visible"><a href="/tags/bathroom/">Bathroom</a></li>
|
||||
</ul>
|
||||
"#;
|
||||
|
||||
let (models, categories) = Porn4fansProvider::extract_page_models_and_categories(html);
|
||||
assert_eq!(models, vec!["Piper Rockelle".to_string()]);
|
||||
assert_eq!(
|
||||
categories,
|
||||
vec![
|
||||
"Striptease".to_string(),
|
||||
"Teen".to_string(),
|
||||
"Bathroom".to_string()
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
1310
src/providers/shooshtime.rs
Normal file
1310
src/providers/shooshtime.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,6 @@ use crate::util::requester::Requester;
|
||||
|
||||
fn normalize_image_url(endpoint: &str) -> String {
|
||||
let endpoint = endpoint.trim_start_matches('/');
|
||||
println!("Normalizing image URL: {endpoint}");
|
||||
if endpoint.starts_with("http://") || endpoint.starts_with("https://") {
|
||||
endpoint.to_string()
|
||||
} else if endpoint.starts_with("hanime-cdn.com/") || endpoint == "hanime-cdn.com" {
|
||||
|
||||
Reference in New Issue
Block a user