shooshtime
This commit is contained in:
@@ -35,6 +35,7 @@ pub mod paradisehill;
|
|||||||
pub mod porn00;
|
pub mod porn00;
|
||||||
pub mod porn4fans;
|
pub mod porn4fans;
|
||||||
pub mod pornzog;
|
pub mod pornzog;
|
||||||
|
pub mod shooshtime;
|
||||||
pub mod sxyprn;
|
pub mod sxyprn;
|
||||||
pub mod tnaflix;
|
pub mod tnaflix;
|
||||||
pub mod tokyomotion;
|
pub mod tokyomotion;
|
||||||
@@ -133,6 +134,10 @@ pub static ALL_PROVIDERS: Lazy<HashMap<&'static str, DynProvider>> = Lazy::new(|
|
|||||||
"porn4fans",
|
"porn4fans",
|
||||||
Arc::new(porn4fans::Porn4fansProvider::new()) as DynProvider,
|
Arc::new(porn4fans::Porn4fansProvider::new()) as DynProvider,
|
||||||
);
|
);
|
||||||
|
m.insert(
|
||||||
|
"shooshtime",
|
||||||
|
Arc::new(shooshtime::ShooshtimeProvider::new()) as DynProvider,
|
||||||
|
);
|
||||||
m.insert(
|
m.insert(
|
||||||
"pornzog",
|
"pornzog",
|
||||||
Arc::new(pornzog::PornzogProvider::new()) as DynProvider,
|
Arc::new(pornzog::PornzogProvider::new()) as DynProvider,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use error_chain::error_chain;
|
|||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use htmlentity::entity::{ICodedDataTrait, decode};
|
use htmlentity::entity::{ICodedDataTrait, decode};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use scraper::{Html, Selector};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
error_chain! {
|
error_chain! {
|
||||||
@@ -266,6 +267,36 @@ impl Porn4fansProvider {
|
|||||||
text.replace("\\/", "/").replace("&", "&")
|
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> {
|
fn extract_views(text: &str) -> Option<u32> {
|
||||||
Regex::new(r"(?i)<svg[^>]+icon-eye[^>]*>.*?</svg>\s*<span>([^<]+)</span>")
|
Regex::new(r"(?i)<svg[^>]+icon-eye[^>]*>.*?</svg>\s*<span>([^<]+)</span>")
|
||||||
.ok()
|
.ok()
|
||||||
@@ -303,6 +334,34 @@ impl Porn4fansProvider {
|
|||||||
None
|
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> {
|
fn parse_video_cards_from_html(&self, html: &str) -> Vec<Porn4fansCard> {
|
||||||
if html.trim().is_empty() {
|
if html.trim().is_empty() {
|
||||||
return vec![];
|
return vec![];
|
||||||
@@ -375,9 +434,17 @@ impl Porn4fansProvider {
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.ok()
|
.ok();
|
||||||
.and_then(|text| Self::extract_direct_video_url_from_page(&text))
|
|
||||||
.unwrap_or_else(|| card.page_url.clone());
|
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(
|
let mut item = VideoItem::new(
|
||||||
card.id,
|
card.id,
|
||||||
@@ -393,6 +460,10 @@ impl Porn4fansProvider {
|
|||||||
if let Some(rating) = card.rating {
|
if let Some(rating) = card.rating {
|
||||||
item = item.rating(rating);
|
item = item.rating(rating);
|
||||||
}
|
}
|
||||||
|
if let Some(model) = models.first() {
|
||||||
|
item = item.uploader(model.clone());
|
||||||
|
}
|
||||||
|
item = item.tags(categories);
|
||||||
item
|
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 {
|
fn normalize_image_url(endpoint: &str) -> String {
|
||||||
let endpoint = endpoint.trim_start_matches('/');
|
let endpoint = endpoint.trim_start_matches('/');
|
||||||
println!("Normalizing image URL: {endpoint}");
|
|
||||||
if endpoint.starts_with("http://") || endpoint.starts_with("https://") {
|
if endpoint.starts_with("http://") || endpoint.starts_with("https://") {
|
||||||
endpoint.to_string()
|
endpoint.to_string()
|
||||||
} else if endpoint.starts_with("hanime-cdn.com/") || endpoint == "hanime-cdn.com" {
|
} else if endpoint.starts_with("hanime-cdn.com/") || endpoint == "hanime-cdn.com" {
|
||||||
|
|||||||
Reference in New Issue
Block a user