Files
live/backend/main.py
T

115 lines
3.9 KiB
Python

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
import asyncio
import time
import psutil
from database import init_db
from services.transcoder import transcode_mgr
from routes import streams_router, nodes_router, auth_router
from routes.settings_route import router as settings_router
from config import settings
from services.load_balancer import lb
from services.node_store import load_nodes
async def monitor_origin():
previous = psutil.net_io_counters()
previous_time = time.monotonic()
while True:
await asyncio.sleep(10)
current = psutil.net_io_counters()
current_time = time.monotonic()
elapsed = current_time - previous_time
network_in = ((current.bytes_recv - previous.bytes_recv) * 8 / elapsed / 1e6) if elapsed > 0 else None
network_out = ((current.bytes_sent - previous.bytes_sent) * 8 / elapsed / 1e6) if elapsed > 0 else None
media_available = False
viewer_count = None
try:
async with httpx.AsyncClient(timeout=3) as client:
response = await client.get(f"http://127.0.0.1:{settings.media_origin_api_port}/v3/paths/list")
response.raise_for_status()
payload = response.json()
viewer_count = sum(item.get("readers", 0) for item in payload.get("items", []))
media_available = True
except Exception:
pass
lb.report_health("default", {
"cpu_percent": psutil.cpu_percent(),
"memory_percent": psutil.virtual_memory().percent,
"bandwidth_mbps": network_in + network_out if network_in is not None and network_out is not None else None,
"network_in_mbps": network_in,
"network_out_mbps": network_out,
"active_connections": len(psutil.net_connections(kind="tcp")),
"hls_readers": viewer_count,
"viewer_count": viewer_count,
"frame_drop_indicators": None,
"packet_loss_indicators": None,
"media_metrics_available": False,
})
previous = current
previous_time = current_time
@asynccontextmanager
async def lifespan(app: FastAPI):
load_nodes()
await init_db()
origin_monitor = asyncio.create_task(monitor_origin())
yield
origin_monitor.cancel()
transcode_mgr.stop_all()
app = FastAPI(title="ProgramCore", 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.include_router(settings_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)