diff --git a/src/providers/all.rs b/src/providers/all.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/providers/hanime.rs b/src/providers/hanime.rs new file mode 100644 index 0000000..0c3ef23 --- /dev/null +++ b/src/providers/hanime.rs @@ -0,0 +1,362 @@ +use std::vec; +use std::env; +use error_chain::error_chain; +use htmlentity::entity::{decode, ICodedDataTrait}; +use reqwest::{Proxy}; + +use crate::providers::Provider; +use crate::util::flaresolverr::{FlareSolverrRequest, Flaresolverr}; +use crate::util::time::parse_time_to_seconds; +use crate::videos::{self, Video_Embed, Video_Item}; // Make sure Provider trait is imported + +error_chain! { + foreign_links { + Io(std::io::Error); + HttpRequest(reqwest::Error); + } +} + +pub struct HanimeProvider { + url: String, +} +impl HanimeProvider { + pub fn new() -> Self { + HanimeProvider { + url: "https://hanime.tv/".to_string(), + } + } + async fn get(&self, page: &u8, featured: String) -> Result> { + let mut prefix_uri = "".to_string(); + if featured == "featured" { + prefix_uri = "featured-scenes/".to_string(); + } + let mut url = format!("{}{}page/{}/", self.url, prefix_uri, page); + if page == &1 { + url = format!("{}{}", self.url, prefix_uri); + } + + + let client = match env::var("BURP_URL").as_deref() { + Ok(burp_url) => + reqwest::Client::builder() + .user_agent("Mozilla/5.0 (iPhone; CPU iPhone OS 14_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/33.0 Mobile/15E148 Safari/605.1.15") + .proxy(Proxy::https(burp_url).unwrap()) + .danger_accept_invalid_certs(true) + .build()?, + Err(_) => reqwest::Client::builder() + .user_agent("Mozilla/5.0 (iPhone; CPU iPhone OS 14_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/33.0 Mobile/15E148 Safari/605.1.15") + .danger_accept_invalid_certs(true) + .build()?, + }; + + let response = client.get(url.clone()).send().await?; + // print!("Response: {:?}\n", response); + if response.status().is_success() { + let text = response.text().await?; + let video_items: Vec = self.get_video_items_from_html(text.clone()); + 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: url.clone(), + maxTimeout: 60000, + }) + .await; + println!("FlareSolverr result: {:?}", result); + 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()); + } + }; + Ok(video_items) + } + } + async fn query(&self, page: &u8, query: &str) -> Result> { + println!("query: {}", query); + let search_string = query.replace(" ", "+"); + let mut url = format!( + "{}advanced-search/?_sf_s={}&sf_paged={}", + self.url, search_string, page + ); + if page == &1 { + url = format!("{}advanced-search/?_sf_s={}", self.url, search_string); + } + + + let client = match env::var("BURP_URL").as_deref() { + Ok(burp_url) => + reqwest::Client::builder() + .user_agent("Mozilla/5.0 (iPhone; CPU iPhone OS 14_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/33.0 Mobile/15E148 Safari/605.1.15") + .proxy(Proxy::https(burp_url).unwrap()) + .danger_accept_invalid_certs(true) + .build()?, + Err(_) => reqwest::Client::builder() + .user_agent("Mozilla/5.0 (iPhone; CPU iPhone OS 14_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/33.0 Mobile/15E148 Safari/605.1.15") + .danger_accept_invalid_certs(true) + .build()?, + }; + + let response = client.get(url.clone()).send().await?; + // print!("Response: {:?}\n", response); + if response.status().is_success() { + let text = response.text().await?; + let video_items: Vec = self.get_video_items_from_html_query(text.clone()); + 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: url.clone(), + maxTimeout: 60000, + }) + .await; + println!("FlareSolverr result: {:?}", result); + let video_items = match result { + Ok(res) => { + // println!("FlareSolverr response: {}", res); + self.get_video_items_from_html_query(res.solution.response) + } + Err(e) => { + println!("Error solving FlareSolverr: {}", e); + return Err("Failed to solve FlareSolverr".into()); + } + }; + Ok(video_items) + } + } + + fn get_video_items_from_html(&self, html: String) -> Vec { + // println!("HTML: {}", html); + let mut items: Vec = Vec::new(); + let video_listing_content = html.split("video-listing-content").collect::>()[1]; + let raw_videos = video_listing_content + .split("video-item post") + .collect::>()[1..] + .to_vec(); + for video_segment in &raw_videos { + let vid = video_segment.split("\n").collect::>(); + if vid.len() > 20 { + println!("Skipping video segment with unexpected length: {}", vid.len()); + continue; + } + for (i, line) in vid.iter().enumerate() { + if line.trim().is_empty() { + println!("Empty line at index {}: {}", i, line); + } + } + let mut title = vid[1].split(">").collect::>()[1] + .split("<") + .collect::>()[0] + .to_string(); + // html decode + title = decode(title.as_bytes()).to_string().unwrap_or(title); + let url = vid[1].split("iframe src="").collect::>()[1] + .split(""") + .collect::>()[0] + .to_string(); + let id = url.split("data=").collect::>()[1] + .split("&") + .collect::>()[0] + .to_string(); + let raw_duration = match vid.len() { + 10 => vid[6].split("time_dur\">").collect::>()[1] + .split("<") + .collect::>()[0] + .to_string(), + _ => "00:00".to_string(), + }; + let duration = parse_time_to_seconds(&raw_duration).unwrap_or(0) as u32; + + let thumb = match vid[4].contains("srcset=") { + true => vid[4].split("sizes=").collect::>()[1] + .split("w, ") + .collect::>() + .last() + .unwrap() + .to_string() + .split(" ") + .collect::>()[0] + .to_string(), + false => vid[4].split("src=\"").collect::>()[1] + .split("\"") + .collect::>()[0] + .to_string(), + }; + let embed_html = vid[1].split("data-embed='").collect::>()[1] + .split("'") + .collect::>()[0] + .to_string(); + let referer_url = vid[1].split("data-url='").collect::>()[1] + .split("'") + .collect::>()[0] + .to_string(); + let embed = Video_Embed::new(embed_html, url.clone()); + + let mut tags: Vec = Vec::new(); // Placeholder for tags, adjust as needed + for tag in vid[0].split(" ").collect::>(){ + if tag.starts_with("tag-") { + let tag_name = tag.split("tag-").collect::>()[1] + .to_string(); + if !tag_name.is_empty() { + tags.push(tag_name.replace("-", " ").to_string()); + } + } + } + let mut video_item = Video_Item::new( + id, + title, + url.clone(), + "hanime".to_string(), + thumb, + duration, + ).tags(tags) + .embed(embed.clone()); + let mut format = + videos::Video_Format::new(url.clone(), "1080".to_string(), "m3u8".to_string()); + format.add_http_header("Referer".to_string(), referer_url.clone()); + if let Some(formats) = video_item.formats.as_mut() { + formats.push(format); + } else { + video_item.formats = Some(vec![format]); + } + items.push(video_item); + } + + return items; + } + + fn get_video_items_from_html_query(&self, html: String) -> Vec { + let mut items: Vec = Vec::new(); + let video_listing_content = html.split("search-filter-results-").collect::>()[1]; + let raw_videos = video_listing_content + .split("video-item post") + .collect::>()[1..] + .to_vec(); + for video_segment in &raw_videos { + let vid = video_segment.split("\n").collect::>(); + if vid.len() > 20 { + continue; + } + let mut title = vid[3].split("title='").collect::>()[1] + .split("'") + .collect::>()[0] + .to_string(); + title = decode(title.as_bytes()).to_string().unwrap_or(title); + let url = vid[4].split("iframe src="").collect::>()[1] + .split(""") + .collect::>()[0] + .to_string(); + let id = url.split("data=").collect::>()[1] + .split("&") + .collect::>()[0] + .to_string(); + let raw_duration = match vid.len() { + 18 => vid[16].split("time_dur\">").collect::>()[1] + .split("<") + .collect::>()[0] + .to_string(), + _ => "00:00".to_string(), + }; + let duration = parse_time_to_seconds(&raw_duration).unwrap_or(0) as u32; + let thumb_index = match vid.len() { + 18 => 14, + 13 => 8, + _ => { + continue; + } + }; + let thumb = match vid[thumb_index].contains("srcset=") { + true => vid[thumb_index].split("sizes=").collect::>()[1] + .split("w, ") + .collect::>() + .last() + .unwrap() + .to_string() + .split(" ") + .collect::>()[0] + .to_string(), + false => vid[thumb_index].split("src=\"").collect::>()[1] + .split("\"") + .collect::>()[0] + .to_string(), + }; + let embed_html = vid[4].split("data-embed='").collect::>()[1] + .split("'") + .collect::>()[0] + .to_string(); + let referer_url = vid[4].split("data-url='").collect::>()[1] + .split("'") + .collect::>()[0] + .to_string(); + let embed = Video_Embed::new(embed_html, url.clone()); + let mut tags: Vec = Vec::new(); // Placeholder for tags, adjust as needed + for tag in vid[0].split(" ").collect::>(){ + if tag.starts_with("tag-") { + let tag_name = tag.split("tag-").collect::>()[1] + .to_string(); + if !tag_name.is_empty() { + tags.push(tag_name.replace("-", " ").to_string()); + } + } + } + + let mut video_item = Video_Item::new( + id, + title, + url.clone(), + "hanime".to_string(), + thumb, + duration, + ) + .tags(tags) + .embed(embed.clone()); + let mut format = + videos::Video_Format::new(url.clone(), "1080".to_string(), "m3u8".to_string()); + format.add_http_header("Referer".to_string(), referer_url.clone()); + if let Some(formats) = video_item.formats.as_mut() { + formats.push(format); + } else { + video_item.formats = Some(vec![format]); + } + items.push(video_item); + } + + return items; + } +} + +impl Provider for HanimeProvider { + async fn get_videos( + &self, + _channel: String, + sort: String, + query: Option, + page: String, + per_page: String, + featured: String, + ) -> Vec { + let _ = per_page; + let _ = sort; + let videos: std::result::Result, Error> = match query { + Some(q) => self.query(&page.parse::().unwrap_or(1), &q).await, + None => self.get(&page.parse::().unwrap_or(1), featured).await, + }; + match videos { + Ok(v) => v, + Err(e) => { + println!("Error fetching videos: {}", e); + vec![] + } + } + } +} diff --git a/src/providers/perverzija.rs b/src/providers/perverzija.rs index 18e0298..d1cde64 100644 --- a/src/providers/perverzija.rs +++ b/src/providers/perverzija.rs @@ -264,7 +264,7 @@ impl PerverzijaProvider { let url = vid[4].split("iframe src="").collect::>()[1] .split(""") .collect::>()[0] - .to_string(); + .to_string().replace("index.php","xs1.php"); let id = url.split("data=").collect::>()[1] .split("&") .collect::>()[0] @@ -303,10 +303,11 @@ impl PerverzijaProvider { .split("'") .collect::>()[0] .to_string(); - let referer_url = vid[4].split("data-url='").collect::>()[1] - .split("'") - .collect::>()[0] - .to_string(); + // let referer_url = vid[4].split("data-url='").collect::>()[1] + // .split("'") + // .collect::>()[0] + // .to_string(); + let referer_url = "https://xtremestream.xyz/".to_string(); let embed = Video_Embed::new(embed_html, url.clone()); let mut tags: Vec = Vec::new(); // Placeholder for tags, adjust as needed for tag in vid[0].split(" ").collect::>(){