imgthumb / app.py
workebridge's picture
Update app.py
8d0eefb verified
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)")
@app.get("/")
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)}"})