from pathlib import Path from fastapi import APIRouter, HTTPException, Request from fastapi.responses import HTMLResponse from fastapi.templating import Jinja2Templates from app.services.engine_registry import get_engine_registry from app.services.job_store import get_job, list_jobs templates = Jinja2Templates( directory=str(Path(__file__).resolve().parent.parent / "templates") ) router = APIRouter(prefix="", tags=["ui"]) UI_MODEL_LABELS = { "supertonic-2": "Supertonic", "kitten-tts-micro-0.8": "Kitten", "kokoro": "Kokoro", "melo-en": "Melo", } @router.get("/", response_class=HTMLResponse) async def index_page(request: Request) -> HTMLResponse: registry = get_engine_registry() ui_models: list[dict[str, str]] = [] voice_map: dict[str, list[dict]] = {} for model, label in UI_MODEL_LABELS.items(): try: engine = registry.get_engine(model) ui_models.append({"id": model, "label": label}) voice_map[model] = [ {"alias": b["alias"], "canonical_name": b["canonical_name"]} for b in engine.list_voice_bindings() if b.get("source") != "private" ] except Exception: continue return templates.TemplateResponse( "index.html", { "request": request, "ui_models": ui_models, "voice_map": voice_map, "recent_jobs": list_jobs(limit=5), }, ) @router.get("/jobs/{job_id}", response_class=HTMLResponse) async def job_page(request: Request, job_id: str) -> HTMLResponse: job = get_job(job_id) if job is None: raise HTTPException(status_code=404, detail="Job not found") return templates.TemplateResponse("job.html", {"request": request, "job": job})