aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-08-17 19:19:40 -0400
committerhistoria <historiavg@proton.me>2026-08-17 19:19:40 -0400
commitb80fa9db6bab6cdb2856874b606a93149cfc1af2 (patch)
tree7404d8f0592ad225cd2074e81f3920d1a498dfe1 /tests
parent0ad594aa6497c4d41272e503f33fde2103b96cd6 (diff)
downloadtts-audiobook-generator-b80fa9db6bab6cdb2856874b606a93149cfc1af2.tar.gz
feat(converter): add per-chapter and m4b output
Diffstat (limited to 'tests')
-rw-r--r--tests/test_audio.py46
-rw-r--r--tests/test_converter.py21
-rw-r--r--tests/test_extractors.py34
3 files changed, 99 insertions, 2 deletions
diff --git a/tests/test_audio.py b/tests/test_audio.py
index a629224..29bf4ea 100644
--- a/tests/test_audio.py
+++ b/tests/test_audio.py
@@ -1,11 +1,11 @@
-"""Tests for audio helpers: speed parameters and chunk cleanup."""
+"""Tests for audio helpers: speed parameters, chunk cleanup, and encoding."""
import tempfile
import unittest
from pathlib import Path
from converter import config
-from converter.audio import cleanup_chunks, speed_export_params
+from converter.audio import _encode_args, build_ffmetadata, cleanup_chunks, speed_export_params
class SpeedExportParamsTests(unittest.TestCase):
@@ -52,6 +52,48 @@ class CleanupChunksTests(unittest.TestCase):
self.assertFalse((chunks_dir / "chunk_0002.wav").exists())
self.assertTrue((chunks_dir / "keep.txt").exists())
+ def test_removes_chapter_files(self):
+ with tempfile.TemporaryDirectory() as tmp:
+ chunks_dir = Path(tmp)
+ (chunks_dir / "chapter_0001.m4b").write_bytes(b"stale")
+ (chunks_dir / "chunk_0001.wav").write_bytes(b"stale")
+
+ original = config.CHUNKS_FOLDER
+ config.CHUNKS_FOLDER = chunks_dir
+ try:
+ cleanup_chunks()
+ finally:
+ config.CHUNKS_FOLDER = original
+
+ self.assertFalse((chunks_dir / "chapter_0001.m4b").exists())
+ self.assertFalse((chunks_dir / "chunk_0001.wav").exists())
+
+
+class EncodeArgsTests(unittest.TestCase):
+ def test_mp3_uses_bitrate_only(self):
+ self.assertEqual(_encode_args("mp3"), ["-b:a", config.AUDIO_BITRATE])
+
+ def test_m4b_uses_aac(self):
+ self.assertEqual(_encode_args("m4b"), ["-c:a", "aac", "-b:a", config.AUDIO_BITRATE])
+
+
+class BuildFFMetadataTests(unittest.TestCase):
+ def test_writes_chapters(self):
+ chapters = [(0, 1200, "One"), (1200, 2500, "Two")]
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "meta.txt"
+ build_ffmetadata(chapters, path)
+ content = path.read_text(encoding="utf-8")
+
+ self.assertTrue(content.startswith(";FFMETADATA1\n"))
+ self.assertIn("[CHAPTER]", content)
+ self.assertIn("TIMEBASE=1/1000", content)
+ self.assertIn("START=0", content)
+ self.assertIn("END=1200", content)
+ self.assertIn("title=One", content)
+ self.assertIn("START=1200", content)
+ self.assertIn("title=Two", content)
+
if __name__ == "__main__":
unittest.main()
diff --git a/tests/test_converter.py b/tests/test_converter.py
new file mode 100644
index 0000000..1235dda
--- /dev/null
+++ b/tests/test_converter.py
@@ -0,0 +1,21 @@
+"""Tests for the audiobook converter orchestration helpers."""
+
+import unittest
+
+from converter.converter import AudiobookConverter
+
+
+class SanitizeFilenameTests(unittest.TestCase):
+ def test_removes_invalid_characters(self):
+ self.assertEqual(AudiobookConverter._sanitize_filename('A "bad" name: here'),
+ "A bad name here")
+
+ def test_collapses_whitespace(self):
+ self.assertEqual(AudiobookConverter._sanitize_filename(" spaced\tout "), "spaced out")
+
+ def test_empty_falls_back(self):
+ self.assertEqual(AudiobookConverter._sanitize_filename("///"), "chapter")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_extractors.py b/tests/test_extractors.py
index c497267..09b688e 100644
--- a/tests/test_extractors.py
+++ b/tests/test_extractors.py
@@ -90,5 +90,39 @@ class EpubExtractionTests(unittest.TestCase):
text.index("Second chapter text."))
+class ExtractSectionsTests(unittest.TestCase):
+ def setUp(self):
+ try:
+ import ebooklib # noqa: F401
+ except ImportError:
+ self.skipTest("ebooklib not installed")
+
+ def test_epub_sections_split_on_chapters(self):
+ from converter.extractors import extract_sections
+
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "book.epub"
+ _build_test_epub(path)
+ sections = extract_sections(path)
+
+ self.assertEqual(len(sections), 2)
+ self.assertEqual(sections[0].title, "One")
+ self.assertEqual(sections[1].title, "Two")
+ self.assertIn("First chapter text.", sections[0].text)
+ self.assertIn("Second chapter text.", sections[1].text)
+
+ def test_txt_is_single_section(self):
+ from converter.extractors import extract_sections
+
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "book.txt"
+ path.write_text("Hello world.", encoding="utf-8")
+ sections = extract_sections(path)
+
+ self.assertEqual(len(sections), 1)
+ self.assertEqual(sections[0].title, "book")
+ self.assertEqual(sections[0].text, "Hello world.")
+
+
if __name__ == "__main__":
unittest.main()