Update main.py
Browse files
main.py
CHANGED
|
@@ -165,8 +165,8 @@ async def hentai_random(apikey: Optional[str] = Query(None), limit: str = Query(
|
|
| 165 |
key = x_api_key or apikey
|
| 166 |
return await ht_random_app.get_random(key, API_KEY, limit)
|
| 167 |
|
| 168 |
-
@app.get("/api/v1/tiktok/
|
| 169 |
-
async def
|
| 170 |
url: str = Query(...),
|
| 171 |
hd: int = Query(1),
|
| 172 |
apikey: Optional[str] = Query(None),
|
|
@@ -181,92 +181,86 @@ async def tiktok_download(
|
|
| 181 |
raise HTTPException(status_code=400, detail=res["error"])
|
| 182 |
return res
|
| 183 |
|
| 184 |
-
@app.get("/api/v1/tiktok/
|
| 185 |
-
async def
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
type: str = Query("mp4"),
|
| 189 |
apikey: Optional[str] = Query(None),
|
| 190 |
x_api_key: Optional[str] = Header(None, alias="X-API-Key")
|
| 191 |
):
|
| 192 |
if url:
|
| 193 |
key = x_api_key or apikey
|
| 194 |
if not API_KEY or key != API_KEY:
|
| 195 |
-
raise HTTPException(status_code=403, detail="
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
raise HTTPException(status_code=400, detail=res["error"])
|
| 200 |
-
return res
|
| 201 |
-
|
| 202 |
-
if token:
|
| 203 |
data = tik_direct.consume_token(token)
|
| 204 |
if not data:
|
| 205 |
-
raise HTTPException(status_code=404, detail="Token invalid, expired
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
except:
|
| 234 |
-
pass
|
| 235 |
-
|
| 236 |
-
return StreamingResponse(
|
| 237 |
-
stream_ffmpeg(),
|
| 238 |
-
media_type=media_type,
|
| 239 |
-
headers={"Content-Disposition": f"attachment; filename={filename}"}
|
| 240 |
-
)
|
| 241 |
-
except Exception as e:
|
| 242 |
-
raise HTTPException(status_code=500, detail=f"Lỗi convert {ext}: {str(e)}")
|
| 243 |
-
|
| 244 |
-
async def stream_file():
|
| 245 |
-
async with httpx.AsyncClient(follow_redirects=True, timeout=30.0) as client:
|
| 246 |
-
async with client.stream("GET", data["url"]) as r:
|
| 247 |
-
if r.status_code != 200:
|
| 248 |
-
yield b"Error: Could not fetch file from provider"
|
| 249 |
-
return
|
| 250 |
-
async for chunk in r.aiter_bytes(chunk_size=1024*1024):
|
| 251 |
yield chunk
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 270 |
|
| 271 |
raise HTTPException(status_code=400, detail="Provide either 'url' or 'token'")
|
| 272 |
|
|
|
|
| 165 |
key = x_api_key or apikey
|
| 166 |
return await ht_random_app.get_random(key, API_KEY, limit)
|
| 167 |
|
| 168 |
+
@app.get("/api/v1/tiktok/dl")
|
| 169 |
+
async def tiktok_dl(
|
| 170 |
url: str = Query(...),
|
| 171 |
hd: int = Query(1),
|
| 172 |
apikey: Optional[str] = Query(None),
|
|
|
|
| 181 |
raise HTTPException(status_code=400, detail=res["error"])
|
| 182 |
return res
|
| 183 |
|
| 184 |
+
@app.get("/api/v1/tiktok/cdn")
|
| 185 |
+
async def tiktok_cdn(
|
| 186 |
+
token: str = Query(None, description="Token generated from /dl endpoint"),
|
| 187 |
+
url: str = Query(None, description="Direct URL to proxy (Requires API Key)"),
|
| 188 |
+
type: str = Query("mp4", description="File extension type"),
|
| 189 |
apikey: Optional[str] = Query(None),
|
| 190 |
x_api_key: Optional[str] = Header(None, alias="X-API-Key")
|
| 191 |
):
|
| 192 |
if url:
|
| 193 |
key = x_api_key or apikey
|
| 194 |
if not API_KEY or key != API_KEY:
|
| 195 |
+
raise HTTPException(status_code=403, detail="Proxying via URL requires a valid API Key")
|
| 196 |
+
data = {"url": url, "type": type}
|
| 197 |
+
|
| 198 |
+
elif token:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
data = tik_direct.consume_token(token)
|
| 200 |
if not data:
|
| 201 |
+
raise HTTPException(status_code=404, detail="Token invalid, expired or limit reached")
|
| 202 |
+
|
| 203 |
+
else:
|
| 204 |
+
raise HTTPException(status_code=400, detail="Either 'token' or 'url' must be provided")
|
| 205 |
+
|
| 206 |
+
target_url = data["url"]
|
| 207 |
+
media_type_req = data["type"]
|
| 208 |
+
|
| 209 |
+
if media_type_req in ["mp3_convert", "wav_convert"]:
|
| 210 |
+
is_wav = media_type_req == "wav_convert"
|
| 211 |
+
ext = "wav" if is_wav else "mp3"
|
| 212 |
+
acodec = "pcm_s16le" if is_wav else "libmp3lame"
|
| 213 |
+
fmt = "wav" if is_wav else "mp3"
|
| 214 |
+
media_header = "audio/wav" if is_wav else "audio/mpeg"
|
| 215 |
+
|
| 216 |
+
try:
|
| 217 |
+
process = (
|
| 218 |
+
ffmpeg
|
| 219 |
+
.input(target_url)
|
| 220 |
+
.output('pipe:', format=fmt, acodec=acodec, ar='44100')
|
| 221 |
+
.run_async(pipe_stdout=True, pipe_stderr=True)
|
| 222 |
+
)
|
| 223 |
+
|
| 224 |
+
async def stream_ffmpeg():
|
| 225 |
+
try:
|
| 226 |
+
while True:
|
| 227 |
+
chunk = await asyncio.to_thread(process.stdout.read, 8192)
|
| 228 |
+
if not chunk: break
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 229 |
yield chunk
|
| 230 |
+
finally:
|
| 231 |
+
try: process.kill()
|
| 232 |
+
except: pass
|
| 233 |
+
|
| 234 |
+
return StreamingResponse(
|
| 235 |
+
stream_ffmpeg(),
|
| 236 |
+
media_type=media_header,
|
| 237 |
+
headers={"Content-Disposition": f"attachment; filename=Celeskry_{int(time.time())}.{ext}"}
|
| 238 |
+
)
|
| 239 |
+
except Exception as e:
|
| 240 |
+
raise HTTPException(status_code=500, detail=f"Conversion Error: {str(e)}")
|
| 241 |
+
|
| 242 |
+
async def stream_file():
|
| 243 |
+
async with httpx.AsyncClient(follow_redirects=True, timeout=60.0) as client:
|
| 244 |
+
async with client.stream("GET", target_url) as r:
|
| 245 |
+
if r.status_code != 200:
|
| 246 |
+
yield b"Error: Resource not found"
|
| 247 |
+
return
|
| 248 |
+
async for chunk in r.aiter_bytes(chunk_size=1024*1024):
|
| 249 |
+
yield chunk
|
| 250 |
+
|
| 251 |
+
content_types = {
|
| 252 |
+
"mp4": "video/mp4",
|
| 253 |
+
"mp3": "audio/mpeg",
|
| 254 |
+
"jpg": "image/jpeg",
|
| 255 |
+
"png": "image/png"
|
| 256 |
+
}
|
| 257 |
+
final_type = content_types.get(media_type_req, "application/octet-stream")
|
| 258 |
+
|
| 259 |
+
return StreamingResponse(
|
| 260 |
+
stream_file(),
|
| 261 |
+
media_type=final_type,
|
| 262 |
+
headers={"Content-Disposition": f"attachment; filename=Celeste_{int(time.time())}.{media_type_req}"}
|
| 263 |
+
)
|
| 264 |
|
| 265 |
raise HTTPException(status_code=400, detail="Provide either 'url' or 'token'")
|
| 266 |
|