use crate::DbPool; use crate::providers::Provider; use crate::util::cache::VideoCache; use crate::util::requester::Requester; use crate::util::time::parse_time_to_seconds; use crate::videos::ServerOptions; use crate::videos::VideoItem; use async_trait::async_trait; use error_chain::error_chain; use htmlentity::entity::{ICodedDataTrait, decode}; use scraper::{Html, Selector}; use std::vec; error_chain! { foreign_links { Io(std::io::Error); HttpRequest(wreq::Error); JsonError(serde_json::Error); } } // fn has_blacklisted_class(element: &ElementRef, blacklist: &[&str]) -> bool { // element // .value() // .attr("class") // .map(|classes| classes.split_whitespace().any(|c| blacklist.contains(&c))) // .unwrap_or(false) // } #[derive(Debug, Clone)] pub struct SxyprnProvider { url: String, } impl SxyprnProvider { pub fn new() -> Self { SxyprnProvider { url: "https://sxyprn.com".to_string(), } } async fn get( &self, cache: VideoCache, pool: DbPool, page: u8, sort: String, options: ServerOptions, ) -> Result> { let sort_string = match sort.as_str() { "views" => "views", "rating" => "rating", "orgasmic" => "orgasmic", _ => "latest", }; // Extract needed fields from options at the start let filter = options.filter.clone().unwrap(); let filter_string = match filter.as_str() { "other" => "other", "all" => "all", _ => "top", }; let mut requester = options.requester.clone().unwrap(); let url_str = format!( "{}/blog/all/{}.html?fl={}&sm={}", self.url, ((page as u32) - 1) * 20, filter_string, sort_string ); let old_items = match cache.get(&url_str) { Some((time, items)) => { if time.elapsed().unwrap_or_default().as_secs() < 60 * 60 { return Ok(items.clone()); } else { items.clone() } } None => { vec![] } }; let text = requester.get(&url_str, None).await.unwrap(); // Pass a reference to options if needed, or reconstruct as needed let video_items: Vec = self .get_video_items_from_html(text.clone(), pool, requester) .await; if !video_items.is_empty() { cache.remove(&url_str); cache.insert(url_str.clone(), video_items.clone()); } else { return Ok(old_items); } Ok(video_items) } async fn query( &self, cache: VideoCache, pool: DbPool, page: u8, query: &str, sort: String, options: ServerOptions, ) -> Result> { let sort_string = match sort.as_str() { "views" => "views", "rating" => "trending", "orgasmic" => "orgasmic", _ => "latest", }; // Extract needed fields from options at the start let mut requester = options.requester.clone().unwrap(); let search_string = query.replace(" ", "-"); let url_str = format!( "{}/{}.html?page={}&sm={}", self.url, search_string, ((page as u32) - 1) * 20, sort_string ); // Check our Video Cache. If the result is younger than 1 hour, we return it. let old_items = match cache.get(&url_str) { Some((time, items)) => { if time.elapsed().unwrap_or_default().as_secs() < 60 * 60 { return Ok(items.clone()); } else { let _ = cache.check().await; return Ok(items.clone()); } } None => { vec![] } }; let text = requester.get(&url_str, None).await.unwrap(); let video_items: Vec = self .get_video_items_from_html(text.clone(), pool, requester) .await; if !video_items.is_empty() { cache.remove(&url_str); cache.insert(url_str.clone(), video_items.clone()); } else { return Ok(old_items); } Ok(video_items) } async fn get_video_items_from_html( &self, html: String, pool: DbPool, requester: Requester, ) -> Vec { let _ = requester; let _ = pool; if html.is_empty() { println!("HTML is empty"); return vec![]; } let raw_videos = html.split("