"""Central configuration, loaded from environment variables. Every setting has a sane default so the app runs locally without a `.env` file, but production deploys should set these via `compose.yml` / `.env`. """ from __future__ import annotations import os from pathlib import Path def _int_env(name: str, default: int) -> int: try: return int(os.environ.get(name, str(default))) except ValueError: return default def _float_env(name: str, default: float) -> float: try: return float(os.environ.get(name, str(default))) except ValueError: return default # --- Paths ------------------------------------------------------------- DATA_DIR = Path(os.environ.get("DATA_DIR", "/data")) INBOX_DIR = DATA_DIR / "inbox" JOBS_DIR = DATA_DIR / "jobs" ASSETS_DIR = DATA_DIR / "assets" PROCESSED_FILE = DATA_DIR / "processed.json" BACKGROUND_FILE = ASSETS_DIR / "background" FOREGROUND_FILE = ASSETS_DIR / "foreground" BLEND_MODES_FILE = ASSETS_DIR / "blend_modes" FILTERS_JSON = ASSETS_DIR / "filters.json" # --- Processing ---------------------------------------------------------- OUTPUT_COUNT = _int_env("OUTPUT_COUNT", 3) BLEND_OPACITY = os.environ.get("BLEND_OPACITY", "30%") FILTER_TIMEOUT = _int_env("FILTER_TIMEOUT", 120) MAX_FILTER_ATTEMPTS = _int_env("MAX_FILTER_ATTEMPTS", 8) # Generous on purpose: the very first rembg call also downloads the ~176MB # u2net model, which can take a while depending on the link. REMBG_TIMEOUT = _int_env("REMBG_TIMEOUT", 600) NICE_LEVEL = _int_env("NICE_LEVEL", 18) # gmic CLI binary, override for local dev if not on PATH. rembg has no # equivalent here — it runs via `python -m app.rembg_cli` (see pipeline.py # run_rembg / app/rembg_cli.py), not a standalone binary. GMIC_BIN = os.environ.get("GMIC_BIN", "gmic") # Fixed post-processing chain applied to every composed variant, ported # verbatim from make_random.py. POST_FILTERS: tuple[str, ...] = ( "fx_equalize_local_histograms 75,2,4,100,8,1,0", "fx_map_tones 0.5,0.7,0.1,30,0", "fx_LCE 80,0.5,1,1,0,0", ) # Opacity choices offered in the remix form (blend "amount" percentages). OPACITY_CHOICES: tuple[str, ...] = ( "10%", "20%", "30%", "40%", "50%", "60%", "70%", "80%", "90%", "100%", ) SUPPORTED_EXTENSIONS = {".jpg", ".jpeg", ".png", ".webp"} # --- Inbox watcher --------------------------------------------------------- POLL_INTERVAL_SECONDS = _float_env("POLL_INTERVAL_SECONDS", 5) STABLE_WAIT_SECONDS = _float_env("STABLE_WAIT_SECONDS", 2) # --- Web --------------------------------------------------------------- SITE_TITLE = os.environ.get("SITE_TITLE", "live.f12.rocks")