implemented hanime
This commit is contained in:
24
src/api.rs
24
src/api.rs
@@ -1,7 +1,9 @@
|
||||
use futures::channel;
|
||||
use ntex::http::header;
|
||||
use ntex::web;
|
||||
use ntex::web::HttpRequest;
|
||||
|
||||
use crate::providers::hanime::HanimeProvider;
|
||||
use crate::providers::perverzija::PerverzijaProvider;
|
||||
use crate::util::cache::VideoCache;
|
||||
use crate::{providers::*, status::*, videos::*};
|
||||
@@ -158,6 +160,17 @@ async fn status(req: HttpRequest) -> Result<impl web::Responder, web::Error> {
|
||||
],
|
||||
nsfw: true,
|
||||
});
|
||||
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![],
|
||||
nsfw: true,
|
||||
});
|
||||
status.iconUrl = format!("http://{}/favicon.ico", host).to_string();
|
||||
Ok(web::HttpResponse::Ok().json(&status))
|
||||
}
|
||||
@@ -198,10 +211,19 @@ async fn videos_post(
|
||||
.parse()
|
||||
.unwrap();
|
||||
let featured = video_request.featured.as_deref().unwrap_or("all").to_string();
|
||||
let provider = PerverzijaProvider::new();
|
||||
let provider = get_provider(channel.as_str())
|
||||
.ok_or_else(|| web::error::ErrorBadRequest("Invalid channel".to_string()))?;
|
||||
let video_items = provider
|
||||
.get_videos(cache.get_ref().clone(), channel, sort, query, page.to_string(), perPage.to_string(), featured)
|
||||
.await;
|
||||
videos.items = video_items.clone();
|
||||
Ok(web::HttpResponse::Ok().json(&videos))
|
||||
}
|
||||
|
||||
pub fn get_provider(channel: &str) -> Option<AnyProvider> {
|
||||
match channel {
|
||||
"perverzija" => Some(AnyProvider::Perverzija(PerverzijaProvider::new())),
|
||||
"hanime" => Some(AnyProvider::Hanime(HanimeProvider::new())),
|
||||
_ => Some(AnyProvider::Perverzija(PerverzijaProvider::new())),
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
use std::time::Duration;
|
||||
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::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, Video_Embed, Video_Item}; // Make sure Provider trait is imported
|
||||
@@ -16,25 +19,118 @@ error_chain! {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
||||
struct HanimeSearchRequest{
|
||||
search_text: String,
|
||||
tags: Vec<String>,
|
||||
tags_mode: String,
|
||||
brands: Vec<String>,
|
||||
blacklist: Vec<String>,
|
||||
order_by: String,
|
||||
ordering: String,
|
||||
page: u8
|
||||
}
|
||||
|
||||
impl HanimeSearchRequest {
|
||||
pub fn new() -> Self {
|
||||
HanimeSearchRequest {
|
||||
search_text: "".to_string(),
|
||||
tags: vec![],
|
||||
tags_mode: "AND".to_string(),
|
||||
brands: vec![],
|
||||
blacklist: vec![],
|
||||
order_by: "created_at_unix".to_string(),
|
||||
ordering: "desc".to_string(),
|
||||
page: 0
|
||||
}
|
||||
}
|
||||
pub fn tags(mut self, tags: Vec<String>) -> Self {
|
||||
self.tags = tags;
|
||||
self
|
||||
}
|
||||
pub fn search_text(mut self, search_text: String) -> Self {
|
||||
self.search_text = search_text;
|
||||
self
|
||||
}
|
||||
pub fn tags_mode(mut self, tags_mode: String) -> Self {
|
||||
self.tags_mode = tags_mode;
|
||||
self
|
||||
}
|
||||
pub fn brands(mut self, brands: Vec<String>) -> Self {
|
||||
self.brands = brands;
|
||||
self
|
||||
}
|
||||
pub fn blacklist(mut self, blacklist: Vec<String>) -> Self {
|
||||
self.blacklist = blacklist;
|
||||
self
|
||||
}
|
||||
pub fn order_by(mut self, order_by: String) -> Self {
|
||||
self.order_by = order_by;
|
||||
self
|
||||
}
|
||||
pub fn ordering(mut self, ordering: String) -> Self {
|
||||
self.ordering = ordering;
|
||||
self
|
||||
}
|
||||
pub fn page(mut self, page: u8) -> Self {
|
||||
self.page = page;
|
||||
self
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
||||
struct HanimeSearchResponse{
|
||||
page: u8,
|
||||
nbPages:u8,
|
||||
nbHits: u32,
|
||||
hitsPerPage: u8,
|
||||
hits: String
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||
struct HanimeSearchResult{
|
||||
id: u64,
|
||||
name: String,
|
||||
titles: Vec<String>,
|
||||
slug: String,
|
||||
description: String,
|
||||
views: u64,
|
||||
interests: u64,
|
||||
poster_url: String,
|
||||
cover_url: String,
|
||||
brand: String,
|
||||
brand_id: u64,
|
||||
duration_in_ms: u32,
|
||||
is_censored: bool,
|
||||
rating: f32,
|
||||
likes: u64,
|
||||
dislikes: u64,
|
||||
downloads: u64,
|
||||
monthly_ranked: Option<u64>,
|
||||
tags: Vec<String>,
|
||||
created_at: u64,
|
||||
released_at: u64,
|
||||
|
||||
}
|
||||
|
||||
pub struct HanimeProvider {
|
||||
url: String,
|
||||
}
|
||||
|
||||
impl HanimeProvider {
|
||||
pub fn new() -> Self {
|
||||
HanimeProvider {
|
||||
url: "https://hanime.tv/".to_string(),
|
||||
}
|
||||
}
|
||||
async fn get(&self, page: &u8, featured: String) -> Result<Vec<Video_Item>> {
|
||||
let mut prefix_uri = "".to_string();
|
||||
if featured == "featured" {
|
||||
prefix_uri = "featured-scenes/".to_string();
|
||||
}
|
||||
let mut url = format!("{}{}page/{}/", self.url, prefix_uri, page);
|
||||
if page == &1 {
|
||||
url = format!("{}{}", self.url, prefix_uri);
|
||||
}
|
||||
|
||||
async fn get_video_item(&self, hit: HanimeSearchResult) -> Result<(u64,Video_Item)> {
|
||||
let id = hit.id.to_string();
|
||||
let title = hit.name;
|
||||
let thumb = hit.poster_url;
|
||||
let duration = (hit.duration_in_ms / 1000) as u32; // Convert ms to seconds
|
||||
let channel = "hanime".to_string(); // Placeholder, adjust as needed
|
||||
|
||||
let client = match env::var("BURP_URL").as_deref() {
|
||||
Ok(burp_url) =>
|
||||
@@ -48,49 +144,53 @@ impl HanimeProvider {
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()?,
|
||||
};
|
||||
let url = format!("https://h.freeanimehentai.net/api/v8/video?id={}&", hit.slug);
|
||||
let response = client.get(url).send().await?;
|
||||
|
||||
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<Video_Item> = self.get_video_items_from_html(text.clone());
|
||||
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;
|
||||
println!("FlareSolverr result: {:?}", result);
|
||||
let video_items = match result {
|
||||
Ok(res) => {
|
||||
// println!("FlareSolverr response: {}", res);
|
||||
self.get_video_items_from_html(res.solution.response)
|
||||
let text = match response.status().is_success() {
|
||||
true => {
|
||||
response.text().await?},
|
||||
false => {
|
||||
print!("Failed to fetch video item: {}\n\n", response.status());
|
||||
return Err(format!("Failed to fetch video item: {}", response.status()).into());
|
||||
} };
|
||||
let urls = text.split("\"servers\"").collect::<Vec<&str>>()[1];
|
||||
let mut url_vec = vec![];
|
||||
|
||||
for el in urls.split("\"url\":\"").collect::<Vec<&str>>(){
|
||||
let url = el.split("\"").collect::<Vec<&str>>()[0];
|
||||
if !url.is_empty() && url.contains("m3u8") {
|
||||
url_vec.push(url.to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Error solving FlareSolverr: {}", e);
|
||||
return Err("Failed to solve FlareSolverr".into());
|
||||
}
|
||||
Ok((hit.created_at, Video_Item::new(id, title, url_vec[0].clone(), channel, thumb, duration)
|
||||
.tags(hit.tags)
|
||||
.uploader(hit.brand)
|
||||
.views(hit.views as u32)
|
||||
.rating((hit.likes as f32 / (hit.likes + hit.dislikes)as f32) * 100 as f32)
|
||||
.formats(vec![videos::Video_Format::new(url_vec[0].clone(), "1080".to_string(), "m3u8".to_string())])))
|
||||
|
||||
}
|
||||
|
||||
async fn get(&self, cache: VideoCache, page: u8, query: String) -> Result<Vec<Video_Item>> {
|
||||
let index = format!("{}:{}", query, page);
|
||||
|
||||
let old_items = match cache.get(&index) {
|
||||
Some((time, items)) => {
|
||||
if time.elapsed().unwrap_or_default().as_secs() < 60 * 60 * 12 {
|
||||
println!("Cache hit for URL: {}", index);
|
||||
return Ok(items.clone());
|
||||
}
|
||||
else{
|
||||
items.clone()
|
||||
}
|
||||
}
|
||||
None => {
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
Ok(video_items)
|
||||
}
|
||||
}
|
||||
async fn query(&self, page: &u8, query: &str) -> Result<Vec<Video_Item>> {
|
||||
println!("query: {}", query);
|
||||
let search_string = query.replace(" ", "+");
|
||||
let mut url = format!(
|
||||
"{}advanced-search/?_sf_s={}&sf_paged={}",
|
||||
self.url, search_string, page
|
||||
);
|
||||
if page == &1 {
|
||||
url = format!("{}advanced-search/?_sf_s={}", self.url, search_string);
|
||||
}
|
||||
|
||||
|
||||
let search = HanimeSearchRequest::new().page(page-1).search_text(query.clone());
|
||||
let client = match env::var("BURP_URL").as_deref() {
|
||||
Ok(burp_url) =>
|
||||
reqwest::Client::builder()
|
||||
@@ -103,241 +203,45 @@ impl HanimeProvider {
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()?,
|
||||
};
|
||||
let response = client.post("https://search.htv-services.com/search")
|
||||
.json(&search)
|
||||
.send().await?;
|
||||
|
||||
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<Video_Item> = self.get_video_items_from_html_query(text.clone());
|
||||
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;
|
||||
println!("FlareSolverr result: {:?}", result);
|
||||
let video_items = match result {
|
||||
Ok(res) => {
|
||||
// println!("FlareSolverr response: {}", res);
|
||||
self.get_video_items_from_html_query(res.solution.response)
|
||||
}
|
||||
|
||||
|
||||
let hits = match response.json::<HanimeSearchResponse>().await {
|
||||
Ok(resp) => resp.hits,
|
||||
Err(e) => {
|
||||
println!("Error solving FlareSolverr: {}", e);
|
||||
return Err("Failed to solve FlareSolverr".into());
|
||||
println!("Failed to parse HanimeSearchResponse: {}", e);
|
||||
return Ok(old_items);
|
||||
}
|
||||
};
|
||||
let hits_json: Vec<HanimeSearchResult> = serde_json::from_str(hits.as_str())
|
||||
.map_err(|e| format!("Failed to parse hits JSON: {}", e))?;
|
||||
// let timeout_duration = Duration::from_secs(120);
|
||||
let futures = hits_json.into_iter().map(|el| self.get_video_item(el.clone()));
|
||||
let results: Vec<Result<(u64,Video_Item)>> = join_all(futures).await;
|
||||
let mut items: Vec<(u64, Video_Item)> = results
|
||||
.into_iter()
|
||||
.filter_map(Result::ok)
|
||||
.collect();
|
||||
items.sort_by(|a, b| b.0.cmp(&a.0));
|
||||
let video_items: Vec<Video_Item> = items.into_iter().map(|(_, item)| item).collect();
|
||||
if !video_items.is_empty() {
|
||||
cache.remove(&index);
|
||||
cache.insert(index.clone(), video_items.clone());
|
||||
} else {
|
||||
return Ok(old_items);
|
||||
}
|
||||
|
||||
Ok(video_items)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_video_items_from_html(&self, html: String) -> Vec<Video_Item> {
|
||||
// println!("HTML: {}", html);
|
||||
let mut items: Vec<Video_Item> = Vec::new();
|
||||
let video_listing_content = html.split("video-listing-content").collect::<Vec<&str>>()[1];
|
||||
let raw_videos = video_listing_content
|
||||
.split("video-item post")
|
||||
.collect::<Vec<&str>>()[1..]
|
||||
.to_vec();
|
||||
for video_segment in &raw_videos {
|
||||
let vid = video_segment.split("\n").collect::<Vec<&str>>();
|
||||
if vid.len() > 20 {
|
||||
println!("Skipping video segment with unexpected length: {}", vid.len());
|
||||
continue;
|
||||
}
|
||||
for (i, line) in vid.iter().enumerate() {
|
||||
if line.trim().is_empty() {
|
||||
println!("Empty line at index {}: {}", i, line);
|
||||
}
|
||||
}
|
||||
let mut title = vid[1].split(">").collect::<Vec<&str>>()[1]
|
||||
.split("<")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string();
|
||||
// html decode
|
||||
title = decode(title.as_bytes()).to_string().unwrap_or(title);
|
||||
let url = vid[1].split("iframe src="").collect::<Vec<&str>>()[1]
|
||||
.split(""")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string();
|
||||
let id = url.split("data=").collect::<Vec<&str>>()[1]
|
||||
.split("&")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string();
|
||||
let raw_duration = match vid.len() {
|
||||
10 => vid[6].split("time_dur\">").collect::<Vec<&str>>()[1]
|
||||
.split("<")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string(),
|
||||
_ => "00:00".to_string(),
|
||||
};
|
||||
let duration = parse_time_to_seconds(&raw_duration).unwrap_or(0) as u32;
|
||||
|
||||
let thumb = match vid[4].contains("srcset=") {
|
||||
true => vid[4].split("sizes=").collect::<Vec<&str>>()[1]
|
||||
.split("w, ")
|
||||
.collect::<Vec<&str>>()
|
||||
.last()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.split(" ")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string(),
|
||||
false => vid[4].split("src=\"").collect::<Vec<&str>>()[1]
|
||||
.split("\"")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string(),
|
||||
};
|
||||
let embed_html = vid[1].split("data-embed='").collect::<Vec<&str>>()[1]
|
||||
.split("'")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string();
|
||||
let referer_url = vid[1].split("data-url='").collect::<Vec<&str>>()[1]
|
||||
.split("'")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string();
|
||||
let embed = Video_Embed::new(embed_html, url.clone());
|
||||
|
||||
let mut tags: Vec<String> = Vec::new(); // Placeholder for tags, adjust as needed
|
||||
for tag in vid[0].split(" ").collect::<Vec<&str>>(){
|
||||
if tag.starts_with("tag-") {
|
||||
let tag_name = tag.split("tag-").collect::<Vec<&str>>()[1]
|
||||
.to_string();
|
||||
if !tag_name.is_empty() {
|
||||
tags.push(tag_name.replace("-", " ").to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut video_item = Video_Item::new(
|
||||
id,
|
||||
title,
|
||||
url.clone(),
|
||||
"hanime".to_string(),
|
||||
thumb,
|
||||
duration,
|
||||
).tags(tags)
|
||||
.embed(embed.clone());
|
||||
let mut format =
|
||||
videos::Video_Format::new(url.clone(), "1080".to_string(), "m3u8".to_string());
|
||||
format.add_http_header("Referer".to_string(), referer_url.clone());
|
||||
if let Some(formats) = video_item.formats.as_mut() {
|
||||
formats.push(format);
|
||||
} else {
|
||||
video_item.formats = Some(vec![format]);
|
||||
}
|
||||
items.push(video_item);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
fn get_video_items_from_html_query(&self, html: String) -> Vec<Video_Item> {
|
||||
let mut items: Vec<Video_Item> = Vec::new();
|
||||
let video_listing_content = html.split("search-filter-results-").collect::<Vec<&str>>()[1];
|
||||
let raw_videos = video_listing_content
|
||||
.split("video-item post")
|
||||
.collect::<Vec<&str>>()[1..]
|
||||
.to_vec();
|
||||
for video_segment in &raw_videos {
|
||||
let vid = video_segment.split("\n").collect::<Vec<&str>>();
|
||||
if vid.len() > 20 {
|
||||
continue;
|
||||
}
|
||||
let mut title = vid[3].split("title='").collect::<Vec<&str>>()[1]
|
||||
.split("'")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string();
|
||||
title = decode(title.as_bytes()).to_string().unwrap_or(title);
|
||||
let url = vid[4].split("iframe src="").collect::<Vec<&str>>()[1]
|
||||
.split(""")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string();
|
||||
let id = url.split("data=").collect::<Vec<&str>>()[1]
|
||||
.split("&")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string();
|
||||
let raw_duration = match vid.len() {
|
||||
18 => vid[16].split("time_dur\">").collect::<Vec<&str>>()[1]
|
||||
.split("<")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string(),
|
||||
_ => "00:00".to_string(),
|
||||
};
|
||||
let duration = parse_time_to_seconds(&raw_duration).unwrap_or(0) as u32;
|
||||
let thumb_index = match vid.len() {
|
||||
18 => 14,
|
||||
13 => 8,
|
||||
_ => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let thumb = match vid[thumb_index].contains("srcset=") {
|
||||
true => vid[thumb_index].split("sizes=").collect::<Vec<&str>>()[1]
|
||||
.split("w, ")
|
||||
.collect::<Vec<&str>>()
|
||||
.last()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.split(" ")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string(),
|
||||
false => vid[thumb_index].split("src=\"").collect::<Vec<&str>>()[1]
|
||||
.split("\"")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string(),
|
||||
};
|
||||
let embed_html = vid[4].split("data-embed='").collect::<Vec<&str>>()[1]
|
||||
.split("'")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string();
|
||||
let referer_url = vid[4].split("data-url='").collect::<Vec<&str>>()[1]
|
||||
.split("'")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string();
|
||||
let embed = Video_Embed::new(embed_html, url.clone());
|
||||
let mut tags: Vec<String> = Vec::new(); // Placeholder for tags, adjust as needed
|
||||
for tag in vid[0].split(" ").collect::<Vec<&str>>(){
|
||||
if tag.starts_with("tag-") {
|
||||
let tag_name = tag.split("tag-").collect::<Vec<&str>>()[1]
|
||||
.to_string();
|
||||
if !tag_name.is_empty() {
|
||||
tags.push(tag_name.replace("-", " ").to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut video_item = Video_Item::new(
|
||||
id,
|
||||
title,
|
||||
url.clone(),
|
||||
"hanime".to_string(),
|
||||
thumb,
|
||||
duration,
|
||||
)
|
||||
.tags(tags)
|
||||
.embed(embed.clone());
|
||||
let mut format =
|
||||
videos::Video_Format::new(url.clone(), "1080".to_string(), "m3u8".to_string());
|
||||
format.add_http_header("Referer".to_string(), referer_url.clone());
|
||||
if let Some(formats) = video_item.formats.as_mut() {
|
||||
formats.push(format);
|
||||
} else {
|
||||
video_item.formats = Some(vec![format]);
|
||||
}
|
||||
items.push(video_item);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
impl Provider for HanimeProvider {
|
||||
async fn get_videos(
|
||||
&self,
|
||||
cache: VideoCache,
|
||||
_channel: String,
|
||||
sort: String,
|
||||
query: Option<String>,
|
||||
@@ -345,11 +249,12 @@ impl Provider for HanimeProvider {
|
||||
per_page: String,
|
||||
featured: String,
|
||||
) -> Vec<Video_Item> {
|
||||
let _ = featured;
|
||||
let _ = per_page;
|
||||
let _ = sort;
|
||||
let videos: std::result::Result<Vec<Video_Item>, Error> = match query {
|
||||
Some(q) => self.query(&page.parse::<u8>().unwrap_or(1), &q).await,
|
||||
None => self.get(&page.parse::<u8>().unwrap_or(1), featured).await,
|
||||
Some(q) => self.get(cache, page.parse::<u8>().unwrap_or(1), q).await,
|
||||
None => self.get(cache, page.parse::<u8>().unwrap_or(1), "".to_string()).await,
|
||||
};
|
||||
match videos {
|
||||
Ok(v) => v,
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
use crate::{util::cache::VideoCache, videos::Video_Item};
|
||||
use crate::{providers::{hanime::HanimeProvider, perverzija::PerverzijaProvider}, util::cache::VideoCache, videos::Video_Item};
|
||||
|
||||
pub mod perverzija;
|
||||
pub mod hanime;
|
||||
|
||||
pub trait Provider{
|
||||
async fn get_videos(&self, cache: VideoCache ,channel: String, sort: String, query: Option<String>, page: String, per_page: String, featured: String) -> Vec<Video_Item>;
|
||||
}
|
||||
|
||||
pub enum AnyProvider {
|
||||
Perverzija(PerverzijaProvider),
|
||||
Hanime(HanimeProvider),
|
||||
}
|
||||
impl Provider for AnyProvider {
|
||||
async fn get_videos(&self, cache: VideoCache ,channel: String, sort: String, query: Option<String>, page: String, per_page: String, featured: String) -> Vec<Video_Item> {
|
||||
match self {
|
||||
AnyProvider::Perverzija(p) => p.get_videos(cache ,channel, sort, query, page, per_page, featured).await,
|
||||
AnyProvider::Hanime(p) => p.get_videos(cache ,channel, sort, query, page, per_page, featured).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ impl PerverzijaProvider {
|
||||
url: "https://tube.perverzija.com/".to_string(),
|
||||
}
|
||||
}
|
||||
async fn get(&self, cache:VideoCache ,page: &u8, featured: String) -> Result<Vec<Video_Item>> {
|
||||
async fn get(&self, cache:VideoCache ,page: u8, featured: String) -> Result<Vec<Video_Item>> {
|
||||
println!("get");
|
||||
|
||||
//TODO
|
||||
@@ -41,7 +41,7 @@ impl PerverzijaProvider {
|
||||
prefix_uri = "featured-scenes/".to_string();
|
||||
}
|
||||
let mut url = format!("{}{}page/{}/", self.url, prefix_uri, page);
|
||||
if page == &1 {
|
||||
if page == 1 {
|
||||
url = format!("{}{}", self.url, prefix_uri);
|
||||
}
|
||||
|
||||
@@ -116,14 +116,14 @@ impl PerverzijaProvider {
|
||||
Ok(video_items)
|
||||
}
|
||||
}
|
||||
async fn query(&self, cache: VideoCache, page: &u8, query: &str) -> Result<Vec<Video_Item>> {
|
||||
async fn query(&self, cache: VideoCache, page: u8, query: &str) -> Result<Vec<Video_Item>> {
|
||||
println!("query: {}", query);
|
||||
let search_string = query.replace(" ", "+");
|
||||
let mut url = format!(
|
||||
"{}advanced-search/?_sf_s={}&sf_paged={}",
|
||||
self.url, search_string, page
|
||||
);
|
||||
if page == &1 {
|
||||
if page == 1 {
|
||||
url = format!("{}advanced-search/?_sf_s={}", self.url, search_string);
|
||||
}
|
||||
|
||||
@@ -216,11 +216,6 @@ impl PerverzijaProvider {
|
||||
println!("Skipping video segment with unexpected length: {}", vid.len());
|
||||
continue;
|
||||
}
|
||||
for (i, line) in vid.iter().enumerate() {
|
||||
if line.trim().is_empty() {
|
||||
println!("Empty line at index {}: {}", i, line);
|
||||
}
|
||||
}
|
||||
let mut title = vid[1].split(">").collect::<Vec<&str>>()[1]
|
||||
.split("<")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
@@ -283,7 +278,7 @@ impl PerverzijaProvider {
|
||||
let mut video_item = Video_Item::new(
|
||||
id,
|
||||
title,
|
||||
url.clone(),
|
||||
embed.source.clone(),
|
||||
"perverzija".to_string(),
|
||||
thumb,
|
||||
duration,
|
||||
@@ -343,15 +338,8 @@ impl PerverzijaProvider {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let thumb = match vid[thumb_index].contains("srcset=") {
|
||||
true => vid[thumb_index].split("sizes=").collect::<Vec<&str>>()[1]
|
||||
.split("w, ")
|
||||
.collect::<Vec<&str>>()
|
||||
.last()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.split(" ")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
let thumb = match vid[thumb_index].contains("srcset=\"") {
|
||||
true => vid[thumb_index].split(" ").collect::<Vec<&str>>()[0]
|
||||
.to_string(),
|
||||
false => vid[thumb_index].split("src=\"").collect::<Vec<&str>>()[1]
|
||||
.split("\"")
|
||||
@@ -418,8 +406,8 @@ impl Provider for PerverzijaProvider {
|
||||
let _ = per_page;
|
||||
let _ = sort;
|
||||
let videos: std::result::Result<Vec<Video_Item>, Error> = match query {
|
||||
Some(q) => self.query(cache, &page.parse::<u8>().unwrap_or(1), &q).await,
|
||||
None => self.get(cache, &page.parse::<u8>().unwrap_or(1), featured).await,
|
||||
Some(q) => self.query(cache, page.parse::<u8>().unwrap_or(1), &q).await,
|
||||
None => self.get(cache, page.parse::<u8>().unwrap_or(1), featured).await,
|
||||
};
|
||||
match videos {
|
||||
Ok(v) => v,
|
||||
|
||||
@@ -36,8 +36,8 @@ pub struct PageInfo {
|
||||
|
||||
#[derive(serde::Serialize, Debug, Clone)]
|
||||
pub struct Video_Embed{
|
||||
html: String,
|
||||
source: String,
|
||||
pub html: String,
|
||||
pub source: String,
|
||||
}
|
||||
impl Video_Embed {
|
||||
pub fn new(html: String, source: String) -> Self {
|
||||
|
||||
Reference in New Issue
Block a user