Files
till cfe5d36c70 AI-Edit-Ebene via OpenRouter (Gemini Image): Studio-Hintergrund, Retusche, Restaurierung + freier Prompt
- app/ai_edit.py: OpenRouter chat/completions (image in/out), Presets + Prompt, Retry
- bot: AI-Preset-Buttons + Bildunterschrift = freier AI-Prompt; _process refaktoriert
- Freistellen/Upscaling bleiben lokal (originaltreu); AI-Edit fuer generative Edits
- httpx pin, config OPENROUTER_*

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 10:46:38 +00:00

92 lines
3.9 KiB
Python

"""Zentrale Konfiguration aus Umgebungsvariablen."""
from __future__ import annotations
import os
from pathlib import Path
def _get_bool(name: str, default: bool = False) -> bool:
val = os.getenv(name)
if val is None:
return default
return val.strip().lower() in {"1", "true", "yes", "on", "ja"}
def _get_int(name: str, default: int) -> int:
try:
return int(os.getenv(name, str(default)))
except (TypeError, ValueError):
return default
# --- Telegram ---------------------------------------------------------------
BOT_TOKEN: str = os.getenv("BOT_TOKEN", "").strip()
# Kommagetrennte Liste erlaubter Telegram-User-IDs. Leer = jeder darf (nur
# fuer Tests empfohlen). /start zeigt jedem seine ID, damit man hier eintraegt.
_allowed = os.getenv("ALLOWED_USER_IDS", "").replace(";", ",")
ALLOWED_USER_IDS: set[int] = {
int(x) for x in (p.strip() for p in _allowed.split(",")) if x.strip().isdigit()
}
# --- Bild-Pipeline ----------------------------------------------------------
MODELS_DIR: Path = Path(os.getenv("MODELS_DIR", "/app/models"))
WORK_DIR: Path = Path(os.getenv("WORK_DIR", "/tmp/klarbild"))
# rembg-Modell fuers Freistellen: isnet-general-use (gut), u2net, u2netp (leicht),
# birefnet-general (beste Qualitaet, schwer). Wird beim Build vorgeladen.
REMBG_MODEL: str = os.getenv("REMBG_MODEL", "isnet-general-use")
# OpenCV dnn_superres: "fsrcnn" (schnell, Default) oder "edsr" (mehr Detail,
# aber auf CPU sehr langsam ~40s/Kachel -> nur fuer starke Boxen empfohlen).
UPSCALE_MODEL: str = os.getenv("UPSCALE_MODEL", "fsrcnn").lower()
# Optionales Real-ESRGAN-ONNX-Backend (fuer GPU/starke Boxen). Wenn aktiv und
# Modelldatei vorhanden, wird es statt OpenCV genutzt.
ENABLE_REALESRGAN: bool = _get_bool("ENABLE_REALESRGAN", False)
REALESRGAN_ONNX: Path = Path(
os.getenv("REALESRGAN_ONNX", str(MODELS_DIR / "realesrgan_x4.onnx"))
)
# Speicherschutz auf kleinen Boxen (CX33: 8GB, keine GPU):
# Laengste Kante des Eingangsbildes vor dem Upscalen begrenzen.
MAX_INPUT_EDGE: int = _get_int("MAX_INPUT_EDGE", 1600)
# Kachelgroesse fuer das Upscalen (kleiner = weniger RAM, mehr Overhead).
TILE_SIZE: int = _get_int("TILE_SIZE", 256)
# Deckel fuer die Ausgabe-Megapixel (Sicherheitsnetz gegen OOM).
MAX_OUTPUT_MP: float = float(os.getenv("MAX_OUTPUT_MP", "80"))
# Nach dem Upscalen leichte Entrauschung + Schaerfung ("Klarbild"-Finish).
POST_DENOISE: bool = _get_bool("POST_DENOISE", True)
POST_SHARPEN: bool = _get_bool("POST_SHARPEN", True)
# JPEG-Qualitaet fuer zurueckgesendete Fotos (Freistellen liefert immer PNG).
JPEG_QUALITY: int = _get_int("JPEG_QUALITY", 95)
# --- OpenRouter (generative AI-Edits: Hintergrund, Retusche, freier Prompt) --
# Achtung: US-gehostet (Google/OpenAI via OpenRouter). Bewusst gesetzt.
OPENROUTER_API_KEY: str = os.getenv("OPENROUTER_API_KEY", "").strip()
OPENROUTER_BASE: str = os.getenv("OPENROUTER_BASE", "https://openrouter.ai/api/v1")
OPENROUTER_MODEL: str = os.getenv("OPENROUTER_MODEL", "google/gemini-3.1-flash-image")
OPENROUTER_TIMEOUT: int = _get_int("OPENROUTER_TIMEOUT", 120)
# Laengste Kante, auf die Eingangsbilder vor dem Senden verkleinert werden
# (Kosten/Tempo). Das Modell gibt ohnehin ~1024px-Ausgaben zurueck.
AI_EDIT_MAX_EDGE: int = _get_int("AI_EDIT_MAX_EDGE", 1536)
AI_EDIT_ENABLED: bool = bool(OPENROUTER_API_KEY)
# --- Health-Server ----------------------------------------------------------
HEALTH_PORT: int = _get_int("PORT", 8080)
# --- Picdrop (SFTP) ---------------------------------------------------------
PICDROP_HOST: str = os.getenv("PICDROP_HOST", "ftps.picdrop.com")
PICDROP_PORT: int = _get_int("PICDROP_PORT", 22)
PICDROP_USER: str = os.getenv("PICDROP_USER", "")
PICDROP_PASSWORD: str = os.getenv("PICDROP_PASSWORD", "")
PICDROP_ENABLED: bool = bool(PICDROP_USER and PICDROP_PASSWORD)
def validate() -> None:
if not BOT_TOKEN:
raise SystemExit("FEHLER: Umgebungsvariable BOT_TOKEN ist nicht gesetzt.")
WORK_DIR.mkdir(parents=True, exist_ok=True)