Neon-AI commited on
Commit
4a7cad1
·
verified ·
1 Parent(s): ce89513

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +75 -158
app.py CHANGED
@@ -1,8 +1,6 @@
1
  import requests
2
  import tempfile
3
  import os
4
- import shutil
5
- #import cv2
6
  import subprocess
7
  from fastapi import FastAPI, BackgroundTasks
8
  from pydantic import BaseModel
@@ -11,8 +9,7 @@ import re
11
 
12
  app = FastAPI(title="Neon Anime Blur & Upload")
13
 
14
- # Constants
15
- CATBOX_UPLOAD_URL = "https://catbox.moe/user/api.php"
16
  RENDER_UPDATE_ENDPOINT = "https://nt-anime-api.onrender.com/update"
17
  HF_AYANO_BASE = "https://a-y-a-n-o-k-o-j-i-dnd-api.hf.space"
18
 
@@ -22,201 +19,121 @@ queue_lock = Lock()
22
  def log(msg: str):
23
  print(f"[HF] {msg}", flush=True)
24
 
25
- class EpisodeExceedsAvailableCount(Exception):
26
- pass
27
-
28
  class StartPayload(BaseModel):
29
  anime_id: str
30
- anime_name: str # New required field for public title/slug
31
 
32
  def download_video(anime_id: str, episode: int, quality: str) -> str | None:
33
  url = f"{HF_AYANO_BASE}/anime/download?id={anime_id}&episode={episode}&quality={quality}"
34
- print(f"[HF] Calling Ayano: {url}")
35
-
36
- resp = requests.get(url, timeout=20)
37
- if resp.status_code != 200:
38
- print("[HF] Ayano non-200")
39
- return None
40
-
41
- data = resp.json()
42
- print(f"[HF] Ayano JSON: {data}")
43
-
44
- if data.get("status") != 200:
45
- return None
46
-
47
- video_url = data["direct_link"]
48
-
49
  headers = {
50
- "User-Agent": (
51
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
52
- "AppleWebKit/537.36 (KHTML, like Gecko) "
53
- "Chrome/120.0.0.0 Safari/537.36"
54
- ),
55
- "Referer": "https://animepahe.si/",
56
- "Accept": "*/*",
57
- "Connection": "keep-alive",
58
  }
 
 
 
 
 
 
 
 
 
 
59
 
60
  tmp_path = tempfile.mktemp(suffix=".mp4")
61
- print(f"[HF] Downloading video from: {video_url}")
62
-
63
- with requests.get(video_url, headers=headers, stream=True, timeout=60) as r:
64
- r.raise_for_status()
65
- with open(tmp_path, "wb") as f:
66
- shutil.copyfileobj(r.raw, f)
67
-
68
- print(f"[HF] Download complete → {tmp_path}")
69
- return tmp_path
 
 
 
 
70
 
71
  def get_filename(anime_name: str, ep: int, quality: str) -> str:
72
  slug = re.sub(r'[^a-z0-9-]+', '-', anime_name.lower()).strip('-')
73
  return f"nt-animes_{slug}_ep{ep}_{quality}.mp4"
74
 
75
- def upload_to_catbox_public(file_path: str, file_name: str) -> str:
76
- size_mb = os.path.getsize(file_path) / (1024 * 1024)
77
- log(f"Uploading to Catbox: {file_name} (Size: {size_mb:.2f} MB)")
78
-
79
- with open(file_path, "rb") as f:
80
- files = {"fileToUpload": (file_name, f)}
81
- data = {"reqtype": "fileupload"}
82
-
83
- r = requests.post(CATBOX_UPLOAD_URL, data=data, files=files, timeout=60)
84
- log(f"Catbox response status {r.status_code}")
85
- log(f"Catbox response text: {r.text}")
86
-
87
- if r.status_code == 412 or "too large" in r.text.lower():
88
- log("❌ ERROR: File too large (greater than 200 MB)")
89
-
90
- catbox_url = (f"{r.text}")
91
-
92
  r.raise_for_status()
93
- return r.text.strip()
 
 
 
 
 
94
 
95
- def notify_render(anime_id, episode, quality, catbox_url, file_name, status):
96
  payload = {
97
  "anime_id": anime_id,
98
  "episode": episode,
99
  "quality": quality,
100
- "file_url": catbox_url,
101
  "file_name": file_name,
102
  "status": status
103
  }
104
-
105
- log(f"Notify render → {payload}")
106
-
107
  try:
108
- r = requests.post(RENDER_UPDATE_ENDPOINT, json=payload, timeout=10)
109
- log(f"Render response {r.status_code}")
110
  except Exception as e:
111
- log(f"⚠️ Render notify failed: {repr(e)}")
112
-
113
- BLUR_W = 74
114
- BLUR_H = 17
115
- TEXT = "nt-animes"
116
- FONT_COLOR = "white"
117
- FONT_SIZE = 12
118
- TEXT_X = 8
119
- TEXT_Y = 8
120
 
121
  def blur_video(input_path: str) -> str:
