"""Lightweight development backend for UI work. This avoids importing heavy ML dependencies so the UI can run during frontend development without installing large Python packages (torch/whisperx/etc.). Use this when you only need the health/file streaming endpoints. """ from fastapi import FastAPI, Request, HTTPException from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import StreamingResponse from pathlib import Path from routers import audio app = FastAPI(title="TalkEdit Dev Backend", version="0.0.1") app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], expose_headers=["Content-Range", "Accept-Ranges", "Content-Length"], ) MIME_MAP = { ".mp4": "video/mp4", ".mkv": "video/x-matroska", ".mov": "video/quicktime", ".avi": "video/x-msvideo", ".webm": "video/webm", ".m4a": "audio/mp4", ".wav": "audio/wav", ".mp3": "audio/mpeg", ".flac": "audio/flac", } app.include_router(audio.router) @app.get("/health") async def health(): return {"status": "ok"} @app.get("/file") async def serve_local_file(request: Request, path: str): file_path = Path(path) if not file_path.is_file(): raise HTTPException(status_code=404, detail=f"File not found: {path}") file_size = file_path.stat().st_size content_type = MIME_MAP.get(file_path.suffix.lower(), "application/octet-stream") range_header = request.headers.get("range") if range_header: range_spec = range_header.replace("bytes=", "") start_str, end_str = range_spec.split("-") start = int(start_str) if start_str else 0 end = int(end_str) if end_str else file_size - 1 end = min(end, file_size - 1) content_length = end - start + 1 def iter_range(): with open(file_path, "rb") as f: f.seek(start) remaining = content_length while remaining > 0: chunk = f.read(min(65536, remaining)) if not chunk: break remaining -= len(chunk) yield chunk return StreamingResponse( iter_range(), status_code=206, media_type=content_type, headers={ "Content-Range": f"bytes {start}-{end}/{file_size}", "Accept-Ranges": "bytes", "Content-Length": str(content_length), }, ) def iter_file(): with open(file_path, "rb") as f: while chunk := f.read(65536): yield chunk return StreamingResponse( iter_file(), media_type=content_type, headers={ "Accept-Ranges": "bytes", "Content-Length": str(file_size), }, )