from __future__ import annotations import os import shlex from pathlib import Path from pydantic import BaseModel, Field class Settings(BaseModel): sab_url: str = "http://sabnzbd:8080" sab_api_key: str | None = None sab_category: str = "manual" sab_storage_root: Path | None = None download_root: Path = Path("/data/downloads/manual") movies_root: Path = Path("/data/movies") tv_root: Path = Path("/data/tv") state_path: Path = Path("/config/importarr.db") log_level: str = "info" radarr_url: str | None = None radarr_api_key: str | None = None sonarr_url: str | None = None sonarr_api_key: str | None = None auth_token: str | None = None restart_command: list[str] = Field(default_factory=lambda: ["systemctl", "restart", "importarr.service"]) update_command: list[str] = Field(default_factory=lambda: ["sh", "deploy/repo-upgrade.sh"]) update_release_url: str = "https://gitea.delphas.dk/api/v1/repos/daniels/importarr/releases/latest" update_check_timeout_seconds: int = Field(default=15, ge=1) control_command_timeout_seconds: int = Field(default=120, ge=1) bind_host: str = "127.0.0.1" bind_port: int = 8765 poll_seconds: int = Field(default=60, ge=5) @classmethod def from_env(cls) -> "Settings": return cls( sab_url=os.getenv("IMPORTARR_SAB_URL", cls.model_fields["sab_url"].default), sab_api_key=_env_secret("IMPORTARR_SAB_API_KEY"), sab_category=os.getenv("IMPORTARR_SAB_CATEGORY", "manual"), sab_storage_root=Path(os.getenv("IMPORTARR_SAB_STORAGE_ROOT")) if os.getenv("IMPORTARR_SAB_STORAGE_ROOT") else None, download_root=Path(os.getenv("IMPORTARR_DOWNLOAD_ROOT", "/data/downloads/manual")), movies_root=Path(os.getenv("IMPORTARR_MOVIES_ROOT", "/data/movies")), tv_root=Path(os.getenv("IMPORTARR_TV_ROOT", "/data/tv")), state_path=Path(os.getenv("IMPORTARR_STATE_PATH", "/config/importarr.db")), log_level=os.getenv("IMPORTARR_LOG_LEVEL", "info"), radarr_url=os.getenv("IMPORTARR_RADARR_URL"), radarr_api_key=_env_secret("IMPORTARR_RADARR_API_KEY"), sonarr_url=os.getenv("IMPORTARR_SONARR_URL"), sonarr_api_key=_env_secret("IMPORTARR_SONARR_API_KEY"), auth_token=_env_secret("IMPORTARR_AUTH_TOKEN"), restart_command=_env_command("IMPORTARR_RESTART_COMMAND", ["systemctl", "restart", "importarr.service"]), update_command=_env_command("IMPORTARR_UPDATE_COMMAND", ["sh", "deploy/repo-upgrade.sh"]), update_release_url=os.getenv("IMPORTARR_UPDATE_RELEASE_URL", "https://gitea.delphas.dk/api/v1/repos/daniels/importarr/releases/latest"), update_check_timeout_seconds=int(os.getenv("IMPORTARR_UPDATE_CHECK_TIMEOUT_SECONDS", "15")), control_command_timeout_seconds=int(os.getenv("IMPORTARR_CONTROL_COMMAND_TIMEOUT_SECONDS", "120")), bind_host=os.getenv("IMPORTARR_BIND_HOST", "127.0.0.1"), bind_port=int(os.getenv("IMPORTARR_BIND_PORT", "8765")), poll_seconds=int(os.getenv("IMPORTARR_POLL_SECONDS", "60")), ) def resolve_under_download_root(self, user_path: str) -> Path: root = self.download_root.resolve() candidate = Path(user_path) if not candidate.is_absolute(): candidate = root / candidate resolved = candidate.resolve() if resolved != root and root not in resolved.parents: raise ValueError("path must resolve under IMPORTARR_DOWNLOAD_ROOT") return resolved def _env_secret(name: str) -> str | None: file_value = os.getenv(f"{name}_FILE") if file_value: return Path(file_value).read_text(encoding="utf-8").strip() return os.getenv(name) def _env_command(name: str, default: list[str]) -> list[str]: value = os.getenv(name) if not value: return default return shlex.split(value)