122
- tmp_video = tempfile.mktemp(suffix="_video.mp4")
123
- final_video = tempfile.mktemp(suffix="_final.mp4")
124
-
125
- # FFmpeg filtergraph: blur + text overlay
126
  filter_graph = (
127
- f"[0:v]crop={BLUR_W}:{BLUR_H}:0:0,boxblur=luma_radius=4:luma_power=1:chroma_radius=0[top];"
128
- f"[0:v][top]overlay=0:0,"
129
- f"drawtext=text={TEXT}:x={TEXT_X}:y={TEXT_Y}:fontcolor={FONT_COLOR}:fontsize={FONT_SIZE}[v]"
130
  )
131
-
132
  cmd = [
133
- "ffmpeg",
134
- "-y",
135
- "-i", input_path,
136
  "-filter_complex", filter_graph,
137
- "-map", "[v]",
138
- "-map", "0:a?",
139
- "-c:v", "libx264",
140
- "-preset", "fast",
141
- "-crf", "23",
142
- "-c:a", "copy",
143
- tmp_video
144
  ]
145
-
146
- subprocess.run(cmd, check=True)
147
-
148
- # Move temp_video to final_video like your OpenCV version
149
- os.rename(tmp_video, final_video)
150
- return final_video
151
 
152
  def process_anime(anime_id: str, anime_name: str):
153
- log(f"PROCESS STARTED for anime_id={anime_id}")
154
-
155
  episode = 1
156
  while True:
157
- log(f"--- EPISODE LOOP START ep={episode} ---")
158
- episode_processed = False
159
-
160
  for quality in QUALITIES:
161
- log(f"Trying ep={episode}, quality={quality}")
162
-
163
  try:
164
- local_file = download_video(anime_id, episode, quality)
165
-
166
- if local_file is None:
167
- log(f"No video for ep={episode} quality={quality}")
168
  continue
169
-
170
- log(f"Downloaded file: {local_file}")
171
-
172
- blurred_file = blur_video(local_file)
173
- log(f"Blurred file created: {blurred_file}")
174
-
175
- os.remove(local_file)
176
- log(f"Removed original file")
177
-
178
- new_name = get_filename(anime_name, episode, quality)
179
- log(f"Renamed to: {new_name}")
180
-
181
- catbox_url = upload_to_catbox_public(blurred_file, new_name)
182
- log(f"Uploaded to Catbox: {catbox_url}")
183
-
184
- os.remove(blurred_file)
185
- log("Removed blurred temp file")
186
-
187
- notify_render(
188
- anime_id, episode, quality,
189
- catbox_url, new_name, status=2
190
- )
191
- log(f"Notified render SUCCESS ep={episode} quality={quality}")
192
-
193
- episode_processed = True
194
-
195
- except EpisodeExceedsAvailableCount:
196
- log("🚨 EPISODE LIMIT REACHED (422)")
197
- notify_render(anime_id, episode, "", "", "", status=5)
198
- log("Sent FINAL status=5 to render, stopping")
199
- return
200
-
201
- except Exception as e:
202
- log(f"❌ ERROR ep={episode} quality={quality}: {repr(e)}")
203
- notify_render(anime_id, episode, quality, "", "", status=3)
204
-
205
- if not episode_processed:
206
- log("No qualities processed → stopping episode loop")
207
  break
208
-
209
  episode += 1
210
-
211
- log("PROCESS FINISHED NORMALLY")
212
 
213
  @app.post("/start")
214
- def start_endpoint(payload: StartPayload, background_tasks: BackgroundTasks):
215
- log(f"/start called with anime_id={payload.anime_id}, anime_name={payload.anime_name}")
216
-
217
  with queue_lock:
218
- log("Queue lock acquired, scheduling background task")
219
- background_tasks.add_task(process_anime, payload.anime_id, payload.anime_name)
220
-
221
- log("Background task scheduled, returning code 4")
222
- return {"code": 4, "message": "Job queued and processing in background"}
 
1
  import requests
2
  import tempfile
3
  import os
 
 
4
  import subprocess
5
  from fastapi import FastAPI, BackgroundTasks
6
  from pydantic import BaseModel
 
9
 
10
  app = FastAPI(title="Neon Anime Blur & Upload")
11
 
12
+ UPLOAD_URL = "https://0x0.st"
 
13
  RENDER_UPDATE_ENDPOINT = "https://nt-anime-api.onrender.com/update"
14
  HF_AYANO_BASE = "https://a-y-a-n-o-k-o-j-i-dnd-api.hf.space"
15
 
 
19
  def log(msg: str):
20
  print(f"[HF] {msg}", flush=True)
21
 
 
 
 
22
  class StartPayload(BaseModel):
23
  anime_id: str
24
+ anime_name: str
25
 
26
  def download_video(anime_id: str, episode: int, quality: str) -> str | None:
27
  url = f"{HF_AYANO_BASE}/anime/download?id={anime_id}&episode={episode}&quality={quality}"
