"""Application configuration helpers.""" from __future__ import annotations import os from pathlib import Path from typing import Dict from dotenv import load_dotenv DEFAULT_IMMICH_URL = "https://photos.4angle.com/" class ConfigError(RuntimeError): """Raised when critical configuration is missing.""" LEGACY_KEY_MAP = { "immich": "IMMICH_API_KEY", "openai": "OPENAI_API_KEY", } def _load_legacy_api_keys(path: Path) -> Dict[str, str]: """Parse the legacy ``api_keys`` file if it exists.""" if not path.exists(): return {} values: Dict[str, str] = {} for line in path.read_text().splitlines(): line = line.strip() if not line or line.startswith("#"): continue if "=" not in line: continue key, value = [piece.strip() for piece in line.split("=", 1)] mapped_key = LEGACY_KEY_MAP.get(key.lower()) if mapped_key and value: values[mapped_key] = value return values def load_settings() -> Dict[str, str]: """Load configuration from environment variables and ``api_keys``.""" load_dotenv() settings: Dict[str, str] = { "IMMICH_API_URL": os.getenv("IMMICH_API_URL", DEFAULT_IMMICH_URL).rstrip("/"), "IMMICH_API_KEY": os.getenv("IMMICH_API_KEY", ""), "OPENAI_API_KEY": os.getenv("OPENAI_API_KEY", ""), "RECENT_DAYS": int(os.getenv("RECENT_DAYS", "3")), "OPENAI_MODEL": os.getenv("OPENAI_MODEL", "gpt-4o-mini"), "ALT_TEXT_DB": os.getenv("ALT_TEXT_DB", ""), "SECRET_KEY": os.getenv("SECRET_KEY", ""), } legacy_values = _load_legacy_api_keys(Path("api_keys")) for key, value in legacy_values.items(): if not settings.get(key): settings[key] = value missing = [ key for key in ("IMMICH_API_KEY", "OPENAI_API_KEY") if not settings.get(key) ] if missing: raise ConfigError( "Missing required configuration values: " + ", ".join(missing) ) return settings