From 49ca76ab486686d2397b6f063c55dfe24cdb1821 Mon Sep 17 00:00:00 2001 From: Simon Date: Sat, 16 Aug 2025 18:46:10 +0000 Subject: [PATCH] updates --- src/api.rs | 4 +-- src/providers/hentaihaven.rs | 60 ++++++++++++++++++++++++------------ src/videos.rs | 44 ++++++++++++++++++++++++-- 3 files changed, 85 insertions(+), 23 deletions(-) diff --git a/src/api.rs b/src/api.rs index fae8b70..024f4e6 100644 --- a/src/api.rs +++ b/src/api.rs @@ -648,10 +648,10 @@ async fn status(req: HttpRequest) -> Result { nsfw: true, }); - status.add_channel(Channel { + status.add_channel(Channel { id: "hentaihaven".to_string(), name: "Hentaihaven".to_string(), - description: "Watch free hentai video stream in English subtitles".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(), diff --git a/src/providers/hentaihaven.rs b/src/providers/hentaihaven.rs index 3edf5e4..79195a5 100644 --- a/src/providers/hentaihaven.rs +++ b/src/providers/hentaihaven.rs @@ -1,3 +1,4 @@ +use crate::schema::videos::url; use crate::util::parse_abbreviated_number; use crate::DbPool; use crate::providers::Provider; @@ -192,10 +193,10 @@ impl HentaihavenProvider { .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 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] @@ -206,12 +207,24 @@ impl HentaihavenProvider { .split("class=\"btn-link\">").collect::>()[1] .split("<").collect::>()[0] .split(" ").collect::>()[1] == "Season" ; - - let url_part = video_segment.split("chapter font-meta").collect::>()[1] + println!("{:?}",video_segment.split("chapter font-meta").collect::>()[1] .split("href=\"").collect::>()[1] .split("\"").collect::>()[0] - .split("/").collect::>()[4]; + .split("/").collect::>()[4]); + 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); @@ -220,19 +233,28 @@ impl HentaihavenProvider { .split("\"").collect::>()[0] .to_string(), i); let id = format!("{}-{}", url_part, i); - let mut thumb = format!("https://himg.nl/images/hh/{}-{}-eng/poster.jpg", url_part, i); - if season { - thumb = format!("https://himg.nl/images/hh/{}-season-eng/poster.jpg", url_part); - } + + 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] + .to_string(), + false =>video_segment.split(">()[1] + .split("src=\"").collect::>()[1] + .split("\"").collect::>()[0] + .to_string() + }; items.push(VideoItem::new( - id, - title, - video_url, - "hentaihaven".to_string(), - thumb, - 0, // duration is not available - )) - ; + id, + title, + video_url, + "hentaihaven".to_string(), + thumb, + 0, // duration is not available + ) + .aspect_ratio(0.73) + ); } } return items; diff --git a/src/videos.rs b/src/videos.rs index f7faa78..8ceed03 100644 --- a/src/videos.rs +++ b/src/videos.rs @@ -60,21 +60,33 @@ impl VideoEmbed { #[derive(serde::Serialize, Debug, Clone)] pub struct VideoItem { pub duration: u32, // 110, + #[serde(skip_serializing_if = "Option::is_none")] pub views: Option, // 14622653, + #[serde(skip_serializing_if = "Option::is_none")] pub rating: Option, // 0.0, pub id: String, // "c85017ca87477168d648727753c4ded8a35f173e22ef93743e707b296becb299", pub title: String, // "20 Minutes of Adorable Kittens BEST Compilation", pub url: String, // "https://www.youtube.com/watch?v=y0sF5xhGreA", pub channel: String, // "youtube", pub thumb: String, // "https://i.ytimg.com/vi/y0sF5xhGreA/hqdefault.jpg", + #[serde(skip_serializing_if = "Option::is_none")] pub uploader: Option, // "The Pet Collective", + #[serde(skip_serializing_if = "Option::is_none")] pub uploaderUrl: Option, // "https://www.youtube.com/@petcollective", + #[serde(skip_serializing_if = "Option::is_none")] pub verified: Option, // false, + #[serde(skip_serializing_if = "Option::is_none")] pub tags: Option>, // [], + #[serde(skip_serializing_if = "Option::is_none")] pub uploadedAt: Option, // 1741142954 + #[serde(skip_serializing_if = "Option::is_none")] pub formats: Option>, // Additional HTTP headers if needed + #[serde(skip_serializing_if = "Option::is_none")] pub embed: Option, // Optional embed information - pub preview: Option + #[serde(skip_serializing_if = "Option::is_none")] + pub preview: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub aspectRation: Option } #[allow(dead_code)] impl VideoItem { @@ -102,7 +114,8 @@ impl VideoItem { uploadedAt: None, formats: None, // Placeholder for formats embed: None, // Placeholder for embed information - preview: None + preview: None, + aspectRation: None } } pub fn tags(mut self, tags: Vec) -> Self { @@ -152,6 +165,11 @@ impl VideoItem { self.preview = Some(preview); self } + + pub fn aspect_ratio(mut self, aspect_ratio: f32) -> Self { + self.aspectRation = Some(aspect_ratio); + self + } } #[derive(serde::Serialize, Debug, Clone)] @@ -159,27 +177,49 @@ pub struct VideoFormat { url: String, quality: String, format: String, + #[serde(skip_serializing_if = "Option::is_none")] format_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] format_note: Option, + #[serde(skip_serializing_if = "Option::is_none")] filesize: Option, + #[serde(skip_serializing_if = "Option::is_none")] asr: Option, + #[serde(skip_serializing_if = "Option::is_none")] fps: Option, + #[serde(skip_serializing_if = "Option::is_none")] width: Option, + #[serde(skip_serializing_if = "Option::is_none")] height: Option, + #[serde(skip_serializing_if = "Option::is_none")] tbr: Option, + #[serde(skip_serializing_if = "Option::is_none")] language: Option, + #[serde(skip_serializing_if = "Option::is_none")] language_preference: Option, + #[serde(skip_serializing_if = "Option::is_none")] ext: Option, + #[serde(skip_serializing_if = "Option::is_none")] vcodec: Option, + #[serde(skip_serializing_if = "Option::is_none")] acodec: Option, + #[serde(skip_serializing_if = "Option::is_none")] dynamic_range: Option, + #[serde(skip_serializing_if = "Option::is_none")] abr: Option, + #[serde(skip_serializing_if = "Option::is_none")] vbr: Option, + #[serde(skip_serializing_if = "Option::is_none")] container: Option, + #[serde(skip_serializing_if = "Option::is_none")] protocol: Option, + #[serde(skip_serializing_if = "Option::is_none")] audio_ext: Option, + #[serde(skip_serializing_if = "Option::is_none")] video_ext: Option, + #[serde(skip_serializing_if = "Option::is_none")] resolution: Option, + #[serde(skip_serializing_if = "Option::is_none")] http_headers: Option>, }