Quality-first multi-pass audiobook generation from English manuscripts. Includes: - umbraaudio/ core package (pipeline, config, voices, web review UI) - scripts/ CLI utilities + model download script - tts_server/ remote TTS server - specs/ design docs, tests/, pyproject.toml + uv.lock - Qwen3-TTS git submodule (pinned 022e286) with local demo.py UI patch saved at scripts/qwen3_tts_demo_umbra_patch.patch - .env with runtime config/secrets Excluded (see .gitignore): models/ (downloaded via scripts/download_models.sh), output/, logs/, .venv, .umbra runtime data, and book projects (DealHunterShip_*), which are committed separately.
116 lines
4.2 KiB
Python
116 lines
4.2 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
from umbraaudio.generation.staleness import is_current, marker_path, write_marker
|
|
|
|
if TYPE_CHECKING:
|
|
from pathlib import Path
|
|
|
|
|
|
def test_marker_path(tmp_path: Path) -> None:
|
|
wav = tmp_path / "utt_0003.wav"
|
|
assert marker_path(wav) == tmp_path / "utt_0003.wav.txt"
|
|
|
|
|
|
def test_missing_wav_is_stale(tmp_path: Path) -> None:
|
|
wav = tmp_path / "utt_0000.wav"
|
|
assert is_current("Hello", wav) is False
|
|
|
|
|
|
def test_existing_wav_without_marker_is_stale(tmp_path: Path) -> None:
|
|
wav = tmp_path / "utt_0000.wav"
|
|
wav.write_bytes(b"RIFF....")
|
|
assert is_current("Hello", wav) is False
|
|
|
|
|
|
def test_matching_text_is_current(tmp_path: Path) -> None:
|
|
wav = tmp_path / "utt_0001.wav"
|
|
wav.write_bytes(b"RIFF....")
|
|
write_marker("Google uses Android.", wav, speaker="Narrator")
|
|
assert is_current("Google uses Android.", wav, speaker="Narrator") is True
|
|
|
|
|
|
def test_changed_speaker_is_stale(tmp_path: Path) -> None:
|
|
wav = tmp_path / "utt_0005.wav"
|
|
wav.write_bytes(b"RIFF....")
|
|
write_marker("Same words.", wav, speaker="Kabir")
|
|
assert is_current("Same words.", wav, speaker="Nanny") is False
|
|
|
|
|
|
def test_legacy_plain_text_marker_is_stale(tmp_path: Path) -> None:
|
|
wav = tmp_path / "utt_0006.wav"
|
|
wav.write_bytes(b"RIFF....")
|
|
marker_path(wav).write_text("Same words.", encoding="utf-8")
|
|
assert is_current("Same words.", wav, speaker="Kabir") is False
|
|
|
|
|
|
def test_marker_keeps_text_without_speaker_given(tmp_path: Path) -> None:
|
|
wav = tmp_path / "utt_0007.wav"
|
|
wav.write_bytes(b"RIFF....")
|
|
write_marker("Only text recorded.", wav, speaker="Narrator")
|
|
assert is_current("Only text recorded.", wav) is True
|
|
|
|
|
|
def test_changed_text_is_stale(tmp_path: Path) -> None:
|
|
wav = tmp_path / "utt_0002.wav"
|
|
wav.write_bytes(b"RIFF....")
|
|
write_marker("Old sentence from a previous segmentation.", wav, speaker="Narrator")
|
|
assert is_current("New sentence from re-segmentation.", wav, speaker="Narrator") is False
|
|
|
|
|
|
def test_write_marker_creates_sidecar(tmp_path: Path) -> None:
|
|
wav = tmp_path / "utt_0004.wav"
|
|
write_marker("Some text", wav, speaker="Narrator")
|
|
marker = marker_path(wav).read_text(encoding="utf-8")
|
|
assert marker == '{"speaker": "Narrator", "text": "Some text"}'
|
|
|
|
|
|
def test_voice_fingerprint_in_marker(tmp_path: Path) -> None:
|
|
wav = tmp_path / "utt_0010.wav"
|
|
wav.write_bytes(b"RIFF....")
|
|
ref = tmp_path / "female_ref.wav"
|
|
ref.write_bytes(b"RIFF-female-voice-data")
|
|
write_marker("Hello there.", wav, speaker="Nanny", voice_ref=ref)
|
|
marker = marker_path(wav).read_text(encoding="utf-8")
|
|
assert '"voice_fp":' in marker
|
|
|
|
|
|
def test_voice_fingerprint_mismatch_is_stale(tmp_path: Path) -> None:
|
|
wav = tmp_path / "utt_0011.wav"
|
|
wav.write_bytes(b"RIFF....")
|
|
ref_old = tmp_path / "old_ref.wav"
|
|
ref_old.write_bytes(b"RIFF-old-male-voice")
|
|
ref_new = tmp_path / "new_ref.wav"
|
|
ref_new.write_bytes(b"RIFF-new-female-voice")
|
|
write_marker("Same line.", wav, speaker="Nanny", voice_ref=ref_old)
|
|
assert is_current("Same line.", wav, speaker="Nanny", voice_ref=ref_new) is False
|
|
|
|
|
|
def test_voice_fingerprint_match_is_current(tmp_path: Path) -> None:
|
|
wav = tmp_path / "utt_0012.wav"
|
|
wav.write_bytes(b"RIFF....")
|
|
ref = tmp_path / "same_ref.wav"
|
|
ref.write_bytes(b"RIFF-same-voice")
|
|
write_marker("Same line.", wav, speaker="Nanny", voice_ref=ref)
|
|
assert is_current("Same line.", wav, speaker="Nanny", voice_ref=ref) is True
|
|
|
|
|
|
def test_voice_fingerprint_skipped_when_no_ref(tmp_path: Path) -> None:
|
|
wav = tmp_path / "utt_0013.wav"
|
|
wav.write_bytes(b"RIFF....")
|
|
ref = tmp_path / "the_ref.wav"
|
|
ref.write_bytes(b"RIFF-voice")
|
|
write_marker("With fp.", wav, speaker="Nanny", voice_ref=ref)
|
|
assert is_current("With fp.", wav, speaker="Nanny") is True
|
|
|
|
|
|
def test_voice_fingerprint_stale_when_ref_deleted(tmp_path: Path) -> None:
|
|
wav = tmp_path / "utt_0014.wav"
|
|
wav.write_bytes(b"RIFF....")
|
|
ref = tmp_path / "deleted_ref.wav"
|
|
ref.write_bytes(b"RIFF-data")
|
|
write_marker("Gone ref.", wav, speaker="Nanny", voice_ref=ref)
|
|
ref.unlink()
|
|
assert is_current("Gone ref.", wav, speaker="Nanny", voice_ref=ref) is False
|