from pydantic_settings import BaseSettings from pathlib import Path from typing import Literal APP_DIR = Path(__file__).resolve().parent DEFAULT_DATA_DIR = APP_DIR / "data" DEFAULT_DATA_DIR.mkdir(parents=True, exist_ok=True) class Settings(BaseSettings): database_url: str = f"sqlite+aiosqlite:///{(DEFAULT_DATA_DIR / 'streamcore.db').as_posix()}" admin_password: str = "admin123" media_origin_host: str = "127.0.0.1" media_origin_rtmp_port: int = 1935 media_origin_api_port: int = 9997 media_origin_hls_port: int = 8888 # MediaMTX is supervised by the backend: started at boot if the origin is not # already answering, and restarted if it dies. Set LS_MEDIA_ORIGIN_AUTOSTART=false # to manage it externally (e.g. systemd, or an edge node's own launcher). media_origin_autostart: bool = True # "mediamtx" resolves via PATH; the repo-local copies are the fallback so a # checkout works without a system-wide install. On Windows the binary sits in # mediamtx\, on Linux/macOS it is usually a bare `mediamtx` on PATH. media_origin_binary: str = "mediamtx" media_origin_config: str = str(APP_DIR.parent / "mediamtx" / "mediamtx.yml") replay_media_dir: str = str(APP_DIR.parent / "media") hls_runtime_dir: str = str(APP_DIR.parent / "runtime" / "hls") hls_segment_seconds: int = 4 edge_nodes: list[dict] = [ { "id": "default", "name": "Origin (Default)", "host": "127.0.0.1", "hls_port": 8888, "api_port": 9997, "weight": 100, "enabled": True, "subnet": "0.0.0.0/0", "is_origin": True, }, ] load_balance_strategy: Literal["round-robin", "weighted", "least-conn", "subnet"] = "subnet" qualities: list[dict] = [ {"label": "1080p", "width": 1920, "height": 1080, "bitrate": "5000k"}, {"label": "720p", "width": 1280, "height": 720, "bitrate": "2500k"}, {"label": "480p", "width": 854, "height": 480, "bitrate": "1000k"}, {"label": "360p", "width": 640, "height": 360, "bitrate": "600k"}, ] # Extra rendition that is published alongside the configured ones: the # publisher's own video+audio stream remuxed into HLS without re-encoding. # It costs almost no CPU (stream copy) and gives viewers a lossless # "原画" (source) option even when every configured rendition downsizes. source_rendition_label: str = "原画" source_rendition_enabled: bool = True # On-disk/URL name for that rendition. It IS the HLS path segment, and MediaMTX # rejects any path containing non-alphanumeric characters, so the Chinese # display label above cannot be used here — a CI would 500 trying to serve it. source_rendition_slug: str = "source" transcode_profiles: dict[str, list[dict]] = { "standard": qualities, "bandwidth-saver": [ {"label": "720p", "width": 1280, "height": 720, "bitrate": "1800k"}, {"label": "480p", "width": 854, "height": 480, "bitrate": "800k"}, {"label": "360p", "width": 640, "height": 360, "bitrate": "450k"}, ], } multicast_enabled: bool = True multicast_address_base: str = "239.0.0.1" multicast_port_base: int = 5000 brand_name: str = "StreamCore" brand_tagline: str = "Enterprise Streaming Platform" brand_logo_url: str = "" model_config = {"env_prefix": "LS_"} settings = Settings()