test_01 / main.py
lljz66's picture
Update main.py
b5103e0 verified
"""
FootClip AI — FastAPI Backend
"""
import os
import uuid
import asyncio
import logging
from typing import Dict
from fastapi import FastAPI, UploadFile, File, HTTPException, BackgroundTasks
from fastapi.responses import FileResponse
from fastapi.middleware.cors import CORSMiddleware
from models import ProcessRequest, JobStatus
from pipeline import run_pipeline, get_device, WORKSPACE, OUTPUT_DIR
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(name)s] %(message)s")
logger = logging.getLogger("footclip")
app = FastAPI(
title="FootClip AI",
description="Automatic football highlight generator using YOLOv8 + audio analysis",
version="1.0.0",
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
jobs: Dict[str, JobStatus] = {}
@app.get("/")
def root():
return {"status": "ok", "message": "FootClip AI is running", "docs": "/docs"}
@app.get("/api/health")
async def health():
return {"status": "ok", "device": get_device(), "workspace": WORKSPACE}
@app.post("/api/upload")
async def upload_video(file: UploadFile = File(...)):
upload_dir = os.path.join(WORKSPACE, "uploads")
os.makedirs(upload_dir, exist_ok=True)
filename = f"{uuid.uuid4().hex[:8]}_{file.filename}"
path = os.path.join(upload_dir, filename)
with open(path, "wb") as f:
while chunk := await file.read(1024 * 1024):
f.write(chunk)
return {"filename": filename, "path": path}
@app.post("/api/process")
async def start_processing(config: ProcessRequest, background_tasks: BackgroundTasks):
job_id = uuid.uuid4().hex[:12]
job = JobStatus(job_id=job_id, status="queued", stage="queued")
jobs[job_id] = job
background_tasks.add_task(_run_job, config, job)
return {"job_id": job_id, "status": "queued"}
def _run_job(config: ProcessRequest, job: JobStatus):
job.status = "processing"
try:
run_pipeline(config, job, update_cb=lambda j: None)
except Exception as e:
job.status = "error"
job.message = str(e)
logger.error(f"Job {job.job_id} failed: {e}")
@app.get("/api/status/{job_id}")
async def get_status(job_id: str):
job = jobs.get(job_id)
if not job:
raise HTTPException(404, "Job not found")
return job.model_dump()
@app.get("/api/download/{job_id}")
async def download_result(job_id: str):
job = jobs.get(job_id)
if not job:
raise HTTPException(404, "Job not found")
if job.status != "done":
raise HTTPException(400, f"Job not done yet (status: {job.status})")
output_path = job.result.get("output_path", "")
if not os.path.exists(output_path):
raise HTTPException(404, "Output file not found")
return FileResponse(output_path, media_type="video/mp4", filename=f"footclip_{job_id}.mp4")
@app.get("/api/jobs")
async def list_jobs():
return [j.model_dump() for j in jobs.values()]
if __name__ == "__main__":
import uvicorn
port = int(os.environ.get("PORT", 7860))
uvicorn.run(app, host="0.0.0.0", port=port)