fix: inject hls_manager into routes module so preview endpoints work

This commit is contained in:
Zac Gaetano 2026-04-14 10:12:46 -04:00
parent b936647abb
commit 7e51da50c8

View file

@ -14,13 +14,12 @@ from .recorders.recorder import RecorderManager
from .recorders.scte35 import SCTE35Manager from .recorders.scte35 import SCTE35Manager
from .utils.hls import HLSPreviewManager from .utils.hls import HLSPreviewManager
from .api.routes import router from .api.routes import router
from .api import routes as routes_module # to set module-level globals from .api import routes as routes_module
from .api.websocket import ConnectionManager from .api.websocket import ConnectionManager
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
settings = Settings() settings = Settings()
# Global manager instances
recorder_manager: RecorderManager | None = None recorder_manager: RecorderManager | None = None
scte35_manager: SCTE35Manager | None = None scte35_manager: SCTE35Manager | None = None
hls_manager: HLSPreviewManager | None = None hls_manager: HLSPreviewManager | None = None
@ -29,48 +28,43 @@ connection_manager = ConnectionManager()
@asynccontextmanager @asynccontextmanager
async def lifespan(app: FastAPI): async def lifespan(app: FastAPI):
"""Initialize and cleanup application resources."""
global recorder_manager, scte35_manager, hls_manager global recorder_manager, scte35_manager, hls_manager
# Startup
logger.info("Starting Deltacast SDI Recorder...") logger.info("Starting Deltacast SDI Recorder...")
recorder_manager = RecorderManager(settings) recorder_manager = RecorderManager(settings)
recorder_manager.initialize() recorder_manager.initialize()
scte35_manager = SCTE35Manager() scte35_manager = SCTE35Manager()
hls_manager = HLSPreviewManager(settings) hls_manager = HLSPreviewManager(settings)
# Inject managers into routes module # Inject all managers into routes module
routes_module.recorder_manager = recorder_manager routes_module.recorder_manager = recorder_manager
routes_module.scte35_manager = scte35_manager routes_module.scte35_manager = scte35_manager
routes_module.hls_manager = hls_manager
# Start background task for status broadcasting
status_task = asyncio.create_task(broadcast_port_status()) status_task = asyncio.create_task(broadcast_port_status())
logger.info("Deltacast SDI Recorder started successfully") logger.info(f"Deltacast SDI Recorder started — {settings.deltacast_port_count} ports")
yield # Application runs here yield
# Shutdown
logger.info("Shutting down Deltacast SDI Recorder...") logger.info("Shutting down Deltacast SDI Recorder...")
status_task.cancel() status_task.cancel()
try: try:
await status_task await status_task
except asyncio.CancelledError: except asyncio.CancelledError:
pass pass
# Stop all recorders and previews
for port_index in range(settings.deltacast_port_count): for port_index in range(settings.deltacast_port_count):
await recorder_manager.stop_recording(port_index) await recorder_manager.stop_recording(port_index)
await hls_manager.stop_preview(port_index) await hls_manager.stop_preview(port_index)
logger.info("Shutdown complete") logger.info("Shutdown complete")
async def broadcast_port_status(): async def broadcast_port_status():
"""Background task: broadcast port status to all WebSocket clients every 1 second."""
while True: while True:
try: try:
if recorder_manager is not None: if recorder_manager is not None:
@ -92,7 +86,6 @@ app = FastAPI(
lifespan=lifespan lifespan=lifespan
) )
# CORS - allow all origins for local network use
app.add_middleware( app.add_middleware(
CORSMiddleware, CORSMiddleware,
allow_origins=["*"], allow_origins=["*"],
@ -101,18 +94,14 @@ app.add_middleware(
allow_headers=["*"], allow_headers=["*"],
) )
# Include API router
app.include_router(router) app.include_router(router)
@app.websocket("/ws") @app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket): async def websocket_endpoint(websocket: WebSocket):
"""WebSocket endpoint for real-time port status updates."""
await connection_manager.connect(websocket) await connection_manager.connect(websocket)
try: try:
while True: while True:
# Keep connection alive, receive any messages
# (not used but required to detect disconnect)
data = await websocket.receive_text() data = await websocket.receive_text()
except WebSocketDisconnect: except WebSocketDisconnect:
connection_manager.disconnect(websocket) connection_manager.disconnect(websocket)
@ -120,7 +109,6 @@ async def websocket_endpoint(websocket: WebSocket):
@app.get("/hls/{filename}") @app.get("/hls/{filename}")
async def serve_hls(filename: str): async def serve_hls(filename: str):
"""Serve HLS segment and playlist files."""
hls_path = Path("/tmp/hls") / filename hls_path = Path("/tmp/hls") / filename
if not hls_path.exists(): if not hls_path.exists():
raise HTTPException(status_code=404, detail="HLS file not found") raise HTTPException(status_code=404, detail="HLS file not found")