mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 13:40:27 +02:00
feat: Enhance voice formula parsing and validation, implement voice asset caching, and add tests for new functionality
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
from pathlib import Path
|
||||
|
||||
from werkzeug.datastructures import MultiDict
|
||||
|
||||
from abogen.web.routes import _apply_prepare_form
|
||||
from abogen.web.service import PendingJob
|
||||
|
||||
|
||||
def _make_pending_job() -> PendingJob:
|
||||
return PendingJob(
|
||||
id="pending",
|
||||
original_filename="example.epub",
|
||||
stored_path=Path("example.epub"),
|
||||
language="a",
|
||||
voice="af_nova",
|
||||
speed=1.0,
|
||||
use_gpu=False,
|
||||
subtitle_mode="none",
|
||||
output_format="mp3",
|
||||
save_mode="save_next_to_input",
|
||||
output_folder=None,
|
||||
replace_single_newlines=False,
|
||||
subtitle_format="srt",
|
||||
total_characters=0,
|
||||
save_chapters_separately=False,
|
||||
merge_chapters_at_end=True,
|
||||
separate_chapters_format="wav",
|
||||
silence_between_chapters=2.0,
|
||||
save_as_project=False,
|
||||
voice_profile=None,
|
||||
max_subtitle_words=50,
|
||||
metadata_tags={},
|
||||
chapters=[],
|
||||
created_at=0.0,
|
||||
)
|
||||
|
||||
|
||||
def test_apply_prepare_form_handles_custom_mix_for_speakers():
|
||||
pending = _make_pending_job()
|
||||
pending.speakers = {
|
||||
"hero": {
|
||||
"id": "hero",
|
||||
"label": "Hero",
|
||||
}
|
||||
}
|
||||
|
||||
form = MultiDict(
|
||||
{
|
||||
"chapter_intro_delay": "0.5",
|
||||
"speaker-hero-voice": "__custom_mix",
|
||||
"speaker-hero-formula": "af_nova*0.6+am_liam*0.4",
|
||||
}
|
||||
)
|
||||
|
||||
_, _, _, errors, *_ = _apply_prepare_form(pending, form)
|
||||
|
||||
assert not errors
|
||||
hero = pending.speakers["hero"]
|
||||
assert hero["voice_formula"] == "af_nova*0.6+am_liam*0.4"
|
||||
assert hero["resolved_voice"] == "af_nova*0.6+am_liam*0.4"
|
||||
assert "voice" not in hero or hero["voice"] != "__custom_mix"
|
||||
@@ -0,0 +1,56 @@
|
||||
from types import SimpleNamespace
|
||||
from typing import cast
|
||||
|
||||
import pytest
|
||||
|
||||
from abogen.constants import VOICES_INTERNAL
|
||||
from abogen.voice_cache import _CACHED_VOICES, ensure_voice_assets
|
||||
from abogen.web.conversion_runner import _collect_required_voice_ids
|
||||
from abogen.web.service import Job
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def clear_voice_cache():
|
||||
_CACHED_VOICES.clear()
|
||||
yield
|
||||
_CACHED_VOICES.clear()
|
||||
|
||||
|
||||
def test_ensure_voice_assets_downloads_missing(monkeypatch):
|
||||
recorded = []
|
||||
|
||||
def fake_download(**kwargs):
|
||||
recorded.append(kwargs["filename"])
|
||||
return "/tmp/fake"
|
||||
|
||||
monkeypatch.setattr("abogen.voice_cache.hf_hub_download", fake_download)
|
||||
|
||||
downloaded, errors = ensure_voice_assets(["af_nova", "am_liam"])
|
||||
|
||||
assert downloaded == {"af_nova", "am_liam"}
|
||||
assert errors == {}
|
||||
assert recorded == ["voices/af_nova.pt", "voices/am_liam.pt"]
|
||||
|
||||
recorded.clear()
|
||||
downloaded_again, errors_again = ensure_voice_assets(["af_nova"])
|
||||
|
||||
assert downloaded_again == set()
|
||||
assert errors_again == {}
|
||||
assert recorded == []
|
||||
|
||||
|
||||
def test_collect_required_voice_ids_includes_all():
|
||||
job = SimpleNamespace(
|
||||
voice="af_nova",
|
||||
chapters=[{"voice_formula": "af_nova*0.7+am_liam*0.3"}],
|
||||
chunks=[{"voice": "am_michael"}],
|
||||
speakers={
|
||||
"hero": {"voice_formula": "af_nova*0.6+am_liam*0.4"},
|
||||
"narrator": {"voice": "af_nova"},
|
||||
},
|
||||
)
|
||||
|
||||
voices = _collect_required_voice_ids(cast(Job, job))
|
||||
|
||||
assert {"af_nova", "am_liam", "am_michael"}.issubset(voices)
|
||||
assert voices.issuperset(VOICES_INTERNAL)
|
||||
Reference in New Issue
Block a user