o2o-ado2-short-form/server/app/config.py

92 lines
4.6 KiB
Python

"""Centralized runtime configuration — env-driven, no hardcoded constants.
Every value has a sensible default so local/dev runs work with zero config;
override any of them via environment variables (see server/.env.example).
Import as `from app import config as cfg` and read `cfg.<NAME>`.
"""
from __future__ import annotations
import os
from pathlib import Path
ENGINE = Path(__file__).resolve().parents[2] # engine/higgsfield_shorts
def _csv(name: str, default: str) -> list[str]:
raw = os.getenv(name, default)
return [s.strip() for s in raw.split(",") if s.strip()]
def _path(name: str, default: Path) -> Path:
val = os.getenv(name)
return Path(val) if val else default
# ---- HTTP / CORS ----
PORT = int(os.getenv("PORT", "10001"))
CORS_ORIGINS = _csv("CORS_ORIGINS", "*") # 콤마구분. 운영 시 도메인으로 제한.
# ---- 비동기 작업 큐 (인메모리 스레드풀) ----
# 동시에 실제로 생성을 돌릴 작업 수. 초과분은 큐에서 대기(status=queued).
# Higgsfield/Remotion이 무거우므로(CPU·메모리·외부 단가) 보수적으로 둔다.
MAX_CONCURRENT_JOBS = int(os.getenv("MAX_CONCURRENT_JOBS", "2"))
# 완료/실패한 작업 레코드를 메모리에 보관할 시간(초). 지나면 폴링 GET 시 청소.
JOB_RETENTION_SECONDS = int(os.getenv("JOB_RETENTION_SECONDS", "3600"))
# ---- LLM (OpenAI) ----
# 인증: SDK가 OPENAI_API_KEY 를 환경에서 읽음.
OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4o")
# ---- 비디오 생성 백엔드 선택 ----
# "higgsfield" — Higgsfield(dop/seedance v1/kling). 입력 이미지 1장만 사용.
# "fal" — fal.ai Seedance 2.0 reference-to-video. 입력 이미지 최대 9장(멀티) + 9:16 세로.
VIDEO_BACKEND = os.getenv("VIDEO_BACKEND", "higgsfield").lower()
# ---- Higgsfield (공통) ----
# 백엔드 선택: "cli"(기존 검증 경로, ~/.higgsfield 디바이스 로그인) | "api"(공식 SDK, 컨테이너 친화)
HIGGSFIELD_BACKEND = os.getenv("HIGGSFIELD_BACKEND", "cli").lower()
HIGGSFIELD_ASPECT_RATIO = os.getenv("HIGGSFIELD_ASPECT_RATIO", "9:16")
HIGGSFIELD_DURATION = int(os.getenv("HIGGSFIELD_DURATION", "8"))
HIGGSFIELD_WAIT_TIMEOUT = os.getenv("HIGGSFIELD_WAIT_TIMEOUT", "15m")
# ---- Higgsfield: CLI 백엔드 (subprocess) ----
HIGGSFIELD_MODEL = os.getenv("HIGGSFIELD_MODEL", "marketing_studio_video")
HIGGSFIELD_MODE = os.getenv("HIGGSFIELD_MODE", "tv_spot")
HIGGSFIELD_GENERATE_AUDIO = os.getenv("HIGGSFIELD_GENERATE_AUDIO", "true")
# ---- Higgsfield: API 백엔드 (공식 higgsfield-client SDK) ----
# 인증: SDK가 HF_KEY="key:secret" 또는 HF_API_KEY/HF_API_SECRET 를 환경에서 읽음.
# 편의상 HIGGSFIELD_API_KEY/SECRET 로 줘도 아래에서 HF_* 로 매핑.
# 주의: 공개 문서가 다루는 비디오 모델은 DoP image2video. marketing_studio_video 가
# API로 노출되는지는 미검증 → app/variant 를 env로 빼둠(대시보드 확인 후 교체).
HIGGSFIELD_API_APP = os.getenv("HIGGSFIELD_API_APP", "/v1/image2video/dop")
HIGGSFIELD_API_VARIANT = os.getenv("HIGGSFIELD_API_VARIANT", "dop-turbo")
# DoP image2video 는 입력 이미지 정확히 1장만 허용(실측). 모델 교체 시 늘릴 수 있음.
HIGGSFIELD_API_MAX_IMAGES = int(os.getenv("HIGGSFIELD_API_MAX_IMAGES", "1"))
_hf_key = os.getenv("HIGGSFIELD_API_KEY")
_hf_secret = os.getenv("HIGGSFIELD_API_SECRET")
if _hf_key and not os.getenv("HF_API_KEY"):
os.environ["HF_API_KEY"] = _hf_key
if _hf_secret and not os.getenv("HF_API_SECRET"):
os.environ["HF_API_SECRET"] = _hf_secret
# ---- fal.ai (Seedance 2.0 reference-to-video, VIDEO_BACKEND=fal) ----
# 인증: SDK가 FAL_KEY("keyid:secret")를 환경에서 읽음. 편의상 FAL_API_KEY 로 줘도 매핑.
FAL_MODEL_ID = os.getenv("FAL_MODEL_ID", "bytedance/seedance-2.0/reference-to-video")
FAL_MAX_IMAGES = int(os.getenv("FAL_MAX_IMAGES", "9")) # Seedance 2.0 멀티이미지 최대 9장
FAL_ASPECT_RATIO = os.getenv("FAL_ASPECT_RATIO", "9:16") # auto/16:9/9:16/4:3/3:4/1:1/21:9
FAL_RESOLUTION = os.getenv("FAL_RESOLUTION", "720p") # 480p/720p/1080p
FAL_DURATION = os.getenv("FAL_DURATION", "8") # "auto" 또는 4~15(초)
_fal_key = os.getenv("FAL_API_KEY")
if _fal_key and not os.getenv("FAL_KEY"):
os.environ["FAL_KEY"] = _fal_key
# ---- Remotion (Node render) ----
REMOTION_FPS = int(os.getenv("REMOTION_FPS", "30"))
REMOTION_COMPOSITION = os.getenv("REMOTION_COMPOSITION", "MumumShort")
# ---- Paths (Docker는 환경변수로 오버라이드) ----
WEBAPP_DIR = _path("WEBAPP_DIR", ENGINE / "webapp")
OUTPUTS_DIR = _path("OUTPUTS_DIR", ENGINE / "server" / "outputs")
UPLOADS_DIR = _path("UPLOADS_DIR", ENGINE / "server" / ".uploads")