From 0f2983ca1581b73e2a4ea796bf0b8d3a5cb64423 Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 18 Aug 2025 10:03:29 +0000 Subject: [PATCH] delete hentaihavem (not possible) + bugfix on rule34video --- src/api.rs | 77 +++++---- src/providers/hentaihaven.rs | 298 ----------------------------------- src/providers/mod.rs | 6 - src/providers/rule34video.rs | 2 +- 4 files changed, 37 insertions(+), 346 deletions(-) delete mode 100644 src/providers/hentaihaven.rs diff --git a/src/api.rs b/src/api.rs index 024f4e6..3cbda6e 100644 --- a/src/api.rs +++ b/src/api.rs @@ -648,46 +648,42 @@ async fn status(req: HttpRequest) -> Result { nsfw: true, }); - status.add_channel(Channel { - id: "hentaihaven".to_string(), - name: "Hentaihaven".to_string(), - description: "(Work in Progress) Watch free hentai video stream in English subtitles".to_string(), - premium: false, - favicon: "https://www.google.com/s2/favicons?sz=64&domain=hentaihaven.xxx".to_string(), - status: "active".to_string(), - categories: vec![], - options: vec![ChannelOption { - id: "sort".to_string(), - title: "Sort".to_string(), - description: "Sort the Videos".to_string(), //"Sort the videos by Date or Name.".to_string(), - systemImage: "list.number".to_string(), - colorName: "blue".to_string(), - options: vec![ - FilterOption { - id: "latest".to_string(), - title: "Latest".to_string(), - }, - FilterOption { - id: "alphabet".to_string(), - title: "A-Z".to_string(), - }, - FilterOption { - id: "rating".to_string(), - title: "Rating".to_string(), - }, - FilterOption { - id: "views".to_string(), - title: "Most Views".to_string(), - }, - FilterOption { - id: "new-manga".to_string(), - title: "New".to_string(), - }, - ], - multiSelect: false, - }], - nsfw: true, - }); + // status.add_channel(Channel { + // id: "hentaihaven".to_string(), + // name: "Hentaihaven".to_string(), + // description: "(Work in Progress) Watch free hentai video stream in English subtitles".to_string(), + // premium: false, + // favicon: "https://www.google.com/s2/favicons?sz=64&domain=hentaihaven.xxx".to_string(), + // status: "active".to_string(), + // categories: vec![], + // options: vec![ChannelOption { + // id: "sort".to_string(), + // title: "Sort".to_string(), + // description: "Sort the Videos".to_string(), //"Sort the videos by Date or Name.".to_string(), + // systemImage: "list.number".to_string(), + // colorName: "blue".to_string(), + // options: vec![ + // FilterOption { + // id: "new-manga".to_string(), + // title: "New".to_string(), + // }, + // FilterOption { + // id: "alphabet".to_string(), + // title: "A-Z".to_string(), + // }, + // FilterOption { + // id: "rating".to_string(), + // title: "Rating".to_string(), + // }, + // FilterOption { + // id: "views".to_string(), + // title: "Most Views".to_string(), + // }, + // ], + // multiSelect: false, + // }], + // nsfw: true, + // }); status.iconUrl = format!("http://{}/favicon.ico", host).to_string(); Ok(web::HttpResponse::Ok().json(&status)) @@ -810,7 +806,6 @@ pub fn get_provider(channel: &str) -> Option { "perfectgirls" => Some(AnyProvider::Perfectgirls(crate::providers::perfectgirls::PerfectgirlsProvider::new())), "okxxx" => Some(AnyProvider::Okxxx(crate::providers::okxxx::OkxxxProvider::new())), "homoxxx" => Some(AnyProvider::Homoxxx(crate::providers::homoxxx::HomoxxxProvider::new())), - "hentaihaven" => Some(AnyProvider::Hentaihaven(crate::providers::hentaihaven::HentaihavenProvider::new())), _ => Some(AnyProvider::Perverzija(PerverzijaProvider::new())), } } diff --git a/src/providers/hentaihaven.rs b/src/providers/hentaihaven.rs deleted file mode 100644 index b72ce45..0000000 --- a/src/providers/hentaihaven.rs +++ /dev/null @@ -1,298 +0,0 @@ -use crate::schema::videos::url; -use crate::util::parse_abbreviated_number; -use crate::{videos, DbPool}; -use crate::providers::Provider; -use crate::util::cache::VideoCache; -use crate::util::flaresolverr::{FlareSolverrRequest, Flaresolverr}; -use crate::util::time::parse_time_to_seconds; -use crate::videos::{ServerOptions, VideoItem}; -use error_chain::error_chain; -use htmlentity::entity::{ICodedDataTrait, decode}; -use std::env; -use std::vec; -use wreq::{Client, Proxy}; -use wreq_util::Emulation; - -error_chain! { - foreign_links { - Io(std::io::Error); - HttpRequest(wreq::Error); - } -} - -#[derive(Debug, Clone)] -pub struct HentaihavenProvider { - url: String, -} -impl HentaihavenProvider { - pub fn new() -> Self { - HentaihavenProvider { - url: "https://hentaihaven.xxx".to_string(), - } - } - async fn get( - &self, - cache: VideoCache, - page: u8, - sort: &str, - ) -> Result> { - let video_url = format!("{}/page/{}/?m_orderby={}", self.url, page, sort); - let old_items = match cache.get(&video_url) { - Some((time, items)) => { - if time.elapsed().unwrap_or_default().as_secs() < 60 * 5 { - println!("Cache hit for URL: {}", video_url); - return Ok(items.clone()); - } else { - items.clone() - } - } - None => { - vec![] - } - }; - - let proxy = Proxy::all("http://192.168.0.103:8081").unwrap(); - let client = Client::builder().cert_verification(false).emulation(Emulation::Firefox136).build()?; - - let mut response = client.get(video_url.clone()) - .proxy(proxy.clone()) - .send().await?; - if response.status().is_redirection(){ - println!("Redirection detected, following to: {}", response.headers()["Location"].to_str().unwrap()); - response = client.get(response.headers()["Location"].to_str().unwrap()) - // .proxy(proxy) - .send().await?; - } - if response.status().is_success() { - let text = response.text().await?; - let video_items: Vec = self.get_video_items_from_html(text.clone()); - if !video_items.is_empty() { - cache.remove(&video_url); - cache.insert(video_url.clone(), video_items.clone()); - } else { - return Ok(old_items); - } - Ok(video_items) - } else { - let flare_url = env::var("FLARE_URL").expect("FLARE_URL not set"); - let flare = Flaresolverr::new(flare_url); - let result = flare - .solve(FlareSolverrRequest { - cmd: "request.get".to_string(), - url: video_url.clone(), - maxTimeout: 60000, - }) - .await; - let video_items = match result { - Ok(res) => { - // println!("FlareSolverr response: {}", res); - self.get_video_items_from_html(res.solution.response) - } - Err(e) => { - println!("Error solving FlareSolverr: {}", e); - return Err("Failed to solve FlareSolverr".into()); - } - }; - if !video_items.is_empty() { - cache.remove(&video_url); - cache.insert(video_url.clone(), video_items.clone()); - } else { - return Ok(old_items); - } - Ok(video_items) - } - } - async fn query( - &self, - cache: VideoCache, - page: u8, - query: &str, - ) -> Result> { - let search_string = query.to_lowercase().trim().replace(" ", "-"); - let mut video_url = format!("{}/search/{}/{}/", self.url, search_string, page); - - if search_string.starts_with("@"){ - let url_part = search_string.split("@").collect::>()[1].replace(":", "/"); - video_url = format!("{}/{}/", self.url, url_part); - } - // Check our Video Cache. If the result is younger than 1 hour, we return it. - let old_items = match cache.get(&video_url) { - Some((time, items)) => { - if time.elapsed().unwrap_or_default().as_secs() < 60 * 5 { - return Ok(items.clone()); - } else { - let _ = cache.check().await; - return Ok(items.clone()); - } - } - None => { - vec![] - } - }; - - let proxy = Proxy::all("http://192.168.0.103:8081").unwrap(); - let client = Client::builder().cert_verification(false).emulation(Emulation::Firefox136).build()?; - - let mut response = client.get(video_url.clone()) - .proxy(proxy.clone()) - .send().await?; - - if response.status().is_redirection(){ - - response = client.get(self.url.clone() + response.headers()["Location"].to_str().unwrap()) - // .proxy(proxy) - .send().await?; - } - - if response.status().is_success() { - let text = response.text().await?; - let video_items: Vec = self.get_video_items_from_html(text.clone()); - if !video_items.is_empty() { - cache.remove(&video_url); - cache.insert(video_url.clone(), video_items.clone()); - } else { - return Ok(old_items); - } - Ok(video_items) - } else { - let flare_url = env::var("FLARE_URL").expect("FLARE_URL not set"); - let flare = Flaresolverr::new(flare_url); - let result = flare - .solve(FlareSolverrRequest { - cmd: "request.get".to_string(), - url: video_url.clone(), - maxTimeout: 60000, - }) - .await; - let video_items = match result { - Ok(res) => self.get_video_items_from_html(res.solution.response), - Err(e) => { - println!("Error solving FlareSolverr: {}", e); - return Err("Failed to solve FlareSolverr".into()); - } - }; - if !video_items.is_empty() { - cache.remove(&video_url); - cache.insert(video_url.clone(), video_items.clone()); - } else { - return Ok(old_items); - } - Ok(video_items) - } - } - - fn get_video_items_from_html(&self, html: String) -> Vec { - if html.is_empty() { - println!("HTML is empty"); - return vec![]; - } - let mut items: Vec = Vec::new(); - let raw_videos = html.split("\"wp-pagenavi\"").collect::>()[0] - .split("page-item-detail video") - .collect::>()[1..] - .to_vec() - ; - for video_segment in &raw_videos { - // let vid = video_segment.split("\n").collect::>(); - // for (index, line) in vid.iter().enumerate() { - // println!("Line {}: {}", index, line); - // } - - let episode_count = video_segment.split("chapter font-meta").collect::>()[1] - .split("class=\"btn-link\">").collect::>()[1] - .split("<").collect::>()[0] - .split(" ").collect::>()[2] - .to_string().parse::().unwrap(); - let season = video_segment.split("chapter font-meta").collect::>()[1] - .split("class=\"btn-link\">").collect::>()[1] - .split("<").collect::>()[0] - .split(" ").collect::>()[1] == "Season"; - let mut url_part_list = video_segment.split("chapter font-meta").collect::>()[1] - .split("href=\"").collect::>()[1] - .split("\"").collect::>()[0] - .split("/").collect::>()[4] - .split("-").collect::>(); - if url_part_list.len() > 5 { - if let Some(pos) = url_part_list.iter().rposition(|x| *x == "no") { - url_part_list.remove(pos); - } - } - url_part_list.truncate(5); - let url_part = url_part_list.join("-"); - for i in 1..=episode_count { - - let mut video_url = format!("https://master-lengs.org/api/v3/hh/{}-{}-eng/master.m3u8", url_part, i); - if season { - video_url = format!("https://master-lengs.org/api/v3/hh/{}-season-eng/master.m3u8", url_part); - } - let title = format!("{} - {}",video_segment.split("title=\"").collect::>()[1] - .split("\"").collect::>()[0] - .to_string(), i); - let id = format!("{}-{}", url_part, i); - - let thumb = match video_segment.split(">()[1] - .split("").collect::>()[0].contains("data-src=\"") { - true => video_segment.split(">()[1] - .split("data-src=\"").collect::>()[1] - .split("\"").collect::>()[0] - .replace(" ", "%20") - .to_string(), - false =>video_segment.split(">()[1] - .split("src=\"").collect::>()[1] - .split("\"").collect::>()[0] - .replace(" ", "%20") - .to_string() - }; - items.push(VideoItem::new( - id, - title, - video_url.clone(), - "hentaihaven".to_string(), - thumb, - 0, // duration is not available - ) - .formats(vec![videos::VideoFormat::new(video_url.clone(), "1080".to_string(), "m3u8".to_string())]) - .aspect_ratio(0.73) - ); - } - } - return items; - //return items; - } - - -} - -impl Provider for HentaihavenProvider { - async fn get_videos( - &self, - cache: VideoCache, - pool: DbPool, - sort: String, - query: Option, - page: String, - per_page: String, - options: ServerOptions, - ) -> Vec { - let _ = options; - let _ = per_page; - let _ = pool; - let videos: std::result::Result, Error> = match query { - Some(q) => { - self.query(cache, page.parse::().unwrap_or(1), &q,) - .await - } - None => { - self.get(cache, page.parse::().unwrap_or(1), &sort) - .await - } - }; - match videos { - Ok(v) => v, - Err(e) => { - println!("Error fetching videos: {}", e); - vec![] - } - } - } -} diff --git a/src/providers/mod.rs b/src/providers/mod.rs index 3d49339..8a8130a 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -17,7 +17,6 @@ pub mod pornhat; pub mod perfectgirls; pub mod okxxx; pub mod homoxxx; -pub mod hentaihaven; pub trait Provider { async fn get_videos( @@ -47,7 +46,6 @@ pub enum AnyProvider { Perfectgirls(crate::providers::perfectgirls::PerfectgirlsProvider), Okxxx(crate::providers::okxxx::OkxxxProvider), Homoxxx(crate::providers::homoxxx::HomoxxxProvider), - Hentaihaven(crate::providers::hentaihaven::HentaihavenProvider), } impl Provider for AnyProvider { @@ -126,10 +124,6 @@ impl Provider for AnyProvider { p.get_videos(cache, pool, sort, query, page, per_page, options,) .await } - AnyProvider::Hentaihaven(p) => { - p.get_videos(cache, pool, sort, query, page, per_page, options,) - .await - } } } } diff --git a/src/providers/rule34video.rs b/src/providers/rule34video.rs index 3f988af..65d5628 100644 --- a/src/providers/rule34video.rs +++ b/src/providers/rule34video.rs @@ -140,7 +140,7 @@ impl Rule34videoProvider { let index = format!("rule34video:{}:{}:{}", page, sort, query); - let url = format!("{}/search/{}/?mode=async&function=get_block&block_id=custom_list_videos_videos_list_search&tag_ids=&sort_by={}&from_videos={}&from_albums={}&_={}", self.url, query, sort, page, page, timestamp_millis); + let url = format!("{}/search/{}/?mode=async&function=get_block&block_id=custom_list_videos_videos_list_search&tag_ids=&sort_by={}&from_videos={}&from_albums={}&_={}", self.url, query.replace(" ","-"), sort, page, page, timestamp_millis); // Check our Video Cache. If the result is younger than 1 hour, we return it. let old_items = match cache.get(&index) {