from __future__ import annotations import os from dataclasses import dataclass from functools import lru_cache @dataclass(frozen=True) class Settings: host: str = "0.0.0.0" port: int = 8095 token: str = "change-this-token" engine: str = "pillow" device: str = "cpu" max_upload_mb: int = 20 max_input_width: int = 4096 max_input_height: int = 4096 max_output_width: int = 8192 max_output_height: int = 8192 tmp_dir: str = "/app/storage/tmp" output_dir: str = "/app/storage/output" result_ttl_minutes: int = 60 model_dir: str = "/app/app/models" default_model: str = "realesrgan-x4plus" realesrgan_bin: str = "/app/bin/realesrgan-ncnn-vulkan" realesrgan_model_dir: str = "/app/models" realesrgan_default_model: str = "realesrgan-x4plus" realesrgan_anime_model: str = "realesrgan-x4plus-anime" realesrgan_gpu_id: int = -1 realesrgan_tile: int = 0 realesrgan_tta: bool = False realesrgan_verbose: bool = False realesrgan_timeout_seconds: int = 900 realesrgan_preprocess_max_pixels: int = 16_777_216 realesrgan_output_ext: str = "webp" realesrgan_allow_model_fallback: bool = True def _env_int(name: str, default: int) -> int: try: return int(os.getenv(name, str(default)).strip()) except ValueError: return default def _env_bool(name: str, default: bool) -> bool: value = os.getenv(name) if value is None: return default normalized = value.strip().lower() if normalized in {"1", "true", "yes", "on"}: return True if normalized in {"0", "false", "no", "off"}: return False return default @lru_cache(maxsize=1) def get_settings() -> Settings: legacy_model_dir = os.getenv("WORKER_MODEL_DIR", "/app/app/models").strip() or "/app/app/models" legacy_default_model = os.getenv("WORKER_DEFAULT_MODEL", "realesrgan-x4plus").strip() or "realesrgan-x4plus" realesrgan_model_dir = os.getenv( "WORKER_REALESRGAN_MODEL_DIR", legacy_model_dir if legacy_model_dir != "/app/app/models" else "/app/models", ).strip() or (legacy_model_dir if legacy_model_dir != "/app/app/models" else "/app/models") return Settings( host=os.getenv("WORKER_HOST", "0.0.0.0").strip() or "0.0.0.0", port=_env_int("WORKER_PORT", 8095), token=os.getenv("WORKER_TOKEN", "change-this-token").strip(), engine=os.getenv("WORKER_ENGINE", "pillow").strip().lower() or "pillow", device=os.getenv("WORKER_DEVICE", "cpu").strip().lower() or "cpu", max_upload_mb=max(1, _env_int("WORKER_MAX_UPLOAD_MB", 20)), max_input_width=max(1, _env_int("WORKER_MAX_INPUT_WIDTH", 4096)), max_input_height=max(1, _env_int("WORKER_MAX_INPUT_HEIGHT", 4096)), max_output_width=max(1, _env_int("WORKER_MAX_OUTPUT_WIDTH", 8192)), max_output_height=max(1, _env_int("WORKER_MAX_OUTPUT_HEIGHT", 8192)), tmp_dir=os.getenv("WORKER_TMP_DIR", "/app/storage/tmp").strip() or "/app/storage/tmp", output_dir=os.getenv("WORKER_OUTPUT_DIR", "/app/storage/output").strip() or "/app/storage/output", result_ttl_minutes=max(1, _env_int("WORKER_RESULT_TTL_MINUTES", 60)), model_dir=legacy_model_dir, default_model=legacy_default_model, realesrgan_bin=os.getenv("WORKER_REALESRGAN_BIN", "/app/bin/realesrgan-ncnn-vulkan").strip() or "/app/bin/realesrgan-ncnn-vulkan", realesrgan_model_dir=realesrgan_model_dir, realesrgan_default_model=os.getenv("WORKER_REALESRGAN_DEFAULT_MODEL", legacy_default_model).strip() or legacy_default_model, realesrgan_anime_model=os.getenv("WORKER_REALESRGAN_ANIME_MODEL", "realesrgan-x4plus-anime").strip() or "realesrgan-x4plus-anime", realesrgan_gpu_id=_env_int("WORKER_REALESRGAN_GPU_ID", -1), realesrgan_tile=max(0, _env_int("WORKER_REALESRGAN_TILE", 0)), realesrgan_tta=_env_bool("WORKER_REALESRGAN_TTA", False), realesrgan_verbose=_env_bool("WORKER_REALESRGAN_VERBOSE", False), realesrgan_timeout_seconds=max(1, _env_int("WORKER_REALESRGAN_TIMEOUT_SECONDS", 900)), realesrgan_preprocess_max_pixels=max(1, _env_int("WORKER_REALESRGAN_PREPROCESS_MAX_PIXELS", 16_777_216)), realesrgan_output_ext=os.getenv("WORKER_REALESRGAN_OUTPUT_EXT", "webp").strip().lower() or "webp", realesrgan_allow_model_fallback=_env_bool("WORKER_REALESRGAN_ALLOW_MODEL_FALLBACK", True), )