From 9cf532e831f5fae9e14e0c95793a2e785dd45786 Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 13 Jul 2025 09:33:36 +0000 Subject: [PATCH] testing spankbang --- src/api.rs | 24 ---- src/providers/erothots.rs | 287 ------------------------------------- src/providers/fapello.rs | 282 ------------------------------------ src/providers/mod.rs | 8 +- src/providers/spankbang.rs | 150 +++++++++++-------- 5 files changed, 95 insertions(+), 656 deletions(-) delete mode 100644 src/providers/erothots.rs delete mode 100644 src/providers/fapello.rs diff --git a/src/api.rs b/src/api.rs index 38ebc54..b235798 100644 --- a/src/api.rs +++ b/src/api.rs @@ -5,7 +5,6 @@ use ntex::web; use ntex::web::HttpRequest; use tokio::{task, time}; -use crate::providers::erothots::ErothotsProvider; use crate::providers::hanime::HanimeProvider; use crate::providers::perverzija::PerverzijaProvider; use crate::providers::spankbang::SpankbangProvider; @@ -249,28 +248,6 @@ async fn status(req: HttpRequest) -> Result { ], nsfw: true, }); - // status.add_channel(Channel { - // id: "erothots".to_string(), - // name: "Erothots".to_string(), - // description: "Free Onlyfans Content".to_string(), - // premium: false, - // favicon: "https://www.google.com/s2/favicons?sz=64&domain=erothots.co".to_string(), - // status: "active".to_string(), - // categories: vec![], - // options: vec![], - // nsfw: true, - // }); - // status.add_channel(Channel { - // id: "fapello".to_string(), - // name: "Fapello".to_string(), - // description: "Free Onlyfans Content".to_string(), - // premium: false, - // favicon: "https://www.google.com/s2/favicons?sz=64&domain=fapello.com".to_string(), - // status: "active".to_string(), - // categories: vec![], - // options: vec![], - // nsfw: true, - // }); status.add_channel(Channel { id: "spankbang".to_string(), name: "Work in Progress - SpankBang".to_string(), @@ -390,7 +367,6 @@ pub fn get_provider(channel: &str) -> Option { "perverzija" => Some(AnyProvider::Perverzija(PerverzijaProvider::new())), "hanime" => Some(AnyProvider::Hanime(HanimeProvider::new())), "spankbang" => Some(AnyProvider::Spankbang(SpankbangProvider::new())), - "erothots" => Some(AnyProvider::Erothots(ErothotsProvider::new())), // ErothotsProvider is not implemented yet _ => Some(AnyProvider::Perverzija(PerverzijaProvider::new())), } } \ No newline at end of file diff --git a/src/providers/erothots.rs b/src/providers/erothots.rs deleted file mode 100644 index 1ca34de..0000000 --- a/src/providers/erothots.rs +++ /dev/null @@ -1,287 +0,0 @@ -use std::vec; -use std::env; -use error_chain::error_chain; -use htmlentity::entity::{decode, ICodedDataTrait}; -use reqwest::{Proxy}; -use futures::future::join_all; - -use crate::db; -use crate::providers::Provider; -use crate::util::cache::VideoCache; -use crate::util::flaresolverr::{FlareSolverrRequest, Flaresolverr}; -use crate::util::time::parse_time_to_seconds; -use crate::videos::{self, VideoEmbed, VideoItem}; -use crate::DbPool; -use crate::USER_AGENT; // Make sure Provider trait is imported - -error_chain! { - foreign_links { - Io(std::io::Error); - HttpRequest(reqwest::Error); - } -} - - -#[derive(Debug, Clone)] -pub struct ErothotsProvider { - url: String, -} -impl ErothotsProvider { - pub fn new() -> Self { - ErothotsProvider { - url: "https://erothots.co/".to_string(), - } - } - async fn get(&self, cache:VideoCache, page: u8) -> Result> { - - let mut prefix_uri = "videos/new".to_string(); - let mut url = format!("{}{}?p={}", self.url, prefix_uri, page-1); - if page == 1 { - url = format!("{}{}", self.url, prefix_uri); - } - - let old_items = match cache.get(&url) { - Some((time, items)) => { - if time.elapsed().unwrap_or_default().as_secs() < 60 * 60 { - println!("Cache hit for URL: {}", url); - return Ok(items.clone()); - } - else{ - items.clone() - } - } - None => { - vec![] - } - }; - - - let client = match env::var("BURP_URL").as_deref() { - Ok(burp_url) => - reqwest::Client::builder() - .user_agent(USER_AGENT) - .proxy(Proxy::https(burp_url).unwrap()) - .danger_accept_invalid_certs(true) - .build()?, - Err(_) => reqwest::Client::builder() - .user_agent(USER_AGENT) - .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()); - if !video_items.is_empty() { - cache.remove(&url); - cache.insert(url.clone(), video_items.clone()); - } else{ - return Ok(old_items); - } - 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; - 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()); - } - }; - if !video_items.is_empty() { - cache.remove(&url); - cache.insert(url.clone(), video_items.clone()); - } else { - return Ok(old_items); - } - Ok(video_items) - } - } - async fn query(&self, cache: VideoCache, page: u8, query: &str) -> Result> { - - let mut url = format!( - "{}videos/{}?p={}", - self.url, query, page-1 - ); - if page == 1 { - url = format!("{}videos/{}", self.url, query); - } - - // Check our Video Cache. If the result is younger than 1 hour, we return it. - let old_items = match cache.get(&url) { - 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 client = match env::var("BURP_URL").as_deref() { - Ok(burp_url) => - reqwest::Client::builder() - .user_agent(USER_AGENT) - .proxy(Proxy::https(burp_url).unwrap()) - .danger_accept_invalid_certs(true) - .build()?, - Err(_) => reqwest::Client::builder() - .user_agent(USER_AGENT) - .danger_accept_invalid_certs(true) - .build()?, - }; - - let response = client.get(url.clone()).send().await?; - if response.status().is_success() { - let text = response.text().await?; - let video_items: Vec = self.get_video_items_from_html(text.clone()); - if !video_items.is_empty() { - cache.remove(&url); - cache.insert(url.clone(), video_items.clone()); - } else{ - return Ok(old_items); - } - 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; - let video_items = match result { - Ok(res) => { - self.get_video_items_from_html(res.solution.response) - } - Err(e) => { - println!("Error solving FlareSolverr: {}", e); - return Err("Failed to solve FlareSolverr".into()); - } - }; - if !video_items.is_empty() { - cache.remove(&url); - cache.insert(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(); - let raw_videos = html - .split("
") - .collect::>()[1] - .split("
") - .collect::>()[0]; - for vid in raw_videos.split("") - .collect::>()[1] - .split("") - .collect::>()[0] - .trim(); - let thumb = vid.split("data-src=\"") - .collect::>()[1] - .split("\"") - .collect::>()[0] - .trim(); - let url = vid.split("href=\"") - .collect::>()[1] - .split("\"") - .collect::>()[0] - .trim(); - let raw_dur = vid.split("") - .collect::>()[1] - .split("<") - .collect::>()[0] - .trim(); - let duration: u32 = match parse_time_to_seconds(raw_dur) { - Some(secs) => secs as u32, - None => 0, - }; - - let item = VideoItem::new( - url.to_string(), - title.to_string(), - url.to_string(), - "erothots".to_string(), - thumb.to_string(), - duration, - ); - items.push(item); - } - return items; - } - - // async fn get_video_items_from_html_query(&self, html: String, pool:DbPool) -> Vec { - // let raw_videos = html - // .split("video-item post") - // .collect::>()[1..] - // .to_vec(); - // let futures = raw_videos.into_iter().map(|el| self.get_video_item(el, pool.clone())); - // let results: Vec> = join_all(futures).await; - // let items: Vec = results - // .into_iter() - // .filter_map(Result::ok) - // .collect(); - - // return items; - // } -} - -impl Provider for ErothotsProvider { - async fn get_videos( - &self, - cache: VideoCache, - pool: DbPool, - _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(cache, page.parse::().unwrap_or(1), &q).await, - None => self.get(cache, page.parse::().unwrap_or(1)).await, - }; - match videos { - Ok(v) => v, - Err(e) => { - println!("Error fetching videos: {}", e); - vec![] - } - } - } -} diff --git a/src/providers/fapello.rs b/src/providers/fapello.rs deleted file mode 100644 index 60d6157..0000000 --- a/src/providers/fapello.rs +++ /dev/null @@ -1,282 +0,0 @@ -use std::vec; -use std::env; -use error_chain::error_chain; -use htmlentity::entity::{decode, ICodedDataTrait}; -use reqwest::{Proxy}; -use futures::future::join_all; - -use crate::db; -use crate::providers::Provider; -use crate::util::cache::VideoCache; -use crate::util::flaresolverr::{FlareSolverrRequest, Flaresolverr}; -use crate::util::time::parse_time_to_seconds; -use crate::videos::{self, VideoEmbed, VideoItem}; -use crate::DbPool; -use crate::USER_AGENT; // Make sure Provider trait is imported - -error_chain! { - foreign_links { - Io(std::io::Error); - HttpRequest(reqwest::Error); - } -} - - -#[derive(Debug, Clone)] -pub struct FapelloProvider { - url: String, -} -impl FapelloProvider { - pub fn new() -> Self { - FapelloProvider { - url: "https://fapello.com/".to_string(), - } - } - async fn get(&self, cache:VideoCache, page: u8) -> Result> { - - let mut prefix_uri = "videos/".to_string(); - let mut url = format!("{}/ajax/{}page-{}/", self.url, prefix_uri, page); - if page == 1 { - url = format!("{}{}", self.url, prefix_uri); - } - - let old_items = match cache.get(&url) { - Some((time, items)) => { - if time.elapsed().unwrap_or_default().as_secs() < 60 * 60 { - println!("Cache hit for URL: {}", url); - return Ok(items.clone()); - } - else{ - items.clone() - } - } - None => { - vec![] - } - }; - - - let client = match env::var("BURP_URL").as_deref() { - Ok(burp_url) => - reqwest::Client::builder() - .user_agent(USER_AGENT) - .proxy(Proxy::https(burp_url).unwrap()) - .danger_accept_invalid_certs(true) - .build()?, - Err(_) => reqwest::Client::builder() - .user_agent(USER_AGENT) - .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()); - if !video_items.is_empty() { - cache.remove(&url); - cache.insert(url.clone(), video_items.clone()); - } else{ - return Ok(old_items); - } - 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; - 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()); - } - }; - if !video_items.is_empty() { - cache.remove(&url); - cache.insert(url.clone(), video_items.clone()); - } else { - return Ok(old_items); - } - Ok(video_items) - } - } - async fn query(&self, cache: VideoCache, page: u8, query: &str) -> Result> { - - let mut url = format!( - "{}videos/{}?p={}", - self.url, query, page-1 - ); - if page == 1 { - url = format!("{}videos/{}", self.url, query); - } - - // Check our Video Cache. If the result is younger than 1 hour, we return it. - let old_items = match cache.get(&url) { - 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 client = match env::var("BURP_URL").as_deref() { - Ok(burp_url) => - reqwest::Client::builder() - .user_agent(USER_AGENT) - .proxy(Proxy::https(burp_url).unwrap()) - .danger_accept_invalid_certs(true) - .build()?, - Err(_) => reqwest::Client::builder() - .user_agent(USER_AGENT) - .danger_accept_invalid_certs(true) - .build()?, - }; - - let response = client.get(url.clone()).send().await?; - if response.status().is_success() { - let text = response.text().await?; - let video_items: Vec = self.get_video_items_from_html(text.clone()); - if !video_items.is_empty() { - cache.remove(&url); - cache.insert(url.clone(), video_items.clone()); - } else{ - return Ok(old_items); - } - 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; - let video_items = match result { - Ok(res) => { - self.get_video_items_from_html(res.solution.response) - } - Err(e) => { - println!("Error solving FlareSolverr: {}", e); - return Err("Failed to solve FlareSolverr".into()); - } - }; - if !video_items.is_empty() { - cache.remove(&url); - cache.insert(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(); - for vid in html.split("") - .collect::>()[1] - .split("") - .collect::>()[0] - .trim(); - let thumb = vid.split("data-src=\"") - .collect::>()[1] - .split("\"") - .collect::>()[0] - .trim(); - let url = vid.split("href=\"") - .collect::>()[1] - .split("\"") - .collect::>()[0] - .trim(); - let raw_dur = vid.split("") - .collect::>()[1] - .split("<") - .collect::>()[0] - .trim(); - let duration: u32 = match parse_time_to_seconds(raw_dur) { - Some(secs) => secs as u32, - None => 0, - }; - - let item = VideoItem::new( - url.to_string(), - title.to_string(), - url.to_string(), - "erothots".to_string(), - thumb.to_string(), - duration, - ); - items.push(item); - } - return items; - } - - // async fn get_video_items_from_html_query(&self, html: String, pool:DbPool) -> Vec { - // let raw_videos = html - // .split("video-item post") - // .collect::>()[1..] - // .to_vec(); - // let futures = raw_videos.into_iter().map(|el| self.get_video_item(el, pool.clone())); - // let results: Vec> = join_all(futures).await; - // let items: Vec = results - // .into_iter() - // .filter_map(Result::ok) - // .collect(); - - // return items; - // } -} - -impl Provider for FapelloProvider { - async fn get_videos( - &self, - cache: VideoCache, - pool: DbPool, - _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(cache, page.parse::().unwrap_or(1), &q).await, - None => self.get(cache, page.parse::().unwrap_or(1)).await, - }; - match videos { - Ok(v) => v, - Err(e) => { - println!("Error fetching videos: {}", e); - vec![] - } - } - } -} diff --git a/src/providers/mod.rs b/src/providers/mod.rs index 765609c..ccdcfe5 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -1,11 +1,9 @@ -use crate::{providers::{erothots::ErothotsProvider, fapello::FapelloProvider, hanime::HanimeProvider, perverzija::PerverzijaProvider, spankbang::SpankbangProvider}, util::cache::VideoCache, videos::VideoItem, DbPool}; +use crate::{providers::{hanime::HanimeProvider, perverzija::PerverzijaProvider, spankbang::SpankbangProvider}, util::cache::VideoCache, videos::VideoItem, DbPool}; pub mod perverzija; pub mod hanime; pub mod spankbang; -pub mod erothots; -pub mod fapello; pub trait Provider{ async fn get_videos(&self, cache: VideoCache, pool: DbPool, channel: String, sort: String, query: Option, page: String, per_page: String, featured: String) -> Vec; @@ -16,17 +14,13 @@ pub enum AnyProvider { Perverzija(PerverzijaProvider), Hanime(HanimeProvider), Spankbang(SpankbangProvider), - Erothots(ErothotsProvider), - Fapello(FapelloProvider), // Uncomment if you want to include FapelloProvider } impl Provider for AnyProvider { async fn get_videos(&self, cache: VideoCache, pool:DbPool, channel: String, sort: String, query: Option, page: String, per_page: String, featured: String) -> Vec { match self { AnyProvider::Perverzija(p) => p.get_videos(cache.clone(), pool.clone(), channel.clone(), sort.clone(), query.clone(), page.clone(), per_page.clone(), featured.clone()).await, AnyProvider::Hanime(p) => p.get_videos(cache, pool, channel, sort, query, page, per_page, featured).await, - AnyProvider::Erothots(p) => p.get_videos(cache, pool, channel, sort, query, page, per_page, featured).await, AnyProvider::Spankbang(p) => p.get_videos(cache, pool, channel, sort, query, page, per_page, featured).await, - AnyProvider::Fapello(p) => p.get_videos(cache, pool, channel, sort, query, page, per_page, featured).await, } } } diff --git a/src/providers/spankbang.rs b/src/providers/spankbang.rs index b8112c0..33ebfd1 100644 --- a/src/providers/spankbang.rs +++ b/src/providers/spankbang.rs @@ -1,7 +1,9 @@ use std::vec; use std::env; use error_chain::error_chain; +use futures::future::join_all; use htmlentity::entity::{decode, ICodedDataTrait}; +use reqwest::Client; use reqwest::{Proxy}; use crate::providers::Provider; use crate::util::cache::VideoCache; @@ -29,7 +31,7 @@ impl SpankbangProvider { url: "https://spankbang.com/".to_string(), } } - async fn get(&self, cache:VideoCache, page: u8, sort: String) -> Result> { + async fn get(&self, cache:VideoCache, pool: DbPool, page: u8, sort: String) -> Result> { let mut url = format!("{}{}/{}/", self.url, sort, page); let old_items = match cache.get(&url) { @@ -62,9 +64,13 @@ impl SpankbangProvider { }; let response = client.get(url.clone()).send().await?; + println!("Response status: {}", response.status()); if response.status().is_success() { let text = response.text().await?; - let video_items: Vec = self.get_video_items_from_html(text.clone()); + println!("Response text: {}", text); + let video_items: Vec = self.get_video_items_from_html(text.clone(), &client).await; + + if !video_items.is_empty() { cache.remove(&url); cache.insert(url.clone(), video_items.clone()); @@ -85,7 +91,7 @@ impl SpankbangProvider { let video_items = match result { Ok(res) => { // println!("FlareSolverr response: {}", res); - self.get_video_items_from_html(res.solution.response) + self.get_video_items_from_html(res.solution.response, &client).await } Err(e) => { println!("Error solving FlareSolverr: {}", e); @@ -101,7 +107,7 @@ impl SpankbangProvider { Ok(video_items) } } - + async fn query(&self, cache: VideoCache, page: u8, query: &str) -> Result> { let url = format!("{}s/{}/{}/", self.url, query.replace(" ", "+"), page); @@ -152,12 +158,11 @@ impl SpankbangProvider { .map(|(k, v)| format!("{}={}", k, v)) .collect::>() .join("; "); - println!("Cookie: {}", cookies_string); } } if response.status().is_success() { let text = response.text().await?; - let video_items: Vec = self.get_video_items_from_html(text.clone()); + let video_items: Vec = self.get_video_items_from_html(text.clone(), &client).await; if !video_items.is_empty() { cache.remove(&url); cache.insert(url.clone(), video_items.clone()); @@ -178,7 +183,7 @@ impl SpankbangProvider { let video_items = match result { Ok(res) => { // println!("FlareSolverr response: {}", res); - self.get_video_items_from_html(res.solution.response) + self.get_video_items_from_html(res.solution.response, &client).await } Err(e) => { println!("Error solving FlareSolverr: {}", e); @@ -195,7 +200,80 @@ impl SpankbangProvider { } } - fn get_video_items_from_html(&self, html: String) -> Vec { + async fn get_video_url(&self, url:String, client:&Client) -> Result { + let new_url = "".to_string(); + let response = client.get(url.clone()).send().await?; + if response.status().is_success() { + let text = response.text().await?; + let lines = text.split("\n").collect::>(); + let url_line = lines.iter() + .find(|s| s.trim_start().starts_with(">()[1].split("\"").collect::>()[0].to_string()) + } + Err(Error::from("Failed to get video URL")) + } + + async fn parse_video_item( + &self, + html: String, + client: &Client, + ) -> Result { + let vid = html.split("\n").collect::>(); + // for (index,line) in vid.iter().enumerate(){ + // println!("Line {}: {}\n\n", index, line); + // } + let title_line = vid.iter() + .find(|s| s.trim_start().starts_with(">()[1].split("\"").collect::>()[0].to_string(); + title = decode(title.as_bytes()).to_string().unwrap_or(title); + let thumb = vid[13].split("data-src=\"").collect::>()[1].split("\"").collect::>()[0].to_string(); + let mut preview = "".to_string(); + if vid[15].contains("data-preview=\""){ + preview = vid[15].split("data-preview=\"").collect::>()[1].split("\"").collect::>()[0].to_string(); + } + let duration_str = vid.iter().find(|s| s.contains("")).unwrap().split("").collect::>()[1].split("m<").collect::>()[0]; + let duration: u32 = duration_str.parse::().unwrap_or(0) * 60; + let view_and_rating_str: Vec<&str> = vid.iter().copied().filter(|s| s.contains("")).collect(); + let views_str = view_and_rating_str[0].split(">").collect::>()[1].split("K<").collect::>()[0]; + let views = (views_str.parse::().unwrap_or(0.0) * 1000.0) as u32; + let rate_str = view_and_rating_str[1].split(">").collect::>()[1].split("%<").collect::>()[0]; + let rating = rate_str.parse::().unwrap_or(0.0); + let url_part = vid.iter().find(|s| s.contains(">()[1].split("\"").collect::>()[0]; + let url = match self.get_video_url(self.url.clone() + url_part, client).await { + Ok(video_url) => video_url, + Err(e) => { + return Err("Failed to get video URL".into()); + } + }; + let id = url_part.split("/").collect::>()[0].to_string(); + + let quality_str = match vid[25].contains("<"){ + true => vid[25].split(">").collect::>()[1].split("<").collect::>()[0], + false => "SD", + }; + let quality = match quality_str{ + "HD" => "1080", + "4k" => "2160", + "SD" => "720", + _ => "1080", + }; + + + let mut format = + videos::VideoFormat::new(url.clone(), quality.to_string(), "mp4".to_string()); + format.add_http_header("User-Agent".to_string(), USER_AGENT.to_string()); + + let video_item = VideoItem::new(id, title, url.clone().to_string(), "spankbang".to_string(), thumb, duration) + .views(views) + .rating(rating) + // .formats(vec![format]) + .preview(preview); + Ok(video_item) + } + + async fn get_video_items_from_html(&self, html: String, client: &Client) -> Vec { if html.is_empty() { println!("HTML is empty"); return vec![]; @@ -215,53 +293,13 @@ impl SpankbangProvider { return items; } let raw_videos = raw_videos_vec[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 {}: {}\n\n", index, line); - //} - let mut title = vid[4].split("title=\"").collect::>()[1].split("\"").collect::>()[0].to_string(); - title = decode(title.as_bytes()).to_string().unwrap_or(title); - let thumb = vid[14].split("data-src=\"").collect::>()[1].split("\"").collect::>()[0].to_string(); - let mut preview = "".to_string(); - if vid[16].contains("data-preview=\""){ - preview = vid[16].split("data-preview=\"").collect::>()[1].split("\"").collect::>()[0].to_string(); - } - let duration_str = vid.iter().find(|s| s.contains("")).unwrap().split("").collect::>()[1].split("m<").collect::>()[0]; - let duration: u32 = duration_str.parse::().unwrap_or(0) * 60; - let view_and_rating_str: Vec<&str> = vid.iter().copied().filter(|s| s.contains("")).collect(); - let views_str = view_and_rating_str[0].split(">").collect::>()[1].split("K<").collect::>()[0]; - let views = (views_str.parse::().unwrap_or(0.0) * 1000.0) as u32; - let rate_str = view_and_rating_str[1].split(">").collect::>()[1].split("%<").collect::>()[0]; - let rating = rate_str.parse::().unwrap_or(0.0); - let url_part = vid.iter().find(|s| s.contains(">()[1].split("\"").collect::>()[0]; - let url = self.url.clone() + url_part; - let id = url_part.split("/").collect::>()[0].to_string(); - - let quality_str = match vid[25].contains("<"){ - true => vid[25].split(">").collect::>()[1].split("<").collect::>()[0], - false => "SD", - }; - let quality = match quality_str{ - "HD" => "1080", - "4k" => "2160", - "SD" => "720", - _ => "1080", - }; - - - let mut format = - videos::VideoFormat::new(url.clone(), quality.to_string(), "mp4".to_string()); - format.add_http_header("User-Agent".to_string(), USER_AGENT.to_string()); - - let video_item = VideoItem::new(id, title, url.clone().to_string(), "spankbang".to_string(), thumb, duration) - .views(views) - .rating(rating) - // .formats(vec![format]) - .preview(preview); - items.push(video_item); - } - return items; + let futures = raw_videos.into_iter().map(|el| self.parse_video_item(el.to_string(), client)); + let results: Vec> = join_all(futures).await; + let video_items: Vec = results + .into_iter() + .filter_map(Result::ok) + .collect(); + return video_items; } } @@ -287,7 +325,7 @@ impl Provider for SpankbangProvider { } let videos: std::result::Result, Error> = match query { Some(q) => self.query(cache, page.parse::().unwrap_or(1), &q).await, - None => self.get(cache, page.parse::().unwrap_or(1), sort).await, + None => self.get(cache, pool, page.parse::().unwrap_or(1), sort).await, }; match videos { Ok(v) => v,