mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 13:40:27 +02:00
Add voice management functionality and voice synthesis preview
- Implemented voice management routes in `voices.py` for listing, saving, and deleting speaker configurations. - Added a test endpoint for voice synthesis preview, allowing users to test voice settings with provided text and speed. - Introduced utility functions in `voice.py` for building voice catalogs, resolving voice settings, and synthesizing audio from normalized text. - Enhanced speaker roster building and configuration application logic to support dynamic voice settings.
This commit is contained in:
@@ -39,13 +39,13 @@ def test_prepare_project_layout_uses_timestamped_folder(monkeypatch: pytest.Monk
|
||||
|
||||
project_root, audio_dir, subtitle_dir, metadata_dir = _prepare_project_layout(job, tmp_path)
|
||||
|
||||
assert project_root.name.startswith("20250101-120000_sample_title"), project_root.name
|
||||
assert project_root.name.startswith("20250101-120000_Sample_Title"), project_root.name
|
||||
assert audio_dir == project_root
|
||||
assert subtitle_dir == project_root
|
||||
assert metadata_dir is None
|
||||
|
||||
output_path = _build_output_path(audio_dir, job.original_filename, "mp3")
|
||||
assert output_path == project_root / "Sample Title.mp3"
|
||||
assert output_path == project_root / "Sample_Title.mp3"
|
||||
|
||||
|
||||
def test_prepare_project_layout_creates_project_subdirs(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
||||
@@ -66,4 +66,4 @@ def test_prepare_project_layout_creates_project_subdirs(monkeypatch: pytest.Monk
|
||||
assert metadata_dir is not None and metadata_dir.is_dir()
|
||||
|
||||
output_path = _build_output_path(audio_dir, job.original_filename, "wav")
|
||||
assert output_path == audio_dir / "Sample Title.wav"
|
||||
assert output_path == audio_dir / "Sample_Title.wav"
|
||||
|
||||
@@ -2,7 +2,8 @@ from pathlib import Path
|
||||
|
||||
from werkzeug.datastructures import MultiDict
|
||||
|
||||
from abogen.web.routes import _apply_prepare_form, _resolve_voice_setting
|
||||
from abogen.web.routes.utils.form import apply_prepare_form
|
||||
from abogen.web.routes.utils.voice import resolve_voice_setting
|
||||
from abogen.web.service import PendingJob
|
||||
|
||||
|
||||
@@ -55,7 +56,7 @@ def test_apply_prepare_form_handles_custom_mix_for_speakers():
|
||||
}
|
||||
)
|
||||
|
||||
_, _, _, errors, *_ = _apply_prepare_form(pending, form)
|
||||
_, _, _, errors, *_ = apply_prepare_form(pending, form)
|
||||
|
||||
assert not errors
|
||||
hero = pending.speakers["hero"]
|
||||
@@ -75,7 +76,7 @@ def test_resolve_voice_setting_handles_profile_reference():
|
||||
}
|
||||
}
|
||||
|
||||
voice, profile_name, language = _resolve_voice_setting("profile:Blend", profiles=profiles)
|
||||
voice, profile_name, language = resolve_voice_setting("profile:Blend", profiles=profiles)
|
||||
|
||||
assert voice == "af_nova*0.5+am_liam*0.5"
|
||||
assert profile_name == "Blend"
|
||||
@@ -89,6 +90,6 @@ def test_apply_prepare_form_updates_closing_outro_flag():
|
||||
"read_closing_outro": "false",
|
||||
})
|
||||
|
||||
_apply_prepare_form(pending, form)
|
||||
apply_prepare_form(pending, form)
|
||||
|
||||
assert pending.read_closing_outro is False
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import io
|
||||
import time
|
||||
from abogen.web.service import Job, JobStatus, build_service, _JOB_LOGGER, _build_audiobookshelf_metadata
|
||||
from abogen.web.service import Job, JobStatus, build_service, _JOB_LOGGER, build_audiobookshelf_metadata
|
||||
|
||||
|
||||
def test_service_processes_job(tmp_path):
|
||||
@@ -223,7 +223,7 @@ def test_audiobookshelf_metadata_uses_book_number(tmp_path):
|
||||
},
|
||||
)
|
||||
|
||||
metadata = _build_audiobookshelf_metadata(job)
|
||||
metadata = build_audiobookshelf_metadata(job)
|
||||
|
||||
assert metadata["seriesName"] == "Example Saga"
|
||||
assert metadata["seriesSequence"] == "7"
|
||||
@@ -254,7 +254,7 @@ def test_audiobookshelf_metadata_normalizes_sequence_value(tmp_path):
|
||||
},
|
||||
)
|
||||
|
||||
metadata = _build_audiobookshelf_metadata(job)
|
||||
metadata = build_audiobookshelf_metadata(job)
|
||||
|
||||
assert metadata["seriesName"] == "Example Saga"
|
||||
assert metadata["seriesSequence"] == "7"
|
||||
@@ -285,7 +285,7 @@ def test_audiobookshelf_metadata_allows_decimal_sequence(tmp_path):
|
||||
},
|
||||
)
|
||||
|
||||
metadata = _build_audiobookshelf_metadata(job)
|
||||
metadata = build_audiobookshelf_metadata(job)
|
||||
|
||||
assert metadata["seriesSequence"] == "4.5"
|
||||
|
||||
@@ -316,7 +316,7 @@ def test_audiobookshelf_metadata_ignores_author_series_collision(tmp_path):
|
||||
},
|
||||
)
|
||||
|
||||
metadata = _build_audiobookshelf_metadata(job)
|
||||
metadata = build_audiobookshelf_metadata(job)
|
||||
|
||||
assert "seriesName" not in metadata
|
||||
assert "seriesSequence" not in metadata
|
||||
Reference in New Issue
Block a user