Neon-AI commited on
Commit
a12fcb7
·
verified ·
1 Parent(s): 8f85815

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +54 -126
app.py CHANGED
@@ -2,7 +2,6 @@ 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
@@ -12,103 +11,77 @@ import re
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
 
19
  QUALITIES = ["360p", "720p", "1080p"]
20
  queue_lock = Lock()
21
 
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
@@ -119,104 +92,59 @@ 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", "veryfast",
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"}
 
2
  import tempfile
3
  import os
4
  import shutil
 
5
  import subprocess
6
  from fastapi import FastAPI, BackgroundTasks
7
  from pydantic import BaseModel
 
11
  app = FastAPI(title="Neon Anime Blur & Upload")
12
 
13
  # Constants
14
+ UPLOAD_URL = "https://0x0.st"
15
  RENDER_UPDATE_ENDPOINT = "https://nt-anime-api.onrender.com/update"
16
  HF_AYANO_BASE = "https://a-y-a-n-o-k-o-j-i-dnd-api.hf.space"
17
 
18
  QUALITIES = ["360p", "720p", "1080p"]
19
  queue_lock = Lock()
20
 
21
+ def log_error(msg: str):
22
+ print(f"[HF ERROR] {msg}", flush=True)
23
 
24
  class EpisodeExceedsAvailableCount(Exception):
25
  pass
26
 
27
  class StartPayload(BaseModel):
28
  anime_id: str
29
+ anime_name: str
30
 
31
  def download_video(anime_id: str, episode: int, quality: str) -> str | None:
32
  url = f"{HF_AYANO_BASE}/anime/download?id={anime_id}&episode={episode}&quality={quality}"
33
+ try:
34
+ resp = requests.get(url, 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
  return None
42
 
43
+ headers = {"User-Agent": "Mozilla/5.0 Chrome/120.0", "Referer": "https://animepahe.si/"}
 
 
 
 
 
 
 
 
 
 
 
 
44
  tmp_path = tempfile.mktemp(suffix=".mp4")
45
+ try:
46
+ with requests.get(video_url, headers=headers, stream=True, timeout=120) as r:
47
+ r.raise_for_status()
48
+ with open(tmp_path, "wb") as f:
49
+ for chunk in r.iter_content(chunk_size=8192):
50
+ f.write(chunk)
51
+ return tmp_path
52
+ except Exception as e:
53
+ log_error(f"Download failed: {e}")
54
+ if os.path.exists(tmp_path):
55
+ os.remove(tmp_path)
56
+ return None
57
 
58
  def get_filename(anime_name: str, ep: int, quality: str) -> str:
59
  slug = re.sub(r'[^a-z0-9-]+', '-', anime_name.lower()).strip('-')
60
  return f"nt-animes_{slug}_ep{ep}_{quality}.mp4"
61
 
62
+ def upload_to_0x0(file_path: str, file_name: str) -> str:
63
+ try:
64
+ with open(file_path, "rb") as f:
65
+ r = requests.post(UPLOAD_URL, files={"file": (file_name, f)}, timeout=180)
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  r.raise_for_status()
67
  return r.text.strip()
68
+ except Exception as e:
69
+ log_error(f"0x0 upload failed: {e}")
70
+ raise
71
 
72
+ def notify_render(anime_id: str, episode: int, quality: str, file_url: str, file_name: str, status: int):
73
  payload = {
74
  "anime_id": anime_id,
75
  "episode": episode,
76
  "quality": quality,
77
+ "file_url": file_url,
78
  "file_name": file_name,
79
  "status": status
80
  }
 
 
 
81
  try:
82
+ requests.post(RENDER_UPDATE_ENDPOINT, json=payload, timeout=10)
 
83
  except Exception as e:
84
+ log_error(f"Notify failed: {e}")
85
 
86
  BLUR_W = 74
87
  BLUR_H = 17
 
92
  TEXT_Y = 8
93
 
94
  def blur_video(input_path: str) -> str:
95
+ output = tempfile.mktemp(suffix="_blurred.mp4")
 
 
 
96
  filter_graph = (
97
  f"[0:v]crop={BLUR_W}:{BLUR_H}:0:0,boxblur=luma_radius=4:luma_power=1:chroma_radius=0[top];"
98
  f"[0:v][top]overlay=0:0,"
99
  f"drawtext=text={TEXT}:x={TEXT_X}:y={TEXT_Y}:fontcolor={FONT_COLOR}:fontsize={FONT_SIZE}[v]"
100
  )
 
101
  cmd = [
102
+ "ffmpeg", "-y", "-i", input_path,
 
 
103
  "-filter_complex", filter_graph,
104
  "-map", "[v]",
105
  "-map", "0:a?",
106
  "-c:v", "libx264",
107
+ "-preset", "ultrafast",
108
  "-crf", "23",
109
  "-c:a", "copy",
110
+ output
111
  ]
112
+ try:
113
+ subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
114
+ return output
115
+ except Exception as e:
116
+ log_error(f"Blur failed: {e}")
117
+ raise
118
 
119
  def process_anime(anime_id: str, anime_name: str):
 
 
120
  episode = 1
121
  while True:
 
122
  episode_processed = False
 
123
  for quality in QUALITIES:
 
 
124
  try:
125
  local_file = download_video(anime_id, episode, quality)
126
+ if not local_file:
 
 
127
  continue
 
 
 
128
  blurred_file = blur_video(local_file)
 
 
129
  os.remove(local_file)
130
+ file_name = get_filename(anime_name, episode, quality)
131
+ file_url = upload_to_0x0(blurred_file, file_name)
 
 
 
 
 
 
132
  os.remove(blurred_file)
133
+ notify_render(anime_id, episode, quality, file_url, file_name, 2)
 
 
 
 
 
 
 
134
  episode_processed = True
 
135
  except EpisodeExceedsAvailableCount:
136
+ notify_render(anime_id, episode, "", "", "", 5)
 
 
137
  return
 
138
  except Exception as e:
139
+ log_error(f"Error ep{episode} {quality}: {e}")
140
+ notify_render(anime_id, episode, quality, "", "", 3)
 
141
  if not episode_processed:
142
+ notify_render(anime_id, 0, "", "", "", 5) # complete
143
  break
 
144
  episode += 1
145
 
 
 
146
  @app.post("/start")
147
  def start_endpoint(payload: StartPayload, background_tasks: BackgroundTasks):
 
 
148
  with queue_lock:
 
149
  background_tasks.add_task(process_anime, payload.anime_id, payload.anime_name)
 
 
150
  return {"code": 4, "message": "Job queued and processing in background"}