From 20d069f01f7f0c3107df333f9a1cc51ba59b7eaf Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 3 Oct 2025 17:25:47 +0000 Subject: [PATCH] omgxxx --- Cargo.toml | 5 + src/api.rs | 37 ++++++ src/providers/mod.rs | 6 + src/providers/omgxxx.rs | 254 +++++++++++++++++++++++++++++++++++++++ src/providers/youjizz.rs | 1 - 5 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 src/providers/omgxxx.rs diff --git a/Cargo.toml b/Cargo.toml index 0eb6e10..5276f01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,8 @@ capitalize = "0.3.4" url = "2.5.4" base64 = "0.22.1" scraper = "0.24.0" + +[lints.rust] +unexpected_cfgs = "allow" +# Or keep it as a warning but whitelist the cfg: +# unexpected_cfgs = { level = "warn", check-cfg = ['cfg(has_error_description_deprecated)'] } diff --git a/src/api.rs b/src/api.rs index db9a604..5604e1a 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1070,6 +1070,42 @@ async fn status(req: HttpRequest) -> Result { cacheDuration: None, }); + status.add_channel(Channel{ + id: "omgxxx".to_string(), + name: "OMG XXX".to_string(), + description: "Free Porn Site".to_string(), + premium: false, + favicon: "https://www.google.com/s2/favicons?sz=64&domain=www.omg.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-updates".to_string(), + title: "Latest".to_string(), + }, + FilterOption { + id: "most-popular".to_string(), + title: "Most Viewed".to_string(), + }, + FilterOption { + id: "top-rated".to_string(), + title: "Top Rated".to_string(), + }, + ], + multiSelect: false, + } + ], + nsfw: true, + cacheDuration: None, + }); + if clientversion >= ClientVersion::new(22, 105, "22i".to_string()) { //sxyprn status.add_channel(Channel { @@ -1307,6 +1343,7 @@ pub fn get_provider(channel: &str) -> Option { crate::providers::paradisehill::ParadisehillProvider::new(), )), "pornzog" => Some(AnyProvider::Pornzog(crate::providers::pornzog::PornzogProvider::new())), + "omgxxx" => Some(AnyProvider::Omgxxx(crate::providers::omgxxx::OmgxxxProvider::new())), _ => Some(AnyProvider::Perverzija(PerverzijaProvider::new())), } } diff --git a/src/providers/mod.rs b/src/providers/mod.rs index e5fcc94..bc7210c 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -27,6 +27,7 @@ pub mod freshporno; pub mod youjizz; pub mod paradisehill; pub mod pornzog; +pub mod omgxxx; pub trait Provider { @@ -67,6 +68,7 @@ pub enum AnyProvider { Youjizz(crate::providers::youjizz::YoujizzProvider), Paradisehill(crate::providers::paradisehill::ParadisehillProvider), Pornzog(crate::providers::pornzog::PornzogProvider), + Omgxxx(crate::providers::omgxxx::OmgxxxProvider), } impl Provider for AnyProvider { @@ -185,6 +187,10 @@ impl Provider for AnyProvider { p.get_videos(cache, pool, sort, query, page, per_page, options,) .await } + AnyProvider::Omgxxx(p) => { + p.get_videos(cache, pool, sort, query, page, per_page, options,) + .await + } } } } diff --git a/src/providers/omgxxx.rs b/src/providers/omgxxx.rs new file mode 100644 index 0000000..1405e44 --- /dev/null +++ b/src/providers/omgxxx.rs @@ -0,0 +1,254 @@ +use crate::status::*; +use crate::util::parse_abbreviated_number; +use crate::DbPool; +use crate::providers::Provider; +use crate::util::cache::VideoCache; +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::vec; + +error_chain! { + foreign_links { + Io(std::io::Error); + HttpRequest(wreq::Error); + } +} + +#[derive(Debug, Clone)] +pub struct OmgxxxProvider { + url: String, +} +impl OmgxxxProvider { + pub fn new() -> Self { + OmgxxxProvider { + url: "https://www.omg.xxx".to_string(), + } + } + + async fn get_channel(&self) -> crate::status::Channel { + let channel: crate::status::Channel = Channel{ + id: "omgxxx".to_string(), + name: "OMG XXX".to_string(), + description: "Free Porn Site".to_string(), + premium: false, + favicon: "https://www.google.com/s2/favicons?sz=64&domain=www.omg.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-updates".to_string(), + title: "Latest".to_string(), + }, + FilterOption { + id: "most-popular".to_string(), + title: "Most Viewed".to_string(), + }, + FilterOption { + id: "top-rated".to_string(), + title: "Top Rated".to_string(), + }, + ], + multiSelect: false, + } + ], + nsfw: true, + cacheDuration: None, + }; + return channel; + } + + async fn get( + &self, + cache: VideoCache, + page: u8, + sort: &str, + options: ServerOptions, + ) -> Result> { + let sort_string = match sort { + "top-rated" => "top-rated", + "most-popular" => "most-popular", + _ => "latest-updates", + }; + let video_url = format!("{}/{}/{}/", self.url, sort_string, page); + 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 mut requester = options.requester.clone().unwrap(); + + let text = requester.get(&video_url).await.unwrap(); + 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) + } + + async fn query( + &self, + cache: VideoCache, + page: u8, + query: &str, + options: ServerOptions, + ) -> Result> { + let mut search_type = "search"; + if query.starts_with("@models:") { + search_type = "models"; + } + let video_url = format!("{}/{}/{}/{}/", self.url, search_type, query.to_lowercase().trim().replace(" ","-"), page); + // 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 mut requester = options.requester.clone().unwrap(); + + let text = requester.get(&video_url).await.unwrap(); + 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) + } + + 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(); +// list_videos_latest_videos_list_items +// custom_list_videos_videos_list_search_result_items + + let raw_videos = html.split("videos_list_pagination").collect::>()[0] + .split("
>()[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 video_url: String = video_segment.split(">()[1] + .split("\"") + .collect::>()[0].to_string(); + let mut title = video_segment.split(" title=\"").collect::>()[1] + .split("\"").collect::>()[0] + .to_string(); + // html decode + title = decode(title.as_bytes()).to_string().unwrap_or(title); + let id = video_url.split("/").collect::>()[4].to_string(); + + let thumb = video_segment.split("img loading").collect::>()[1] + .split("data-src=\"").collect::>()[1] + .split("\"").collect::>()[0] + .to_string(); + let raw_duration = video_segment.split("").collect::>()[1] + .split("<").collect::>()[0] + .split(" ").collect::>().last().unwrap_or(&"") + .to_string(); + let duration = parse_time_to_seconds(raw_duration.as_str()).unwrap_or(0) as u32; + let views = parse_abbreviated_number(video_segment.split("
").collect::>()[1] + .split("<") + .collect::>()[0] + .to_string().as_str()).unwrap_or(0) as u32; + + let preview = video_segment.split("data-preview=\"").collect::>()[1] + .split("\"").collect::>()[0] + .to_string(); + + let tags = video_segment.split("class=\"models\">").collect::>()[1] + .split("href=\"").collect::>()[1..] + .into_iter().map( + |s| format!("@models:{}", s.split("/").collect::>()[4] + .to_string()) + ).collect::>().to_vec() + ; + + let video_item = VideoItem::new( + id, + title, + video_url.to_string(), + "omgxxx".to_string(), + thumb, + duration, + ) + .views(views) + .preview(preview) + .tags(tags) + ; + items.push(video_item); + } + return items; + } + + +} + +impl Provider for OmgxxxProvider { + async fn get_videos( + &self, + cache: VideoCache, + pool: DbPool, + sort: String, + query: Option, + page: String, + per_page: String, + options: ServerOptions, + ) -> Vec { + let _ = per_page; + let _ = pool; + let videos: std::result::Result, Error> = match query { + Some(q) => { + self.query(cache, page.parse::().unwrap_or(1), &q,options) + .await + } + None => { + self.get(cache, page.parse::().unwrap_or(1), &sort, options) + .await + } + }; + match videos { + Ok(v) => v, + Err(e) => { + println!("Error fetching videos: {}", e); + vec![] + } + } + } +} diff --git a/src/providers/youjizz.rs b/src/providers/youjizz.rs index 1b90888..24e7d66 100644 --- a/src/providers/youjizz.rs +++ b/src/providers/youjizz.rs @@ -42,7 +42,6 @@ impl YoujizzProvider { _ => "/newest-clips", }; let video_url = format!("{}{}/{}.html", self.url, sort_string, page); - println!("Fetching URL: {}", video_url); let old_items = match cache.get(&video_url) { Some((time, items)) => { if time.elapsed().unwrap_or_default().as_secs() < 60 * 5 {