nexus-video-api / app.py
webnowa's picture
Update app.py
e456186 verified
Raw
History Blame Contribute Delete
4.77 kB
import os
import uuid
import asyncio
from typing import Dict, Any
import replicate
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
import httpx
REPLICATE_API_TOKEN = os.getenv("REPLICATE_API_TOKEN")
if not REPLICATE_API_TOKEN:
raise RuntimeError("Missing REPLICATE_API_TOKEN env var")
os.environ["REPLICATE_API_TOKEN"] = REPLICATE_API_TOKEN
VIDEO_OUTPUT_DIR = "video_outputs"
os.makedirs(VIDEO_OUTPUT_DIR, exist_ok=True)
app = FastAPI(title="nexus-video-api", version="2.0.0")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
VIDEO_MODELS = {
"tiktok": "minimax/video-01",
"social": "minimax/video-01",
"cinematic": "minimax/video-01",
"ads": "minimax/video-01",
"music": "minimax/video-01",
"music_long": "minimax/video-01",
"experimental": "minimax/video-01",
"avatar": "minimax/video-01",
}
TASKS: Dict[str, Dict[str, Any]] = {}
TASKS_LOCK = asyncio.Lock()
def create_task(mode: str) -> str:
task_id = str(uuid.uuid4())
TASKS[task_id] = {"mode": mode, "status": "queued", "error": None, "video_path": None}
return task_id
async def set_task(task_id: str, **updates: Any) -> None:
async with TASKS_LOCK:
if task_id in TASKS:
TASKS[task_id].update(updates)
async def run_model(task_id: str, model_id: str, payload: Dict[str, Any]) -> None:
await set_task(task_id, status="processing", error=None)
try:
output = await asyncio.to_thread(
replicate.run,
model_id,
input={"prompt": payload.get("prompt", "")},
)
video_url = output[0] if isinstance(output, list) else str(output)
async with httpx.AsyncClient(timeout=120) as client:
resp = await client.get(video_url)
resp.raise_for_status()
video_bytes = resp.content
if not video_bytes:
raise RuntimeError("Empty video output")
out_path = os.path.join(VIDEO_OUTPUT_DIR, f"{task_id}.mp4")
with open(out_path, "wb") as f:
f.write(video_bytes)
await set_task(task_id, status="done", video_path=out_path)
except Exception as e:
await set_task(task_id, status="error", error=str(e), video_path=None)
@app.get("/")
def root():
return {"status": "ok", "service": "nexus-video-api", "version": "2.0.0"}
@app.get("/health")
def health():
return {"ok": True}
@app.get("/video/status/{task_id}")
async def video_status(task_id: str):
task = TASKS.get(task_id)
if not task:
raise HTTPException(status_code=404, detail="task_not_found")
return {"task_id": task_id, "mode": task["mode"], "status": task["status"], "error": task["error"], "ready": bool(task["video_path"]) and task["status"] == "done"}
@app.get("/video/{task_id}")
async def get_video(task_id: str):
task = TASKS.get(task_id)
if not task:
raise HTTPException(status_code=404, detail="task_not_found")
if not task.get("video_path") or task.get("status") != "done":
return {"error": "video_not_ready"}
return FileResponse(task["video_path"], media_type="video/mp4")
@app.post("/video/tiktok")
async def video_tiktok(data: dict):
task_id = create_task("tiktok")
asyncio.create_task(run_model(task_id, VIDEO_MODELS["tiktok"], data))
return {"task_id": task_id}
@app.post("/video/social")
async def video_social(data: dict):
task_id = create_task("social")
asyncio.create_task(run_model(task_id, VIDEO_MODELS["social"], data))
return {"task_id": task_id}
@app.post("/video/cinematic")
async def video_cinematic(data: dict):
task_id = create_task("cinematic")
asyncio.create_task(run_model(task_id, VIDEO_MODELS["cinematic"], data))
return {"task_id": task_id}
@app.post("/video/ads")
async def video_ads(data: dict):
task_id = create_task("ads")
asyncio.create_task(run_model(task_id, VIDEO_MODELS["ads"], data))
return {"task_id": task_id}
@app.post("/video/music")
async def video_music(data: dict):
task_id = create_task("music")
asyncio.create_task(run_model(task_id, VIDEO_MODELS["music"], data))
return {"task_id": task_id}
@app.post("/video/music_long")
async def video_music_long(data: dict):
task_id = create_task("music_long")
asyncio.create_task(run_model(task_id, VIDEO_MODELS["music_long"], data))
return {"task_id": task_id}
@app.post("/video/experimental")
async def video_experimental(data: dict):
task_id = create_task("experimental")
asyncio.create_task(run_model(task_id, VIDEO_MODELS["experimental"], data))
return {"task_id": task_id}