From 37d11034d82fafd9a3918c391c6737a182a2b8d4 Mon Sep 17 00:00:00 2001
From: Simon
Date: Wed, 1 Oct 2025 19:28:41 +0000
Subject: [PATCH] pornzog
---
src/api.rs | 46 ++++++++++
src/providers/mod.rs | 6 ++
src/providers/pornzog.rs | 189 +++++++++++++++++++++++++++++++++++++++
3 files changed, 241 insertions(+)
create mode 100644 src/providers/pornzog.rs
diff --git a/src/api.rs b/src/api.rs
index c89be24..db9a604 100644
--- a/src/api.rs
+++ b/src/api.rs
@@ -310,6 +310,50 @@ async fn status(req: HttpRequest) -> Result {
});
}
+ // 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(),
@@ -406,6 +450,7 @@ async fn status(req: HttpRequest) -> Result {
//cacheDuration: Some(1800),
// });
+ // rule34video
status.add_channel(Channel {
id: "rule34video".to_string(),
name: "Rule34Video".to_string(),
@@ -1261,6 +1306,7 @@ pub fn get_provider(channel: &str) -> Option {
"paradisehill" => Some(AnyProvider::Paradisehill(
crate::providers::paradisehill::ParadisehillProvider::new(),
)),
+ "pornzog" => Some(AnyProvider::Pornzog(crate::providers::pornzog::PornzogProvider::new())),
_ => Some(AnyProvider::Perverzija(PerverzijaProvider::new())),
}
}
diff --git a/src/providers/mod.rs b/src/providers/mod.rs
index fbe12bd..e5fcc94 100644
--- a/src/providers/mod.rs
+++ b/src/providers/mod.rs
@@ -26,6 +26,7 @@ pub mod porn00;
pub mod freshporno;
pub mod youjizz;
pub mod paradisehill;
+pub mod pornzog;
pub trait Provider {
@@ -65,6 +66,7 @@ pub enum AnyProvider {
Freshporno(crate::providers::freshporno::FreshpornoProvider),
Youjizz(crate::providers::youjizz::YoujizzProvider),
Paradisehill(crate::providers::paradisehill::ParadisehillProvider),
+ Pornzog(crate::providers::pornzog::PornzogProvider),
}
impl Provider for AnyProvider {
@@ -179,6 +181,10 @@ impl Provider for AnyProvider {
p.get_videos(cache, pool, sort, query, page, per_page, options,)
.await
}
+ AnyProvider::Pornzog(p) => {
+ p.get_videos(cache, pool, sort, query, page, per_page, options,)
+ .await
+ }
}
}
}
diff --git a/src/providers/pornzog.rs b/src/providers/pornzog.rs
new file mode 100644
index 0000000..ea5071b
--- /dev/null
+++ b/src/providers/pornzog.rs
@@ -0,0 +1,189 @@
+use crate::DbPool;
+use crate::providers::Provider;
+use crate::util::cache::VideoCache;
+use crate::util::parse_abbreviated_number;
+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 PornzogProvider {
+ url: String,
+}
+impl PornzogProvider {
+ pub fn new() -> Self {
+ PornzogProvider {
+ url: "https://pornzog.com".to_string(),
+ }
+ }
+ async fn query(
+ &self,
+ cache: VideoCache,
+ page: u8,
+ query: &str,
+ sort: String,
+ options: ServerOptions,
+ ) -> Result> {
+ let mut search_params = vec![format!("page={}", page), "site=hdzog".to_string()];
+ if !query.is_empty() {
+ search_params.push(format!("s={}", query.replace(" ", "+")));
+ }
+ let sort_string = match sort.as_str() {
+ "relevance" => "o=relevance",
+ "viewed" => "o=viewed",
+ "rated" => "o=rated",
+ "longest" => "o=longest",
+ _ => "o=recent",
+ };
+ search_params.push(format!("{}", &sort_string));
+
+ let video_url = format!("{}/search/?{}", self.url, search_params.join("&"));
+ // 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();
+ println!("Fetching URL: {}", video_url);
+ 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();
+ let raw_videos = html.split("class=\"paginator\"").collect::>()[0]
+ .split("class=\"thumb-video ")
+ .collect::>()[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 mut video_url: String = video_segment.split("href=\"").collect::>()[1]
+ .split("\"")
+ .collect::>()[0]
+ .to_string();
+ if video_url.starts_with("/") {
+ video_url = format!("{}{}", self.url, video_url);
+ }
+ let mut title = video_segment.split("alt=\"").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 = format!(
+ "{}",
+ video_segment.split("
>()[1]
+ .split("data-original=\"")
+ .collect::>()[1]
+ .split("\"")
+ .collect::>()[0]
+ .to_string()
+ );
+ let raw_duration = video_segment
+ .split("class=\"duration\">")
+ .collect::>()[1]
+ .split("<")
+ .collect::>()[0]
+ .to_string();
+ let duration = parse_time_to_seconds(raw_duration.as_str()).unwrap_or(0) as u32;
+ // let uploader = video_segment.split("class=\"source\">").collect::>()[1]
+ // .split(">").collect::>()[1]
+ // .split("<").collect::>()[0]
+ // .to_string();
+
+ let tags = video_segment.split("class=\"tags\"").collect::>()[1]
+ .split("
")
+ .collect::>()[0]
+ .split(">()[1..]
+ .iter()
+ .map(|el| {
+ el.split(">").collect::>()[1]
+ .split("<")
+ .collect::>()[0]
+ .to_string()
+ })
+ .collect::>();
+
+ let video_item = VideoItem::new(
+ id,
+ title,
+ video_url.to_string(),
+ "pornzog".to_string(),
+ thumb,
+ duration,
+ )
+ // .uploader(uploader)
+ .tags(tags);
+ items.push(video_item);
+ }
+ return items;
+ }
+}
+
+impl Provider for PornzogProvider {
+ 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> = self
+ .query(
+ cache,
+ page.parse::().unwrap_or(1),
+ query.unwrap_or("".to_string()).as_str(),
+ sort,
+ options,
+ )
+ .await;
+ match videos {
+ Ok(v) => v,
+ Err(e) => {
+ println!("Error fetching videos: {}", e);
+ vec![]
+ }
+ }
+ }
+}