"""Load settings.yaml. One place for every tunable value.""" import os import yaml PIPELINE_DIR = os.path.dirname(os.path.abspath(__file__)) REPO_ROOT = os.path.dirname(PIPELINE_DIR) def load(path=None): path = path or os.environ.get("AUDIOBOOK_SETTINGS") or os.path.join(PIPELINE_DIR, "settings.yaml") with open(path, encoding="utf-8") as f: cfg = yaml.safe_load(f) cfg["_path"] = path apply_env(cfg) return cfg # Environment variables that override settings.yaml (used by the CI job; nothing is hard-coded to a host). ENV_OVERRIDES = [ ("AUDIOBOOK_BOOKS_DIR", ("books_dir",)), ("AUDIOBOOK_WORK_DIR", ("work_dir",)), ("AUDIOBOOK_ARCHIVE_DIR", ("archive_dir",)), ("AUDIOBOOK_VOICE_ENDPOINT", ("voice", "endpoint")), ("AUDIOBOOK_LLM_ENDPOINT", ("llm", "endpoint")), ("AUDIOBOOK_LLM_MODEL", ("llm", "model")), ] def apply_env(cfg, environ=None): environ = os.environ if environ is None else environ for var, keys in ENV_OVERRIDES: value = environ.get(var, "").strip() if value: node = cfg for k in keys[:-1]: node = node[k] node[keys[-1]] = value flag = environ.get("AUDIOBOOK_REQUIRE_OCR", "").strip().lower() if flag: cfg["ocr"]["required"] = flag in ("1", "true", "yes") def resolve(cfg, key): """A path setting as an absolute path (relative values are relative to the repo root).""" value = cfg[key] return value if os.path.isabs(value) else os.path.join(REPO_ROOT, value) def audio_dir(cfg, book_dir): """Where audio for a book goes: the book folder by default, else under archive_dir.""" if not cfg.get("archive_dir"): return os.path.join(book_dir, "audio") base = cfg["archive_dir"] base = base if os.path.isabs(base) else os.path.join(REPO_ROOT, base) parts = os.path.normpath(book_dir).split(os.sep)[-2:] # / return os.path.join(base, *parts, "audio")