hanime work in progress...
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
use std::vec;
|
||||
use async_trait::async_trait;
|
||||
use error_chain::error_chain;
|
||||
use futures::future::join_all;
|
||||
use serde_json::json;
|
||||
use std::vec;
|
||||
|
||||
use crate::DbPool;
|
||||
use crate::db;
|
||||
use crate::providers::Provider;
|
||||
use crate::util::cache::VideoCache;
|
||||
use crate::videos::{self, ServerOptions, VideoItem};
|
||||
use crate::DbPool;
|
||||
|
||||
error_chain! {
|
||||
foreign_links {
|
||||
@@ -17,7 +18,7 @@ error_chain! {
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||
struct HanimeSearchRequest{
|
||||
struct HanimeSearchRequest {
|
||||
search_text: String,
|
||||
tags: Vec<String>,
|
||||
tags_mode: String,
|
||||
@@ -25,7 +26,7 @@ struct HanimeSearchRequest{
|
||||
blacklist: Vec<String>,
|
||||
order_by: String,
|
||||
ordering: String,
|
||||
page: u8
|
||||
page: u8,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -39,7 +40,7 @@ impl HanimeSearchRequest {
|
||||
blacklist: vec![],
|
||||
order_by: "created_at_unix".to_string(),
|
||||
ordering: "desc".to_string(),
|
||||
page: 0
|
||||
page: 0,
|
||||
}
|
||||
}
|
||||
pub fn tags(mut self, tags: Vec<String>) -> Self {
|
||||
@@ -74,20 +75,19 @@ impl HanimeSearchRequest {
|
||||
self.page = page;
|
||||
self
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
||||
struct HanimeSearchResponse{
|
||||
struct HanimeSearchResponse {
|
||||
page: u8,
|
||||
nbPages:u8,
|
||||
nbPages: u8,
|
||||
nbHits: u32,
|
||||
hitsPerPage: u8,
|
||||
hits: String
|
||||
hits: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||
struct HanimeSearchResult{
|
||||
struct HanimeSearchResult {
|
||||
id: u64,
|
||||
name: String,
|
||||
titles: Vec<String>,
|
||||
@@ -109,7 +109,6 @@ struct HanimeSearchResult{
|
||||
tags: Vec<String>,
|
||||
created_at: u64,
|
||||
released_at: u64,
|
||||
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -125,24 +124,59 @@ impl HanimeProvider {
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_video_item(&self, hit: HanimeSearchResult, pool: DbPool, options: ServerOptions) -> Result<VideoItem> {
|
||||
async fn get_video_item(
|
||||
&self,
|
||||
hit: HanimeSearchResult,
|
||||
pool: DbPool,
|
||||
options: ServerOptions,
|
||||
) -> Result<VideoItem> {
|
||||
let mut conn = pool.get().expect("couldn't get db connection from pool");
|
||||
let db_result = db::get_video(&mut conn,format!("https://h.freeanimehentai.net/api/v8/video?id={}&", hit.slug.clone()));
|
||||
let db_result = db::get_video(
|
||||
&mut conn,
|
||||
format!(
|
||||
"https://h.freeanimehentai.net/api/v8/video?id={}&",
|
||||
hit.slug.clone()
|
||||
),
|
||||
);
|
||||
drop(conn);
|
||||
let id = hit.id.to_string();
|
||||
let title = hit.name;
|
||||
let thumb = hit.cover_url.replace("https://hanime-cdn.com", "https://hottub.spacemoehre.de/proxy/hanime-cdn");
|
||||
let thumb = hit.cover_url.replace(
|
||||
"https://hanime-cdn.com",
|
||||
"https://hottub.spacemoehre.de/proxy/hanime-cdn",
|
||||
);
|
||||
let duration = (hit.duration_in_ms / 1000) as u32; // Convert ms to seconds
|
||||
let channel = "hanime".to_string(); // Placeholder, adjust as needed
|
||||
match db_result {
|
||||
Ok(Some(video_url)) => {
|
||||
return Ok(VideoItem::new(id, title, video_url.clone(), channel, thumb, duration)
|
||||
if video_url != "https://streamable.cloud/hls/stream.m3u8" {
|
||||
return Ok(VideoItem::new(
|
||||
id,
|
||||
title,
|
||||
video_url.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)
|
||||
.rating((hit.likes as f32 / (hit.likes + hit.dislikes) as f32) * 100 as f32)
|
||||
.aspect_ratio(0.68)
|
||||
.formats(vec![videos::VideoFormat::new(video_url.clone(), "1080".to_string(), "m3u8".to_string())]));
|
||||
.formats(vec![videos::VideoFormat::new(
|
||||
video_url.clone(),
|
||||
"1080".to_string(),
|
||||
"m3u8".to_string(),
|
||||
)]));
|
||||
} else {
|
||||
let _ = db::delete_video(
|
||||
&mut pool.get().expect("couldn't get db connection from pool"),
|
||||
format!(
|
||||
"https://h.freeanimehentai.net/api/v8/video?id={}&",
|
||||
hit.slug.clone()
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(None) => (),
|
||||
Err(e) => {
|
||||
@@ -150,49 +184,103 @@ impl HanimeProvider {
|
||||
// return Err(format!("Error fetching video from database: {}", e).into());
|
||||
}
|
||||
}
|
||||
let url = format!("https://h.freeanimehentai.net/api/v8/video?id={}&", hit.slug);
|
||||
let url = format!(
|
||||
"https://cached.freeanimehentai.net/api/v8/guest/videos/{}/manifest",
|
||||
id
|
||||
);
|
||||
|
||||
let mut requester = options.requester.clone().unwrap();
|
||||
let text = requester.get(&url, None).await.unwrap();
|
||||
|
||||
let urls = text.split("\"servers\"").collect::<Vec<&str>>()[1];
|
||||
let payload = json!({
|
||||
"width": 571, "height": 703, "ab": "kh" }
|
||||
);
|
||||
let _ = requester
|
||||
.post_json(
|
||||
&format!(
|
||||
"https://cached.freeanimehentai.net/api/v8/hentai_videos/{}/play",
|
||||
hit.slug
|
||||
),
|
||||
&payload,
|
||||
vec![
|
||||
("Origin".to_string(), "https://hanime.tv".to_string()),
|
||||
("Referer".to_string(), "https://hanime.tv/".to_string()),
|
||||
],
|
||||
)
|
||||
.await; // Initial request to set cookies
|
||||
ntex::time::sleep(ntex::time::Seconds(1)).await;
|
||||
let text = requester
|
||||
.get_raw_with_headers(
|
||||
&url,
|
||||
vec![
|
||||
("Origin".to_string(), "https://hanime.tv".to_string()),
|
||||
("Referer".to_string(), "https://hanime.tv/".to_string()),
|
||||
],
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.text()
|
||||
.await
|
||||
.unwrap();
|
||||
if text.contains("Unautho") {
|
||||
println!("Fetched video details for {}: {}", title, text);
|
||||
return Err(Error::from("Unauthorized"));
|
||||
}
|
||||
let urls = text.split("streams").collect::<Vec<&str>>()[1];
|
||||
let mut url_vec = vec![];
|
||||
|
||||
for el in urls.split("\"url\":\"").collect::<Vec<&str>>(){
|
||||
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());
|
||||
}
|
||||
}
|
||||
let mut conn = pool.get().expect("couldn't get db connection from pool");
|
||||
let _ = db::insert_video(&mut conn, &format!("https://h.freeanimehentai.net/api/v8/video?id={}&", hit.slug.clone()), &url_vec[0].clone());
|
||||
let _ = db::insert_video(
|
||||
&mut conn,
|
||||
&format!(
|
||||
"https://h.freeanimehentai.net/api/v8/video?id={}&",
|
||||
hit.slug.clone()
|
||||
),
|
||||
&url_vec[0].clone(),
|
||||
);
|
||||
drop(conn);
|
||||
Ok(VideoItem::new(id, title, url_vec[0].clone(), channel, thumb, duration)
|
||||
Ok(
|
||||
VideoItem::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::VideoFormat::new(url_vec[0].clone(), "1080".to_string(), "m3u8".to_string())]))
|
||||
|
||||
.rating((hit.likes as f32 / (hit.likes + hit.dislikes) as f32) * 100 as f32)
|
||||
.formats(vec![videos::VideoFormat::new(
|
||||
url_vec[0].clone(),
|
||||
"1080".to_string(),
|
||||
"m3u8".to_string(),
|
||||
)]),
|
||||
)
|
||||
}
|
||||
|
||||
async fn get(&self, cache: VideoCache, pool: DbPool, page: u8, query: String, sort:String, options: ServerOptions) -> Result<Vec<VideoItem>> {
|
||||
async fn get(
|
||||
&self,
|
||||
cache: VideoCache,
|
||||
pool: DbPool,
|
||||
page: u8,
|
||||
query: String,
|
||||
sort: String,
|
||||
options: ServerOptions,
|
||||
) -> Result<Vec<VideoItem>> {
|
||||
let index = format!("hanime:{}:{}:{}", query, page, sort);
|
||||
let order_by = match sort.contains("."){
|
||||
let order_by = match sort.contains(".") {
|
||||
true => sort.split(".").collect::<Vec<&str>>()[0].to_string(),
|
||||
false => "created_at_unix".to_string(),
|
||||
};
|
||||
let ordering = match sort.contains("."){
|
||||
let ordering = match sort.contains(".") {
|
||||
true => sort.split(".").collect::<Vec<&str>>()[1].to_string(),
|
||||
false => "desc".to_string(),
|
||||
};
|
||||
let old_items = match cache.get(&index) {
|
||||
Some((time, items)) => {
|
||||
if time.elapsed().unwrap_or_default().as_secs() < 60 * 60 * 12 {
|
||||
if time.elapsed().unwrap_or_default().as_secs() < 1 {
|
||||
//println!("Cache hit for URL: {}", index);
|
||||
return Ok(items.clone());
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
items.clone()
|
||||
}
|
||||
}
|
||||
@@ -202,15 +290,16 @@ impl HanimeProvider {
|
||||
};
|
||||
|
||||
let search = HanimeSearchRequest::new()
|
||||
.page(page-1)
|
||||
.page(page - 1)
|
||||
.search_text(query.clone())
|
||||
.order_by(order_by)
|
||||
.ordering(ordering);
|
||||
|
||||
let mut requester = options.requester.clone().unwrap();
|
||||
let response = requester.post_json("https://search.htv-services.com/search", &search, vec![]).await.unwrap();
|
||||
|
||||
|
||||
let response = requester
|
||||
.post_json("https://search.htv-services.com/search", &search, vec![])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let hits = match response.json::<HanimeSearchResponse>().await {
|
||||
Ok(resp) => resp.hits,
|
||||
@@ -222,12 +311,11 @@ impl HanimeProvider {
|
||||
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(), pool.clone(), options.clone()));
|
||||
let results: Vec<Result<VideoItem>> = join_all(futures).await;
|
||||
let video_items: Vec<VideoItem> = results
|
||||
let futures = hits_json
|
||||
.into_iter()
|
||||
.filter_map(Result::ok)
|
||||
.collect();
|
||||
.map(|el| self.get_video_item(el.clone(), pool.clone(), options.clone()));
|
||||
let results: Vec<Result<VideoItem>> = join_all(futures).await;
|
||||
let video_items: Vec<VideoItem> = results.into_iter().filter_map(Result::ok).collect();
|
||||
if !video_items.is_empty() {
|
||||
cache.remove(&index);
|
||||
cache.insert(index.clone(), video_items.clone());
|
||||
@@ -255,8 +343,28 @@ impl Provider for HanimeProvider {
|
||||
let _ = per_page;
|
||||
let _ = sort;
|
||||
let videos: std::result::Result<Vec<VideoItem>, Error> = match query {
|
||||
Some(q) => self.get(cache, pool, page.parse::<u8>().unwrap_or(1), q, sort, options).await,
|
||||
None => self.get(cache, pool, page.parse::<u8>().unwrap_or(1), "".to_string(), sort, options).await,
|
||||
Some(q) => {
|
||||
self.get(
|
||||
cache,
|
||||
pool,
|
||||
page.parse::<u8>().unwrap_or(1),
|
||||
q,
|
||||
sort,
|
||||
options,
|
||||
)
|
||||
.await
|
||||
}
|
||||
None => {
|
||||
self.get(
|
||||
cache,
|
||||
pool,
|
||||
page.parse::<u8>().unwrap_or(1),
|
||||
"".to_string(),
|
||||
sort,
|
||||
options,
|
||||
)
|
||||
.await
|
||||
}
|
||||
};
|
||||
match videos {
|
||||
Ok(v) => v,
|
||||
|
||||
Reference in New Issue
Block a user