"""
Video Upscaler - HuggingFace Spaces (FastAPI + HTML)
Simple, reliable video upscaling using FFmpeg.
No Gradio - cleaner and fewer dependencies.
"""
from fastapi import FastAPI, File, UploadFile, Form
from fastapi.responses import HTMLResponse, FileResponse
from fastapi.staticfiles import StaticFiles
import subprocess
import os
import tempfile
import shutil
import uuid
app = FastAPI(title="Video Upscaler 4K")
# Upscale resolutions
RESOLUTIONS = {
"720p": (1280, 720),
"1080p": (1920, 1080),
"1440p": (2560, 1440),
"2160p": (3840, 2160),
}
# Create temp directory for outputs
OUTPUT_DIR = "/tmp/upscaled"
os.makedirs(OUTPUT_DIR, exist_ok=True)
HTML_PAGE = """
🎬 Video Upscaler 4K
🎬 Video Upscaler 4K
Upscale your videos to Ultra HD quality
💡 Tips:
- 4K (2160p) = 3840x2160 pixels
- Processing time: ~2-5 min per minute of video
- Best Quality uses CRF 15 (near-lossless)
- Max file size: 100MB for free tier
"""
@app.get("/", response_class=HTMLResponse)
async def home():
return HTML_PAGE
@app.post("/upscale")
async def upscale_video(
video: UploadFile = File(...),
resolution: str = Form("2160p"),
quality: str = Form("balanced")
):
try:
# Get target dimensions
if resolution not in RESOLUTIONS:
resolution = "2160p"
width, height = RESOLUTIONS[resolution]
# Quality settings
quality_map = {
"fast": ("23", "fast"),
"balanced": ("18", "medium"),
"best": ("15", "slow")
}
crf, preset = quality_map.get(quality, ("18", "medium"))
# Save uploaded file
upload_id = str(uuid.uuid4())[:8]
input_path = f"/tmp/input_{upload_id}.mp4"
output_path = f"{OUTPUT_DIR}/upscaled_{upload_id}.mp4"
with open(input_path, "wb") as f:
content = await video.read()
f.write(content)
# FFmpeg command for high-quality upscaling
cmd = [
'ffmpeg', '-y',
'-i', input_path,
'-vf', f'scale={width}:{height}:flags=lanczos',
'-c:v', 'libx264',
'-crf', crf,
'-preset', preset,
'-c:a', 'aac', '-b:a', '192k',
'-movflags', '+faststart',
output_path
]
# Run FFmpeg
process = subprocess.run(cmd, capture_output=True, text=True, timeout=3600)
# Clean up input
os.remove(input_path)
if process.returncode == 0 and os.path.exists(output_path):
size_mb = round(os.path.getsize(output_path) / (1024 * 1024), 1)
return {
"success": True,
"resolution": f"{width}x{height}",
"size_mb": size_mb,
"download_url": f"/download/{upload_id}"
}
else:
return {"success": False, "error": "FFmpeg processing failed"}
except subprocess.TimeoutExpired:
return {"success": False, "error": "Processing timeout (>1 hour)"}
except Exception as e:
return {"success": False, "error": str(e)}
@app.get("/download/{upload_id}")
async def download_file(upload_id: str):
file_path = f"{OUTPUT_DIR}/upscaled_{upload_id}.mp4"
if os.path.exists(file_path):
return FileResponse(file_path, filename=f"upscaled_{upload_id}.mp4", media_type="video/mp4")
return {"error": "File not found"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)