refactor: metadata + markers unified in shared layer

- MarkerCollector: SRP extraction from executor (observation, not execution)
- Outro marker: executor now records outro as chapter marker
- Voice format: chapter voices [{provider, voice}], chunk voice {provider, voice}
- _finalize(): build_metadata_payload() + metadata.json + record_override_usage()
- ffmetadata: voices list → comma-separated string
- EPUB3: ChunkOverlay.voice = dict, _render_chunk_inline handles dict format
- 8 new tests: multi-speaker, ffmetadata format, EPUB3 format
- Updated existing tests for new voice format
This commit is contained in:
Artem Akymenko
2026-07-26 11:42:27 +03:00
parent 0b953d48e8
commit 6497e8c47a
7 changed files with 456 additions and 33 deletions
+2 -2
View File
@@ -14,7 +14,7 @@ def test_render_ffmetadata_includes_chapters(tmp_path):
"publisher": "ACME=Corp",
}
chapters = [
{"start": 0.0, "end": 5.0, "title": "Intro", "voice": "voice_a"},
{"start": 0.0, "end": 5.0, "title": "Intro", "voices": [{"provider": "kokoro", "voice": "voice_a"}]},
{"start": 5.0, "end": 12.345, "title": "Chapter 2"},
]
@@ -28,7 +28,7 @@ def test_render_ffmetadata_includes_chapters(tmp_path):
assert rendered.count("[CHAPTER]") == 2
assert "START=0" in rendered
assert "END=5000" in rendered
assert "voice=voice_a" in rendered
assert "voice=voice_a@kokoro" in rendered
audio_path = tmp_path / "book.m4b"
metadata_path = svc.write_ffmetadata_file(audio_path, metadata, chapters)