import httpx import asyncio from config import settings class MediaMTXService: @staticmethod def get_rtmp_url(stream_key: str) -> str: return f"rtmp://{settings.media_origin_host}:{settings.media_origin_rtmp_port}/live/{stream_key}" @staticmethod def get_hls_url(node: dict, stream_key: str) -> str: return f"http://{node['host']}:{node['hls_port']}/{stream_key}/index.m3u8" @staticmethod async def get_origin_status(stream_key: str) -> dict | None: try: async with httpx.AsyncClient(timeout=httpx.Timeout(5)) as client: resp = await client.get( f"http://{settings.media_origin_host}:{settings.media_origin_api_port}/v3/paths/list" ) if resp.status_code == 200: data = resp.json() for item in data.get("items", []): # Paths are named after the RTMP URL key, so a publisher on # rtmp://host/live/ shows up as "live/", never as # the bare key. This MediaMTX exposes the reader list itself # rather than a readersCount/sourceReady pair. if item.get("name") in (stream_key, f"live/{stream_key}"): return { "ready": item.get("ready", False), "source_ready": bool(item.get("source")), "readers": len(item.get("readers") or []), } except Exception: return None return None @staticmethod async def get_edge_status(node: dict, stream_key: str) -> dict | None: try: async with httpx.AsyncClient(timeout=httpx.Timeout(3)) as client: resp = await client.get( f"http://{node['host']}:{node['api_port']}/v3/paths/list" ) if resp.status_code == 200: data = resp.json() for item in data.get("items", []): if item.get("name") == stream_key: return {"ready": item.get("ready", False), "readers": item.get("readersCount", 0)} except Exception: return None return None media_service = MediaMTXService()