"""Waxwing application config — env-driven, no pydantic-settings dependency.""" from __future__ import annotations import os from pathlib import Path from circuitforge_core.config.settings import load_env _ROOT = Path(__file__).resolve().parents[2] load_env(_ROOT / ".env") class Settings: API_PREFIX: str = os.environ.get("API_PREFIX", "/api/v1") PROJECT_NAME: str = "Waxwing — Gardening Assistant" CORS_ORIGINS: list[str] = [ o.strip() for o in os.environ.get("CORS_ORIGINS", "").split(",") if o.strip() ] DATA_DIR: Path = Path(os.environ.get("DATA_DIR", str(_ROOT / "data"))) DB_PATH: Path = Path(os.environ.get("DB_PATH", str(DATA_DIR / "waxwing.db"))) COORDINATOR_URL: str = os.environ.get("COORDINATOR_URL", "http://localhost:7700") CF_LICENSE_KEY: str | None = os.environ.get("CF_LICENSE_KEY") # Plex integration (Strahl → cf-video → knowledge pipeline) PLEX_URL: str = os.environ.get("PLEX_URL", "http://10.1.10.117:32400") PLEX_TOKEN: str | None = os.environ.get("PLEX_TOKEN") # Shared secret appended to the Plex webhook URL as ?token= # Required — endpoint returns 503 if unset (fail closed) PLEX_WEBHOOK_SECRET: str | None = os.environ.get("PLEX_WEBHOOK_SECRET") # Comma-separated show titles (grandparentTitle in Plex webhooks) — matched case-insensitively. # Strahl uses generic section names ("Series", "Movies"); individual show titles are used here. PLEX_GARDENING_SECTIONS: list[str] = [ s.strip() for s in os.environ.get("PLEX_GARDENING_SECTIONS", "Garden Rescue,Gardener's World").split(",") if s.strip() ] # Direct URL to a running cf-video service (http://127.0.0.1:8016 on Muninn, or via tunnel) CF_VIDEO_URL: str | None = os.environ.get("CF_VIDEO_URL") DEBUG: bool = os.environ.get("DEBUG", "false").lower() in ("1", "true", "yes") CLOUD_MODE: bool = os.environ.get("CLOUD_MODE", "false").lower() in ("1", "true", "yes") CLOUD_DATA_ROOT: Path = Path(os.environ.get("CLOUD_DATA_ROOT", "/devl/waxwing-cloud-data")) def ensure_dirs(self) -> None: self.DATA_DIR.mkdir(parents=True, exist_ok=True) settings = Settings()