From f0793738218d5c0fb6ea7fb856a1ae971820a8d5 Mon Sep 17 00:00:00 2001 From: Artem Akymenko Date: Mon, 6 Jul 2026 11:47:55 +0000 Subject: [PATCH] refactor: migrate preview and conversion code to use TTSBackendRegistry Migrate all preview/debug/conversion pipeline creation to use TTSBackendRegistry.create_backend() instead of direct imports: - debug_tts_runner._load_pipeline(): Kokoro via registry - preview.get_preview_pipeline(): Kokoro via registry - preview.generate_preview_audio(): Supertonic via registry - voice.get_preview_pipeline(): Kokoro via registry - conversion_runner._load_pipeline(): both backends via registry - conversion_runner inline pipeline creation: both via registry - test: update mock to target tts_backend_registry.create_backend --- abogen/webui/conversion_runner.py | 18 +++++++++--------- abogen/webui/debug_tts_runner.py | 5 ++--- abogen/webui/routes/utils/preview.py | 9 ++++----- abogen/webui/routes/utils/voice.py | 5 ++--- tests/test_preview_applies_manual_overrides.py | 17 +++++++++++------ 5 files changed, 28 insertions(+), 26 deletions(-) diff --git a/abogen/webui/conversion_runner.py b/abogen/webui/conversion_runner.py index 1dd6318..24ad066 100644 --- a/abogen/webui/conversion_runner.py +++ b/abogen/webui/conversion_runner.py @@ -40,14 +40,14 @@ from abogen.utils import ( get_user_output_path, load_config, ) -from abogen.tts_backends.kokoro import load_numpy_kpipeline +from abogen.tts_backend_registry import create_backend from abogen.tts_backend import TTSBackend from abogen.voice_cache import ensure_voice_assets from abogen.voice_formulas import extract_voice_ids, get_new_voice from abogen.voice_profiles import load_profiles, normalize_profile_entry from abogen.pronunciation_store import increment_usage from abogen.llm_client import LLMClientError -from abogen.tts_backends.supertonic import DEFAULT_SUPERTONIC_VOICES, SupertonicPipeline +from abogen.tts_backends.supertonic import DEFAULT_SUPERTONIC_VOICES from .service import Job, JobStatus @@ -1582,7 +1582,8 @@ def run_conversion_job(job: Job) -> None: return existing if provider_norm == "supertonic": - pipelines[provider_norm] = SupertonicPipeline( + pipelines[provider_norm] = create_backend( + "supertonic", sample_rate=SAMPLE_RATE, auto_download=True, total_steps=int(getattr(job, "supertonic_total_steps", 5) or 5), @@ -1595,11 +1596,10 @@ def run_conversion_job(job: Job) -> None: device = "cpu" if not disable_gpu: device = _select_device() - _np, KPipeline = load_numpy_kpipeline() # Create KPipeline instance directly (conforms to TTSBackend protocol) - pipelines[provider_norm] = KPipeline( + pipelines[provider_norm] = create_backend( + "kokoro", lang_code=job.language, - repo_id="hexgrad/Kokoro-82M", device=device ) if not kokoro_cache_ready: @@ -2443,7 +2443,8 @@ def _load_pipeline(job: Job): disable_gpu = not job.use_gpu or not cfg.get("use_gpu", True) provider = str(getattr(job, "tts_provider", "kokoro") or "kokoro").strip().lower() if provider == "supertonic": - return SupertonicPipeline( + return create_backend( + "supertonic", sample_rate=SAMPLE_RATE, auto_download=True, total_steps=int(getattr(job, "supertonic_total_steps", 5) or 5), @@ -2452,8 +2453,7 @@ def _load_pipeline(job: Job): device = "cpu" if not disable_gpu: device = _select_device() - _np, KPipeline = load_numpy_kpipeline() - return KPipeline(lang_code=job.language, repo_id="hexgrad/Kokoro-82M", device=device) + return create_backend("kokoro", lang_code=job.language, device=device) def _select_device() -> str: diff --git a/abogen/webui/debug_tts_runner.py b/abogen/webui/debug_tts_runner.py index 06396f3..b5634e8 100644 --- a/abogen/webui/debug_tts_runner.py +++ b/abogen/webui/debug_tts_runner.py @@ -15,7 +15,7 @@ from abogen.normalization_settings import build_apostrophe_config from abogen.text_extractor import extract_from_path from abogen.voice_cache import ensure_voice_assets from abogen.webui.conversion_runner import SAMPLE_RATE, SPLIT_PATTERN, _select_device, _to_float32, _resolve_voice, _spec_to_voice_ids -from abogen.tts_backends.kokoro import load_numpy_kpipeline +from abogen.tts_backend_registry import create_backend _MARKER_RE = re.compile(re.escape(MARKER_PREFIX) + r"(?P[A-Z0-9_]+)" + re.escape(MARKER_SUFFIX)) @@ -45,8 +45,7 @@ def _load_pipeline(language: str, use_gpu: bool) -> Any: device = "cpu" if use_gpu: device = _select_device() - _np, KPipeline = load_numpy_kpipeline() - return KPipeline(lang_code=language, repo_id="hexgrad/Kokoro-82M", device=device) + return create_backend("kokoro", lang_code=language, device=device) def _extract_cases_from_text(text: str) -> List[Tuple[str, str]]: diff --git a/abogen/webui/routes/utils/preview.py b/abogen/webui/routes/utils/preview.py index e4573d7..6ab6ad9 100644 --- a/abogen/webui/routes/utils/preview.py +++ b/abogen/webui/routes/utils/preview.py @@ -78,10 +78,9 @@ def get_preview_pipeline(language: str, device: str) -> Any: pipeline = _preview_pipelines.get(key) if pipeline is not None: return pipeline - from abogen.tts_backends.kokoro import load_numpy_kpipeline + from abogen.tts_backend_registry import create_backend - _, KPipeline = load_numpy_kpipeline() - pipeline = KPipeline(lang_code=language, repo_id="hexgrad/Kokoro-82M", device=device) + pipeline = create_backend("kokoro", lang_code=language, device=device) _preview_pipelines[key] = pipeline return pipeline @@ -137,9 +136,9 @@ def generate_preview_audio( normalized_text = source_text if provider == "supertonic": - from abogen.tts_backends.supertonic import SupertonicPipeline + from abogen.tts_backend_registry import create_backend - pipeline = SupertonicPipeline(sample_rate=SAMPLE_RATE, auto_download=True, total_steps=supertonic_total_steps) + pipeline = create_backend("supertonic", sample_rate=SAMPLE_RATE, auto_download=True, total_steps=supertonic_total_steps) segments = pipeline( normalized_text, voice=voice_spec, diff --git a/abogen/webui/routes/utils/voice.py b/abogen/webui/routes/utils/voice.py index 48e34cc..8401e1e 100644 --- a/abogen/webui/routes/utils/voice.py +++ b/abogen/webui/routes/utils/voice.py @@ -20,7 +20,7 @@ from abogen.constants import ( VOICES_INTERNAL, ) from abogen.speaker_configs import list_configs -from abogen.tts_backends.kokoro import load_numpy_kpipeline +from abogen.tts_backend_registry import create_backend from abogen.webui.conversion_runner import _select_device, _to_float32, SAMPLE_RATE, SPLIT_PATTERN _preview_pipeline_lock = threading.RLock() @@ -741,8 +741,7 @@ def get_preview_pipeline(language: str, device: str): pipeline = _preview_pipelines.get(key) if pipeline is not None: return pipeline - _, KPipeline = load_numpy_kpipeline() - pipeline = KPipeline(lang_code=language, repo_id="hexgrad/Kokoro-82M", device=device) + pipeline = create_backend("kokoro", lang_code=language, device=device) _preview_pipelines[key] = pipeline return pipeline diff --git a/tests/test_preview_applies_manual_overrides.py b/tests/test_preview_applies_manual_overrides.py index 4b61c36..69630c1 100644 --- a/tests/test_preview_applies_manual_overrides.py +++ b/tests/test_preview_applies_manual_overrides.py @@ -19,7 +19,7 @@ def test_preview_applies_manual_override_before_normalization(monkeypatch): # And stub the kokoro pipeline path so generate_preview_audio won't proceed. # We'll instead validate by calling the override logic through generate_preview_audio - # with provider=supertonic and stub SupertonicPipeline to capture input. + # with provider=supertonic and stub create_backend to capture input. captured = {} class DummyPipeline: @@ -30,11 +30,16 @@ def test_preview_applies_manual_override_before_normalization(monkeypatch): captured["text"] = text return iter(()) - monkeypatch.setitem( - __import__("sys").modules, - "abogen.tts_backends.supertonic", - type("M", (), {"SupertonicPipeline": DummyPipeline}), - ) + from abogen import tts_backend_registry + + original_create_backend = tts_backend_registry.create_backend + + def _mock_create_backend(backend_id, **kwargs): + if backend_id == "supertonic": + return DummyPipeline(**kwargs) + return original_create_backend(backend_id, **kwargs) + + monkeypatch.setattr(tts_backend_registry, "create_backend", _mock_create_backend) try: preview.generate_preview_audio(