28
+ log(f"Fetching link ep {episode} {quality}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  headers = {
30
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0 Safari/537.36",
31
+ "Referer": "https://animepahe.si/"
 
 
 
 
 
 
32
  }
33
+ try:
34
+ resp = requests.get(url, headers=headers, timeout=20)
35
+ resp.raise_for_status()
36
+ data = resp.json()
37
+ if data.get("status") != 200:
38
+ return None
39
+ video_url = data["direct_link"]
40
+ except:
41
+ log(f"Link failed ep {episode} {quality}")
42
+ return None
43
 
44
  tmp_path = tempfile.mktemp(suffix=".mp4")
45
+ log(f"Downloading ep {episode} {quality}")
46
+ try:
47
+ with requests.get(video_url, headers=headers, stream=True, timeout=120) as r:
48
+ r.raise_for_status()
49
+ with open(tmp_path, "wb") as f:
50
+ shutil.copyfileobj(r.raw, f)
51
+ log(f"Downloaded ep {episode} {quality}")
52
+ return tmp_path
53
+ except:
54
+ log(f"Download failed ep {episode} {quality}")
55
+ if os.path.exists(tmp_path):
56
+ os.remove(tmp_path)
57
+ return None
58
 
59
  def get_filename(anime_name: str, ep: int, quality: str) -> str:
60
  slug = re.sub(r'[^a-z0-9-]+', '-', anime_name.lower()).strip('-')
61
  return f"nt-animes_{slug}_ep{ep}_{quality}.mp4"
62
 
63
+ def upload_to_0x0(file_path: str, file_name: str) -> str:
64
+ log(f"Uploading {file_name}")
65
+ try:
66
+ with open(file_path, "rb") as f:
67
+ r = requests.post(UPLOAD_URL, files={"file": (file_name, f)}, timeout=180)
 
 
 
 
 
 
 
 
 
 
 
 
68
  r.raise_for_status()
69
+ url = r.text.strip()
70
+ log(f"Uploaded: {url}")
71
+ return url
72
+ except Exception as e:
73
+ log(f"Upload failed: {e}")
74
+ raise
75
 
76
+ def notify_render(anime_id: str, episode: int, quality: str, file_url: str, file_name: str, status: int):
77
  payload = {
78
  "anime_id": anime_id,
79
  "episode": episode,
80
  "quality": quality,
81
+ "file_url": file_url,
82
  "file_name": file_name,
83
  "status": status
84
  }
85
+ log(f"Notifying Render status={status} ep={episode} {quality}")
 
 
86
  try:
87
+ requests.post(RENDER_UPDATE_ENDPOINT, json=payload, timeout=10)
 
88
  except Exception as e:
89
+ log(f"Notify failed: {e}")
 
 
 
 
 
 
 
 
90
 
91
  def blur_video(input_path: str) -> str:
92
+ output = tempfile.mktemp(suffix="_blurred.mp4")
 
 
 
93
  filter_graph = (
94
+ "[0:v]crop=74:17:0:0,boxblur=luma_radius=4:luma_power=1:chroma_radius=0[top];"
95
+ "[0:v][top]overlay=0:0,"
96
+ "drawtext=text='nt-animes':x=8:y=8:fontcolor=white:fontsize=12"
97
  )
 
98
  cmd = [
99
+ "ffmpeg", "-y", "-i", input_path,
 
 
100
  "-filter_complex", filter_graph,
101
+ "-c:v", "libx264", "-preset", "fast", "-crf", "23",
102
+ "-c:a", "copy", output
 
 
 
 
 
103
  ]
104
+ subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
105
+ return output
 
 
 
 
106
 
107
  def process_anime(anime_id: str, anime_name: str):
108
+ log(f"Started {anime_id} - {anime_name}")
 
109
  episode = 1
110
  while True:
111
+ processed = False
 
 
112
  for quality in QUALITIES:
 
 
113
  try:
114
+ file = download_video(anime_id, episode, quality)
115
+ if not file:
 
 
116
  continue
117
+ log(f"Blurring ep {episode} {quality}")
118
+ blurred = blur_video(file)
119
+ os.remove(file)
120
+ name = get_filename(anime_name, episode, quality)
121
+ url = upload_to_0x0(blurred, name)
122
+ os.remove(blurred)
123
+ notify_render(anime_id, episode, quality, url, name, 2)
124
+ processed = True
125
+ except:
126
+ notify_render(anime_id, episode, quality, "", "", 3)
127
+ if not processed:
128
+ log("No more episodes")
129
+ notify_render(anime_id, 0, "", "", "", 5)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  break
 
131
  episode += 1
132
+ log("Complete")
 
133
 
134
  @app.post("/start")
135
+ def start(payload: StartPayload, bg: BackgroundTasks):
136
+ log(f"Queueing {payload.anime_id} - {payload.anime_name}")
 
137
  with queue_lock:
138
+ bg.add_task(process_anime, payload.anime_id, payload.anime_name)
139
+ return {"code": 4, "message": "Queued"}