"""Main FastAPI application for Deltacast SDI Recorder.""" import asyncio import json import logging from contextlib import asynccontextmanager from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse from pathlib import Path from .config import Settings from .recorders.recorder import RecorderManager from .recorders.scte35 import SCTE35Manager from .utils.hls import HLSPreviewManager from .api.routes import router from .api import routes as routes_module from .api.websocket import ConnectionManager logger = logging.getLogger(__name__) settings = Settings() recorder_manager: RecorderManager | None = None scte35_manager: SCTE35Manager | None = None hls_manager: HLSPreviewManager | None = None connection_manager = ConnectionManager() @asynccontextmanager async def lifespan(app: FastAPI): global recorder_manager, scte35_manager, hls_manager logger.info("Starting Deltacast SDI Recorder...") recorder_manager = RecorderManager(settings) recorder_manager.initialize() scte35_manager = SCTE35Manager() hls_manager = HLSPreviewManager(settings) # Inject all managers into routes module routes_module.recorder_manager = recorder_manager routes_module.scte35_manager = scte35_manager routes_module.hls_manager = hls_manager status_task = asyncio.create_task(broadcast_port_status()) logger.info(f"Deltacast SDI Recorder started — {settings.deltacast_port_count} ports") yield logger.info("Shutting down Deltacast SDI Recorder...") status_task.cancel() try: await status_task except asyncio.CancelledError: pass for port_index in range(settings.deltacast_port_count): await recorder_manager.stop_recording(port_index) await hls_manager.stop_preview(port_index) logger.info("Shutdown complete") async def broadcast_port_status(): while True: try: if recorder_manager is not None: statuses = recorder_manager.get_all_status() data = { "type": "port_status", "ports": [s.model_dump() for s in statuses] } await connection_manager.broadcast(data) except Exception as e: logger.error(f"Error broadcasting port status: {e}") await asyncio.sleep(1) app = FastAPI( title="Deltacast SDI Recorder", description="Web GUI for Deltacast SDI capture card recording", version="1.0.0", lifespan=lifespan ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(router) @app.websocket("/ws") async def websocket_endpoint(websocket: WebSocket): await connection_manager.connect(websocket) try: while True: data = await websocket.receive_text() except WebSocketDisconnect: connection_manager.disconnect(websocket) @app.get("/hls/{filename}") async def serve_hls(filename: str): hls_path = Path("/tmp/hls") / filename if not hls_path.exists(): raise HTTPException(status_code=404, detail="HLS file not found") return FileResponse(str(hls_path))