hanime changes

This commit is contained in:
Simon
2026-05-22 09:07:18 +00:00
committed by ForgeCode
parent 7149847a2a
commit a5c6290596
4 changed files with 221 additions and 135 deletions

View File

@@ -174,99 +174,47 @@ impl HanimeProvider {
}
}
async fn get_video_item(
&self,
hit: HanimeSearchResult,
pool: DbPool,
options: ServerOptions,
) -> Result<VideoItem> {
let mut conn = match pool.get() {
Ok(conn) => conn,
Err(e) => {
report_provider_error("hanime", "get_video_item.pool_get", &e.to_string()).await;
return Err(Error::from("Failed to get DB connection"));
}
};
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 = crate::providers::build_proxy_url(
&options,
"hanime-cdn",
&crate::providers::strip_url_scheme(&hit.cover_url),
);
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)) => {
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)
.aspect_ratio(0.68)
.formats(vec![videos::VideoFormat::new(
video_url.clone(),
"1080".to_string(),
"m3u8".to_string(),
)]));
} else {
match pool.get() {
Ok(mut conn) => {
let _ = db::delete_video(
&mut conn,
format!(
"https://h.freeanimehentai.net/api/v8/video?id={}&",
hit.slug.clone()
),
);
}
Err(e) => {
report_provider_error_background(
"hanime",
"get_video_item.delete_video.pool_get",
&e.to_string(),
);
}
}
}
}
Ok(None) => (),
Err(e) => {
println!("Error fetching video from database: {}", e);
// return Err(format!("Error fetching video from database: {}", e).into());
}
}
let url = format!(
"https://cached.freeanimehentai.net/api/v8/guest/videos/{}/manifest",
id
);
fn db_key(slug: &str) -> String {
format!("https://h.freeanimehentai.net/api/v8/video?id={slug}&")
}
let mut requester =
crate::providers::requester_or_default(&options, module_path!(), "missing_requester");
let payload = json!({
"width": 571, "height": 703, "ab": "kh" }
fn build_video_item(
id: String,
title: String,
video_url: String,
channel: String,
thumb: String,
duration: u32,
tags: Vec<String>,
brand: String,
views: u64,
likes: u64,
dislikes: u64,
) -> VideoItem {
VideoItem::new(id, title, video_url.clone(), channel, thumb, duration)
.tags(tags)
.uploader(brand)
.views(views as u32)
.rating((likes as f32 / (likes + dislikes) as f32) * 100_f32)
.aspect_ratio(0.68)
.formats(vec![videos::VideoFormat::new(
video_url,
"1080".to_string(),
"m3u8".to_string(),
)])
}
async fn fetch_stream_url(&self, id: &str, slug: &str, options: &ServerOptions) -> Result<String> {
let manifest_url = format!(
"https://cached.freeanimehentai.net/api/v8/guest/videos/{id}/manifest"
);
let mut requester =
crate::providers::requester_or_default(options, module_path!(), "missing_requester");
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
"https://cached.freeanimehentai.net/api/v8/hentai_videos/{slug}/play"
),
&payload,
vec![
@@ -274,11 +222,11 @@ impl HanimeProvider {
("Referer".to_string(), "https://hanime.tv/".to_string()),
],
)
.await; // Initial request to set cookies
.await;
ntex::time::sleep(ntex::time::Seconds(1)).await;
let text = requester
.get_raw_with_headers(
&url,
&manifest_url,
vec![
("Origin".to_string(), "https://hanime.tv".to_string()),
("Referer".to_string(), "https://hanime.tv/".to_string()),
@@ -288,77 +236,97 @@ impl HanimeProvider {
.map_err(|e| {
report_provider_error_background(
"hanime",
"get_video_item.get_raw_with_headers",
"fetch_stream_url.get_raw_with_headers",
&e.to_string(),
);
Error::from(format!("Failed to fetch manifest response: {e}"))
Error::from(format!("Failed to fetch manifest: {e}"))
})?
.text()
.await
.map_err(|e| {
report_provider_error_background(
"hanime",
"get_video_item.response_text",
"fetch_stream_url.response_text",
&e.to_string(),
);
Error::from(format!("Failed to decode manifest response body: {e}"))
Error::from(format!("Failed to decode manifest body: {e}"))
})?;
if text.contains("Unautho") {
println!("Fetched video details for {}: {}", title, text);
return Err(Error::from("Unauthorized"));
}
let urls = text
let urls_section = text
.split("streams")
.nth(1)
.ok_or_else(|| Error::from("Missing streams section in manifest"))?;
let mut url_vec = vec![];
for el in urls.split("\"url\":\"").collect::<Vec<&str>>() {
let url = el
.split("\"")
.collect::<Vec<&str>>()
.get(0)
.copied()
.unwrap_or_default();
let mut url_vec = vec![];
for el in urls_section.split("\"url\":\"") {
let url = el.split('"').next().unwrap_or_default();
if !url.is_empty() && url.contains("m3u8") {
url_vec.push(url.to_string());
}
}
let first_url = url_vec
.first()
.cloned()
.ok_or_else(|| Error::from("No stream URL found in manifest"))?;
match pool.get() {
Ok(mut conn) => {
let _ = db::insert_video(
&mut conn,
&format!(
"https://h.freeanimehentai.net/api/v8/video?id={}&",
hit.slug.clone()
),
&first_url,
);
url_vec
.into_iter()
.next()
.ok_or_else(|| Error::from("No stream URL found in manifest"))
}
async fn get_video_item(
&self,
hit: HanimeSearchResult,
pool: DbPool,
options: ServerOptions,
) -> Result<VideoItem> {
let id = hit.id.to_string();
let title = hit.name;
let thumb = crate::providers::build_proxy_url(
&options,
"hanime-cdn",
&crate::providers::strip_url_scheme(&hit.cover_url),
);
let duration = (hit.duration_in_ms / 1000) as u32;
let channel = "hanime".to_string();
let db_key = Self::db_key(&hit.slug);
match self.fetch_stream_url(&id, &hit.slug, &options).await {
Ok(stream_url) => {
if let Ok(mut conn) = pool.get() {
let _ = db::insert_video(&mut conn, &db_key, &stream_url);
}
return Ok(Self::build_video_item(
id, title, stream_url, channel, thumb, duration,
hit.tags, hit.brand, hit.views, hit.likes, hit.dislikes,
));
}
Err(e) => {
report_provider_error_background(
"hanime",
"get_video_item.insert_video.pool_get",
&e.to_string(),
);
report_provider_error_background("hanime", "get_video_item.fetch_stream_url", &e.to_string());
}
}
Ok(
VideoItem::new(id, title, first_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)
.formats(vec![videos::VideoFormat::new(
first_url,
"1080".to_string(),
"m3u8".to_string(),
)]),
)
// API failed — fall back to DB
let db_result = pool.get().ok().and_then(|mut conn| {
db::get_video(&mut conn, db_key.clone()).ok().flatten()
});
match db_result {
Some(video_url) if video_url != "https://streamable.cloud/hls/stream.m3u8" => {
Ok(Self::build_video_item(
id, title, video_url, channel, thumb, duration,
hit.tags, hit.brand, hit.views, hit.likes, hit.dislikes,
))
}
Some(_) => {
if let Ok(mut conn) = pool.get() {
let _ = db::delete_video(&mut conn, db_key);
}
Err(Error::from("Stale DB entry and API unavailable"))
}
None => Err(Error::from("API unavailable and no DB fallback")),
}
}
async fn get(