From 53ac33f856a49bceadb235dbbf9d200672bb1404 Mon Sep 17 00:00:00 2001 From: Simon Date: Sat, 29 Nov 2025 08:20:38 +0000 Subject: [PATCH] hqporner --- src/providers/hqporner.rs | 275 ++++++++++++++++++++++++++++++++++++++ src/providers/mod.rs | 2 + 2 files changed, 277 insertions(+) create mode 100644 src/providers/hqporner.rs diff --git a/src/providers/hqporner.rs b/src/providers/hqporner.rs new file mode 100644 index 0000000..bfbbb4b --- /dev/null +++ b/src/providers/hqporner.rs @@ -0,0 +1,275 @@ +use crate::util::requester::Requester; +use crate::{DbPool}; +use crate::api::ClientVersion; +use crate::providers::Provider; +use crate::status::*; +use crate::util::cache::VideoCache; +use crate::util::time::parse_time_to_seconds; +use crate::videos::{ServerOptions, VideoFormat, VideoItem}; +use async_trait::async_trait; +use error_chain::error_chain; +use futures::future::join_all; +use htmlentity::entity::{ICodedDataTrait, decode}; +use std::vec; + +error_chain! { + foreign_links { + Io(std::io::Error); + HttpRequest(wreq::Error); + } +} + +#[derive(Debug, Clone)] +pub struct HqpornerProvider { + url: String, + // stars: Arc>>, + // categories: Arc>>, +} +impl HqpornerProvider { + pub fn new() -> Self { + let provider = HqpornerProvider { + url: "https://hqporner.com".to_string(), + // stars: Arc::new(RwLock::new(vec![])), + // categories: Arc::new(RwLock::new(vec![])), + }; + provider + } + + fn build_channel(&self, clientversion: ClientVersion) -> Channel { + let _ = clientversion; + Channel { + id: "hqporner".to_string(), + name: "HQPorner".to_string(), + description: "HD Porn Videos Tube".to_string(), + premium: false, + favicon: "https://www.google.com/s2/favicons?sz=64&domain=hqporner.com".to_string(), + status: "active".to_string(), + categories: vec![], + options: vec![], + nsfw: true, + cacheDuration: None, + } + } + + async fn get( + &self, + cache: VideoCache, + page: u8, + sort: &str, + options: ServerOptions, + ) -> Result> { + let _ = sort; + let video_url = format!("{}/hdporn/{}", self.url, 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(), &mut requester).await; + 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 search_string = query.trim().to_string(); + + let video_url = format!("{}/?q={}&p={}", self.url, search_string, 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(), &mut requester).await; + 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 get_video_items_from_html(&self, html: String, requester: &mut Requester) -> Vec { + if html.is_empty() || html.contains("404 Not Found") { + return vec![]; + } + let raw_videos = html.split("id=\"footer\"").collect::>()[0] + .split("
") + .collect::>()[2] + .split("
") + .collect::>()[1..] + .to_vec(); + + let futures = raw_videos.into_iter().map(|el| self.get_video_item(el.to_string(), requester.clone())); + let results: Vec> = join_all(futures).await; + let video_items: Vec = results + .into_iter() + .filter_map(Result::ok) + .collect(); + return video_items; + } + + async fn get_video_item(&self, video_segment: String, mut requester: Requester) -> Result { + let vid = video_segment.split("\n").collect::>(); + for (index, line) in vid.iter().enumerate() { + println!("Line {}: {}\n", index, line); + } + let video_url: String = format!( + "{}{}", + self.url, + video_segment.split(">()[1] + .split("\"").collect::>()[0] + .to_string() + ); + let mut title = video_segment + .split("