noodlemagazine thumb proxy
This commit is contained in:
@@ -10,6 +10,7 @@ use crate::videos::{ServerOptions, VideoFormat, VideoItem};
|
||||
use async_trait::async_trait;
|
||||
use error_chain::error_chain;
|
||||
use htmlentity::entity::{ICodedDataTrait, decode};
|
||||
use url::Url;
|
||||
use std::vec;
|
||||
use titlecase::Titlecase;
|
||||
use wreq::Version;
|
||||
@@ -86,8 +87,7 @@ impl NoodlemagazineProvider {
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
let proxy_base_url = options.public_url_base.as_deref().unwrap_or_default();
|
||||
let items = self.get_video_items_from_html(text, proxy_base_url);
|
||||
let items = self.get_video_items_from_html(text, &options);
|
||||
|
||||
if items.is_empty() {
|
||||
Ok(old_items)
|
||||
@@ -124,8 +124,7 @@ impl NoodlemagazineProvider {
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
let proxy_base_url = options.public_url_base.as_deref().unwrap_or_default();
|
||||
let items = self.get_video_items_from_html(text, proxy_base_url);
|
||||
let items = self.get_video_items_from_html(text, &options);
|
||||
|
||||
if items.is_empty() {
|
||||
Ok(old_items)
|
||||
@@ -136,7 +135,7 @@ impl NoodlemagazineProvider {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_video_items_from_html(&self, html: String, proxy_base_url: &str) -> Vec<VideoItem> {
|
||||
fn get_video_items_from_html(&self, html: String, options: &ServerOptions) -> Vec<VideoItem> {
|
||||
if html.is_empty() || html.contains("404 Not Found") {
|
||||
return vec![];
|
||||
}
|
||||
@@ -157,31 +156,76 @@ impl NoodlemagazineProvider {
|
||||
list.split("<div class=\"item\">")
|
||||
.skip(1)
|
||||
.filter_map(|segment| {
|
||||
self.get_video_item(segment.to_string(), proxy_base_url)
|
||||
.ok()
|
||||
self.get_video_item(segment.to_string(), options).ok()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn proxy_url(&self, proxy_base_url: &str, video_url: &str) -> String {
|
||||
let target = video_url
|
||||
.strip_prefix("https://")
|
||||
.or_else(|| video_url.strip_prefix("http://"))
|
||||
.unwrap_or(video_url)
|
||||
.trim_start_matches('/');
|
||||
|
||||
if proxy_base_url.is_empty() {
|
||||
return format!("/proxy/noodlemagazine/{target}");
|
||||
}
|
||||
|
||||
format!(
|
||||
"{}/proxy/noodlemagazine/{}",
|
||||
proxy_base_url.trim_end_matches('/'),
|
||||
target
|
||||
fn proxy_url(&self, options: &ServerOptions, video_url: &str) -> String {
|
||||
crate::providers::build_proxy_url(
|
||||
options,
|
||||
"noodlemagazine",
|
||||
&crate::providers::strip_url_scheme(video_url),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_video_item(&self, video_segment: String, proxy_base_url: &str) -> Result<VideoItem> {
|
||||
fn normalize_thumb_url(&self, thumb: &str) -> String {
|
||||
let thumb = thumb.trim();
|
||||
if thumb.is_empty() {
|
||||
return String::new();
|
||||
}
|
||||
|
||||
if thumb.starts_with("http://") || thumb.starts_with("https://") {
|
||||
return thumb.to_string();
|
||||
}
|
||||
|
||||
if thumb.starts_with("//") {
|
||||
return format!("https:{thumb}");
|
||||
}
|
||||
|
||||
if thumb.starts_with('/') {
|
||||
return format!("{}{}", self.url, thumb);
|
||||
}
|
||||
|
||||
format!("{}/{}", self.url.trim_end_matches('/'), thumb.trim_start_matches('/'))
|
||||
}
|
||||
|
||||
fn has_allowed_image_extension(path: &str) -> bool {
|
||||
let path = path.to_ascii_lowercase();
|
||||
[".jpg", ".jpeg", ".png", ".webp", ".avif", ".gif"]
|
||||
.iter()
|
||||
.any(|ext| path.ends_with(ext))
|
||||
}
|
||||
|
||||
fn is_allowed_thumb_url(&self, url: &str) -> bool {
|
||||
let Some(url) = Url::parse(url).ok() else {
|
||||
return false;
|
||||
};
|
||||
if url.scheme() != "https" {
|
||||
return false;
|
||||
}
|
||||
let Some(host) = url.host_str() else {
|
||||
return false;
|
||||
};
|
||||
let is_noodlemagazine_host = host == "noodlemagazine.com" || host.ends_with(".noodlemagazine.com");
|
||||
|
||||
is_noodlemagazine_host && Self::has_allowed_image_extension(url.path())
|
||||
}
|
||||
|
||||
fn proxied_thumb(&self, options: &ServerOptions, thumb: &str) -> String {
|
||||
let normalized = self.normalize_thumb_url(thumb);
|
||||
if normalized.is_empty() || !self.is_allowed_thumb_url(&normalized) {
|
||||
return String::new();
|
||||
}
|
||||
|
||||
crate::providers::build_proxy_url(
|
||||
options,
|
||||
"noodlemagazine-thumb",
|
||||
&crate::providers::strip_url_scheme(&normalized),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_video_item(&self, video_segment: String, options: &ServerOptions) -> Result<VideoItem> {
|
||||
let href = video_segment
|
||||
.split("<a href=\"")
|
||||
.nth(1)
|
||||
@@ -231,14 +275,15 @@ impl NoodlemagazineProvider {
|
||||
.and_then(|s| s.split('<').next())
|
||||
.and_then(|v| parse_abbreviated_number(v.trim()))
|
||||
.unwrap_or(0);
|
||||
let proxy_url = self.proxy_url(proxy_base_url, &video_url);
|
||||
let proxy_url = self.proxy_url(options, &video_url);
|
||||
let proxied_thumb = self.proxied_thumb(options, &thumb);
|
||||
|
||||
Ok(VideoItem::new(
|
||||
id,
|
||||
title,
|
||||
proxy_url.clone(),
|
||||
"noodlemagazine".into(),
|
||||
thumb,
|
||||
proxied_thumb,
|
||||
duration,
|
||||
)
|
||||
.views(views)
|
||||
@@ -287,16 +332,33 @@ impl Provider for NoodlemagazineProvider {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::NoodlemagazineProvider;
|
||||
use crate::videos::ServerOptions;
|
||||
|
||||
fn options() -> ServerOptions {
|
||||
ServerOptions {
|
||||
featured: None,
|
||||
category: None,
|
||||
sites: None,
|
||||
filter: None,
|
||||
language: None,
|
||||
public_url_base: Some("https://example.com".to_string()),
|
||||
requester: None,
|
||||
network: None,
|
||||
stars: None,
|
||||
categories: None,
|
||||
duration: None,
|
||||
sort: None,
|
||||
sexuality: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rewrites_video_pages_to_hottub_proxy() {
|
||||
let provider = NoodlemagazineProvider::new();
|
||||
let options = options();
|
||||
|
||||
assert_eq!(
|
||||
provider.proxy_url(
|
||||
"https://example.com",
|
||||
"https://noodlemagazine.com/watch/-123_456"
|
||||
),
|
||||
provider.proxy_url(&options, "https://noodlemagazine.com/watch/-123_456"),
|
||||
"https://example.com/proxy/noodlemagazine/noodlemagazine.com/watch/-123_456"
|
||||
);
|
||||
}
|
||||
@@ -304,11 +366,12 @@ mod tests {
|
||||
#[test]
|
||||
fn parses_listing_without_detail_page_requests() {
|
||||
let provider = NoodlemagazineProvider::new();
|
||||
let options = options();
|
||||
let html = r#"
|
||||
<div class="list_videos" id="list_videos">
|
||||
<div class="item">
|
||||
<a href="/watch/-123_456">
|
||||
<img data-src="https://thumb.example/test.jpg" />
|
||||
<img data-src="https://noodlemagazine.com/thumbs/test.jpg" />
|
||||
</a>
|
||||
<div class="title">sample & title</div>
|
||||
<svg><use></use></svg>#clock-o"></use></svg>12:34<
|
||||
@@ -317,13 +380,48 @@ mod tests {
|
||||
>Show more</div>
|
||||
"#;
|
||||
|
||||
let items = provider.get_video_items_from_html(html.to_string(), "https://example.com");
|
||||
let items = provider.get_video_items_from_html(html.to_string(), &options);
|
||||
|
||||
assert_eq!(items.len(), 1);
|
||||
assert_eq!(
|
||||
items[0].url,
|
||||
"https://example.com/proxy/noodlemagazine/noodlemagazine.com/watch/-123_456"
|
||||
);
|
||||
assert_eq!(
|
||||
items[0].thumb,
|
||||
"https://example.com/proxy/noodlemagazine-thumb/noodlemagazine.com/thumbs/test.jpg"
|
||||
);
|
||||
assert_eq!(items[0].formats.as_ref().map(|f| f.len()), Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drops_non_noodlemagazine_or_non_image_thumbs() {
|
||||
let provider = NoodlemagazineProvider::new();
|
||||
let options = options();
|
||||
let html = r#"
|
||||
<div class="list_videos" id="list_videos">
|
||||
<div class="item">
|
||||
<a href="/watch/-123_456">
|
||||
<img data-src="https://cdn.example/thumb.jpg" />
|
||||
</a>
|
||||
<div class="title">sample</div>
|
||||
<svg><use></use></svg>#clock-o"></use></svg>12:34<
|
||||
<svg><use></use></svg>#eye"></use></svg>1.2K<
|
||||
</div>
|
||||
<div class="item">
|
||||
<a href="/watch/-555_666">
|
||||
<img data-src="https://noodlemagazine.com/watch/not-an-image" />
|
||||
</a>
|
||||
<div class="title">sample 2</div>
|
||||
<svg><use></use></svg>#clock-o"></use></svg>00:42<
|
||||
<svg><use></use></svg>#eye"></use></svg>123<
|
||||
</div>
|
||||
>Show more</div>
|
||||
"#;
|
||||
|
||||
let items = provider.get_video_items_from_html(html.to_string(), &options);
|
||||
|
||||
assert_eq!(items.len(), 2);
|
||||
assert!(items.iter().all(|item| item.thumb.is_empty()));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user