feat: Add debug TTS functionality with EPUB generation, WAV artifact creation, and settings integration

This commit is contained in:
JB
2025-12-15 13:46:13 -08:00
parent daf4d78766
commit aa71783e5a
6 changed files with 551 additions and 1 deletions
+97
View File
@@ -0,0 +1,97 @@
import json
from pathlib import Path
import numpy as np
import pytest
from abogen.debug_tts_samples import DEBUG_TTS_SAMPLES, MARKER_PREFIX, MARKER_SUFFIX, iter_expected_codes
from abogen.kokoro_text_normalization import HAS_NUM2WORDS, normalize_for_pipeline
from abogen.normalization_settings import build_apostrophe_config
from abogen.text_extractor import extract_from_path
from abogen.web.app import create_app
def test_debug_epub_contains_all_codes():
epub_path = Path("tests/fixtures/abogen_debug_tts_samples.epub")
assert epub_path.exists()
extraction = extract_from_path(epub_path)
combined = extraction.combined_text or "\n\n".join((c.text or "") for c in extraction.chapters)
for code in iter_expected_codes():
marker = f"{MARKER_PREFIX}{code}{MARKER_SUFFIX}"
assert marker in combined
def test_debug_samples_normalize_smoke():
# Use the same defaults as the web UI.
from abogen.web.routes.utils.settings import settings_defaults
settings = settings_defaults()
apostrophe = build_apostrophe_config(settings=settings)
runtime = dict(settings)
normalized = {
sample.code: normalize_for_pipeline(sample.text, config=apostrophe, settings=runtime)
for sample in DEBUG_TTS_SAMPLES
}
# Contractions should expand under defaults.
assert "it is" in normalized["APOS_001"].lower()
# Titles should expand.
assert "doctor" in normalized["TITLE_001"].lower()
# Footnotes should be removed.
assert "[1]" not in normalized["FOOT_001"]
# Terminal punctuation should be added.
assert normalized["PUNC_001"].strip()[-1] in {".", "!", "?"}
if HAS_NUM2WORDS:
# Currency and numbers should expand to words when num2words is available.
assert "dollar" in normalized["CUR_001"].lower()
assert "thousand" in normalized["NUM_001"].lower()
def test_settings_debug_route_writes_manifest(tmp_path, monkeypatch):
# Avoid pulling Kokoro models in tests: stub the pipeline.
from abogen.web import debug_tts_runner as runner
class _Seg:
def __init__(self, audio):
self.audio = audio
class DummyPipeline:
def __call__(self, text, **kwargs):
# 100ms of audio per call, deterministic.
audio = np.zeros(int(0.1 * runner.SAMPLE_RATE), dtype="float32")
audio[::100] = 0.1
yield _Seg(audio)
monkeypatch.setattr(runner, "_load_pipeline", lambda language, use_gpu: DummyPipeline())
app = create_app(
{
"TESTING": True,
"SECRET_KEY": "test",
"OUTPUT_FOLDER": str(tmp_path),
"UPLOAD_FOLDER": str(tmp_path / "uploads"),
}
)
with app.test_client() as client:
resp = client.post("/settings/debug/run")
assert resp.status_code in {302, 303}
location = resp.headers.get("Location", "")
assert "debug_run_id=" in location
# Extract run id from query string.
run_id = location.split("debug_run_id=")[1].split("&")[0].split("#")[0]
manifest_path = tmp_path / "debug" / run_id / "manifest.json"
assert manifest_path.exists()
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
filenames = {item["filename"] for item in manifest.get("artifacts", [])}
assert "overall.wav" in filenames
assert any(name.startswith("case_") and name.endswith(".wav") for name in filenames)