import os from contextlib import asynccontextmanager from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse from starlette.responses import RedirectResponse from starlette.background import BackgroundTask import httpx from database import init_db from services.transcoder import transcode_mgr from routes import streams_router, nodes_router, auth_router from config import settings @asynccontextmanager async def lifespan(app: FastAPI): await init_db() yield transcode_mgr.stop_all() app = FastAPI(title="StreamCore", lifespan=lifespan) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(streams_router) app.include_router(nodes_router) app.include_router(auth_router) @app.get("/api/health") async def health(): from services.multicast_detector import multicast_detector return { "status": "ok", "multicast_available": multicast_detector.is_available(), } @app.get("/hls/{path:path}") async def serve_hls(path: str): hls_root = "/tmp/hls" fs_path = os.path.join(hls_root, path) if os.path.isfile(fs_path): return FileResponse(fs_path) target = f"http://127.0.0.1:{settings.media_origin_hls_port}/{path}" try: async with httpx.AsyncClient(timeout=httpx.Timeout(10)) as client: req = client.build_request("GET", target) resp = await client.send(req, stream=True) from starlette.responses import StreamingResponse return StreamingResponse( resp.aiter_bytes(), status_code=resp.status_code, headers=dict(resp.headers), media_type=resp.headers.get("content-type"), ) except Exception: raise HTTPException(status_code=404)