"""Runtime settings store. Configuration can come from two places: environment variables (the deploy-time defaults) and a JSON file written by the in-app Settings page. The file, when present, wins. ``load`` returns the effective settings; ``save`` persists the editable subset and returns the new effective settings. """ from __future__ import annotations import json import os from pathlib import Path SETTINGS_FILE = Path(os.environ.get("SETTINGS_FILE", "cache/settings.json")) FIELDS = ( "emby_url", "emby_api_key", "navidrome_url", "navidrome_user", "navidrome_password", "music_root", ) def env_defaults() -> dict: return { "emby_url": os.environ.get("EMBY_URL", "http://10.0.0.2:8096"), "emby_api_key": os.environ.get("EMBY_API_KEY", ""), "navidrome_url": os.environ.get("NAVIDROME_URL", "http://10.0.0.2:4533"), "navidrome_user": os.environ.get("NAVIDROME_USER", ""), "navidrome_password": os.environ.get("NAVIDROME_PASSWORD", ""), "music_root": os.environ.get("MUSIC_ROOT", r"\\Matt-htpc\d\Music"), } def _read_file() -> dict: if not SETTINGS_FILE.exists(): return {} try: data = json.loads(SETTINGS_FILE.read_text(encoding="utf-8")) except (OSError, ValueError): return {} return {k: str(v) for k, v in data.items() if k in FIELDS and v is not None} def load() -> dict: """Effective settings: env defaults overlaid with the saved file.""" values = env_defaults() values.update(_read_file()) return values def save(updates: dict) -> dict: """Persist the editable subset of ``updates`` and return effective settings.""" current = _read_file() for key in FIELDS: if key in updates and updates[key] is not None: current[key] = str(updates[key]).strip() SETTINGS_FILE.parent.mkdir(parents=True, exist_ok=True) SETTINGS_FILE.write_text(json.dumps(current, indent=2), encoding="utf-8") return load()