Spaces:
Sleeping
Sleeping
Create main.py
Browse files
main.py
ADDED
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Optional
|
2 |
+
from fastapi import FastAPI, HTTPException, Request
|
3 |
+
from fastapi.responses import JSONResponse
|
4 |
+
import httpx
|
5 |
+
import random
|
6 |
+
import string
|
7 |
+
|
8 |
+
app = FastAPI()
|
9 |
+
|
10 |
+
# Utility function to generate random hex strings
|
11 |
+
def random_hex(length=32):
|
12 |
+
return ''.join(random.choices(string.hexdigits, k=length))
|
13 |
+
|
14 |
+
# Fetch JSON from the given URL with required headers
|
15 |
+
async def jsongen(url):
|
16 |
+
headers = {
|
17 |
+
"X-Signature-Version": "web2",
|
18 |
+
"X-Signature": random_hex(),
|
19 |
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36",
|
20 |
+
}
|
21 |
+
try:
|
22 |
+
async with httpx.AsyncClient() as client:
|
23 |
+
response = await client.get(url, headers=headers)
|
24 |
+
response.raise_for_status()
|
25 |
+
return response.json()
|
26 |
+
except httpx.HTTPError as e:
|
27 |
+
raise HTTPException(status_code=500, detail=f"Error fetching data: {e}")
|
28 |
+
|
29 |
+
# Route to fetch trending videos
|
30 |
+
@app.get("/trending/{time}")
|
31 |
+
async def get_trending(time: str, page: Optional[int] = 0):
|
32 |
+
trending_url = f"https://hanime.tv/api/v8/browse-trending?time={time}&page={page}&order_by=views&ordering=desc"
|
33 |
+
urldata = await jsongen(trending_url)
|
34 |
+
jsondata = [
|
35 |
+
{
|
36 |
+
"id": x["id"],
|
37 |
+
"name": x["name"],
|
38 |
+
"slug": x["slug"],
|
39 |
+
"cover_url": x["cover_url"],
|
40 |
+
"views": x["views"],
|
41 |
+
"link": f"/watch/{x['slug']}",
|
42 |
+
}
|
43 |
+
for x in urldata["hentai_videos"]
|
44 |
+
]
|
45 |
+
next_page = f"/trending/{time}/{page + 1}"
|
46 |
+
return {"results": jsondata, "next_page": next_page}
|
47 |
+
|
48 |
+
# Route to fetch video details
|
49 |
+
@app.get("/watch/{slug}")
|
50 |
+
async def get_video(slug: str):
|
51 |
+
video_api_url = f"https://hanime.tv/api/v8/video?id={slug}"
|
52 |
+
video_data = await jsongen(video_api_url)
|
53 |
+
tags = [
|
54 |
+
{"name": t["text"], "link": f"/hentai-tags/{t['text']}/0"}
|
55 |
+
for t in video_data["hentai_tags"]
|
56 |
+
]
|
57 |
+
streams = [
|
58 |
+
{"width": s["width"], "height": s["height"], "size_mbs": s["filesize_mbs"], "url": s["url"]}
|
59 |
+
for s in video_data["videos_manifest"]["servers"][0]["streams"]
|
60 |
+
]
|
61 |
+
episodes = [
|
62 |
+
{
|
63 |
+
"id": e["id"],
|
64 |
+
"name": e["name"],
|
65 |
+
"slug": e["slug"],
|
66 |
+
"cover_url": e["cover_url"],
|
67 |
+
"views": e["views"],
|
68 |
+
"link": f"/watch/{e['slug']}",
|
69 |
+
}
|
70 |
+
for e in video_data["hentai_franchise_hentai_videos"]
|
71 |
+
]
|
72 |
+
jsondata = {
|
73 |
+
"id": video_data["hentai_video"]["id"],
|
74 |
+
"name": video_data["hentai_video"]["name"],
|
75 |
+
"description": video_data["hentai_video"]["description"],
|
76 |
+
"poster_url": video_data["hentai_video"]["poster_url"],
|
77 |
+
"cover_url": video_data["hentai_video"]["cover_url"],
|
78 |
+
"views": video_data["hentai_video"]["views"],
|
79 |
+
"streams": streams,
|
80 |
+
"tags": tags,
|
81 |
+
"episodes": episodes,
|
82 |
+
}
|
83 |
+
return {"results": [jsondata]}
|
84 |
+
|
85 |
+
# Route to fetch browse data
|
86 |
+
@app.get("/browse/{type}")
|
87 |
+
async def get_browse(type: str):
|
88 |
+
browse_url = "https://hanime.tv/api/v8/browse"
|
89 |
+
data = await jsongen(browse_url)
|
90 |
+
jsondata = data[type]
|
91 |
+
if type == "hentai_tags":
|
92 |
+
jsondata = [{"name": x["text"], "url": f"/hentai-tags/{x['text']}/0"} for x in jsondata]
|
93 |
+
elif type == "brands":
|
94 |
+
jsondata = [{"name": x["name"], "url": f"/brands/{x['slug']}/0"} for x in jsondata]
|
95 |
+
return {"results": jsondata}
|
96 |
+
|
97 |
+
# Route to fetch tags
|
98 |
+
@app.get("/tags")
|
99 |
+
async def get_tags():
|
100 |
+
browse_url = "https://hanime.tv/api/v8/browse"
|
101 |
+
data = await jsongen(browse_url)
|
102 |
+
jsondata = [{"name": x["text"], "url": f"/tags/{x['text']}/0"} for x in data["hentai_tags"]]
|
103 |
+
return {"results": jsondata}
|
104 |
+
|
105 |
+
# Route to fetch browse videos
|
106 |
+
@app.get("/{type}/{category}")
|
107 |
+
async def get_browse_videos(type: str, category: str, page: Optional[int] = 0):
|
108 |
+
browse_url = f"https://hanime.tv/api/v8/browse/{type}/{category}?page={page}&order_by=views&ordering=desc"
|
109 |
+
browsedata = await jsongen(browse_url)
|
110 |
+
jsondata = [
|
111 |
+
{
|
112 |
+
"id": x["id"],
|
113 |
+
"name": x["name"],
|
114 |
+
"slug": x["slug"],
|
115 |
+
"cover_url": x["cover_url"],
|
116 |
+
"views": x["views"],
|
117 |
+
"link": f"/watch/{x['slug']}",
|
118 |
+
}
|
119 |
+
for x in browsedata["hentai_videos"]
|
120 |
+
]
|
121 |
+
next_page = f"/{type}/{category}/{page + 1}"
|
122 |
+
return {"results": jsondata, "next_page": next_page}
|
123 |
+
|
124 |
+
# Root route
|
125 |
+
@app.get("/")
|
126 |
+
async def root():
|
127 |
+
return {"message": "Welcome to Hanime API π"}
|
128 |
+
|
129 |
+
# Custom error handler
|
130 |
+
@app.exception_handler(Exception)
|
131 |
+
async def custom_exception_handler(request: Request, exc: Exception):
|
132 |
+
return JSONResponse(
|
133 |
+
status_code=500,
|
134 |
+
content={"error": "Something went wrong", "details": str(exc)},
|
135 |
+
)
|
136 |
+
|