103 lines
5.2 KiB
Python
103 lines
5.2 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
|
|
|
|
# ---- Azure Blob Storage (완성 영상 영속 저장 + 갤러리 조회) ----
|
|
# BASE_URL: 컨테이너 내 가상 폴더 경로. 끝에 / 포함.
|
|
# 예) https://account.blob.core.windows.net/container/prefix/
|
|
# SAS_TOKEN: 컨테이너 범위 SAS (sp=racwdl 이상 필요).
|
|
# 비어있으면 로컬 파일시스템 fallback.
|
|
AZURE_BLOB_BASE_URL = os.getenv("AZURE_BLOB_BASE_URL", "").rstrip("/") + "/"
|
|
AZURE_BLOB_SAS_TOKEN = os.getenv("AZURE_BLOB_SAS_TOKEN", "")
|
|
|
|
def azure_enabled() -> bool:
|
|
return bool(AZURE_BLOB_BASE_URL.strip("/") and AZURE_BLOB_SAS_TOKEN)
|
|
|
|
# ---- 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")
|