aboutsummaryrefslogtreecommitdiff
path: root/tests/test_audio.py
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-08-18 04:05:10 -0400
committerhistoria <historiavg@proton.me>2026-08-18 04:05:10 -0400
commit50f1825f05972e3685c55beb10c288899959b2e5 (patch)
treea5aa104d603ce9c710499184c133abe86497a16e /tests/test_audio.py
parent86d2eb8d789f82dd8e56dd0ff53933152ba94e6b (diff)
downloadtts-audiobook-generator-50f1825f05972e3685c55beb10c288899959b2e5.tar.gz
feat: add metadata to audio files including generated cover art
Diffstat (limited to 'tests/test_audio.py')
-rw-r--r--tests/test_audio.py135
1 files changed, 135 insertions, 0 deletions
diff --git a/tests/test_audio.py b/tests/test_audio.py
index 7116e2b..97280ae 100644
--- a/tests/test_audio.py
+++ b/tests/test_audio.py
@@ -8,8 +8,11 @@ from pathlib import Path
from converter import audio
from converter import config
from converter.audio import (
+ TrackMeta,
_collect_chunk_files,
+ _cover_args,
_encode_args,
+ _tag_args,
build_concat_command,
build_ffmetadata,
build_m4b_chapters_command,
@@ -287,5 +290,137 @@ class BuildFFMetadataTests(unittest.TestCase):
self.assertNotIn("title=Two\n", content)
+class TagArgsTests(unittest.TestCase):
+ META = TrackMeta(title="Dune", artist="Frank Herbert", album="Dune",
+ track=2, total_tracks=5)
+
+ def test_full_meta_written(self):
+ args = _tag_args(self.META, "mp3")
+ for pair in ("title=Dune", "artist=Frank Herbert",
+ "album=Dune", "track=2/5"):
+ self.assertIn(pair, args)
+
+ def test_mp3_gets_id3v23(self):
+ mp3_args = _tag_args(self.META, "mp3")
+ self.assertIn("-id3v2_version", mp3_args)
+ self.assertEqual(mp3_args[mp3_args.index("-id3v2_version") + 1], "3")
+ self.assertNotIn("-id3v2_version", _tag_args(self.META, "flac"))
+
+ def test_empty_fields_omitted(self):
+ meta = TrackMeta(title="Only Title")
+ args = _tag_args(meta, "flac")
+ self.assertNotIn("artist", args)
+ self.assertNotIn("album", args)
+ self.assertNotIn("track", args)
+
+ def test_track_requires_total(self):
+ meta = TrackMeta(title="T", track=3)
+ self.assertNotIn("track", _tag_args(meta, "mp3"))
+
+
+class CoverArgsTests(unittest.TestCase):
+ def test_mp3_copies_png_stream(self):
+ args = _cover_args("mp3", 1)
+ self.assertIn("copy", args)
+ self.assertIn("attached_pic", args)
+ self.assertIn("1:v", args)
+
+ def test_m4b_reencodes_to_jpeg(self):
+ args = _cover_args("m4b", 2)
+ self.assertIn("mjpeg", args)
+ self.assertIn("attached_pic", args)
+ self.assertIn("3", args) # jpeg quality
+
+ def test_ogg_and_wav_have_no_cover(self):
+ self.assertEqual(_cover_args("ogg", 1), [])
+ self.assertEqual(_cover_args("wav", 1), [])
+
+
+class BuildConcatCommandMetaTests(unittest.TestCase):
+ META = TrackMeta(title="Chapter 1", artist="Author", album="Book",
+ track=1, total_tracks=3)
+
+ def test_cover_added_as_second_input(self):
+ cmd = build_concat_command(Path("list.txt"), Path("out.mp3"), "mp3",
+ meta=self.META, cover=Path("cover.png"))
+ # The cover is the second input, after the concat list
+ self.assertIn("cover.png", cmd)
+ self.assertLess(cmd.index("list.txt"), cmd.index("cover.png"))
+ self.assertIn("-map", cmd)
+ self.assertIn("1:v", cmd)
+ self.assertIn("attached_pic", cmd)
+ self.assertEqual(cmd[-1], "out.mp3")
+
+ def test_audio_explicitly_mapped_when_cover_present(self):
+ cmd = build_concat_command(Path("list.txt"), Path("out.flac"), "flac",
+ cover=Path("cover.png"))
+ self.assertIn("0:a", cmd)
+
+ def test_no_cover_keeps_single_input(self):
+ cmd = build_concat_command(Path("list.txt"), Path("out.mp3"), "mp3",
+ meta=self.META)
+ self.assertEqual(cmd.count("-i"), 1)
+ self.assertNotIn("attached_pic", cmd)
+
+ def test_ogg_never_gets_cover_input(self):
+ cmd = build_concat_command(Path("list.txt"), Path("out.ogg"), "ogg",
+ meta=self.META, cover=Path("cover.png"))
+ self.assertEqual(cmd.count("-i"), 1)
+ self.assertNotIn("attached_pic", cmd)
+
+ def test_speed_copy_gets_tags_and_cover(self):
+ cmd = build_concat_command(Path("list.txt"), Path("out.mp3"), "mp3",
+ speed=1.5, speed_path=Path("out_1.5.mp3"),
+ meta=self.META, cover=Path("cover.png"))
+ self.assertEqual(cmd.count("attached_pic"), 2)
+ self.assertEqual(cmd.count("title=Chapter 1"), 2)
+ self.assertEqual(cmd.count("1:v"), 2)
+
+ def test_tags_without_cover_present(self):
+ cmd = build_concat_command(Path("list.txt"), Path("out.mp3"), "mp3",
+ meta=self.META)
+ self.assertIn("title=Chapter 1", cmd)
+ self.assertIn("artist=Author", cmd)
+ self.assertIn("album=Book", cmd)
+ self.assertIn("track=1/3", cmd)
+
+
+class BuildM4bChaptersCommandMetaTests(unittest.TestCase):
+ def setUp(self):
+ self._original = audio._brand_supported
+ audio._brand_supported = True
+
+ def tearDown(self):
+ audio._brand_supported = self._original
+
+ def test_cover_indexed_after_metadata_inputs(self):
+ cmd = build_m4b_chapters_command(Path("list.txt"), Path("meta.txt"),
+ Path("out.m4b"), cover=Path("cover.png"))
+ # Inputs: 0=audio, 1=ffmetadata, 2=cover
+ self.assertIn("-i", cmd)
+ self.assertIn("2:v", cmd)
+ self.assertIn("attached_pic", cmd)
+
+ def test_speed_variant_cover_is_input_three(self):
+ cmd = build_m4b_chapters_command(
+ Path("list.txt"), Path("meta.txt"), Path("out.m4b"),
+ speed=2.0, speed_path=Path("out_2.m4b"), speed_metadata_file=Path("meta2.txt"),
+ meta=TrackMeta(title="Book"), cover=Path("cover.png"),
+ )
+ self.assertEqual(cmd.count("3:v"), 2) # both outputs attach the cover
+ self.assertNotIn("2:v", cmd)
+ self.assertEqual(cmd.count("title=Book"), 2)
+ # Chapter metadata inputs keep their 1/2 mapping
+ chapter_flags = [i for i, v in enumerate(cmd) if v == "-map_chapters"]
+ self.assertEqual(cmd[chapter_flags[0] + 1], "1")
+ self.assertEqual(cmd[chapter_flags[1] + 1], "2")
+
+ def test_without_cover_regression(self):
+ cmd = build_m4b_chapters_command(Path("list.txt"), Path("meta.txt"),
+ Path("out.m4b"))
+ self.assertNotIn("attached_pic", cmd)
+ self.assertNotIn("-metadata", cmd)
+
+
if __name__ == "__main__":
unittest.main()