deltacast-sdi-recorder/backend/app/main.py

115 lines
3.2 KiB
Python

"""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))