"""엔진 레지스트리. 엔진을 lazy 하게 생성하고 id 로 조회한다. 사용 불가한 엔진(전용 venv/모델 미설치)은 자동으로 목록에서 제외한다. """ from __future__ import annotations import threading from .base import BaseEngine _ENGINES: dict[str, BaseEngine] = {} _LOCK = threading.Lock() _INITED = False def _build() -> None: global _INITED if _INITED: return with _LOCK: if _INITED: return # MeloTTS (항상 사용 가능) try: from .melo_engine import MeloEngine e = MeloEngine() _ENGINES[e.id] = e except Exception as exc: # pragma: no cover print(f"[registry] MeloTTS 로드 실패: {exc}") # Supertonic (자연스러운 한국어, 전용 워커 URL 이 설정된 경우에만) try: from .supertonic_engine import SupertonicEngine if SupertonicEngine.available(): e = SupertonicEngine() _ENGINES[e.id] = e except Exception as exc: # pragma: no cover print(f"[registry] Supertonic 엔진 스킵: {exc}") # OuteTTS (개인용/비상업, 전용 워커 URL 이 설정된 경우에만) try: from .outetts_engine import OuteTTSEngine if OuteTTSEngine.available(): e = OuteTTSEngine() _ENGINES[e.id] = e except Exception as exc: # pragma: no cover print(f"[registry] OuteTTS 엔진 스킵: {exc}") _INITED = True def get_engine(engine_id: str) -> BaseEngine: _build() if engine_id not in _ENGINES: raise ValueError(f"알 수 없는 엔진입니다: {engine_id}") return _ENGINES[engine_id] def list_engines() -> list[BaseEngine]: _build() return list(_ENGINES.values()) def default_engine_id() -> str: _build() # MeloTTS 우선 if "melo" in _ENGINES: return "melo" return next(iter(_ENGINES)) if _ENGINES else "" def warmup_all() -> None: _build() for e in _ENGINES.values(): try: e.warmup() except Exception as exc: # pragma: no cover print(f"[registry] {e.id} warmup 실패: {exc}")