use capitalize::Capitalize; use ntex::http::header; use ntex::web; use ntex::web::HttpRequest; use std::cmp::Ordering; use std::{fs, io}; use tokio::task; use crate::providers::all::AllProvider; use crate::providers::hanime::HanimeProvider; use crate::providers::okporn::OkpornProvider; use crate::providers::perverzija::PerverzijaProvider; use crate::providers::pornhub::PornhubProvider; use crate::providers::redtube::RedtubeProvider; use crate::providers::rule34video::Rule34videoProvider; // use crate::providers::spankbang::SpankbangProvider; use crate::providers::{ALL_PROVIDERS, DynProvider}; use crate::util::cache::VideoCache; use crate::util::discord::send_discord_error_report; use crate::util::proxy::{Proxy, all_proxies_snapshot}; use crate::util::requester::Requester; use crate::{DbPool, db, status::*, videos::*}; use cute::c; use std::sync::Arc; #[derive(Debug, Clone)] pub struct ClientVersion { version: u32, subversion: u32, name: String, } impl ClientVersion { pub fn new(version: u32, subversion: u32, name: String) -> ClientVersion { ClientVersion { version, subversion, name, } } pub fn parse(input: &str) -> Option { // Example input: "Hot%20Tub/22c CFNetwork/1494.0.7 Darwin/23.4.0 0.002478" let first_part = input.split_whitespace().next()?; let mut name_version = first_part.splitn(2, '/'); let name = name_version.next()?; let version_str = name_version.next()?; // Find the index where the numeric part ends let split_idx = version_str .find(|c: char| !c.is_ascii_digit()) .unwrap_or(version_str.len()); let (v_num, v_alpha) = version_str.split_at(split_idx); // Parse the numeric version let version = v_num.parse::().ok()?; // Convert the first character of the subversion to u32 (ASCII value), // or 0 if it doesn't exist. let subversion = v_alpha.chars().next().map(|ch| ch as u32).unwrap_or(0); Some(Self { version, subversion, name: name.to_string(), }) } } // Implement comparisons impl PartialEq for ClientVersion { fn eq(&self, other: &Self) -> bool { self.name == other.name } } impl Eq for ClientVersion {} impl PartialOrd for ClientVersion { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for ClientVersion { fn cmp(&self, other: &Self) -> Ordering { self.version .cmp(&other.version) .then_with(|| self.subversion.cmp(&other.subversion)) } } pub fn config(cfg: &mut web::ServiceConfig) { cfg.service( web::resource("/status") .route(web::post().to(status)) .route(web::get().to(status)), ) .service( web::resource("/videos") // .route(web::get().to(videos_get)) .route(web::post().to(videos_post)), ) .service(web::resource("/test").route(web::get().to(test))) .service(web::resource("/proxies").route(web::get().to(proxies))); } async fn status(req: HttpRequest) -> Result { let clientversion: ClientVersion = match req.headers().get("User-Agent") { Some(v) => match v.to_str() { Ok(useragent) => ClientVersion::parse(useragent) .unwrap_or_else(|| ClientVersion::new(999, 0, "999".to_string())), Err(_) => ClientVersion::new(999, 0, "999".to_string()), }, _ => ClientVersion::new(999, 0, "999".to_string()), }; println!( "Received status request with client version: {:?}", clientversion ); let host = req .headers() .get(header::HOST) .and_then(|h| h.to_str().ok()) .unwrap_or_default() .to_string(); let mut status = Status::new(); // pronhub status.add_channel(Channel { id: "pornhub".to_string(), name: "Pornhub".to_string(), description: "Pornhub Free Videos".to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=pornhub.com".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: "mr".to_string(), title: "Most Recent".to_string(), }, FilterOption { id: "mv".to_string(), title: "Most Viewed".to_string(), }, FilterOption { id: "tr".to_string(), title: "Top Rated".to_string(), }, FilterOption { id: "lg".to_string(), title: "Longest".to_string(), }, FilterOption { id: "cm".to_string(), title: "Newest".to_string(), }, ], multiSelect: false, }], nsfw: true, cacheDuration: Some(1800), }); // perverzija status.add_channel(Channel { id: "perverzija".to_string(), name: "Perverzija".to_string(), description: "Free videos from Perverzija".to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=tube.perverzija.com".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: "date".to_string(), // title: "Date".to_string(), // }, // FilterOption { // id: "name".to_string(), // title: "Name".to_string(), // }, // ], // multiSelect: false, // }, ChannelOption { id: "featured".to_string(), title: "Featured".to_string(), description: "Filter Featured Videos.".to_string(), systemImage: "star".to_string(), colorName: "red".to_string(), options: vec![ FilterOption { id: "all".to_string(), title: "No".to_string(), }, FilterOption { id: "featured".to_string(), title: "Yes".to_string(), }, ], multiSelect: false, }, // ChannelOption { // id: "duration".to_string(), // title: "Duration".to_string(), // description: "Filter the videos by duration.".to_string(), // systemImage: "timer".to_string(), // colorName: "green".to_string(), // options: vec![ // FilterOption { // id: "short".to_string(), // title: "< 1h".to_string(), // }, // FilterOption { // id: "long".to_string(), // title: "> 1h".to_string(), // }, // ], // multiSelect: true, // }, ], nsfw: true, cacheDuration: None, }); // pornzog status.add_channel(Channel { id: "pornzog".to_string(), name: "Pornzog".to_string(), description: "Watch free porn videos at PornZog Free Porn Clips. More than 1 million videos, watch for free now!".to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=pornzog.com".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: "recent".to_string(), title: "Recent".to_string(), }, FilterOption { id: "relevance".to_string(), title: "Relevance".to_string(), }, FilterOption { id: "viewed".to_string(), title: "Most Viewed".to_string(), }, FilterOption { id: "rated".to_string(), title: "Most Rated".to_string(), }, FilterOption { id: "longest".to_string(), title: "Longest".to_string(), } ], multiSelect: false, }], nsfw: true, cacheDuration: None, }); // Hanime status.add_channel(Channel { id: "hanime".to_string(), name: "Hanime".to_string(), description: "Free Hentai from Hanime".to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=hanime.tv".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: "created_at_unix.desc".to_string(), title: "Recent Upload".to_string(), }, FilterOption { id: "created_at_unix.asc".to_string(), title: "Old Upload".to_string(), }, FilterOption { id: "views.desc".to_string(), title: "Most Views".to_string(), }, FilterOption { id: "views.asc".to_string(), title: "Least Views".to_string(), }, FilterOption { id: "likes.desc".to_string(), title: "Most Likes".to_string(), }, FilterOption { id: "likes.asc".to_string(), title: "Least Likes".to_string(), }, FilterOption { id: "released_at_unix.desc".to_string(), title: "New".to_string(), }, FilterOption { id: "released_at_unix.asc".to_string(), title: "Old".to_string(), }, FilterOption { id: "title_sortable.asc".to_string(), title: "A - Z".to_string(), }, FilterOption { id: "title_sortable.desc".to_string(), title: "Z - A".to_string(), }, ], multiSelect: false, }], nsfw: true, cacheDuration: None, }); // rule34video status.add_channel(Channel { id: "rule34video".to_string(), name: "Rule34Video".to_string(), description: "If it exists, there is porn".to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=rule34video.com".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: "post_date".to_string(), title: "Newest".to_string(), }, FilterOption { id: "video_viewed".to_string(), title: "Most Viewed".to_string(), }, FilterOption { id: "rating".to_string(), title: "Top Rated".to_string(), }, FilterOption { id: "duration".to_string(), title: "Longest".to_string(), }, FilterOption { id: "pseudo_random".to_string(), title: "Random".to_string(), }, ], multiSelect: false, }], nsfw: true, cacheDuration: Some(1800), }); let files = fs::read_dir("./src/providers").unwrap(); let providers = files .map(|entry| entry.unwrap().file_name()) .filter(|name| name.to_str().unwrap().ends_with(".rs")) .filter(|name| { !name.to_str().unwrap().contains("mod.rs") && !name.to_str().unwrap().contains("all.rs") }) .map(|name| name.to_str().unwrap().replace(".rs", "")) .collect::>(); let sites = c![FilterOption { id: x.to_string(), title: x.capitalize().to_string(), }, for x in providers.iter()]; // All status.add_channel(Channel { id: "all".to_string(), name: "All".to_string(), description: "Query from all sites of this Server".to_string(), premium: false, favicon: "https://hottub.spacemoehre.de/favicon.ico".to_string(), status: "active".to_string(), categories: vec![], options: vec![ChannelOption { id: "sites".to_string(), title: "Sites".to_string(), description: "What Sites to use".to_string(), //"Sort the videos by Date or Name.".to_string(), systemImage: "list.number".to_string(), colorName: "green".to_string(), options: sites, multiSelect: true, }], nsfw: true, cacheDuration: Some(1800), }); // Redtube status.add_channel(Channel { id: "redtube".to_string(), name: "Redtube".to_string(), description: "Redtube brings you NEW porn videos every day for free".to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=www.redtube.com".to_string(), status: "active".to_string(), categories: vec![], options: vec![], nsfw: true, cacheDuration: Some(1800), }); // ok.porn status.add_channel(Channel { id: "okporn".to_string(), name: "Ok.porn".to_string(), description: "Tons of HD porno movies".to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=ok.porn".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: "new".to_string(), title: "New".to_string(), }, FilterOption { id: "popular".to_string(), title: "Popular".to_string(), }, FilterOption { id: "trending".to_string(), title: "Trending".to_string(), }, ], multiSelect: false, }], nsfw: true, cacheDuration: Some(1800), }); // pornhat status.add_channel(Channel { id: "pornhat".to_string(), name: "Pornhat".to_string(), description: "free HD porn videos".to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=pornhat.com".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: "new".to_string(), title: "New".to_string(), }, FilterOption { id: "popular".to_string(), title: "Popular".to_string(), }, FilterOption { id: "trending".to_string(), title: "Trending".to_string(), }, ], multiSelect: false, }], nsfw: true, cacheDuration: Some(1800), }); //perfectgirls status.add_channel(Channel { id: "perfectgirls".to_string(), name: "Perfectgirls".to_string(), description: "Perfect Girls Tube".to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=perfectgirls.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: "new".to_string(), title: "New".to_string(), }, FilterOption { id: "popular".to_string(), title: "Popular".to_string(), }, FilterOption { id: "trending".to_string(), title: "Trending".to_string(), }, ], multiSelect: false, }], nsfw: true, cacheDuration: Some(1800), }); // okxxx status.add_channel(Channel { id: "okxxx".to_string(), name: "Ok.xxx".to_string(), description: "free porn tube!".to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=ok.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: "new".to_string(), title: "New".to_string(), }, FilterOption { id: "popular".to_string(), title: "Popular".to_string(), }, FilterOption { id: "trending".to_string(), title: "Trending".to_string(), }, ], multiSelect: false, }], nsfw: true, cacheDuration: Some(1800), }); // homoxxx status.add_channel(Channel { id: "homoxxx".to_string(), name: "Homo.xxx".to_string(), description: "Best Gay Porn".to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=homo.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: "new".to_string(), title: "New".to_string(), }, FilterOption { id: "popular".to_string(), title: "Popular".to_string(), }, FilterOption { id: "trending".to_string(), title: "Trending".to_string(), }, ], multiSelect: false, }], nsfw: true, cacheDuration: Some(1800), }); // xxthots status.add_channel(Channel { id: "xxthots".to_string(), name: "XXTHOTS".to_string(), description: "Free XXX Onlyfans Leaks Videos".to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=xxthots.com".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: "new".to_string(), title: "New".to_string(), }, FilterOption { id: "popular".to_string(), title: "Popular".to_string(), }, FilterOption { id: "top-rated".to_string(), title: "Top Rated".to_string(), }, ], multiSelect: false, }], nsfw: true, cacheDuration: Some(1800), }); // porn00 status.add_channel(Channel { id: "porn00".to_string(), name: "Porn00".to_string(), description: "HD Porn".to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=www.porn00.org".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: "new".to_string(), title: "New".to_string(), }, FilterOption { id: "popular".to_string(), title: "Popular".to_string(), }, FilterOption { id: "top-rated".to_string(), title: "Top Rated".to_string(), }, ], multiSelect: false, }], nsfw: true, cacheDuration: Some(1800), }); // paradisehill status.add_channel(Channel { id: "paradisehill".to_string(), name: "Paradisehill".to_string(), description: "Porn Movies on Paradise Hill".to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=en.paradisehill.cc".to_string(), status: "active".to_string(), categories: vec![], options: vec![], nsfw: true, cacheDuration: None, }); // youjizz status.add_channel(Channel { id: "youjizz".to_string(), name: "YouJizz".to_string(), description: "YouJizz Porntube".to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=www.youjizz.com".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: "new".to_string(), title: "New".to_string(), }, FilterOption { id: "popular".to_string(), title: "Popular".to_string(), }, FilterOption { id: "top-rated".to_string(), title: "Top Rated".to_string(), }, FilterOption { id: "top-rated-week".to_string(), title: "Top Rated (Week)".to_string(), }, FilterOption { id: "top-rated-month".to_string(), title: "Top Rated (Month)".to_string(), }, FilterOption { id: "trending".to_string(), title: "Trending".to_string(), }, FilterOption { id: "random".to_string(), title: "Random".to_string(), }, ], multiSelect: false, }], nsfw: true, cacheDuration: None, }); //missav status.add_channel(Channel { id: "missav".to_string(), name: "MissAV".to_string(), description: "Watch HD JAV Online".to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=missav.ws".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: "released_at".to_string(), title: "Release Date".to_string(), }, FilterOption { id: "published_at".to_string(), title: "Recent Update".to_string(), }, FilterOption { id: "today_views".to_string(), title: "Today Views".to_string(), }, FilterOption { id: "weekly_views".to_string(), title: "Weekly Views".to_string(), }, FilterOption { id: "monthly_views".to_string(), title: "Monthly Views".to_string(), }, FilterOption { id: "views".to_string(), title: "Total Views".to_string(), }, ], multiSelect: false, }, ChannelOption { id: "filter".to_string(), title: "Filter".to_string(), description: "Filter the Videos".to_string(), //"Sort the videos by Date or Name.".to_string(), systemImage: "line.horizontal.3.decrease.circle".to_string(), colorName: "green".to_string(), options: vec![ FilterOption { id: "new".to_string(), title: "Recent update".to_string(), }, FilterOption { id: "release".to_string(), title: "New Releases".to_string(), }, FilterOption { id: "uncensored-leak".to_string(), title: "Uncensored".to_string(), }, FilterOption { id: "english-subtitle".to_string(), title: "English subtitle".to_string(), }, ], multiSelect: false, }, ChannelOption { id: "language".to_string(), title: "Language".to_string(), description: "What Language to fetch".to_string(), //"Sort the videos by Date or Name.".to_string(), systemImage: "flag.fill".to_string(), colorName: "gray".to_string(), options: vec![ FilterOption { id: "en".to_string(), title: "English".to_string(), }, FilterOption { id: "cn".to_string(), title: "简体中文".to_string(), }, FilterOption { id: "ja".to_string(), title: "日本語".to_string(), }, FilterOption { id: "ko".to_string(), title: "한국의".to_string(), }, FilterOption { id: "ms".to_string(), title: "Melayu".to_string(), }, FilterOption { id: "th".to_string(), title: "ไทย".to_string(), }, FilterOption { id: "de".to_string(), title: "Deutsch".to_string(), }, FilterOption { id: "fr".to_string(), title: "Français".to_string(), }, FilterOption { id: "vi".to_string(), title: "Tiếng Việt".to_string(), }, FilterOption { id: "id".to_string(), title: "Bahasa Indonesia".to_string(), }, FilterOption { id: "fil".to_string(), title: "Filipino".to_string(), }, FilterOption { id: "pt".to_string(), title: "Português".to_string(), }, ], multiSelect: false, }, ], nsfw: true, cacheDuration: None, }); // if clientversion >= ClientVersion::new(22, 105, "22i".to_string()) { //sxyprn status.add_channel(Channel { id: "sxyprn".to_string(), name: "SexyPorn".to_string(), description: "Free Porn Site".to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=sxyprn.com".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".to_string(), title: "Latest".to_string(), }, FilterOption { id: "views".to_string(), title: "Views".to_string(), }, FilterOption { id: "rating".to_string(), title: "Rating".to_string(), }, FilterOption { id: "orgasmic".to_string(), title: "Orgasmic".to_string(), }, ], multiSelect: false, }, ChannelOption { id: "filter".to_string(), title: "Filter".to_string(), description: "Filter the Videos".to_string(), //"Sort the videos by Date or Name.".to_string(), systemImage: "line.horizontal.3.decrease.circle".to_string(), colorName: "green".to_string(), options: vec![ FilterOption { id: "top".to_string(), title: "Top".to_string(), }, FilterOption { id: "other".to_string(), title: "Other".to_string(), }, FilterOption { id: "all".to_string(), title: "All".to_string(), }, ], multiSelect: false, }, ], nsfw: true, cacheDuration: Some(1800), }); // } for provider in ALL_PROVIDERS.values() { if let Some(channel) = provider.get_channel(clientversion.clone()) { status.add_channel(channel); } } status.iconUrl = format!("http://{}/favicon.ico", host).to_string(); Ok(web::HttpResponse::Ok().json(&status)) } async fn videos_post( mut video_request: web::types::Json, cache: web::types::State, pool: web::types::State, requester: web::types::State, ) -> Result { match video_request.query.as_deref() { Some(query) if query.starts_with("#") => { video_request.query = Some(query.trim_start_matches("#").to_string()); } _ => {} } let requester = requester.get_ref().clone(); let mut conn = pool.get().expect("couldn't get db connection from pool"); // Ensure "videos" table exists with two string columns if !(db::has_table(&mut conn, "videos").unwrap()) { let _ = db::create_table( &mut conn, "CREATE TABLE videos (id TEXT NOT NULL, url TEXT NOT NULL);", ); } let mut videos = Videos { pageInfo: PageInfo { hasNextPage: true, resultsPerPage: 10, }, items: vec![], }; let channel: String = video_request .channel .as_deref() .unwrap_or("all") .to_string(); let sort: String = video_request.sort.as_deref().unwrap_or("date").to_string(); let mut query: Option = video_request.query.clone(); if video_request.query.as_deref() == Some("") { query = None; } let page: u8 = video_request .page .as_ref() .and_then(|value| value.to_u8()) .unwrap_or(1); let perPage: u8 = video_request .perPage .as_ref() .and_then(|value| value.to_u8()) .unwrap_or(10); let featured = video_request .featured .as_deref() .unwrap_or("all") .to_string(); let provider = get_provider(channel.as_str()) .ok_or_else(|| web::error::ErrorBadRequest("Invalid channel".to_string()))?; let category = video_request .category .as_deref() .unwrap_or("all") .to_string(); let sites = video_request.sites.as_deref().unwrap_or("").to_string(); let filter = video_request.filter.as_deref().unwrap_or("new").to_string(); let language = video_request .language .as_deref() .unwrap_or("en") .to_string(); let network = video_request.networks.as_deref().unwrap_or("").to_string(); let stars = video_request.stars.as_deref().unwrap_or("").to_string(); let categories = video_request .categories .as_deref() .unwrap_or("") .to_string(); let duration = video_request.duration.as_deref().unwrap_or("").to_string(); let options = ServerOptions { featured: Some(featured), category: Some(category), sites: Some(sites), filter: Some(filter), language: Some(language), requester: Some(requester), network: Some(network), stars: Some(stars), categories: Some(categories), duration: Some(duration), sort: Some(sort.clone()), }; let video_items = provider .get_videos( cache.get_ref().clone(), pool.get_ref().clone(), sort.clone(), query.clone(), page.to_string(), perPage.to_string(), options.clone(), ) .await; videos.items = video_items.clone(); if video_items.len() == 0 { videos.pageInfo = PageInfo { hasNextPage: false, resultsPerPage: 10, } } //### let next_page = page.to_string().parse::().unwrap_or(1) + 1; let provider_clone = provider.clone(); let cache_clone = cache.get_ref().clone(); let pool_clone = pool.get_ref().clone(); let sort_clone = sort.clone(); let query_clone = query.clone(); let per_page_clone = perPage.to_string(); let options_clone = options.clone(); task::spawn_local(async move { // if let AnyProvider::Spankbang(_) = provider_clone { // // Spankbang has a delay for the next page // ntex::time::sleep(ntex::time::Seconds(80)).await; // } let _ = provider_clone .get_videos( cache_clone, pool_clone, sort_clone, query_clone, next_page.to_string(), per_page_clone, options_clone, ) .await; }); //### for video in videos.items.iter_mut() { if video.duration <= 120 { let mut preview_url = video.url.clone(); if let Some(x) = &video.formats { // preview is a String here, so use it directly preview_url = x[0].url.clone(); } video.preview = Some(preview_url); } } Ok(web::HttpResponse::Ok().json(&videos)) } pub fn get_provider(channel: &str) -> Option { match channel { "all" => Some(Arc::new(AllProvider::new())), "perverzija" => Some(Arc::new(PerverzijaProvider::new())), "hanime" => Some(Arc::new(HanimeProvider::new())), "pornhub" => Some(Arc::new(PornhubProvider::new())), "rule34video" => Some(Arc::new(Rule34videoProvider::new())), "redtube" => Some(Arc::new(RedtubeProvider::new())), "okporn" => Some(Arc::new(OkpornProvider::new())), "pornhat" => Some(Arc::new(crate::providers::pornhat::PornhatProvider::new())), "perfectgirls" => Some(Arc::new( crate::providers::perfectgirls::PerfectgirlsProvider::new(), )), "okxxx" => Some(Arc::new(crate::providers::okxxx::OkxxxProvider::new())), "homoxxx" => Some(Arc::new(crate::providers::homoxxx::HomoxxxProvider::new())), "missav" => Some(Arc::new(crate::providers::missav::MissavProvider::new())), "xxthots" => Some(Arc::new(crate::providers::xxthots::XxthotsProvider::new())), "sxyprn" => Some(Arc::new(crate::providers::sxyprn::SxyprnProvider::new())), "porn00" => Some(Arc::new(crate::providers::porn00::Porn00Provider::new())), "youjizz" => Some(Arc::new(crate::providers::youjizz::YoujizzProvider::new())), "paradisehill" => Some(Arc::new( crate::providers::paradisehill::ParadisehillProvider::new(), )), "pornzog" => Some(Arc::new(crate::providers::pornzog::PornzogProvider::new())), // fallback to the dynamic registry x => ALL_PROVIDERS.get(x).cloned(), } } pub async fn test() -> Result { // Simply await the function instead of blocking the thread let e = io::Error::new(io::ErrorKind::Other, "test error"); let _ = send_discord_error_report( e.to_string(), Some("chain_str".to_string()), Some("Context"), Some("xtra info"), file!(), line!(), module_path!(), ) .await; Ok(web::HttpResponse::Ok()) } pub async fn proxies() -> Result { let proxies = all_proxies_snapshot().await.unwrap_or_default(); let mut by_protocol: std::collections::BTreeMap> = std::collections::BTreeMap::new(); for proxy in proxies { by_protocol .entry(proxy.protocol.clone()) .or_default() .push(proxy); } for proxies in by_protocol.values_mut() { proxies.sort_by(|a, b| { a.host .cmp(&b.host) .then(a.port.cmp(&b.port)) .then(a.username.cmp(&b.username)) .then(a.password.cmp(&b.password)) }); } Ok(web::HttpResponse::Ok().json(&by_protocol)) }