Spaces:
Runtime error
Runtime error
| from fastapi import FastAPI, Query | |
| from fastapi.responses import JSONResponse | |
| import subprocess | |
| import base64 | |
| import os | |
| import tempfile | |
| app = FastAPI(title="MP4 Thumbnail from URL (FFmpeg)") | |
| def get_thumbnail( | |
| file: str = Query(..., description="Direct URL to .mp4 file"), | |
| seconds: float = Query(20.0, ge=0.0, description="Seconds to seek (can be float, default 20)"), | |
| ): | |
| try: | |
| with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp_file: | |
| output_path = tmp_file.name | |
| # FFmpeg command β seek BEFORE input for remote HTTP efficiency | |
| cmd = [ | |
| "ffmpeg", | |
| "-user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", | |
| "-ss", str(seconds), # seek early (fast for remote) | |
| "-i", file, | |
| "-frames:v", "1", | |
| "-q:v", "5", # good quality | |
| "-f", "image2", | |
| output_path | |
| ] | |
| result = subprocess.run( | |
| cmd, | |
| capture_output=True, | |
| text=True, | |
| timeout=60 | |
| ) | |
| if result.returncode != 0: | |
| error_detail = result.stderr.strip() or "FFmpeg exited with error (no details)" | |
| os.unlink(output_path) # cleanup | |
| return JSONResponse( | |
| status_code=400, | |
| content={ | |
| "error": "FFmpeg failed to process video", | |
| "ffmpeg_error": error_detail, | |
| "return_code": result.returncode | |
| } | |
| ) | |
| if not os.path.exists(output_path) or os.path.getsize(output_path) < 1000: | |
| os.unlink(output_path) | |
| return JSONResponse( | |
| status_code=400, | |
| content={"error": "No valid frame extracted (empty or tiny output file)"} | |
| ) | |
| with open(output_path, "rb") as f: | |
| img_bytes = f.read() | |
| os.unlink(output_path) # cleanup | |
| img_base64 = base64.b64encode(img_bytes).decode("utf-8") | |
| return { | |
| "base64": img_base64, | |
| "format": "jpeg", | |
| "taken_at_seconds": seconds, | |
| "data_url": f"data:image/jpeg;base64,{img_base64}", | |
| "debug": "Success β frame extracted" | |
| } | |
| except subprocess.TimeoutExpired: | |
| return JSONResponse(status_code=504, content={"error": "Timeout β source too slow or unreachable"}) | |
| except Exception as e: | |
| return JSONResponse(status_code=500, content={"error": f"Unexpected error: {str(e)}"}) |