diff options
| author | historia <historiavg@proton.me> | 2026-08-19 05:25:14 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-08-19 05:25:14 -0400 |
| commit | c2afb9d01b854bb709345c1b33bdba741daceb25 (patch) | |
| tree | 7317daa34c0e2c4a6d37e88a2c6377cac9b799e5 | |
| parent | 94ddbb0634022a6e5209b5221c057228ec3d1418 (diff) | |
| download | tts-audiobook-generator-c2afb9d01b854bb709345c1b33bdba741daceb25.tar.gz | |
fix: skip tests if no ebooklib
| -rw-r--r-- | converter/audio.py | 24 | ||||
| -rw-r--r-- | converter/converter.py | 9 | ||||
| -rw-r--r-- | tests/test_audio.py | 17 | ||||
| -rw-r--r-- | tests/test_converter.py | 3 | ||||
| -rw-r--r-- | tests/test_extractors.py | 4 | ||||
| -rw-r--r-- | tests/test_tts.py | 1 |
6 files changed, 15 insertions, 43 deletions
diff --git a/converter/audio.py b/converter/audio.py index 3e163be..01f78ab 100644 --- a/converter/audio.py +++ b/converter/audio.py @@ -337,35 +337,27 @@ def verify_output_duration(path: Path, expected_ms: int) -> bool: def _collect_chunk_files(total_chunks: int, - chunk_results: Optional[Dict[int, Optional[Path]]] = None + chunk_results: Dict[int, Optional[Path]] ) -> Tuple[List[Path], List[int]]: """Resolve chunk audio files in book order. - When ``chunk_results`` is provided (chunk number -> path written, or None - for a failed chunk), the recorded paths are used exactly as-is so stale - files from a previous chapter can never leak in. Without it, the chunks - folder is globbed per index (legacy discovery). + ``chunk_results`` maps chunk number -> path written (or None for a failed + chunk); recorded paths are used exactly as-is so stale files from a + previous chapter can never leak in. """ chunk_files: List[Path] = [] missing: List[int] = [] for i in range(1, total_chunks + 1): - if chunk_results is not None: - recorded = chunk_results.get(i) - if recorded is not None and Path(recorded).exists(): - chunk_files.append(Path(recorded)) - else: - missing.append(i) - continue - matches = sorted(CHUNKS_FOLDER.glob(f"chunk_{i:04d}.*")) - if matches: - chunk_files.append(matches[0]) + recorded = chunk_results.get(i) + if recorded is not None and Path(recorded).exists(): + chunk_files.append(Path(recorded)) else: missing.append(i) return chunk_files, missing def combine_chunks(total_chunks: int, output_path: Path, - chunk_results: Optional[Dict[int, Optional[Path]]] = None, + chunk_results: Dict[int, Optional[Path]], speed: float = 1.0, output_format: str = config.AUDIO_FORMAT, intermediate: bool = False, meta: Optional[TrackMeta] = None, diff --git a/converter/converter.py b/converter/converter.py index e36face..4422316 100644 --- a/converter/converter.py +++ b/converter/converter.py @@ -15,6 +15,7 @@ from typing import Dict, List, Optional, Tuple from . import audio, chunking, config, cover, extractors from .audio import TrackMeta from .tts import ( + MODEL_SIZE, VOICE_MODE_CLONE, VOICE_MODE_CUSTOM, VOICE_MODES, @@ -435,14 +436,12 @@ class AudiobookConverter: logger.info("Extracted %d characters (%d words)", len(text), len(text.split())) - # Split into chunks chunks = chunking.split_into_chunks(text) total_chunks = len(chunks) if total_chunks == 0: logger.error("No chunks created") return False - # Log chunk info chunk_sizes = [len(chunk.split()) for chunk in chunks] avg_chunk_size = sum(chunk_sizes) / len(chunk_sizes) logger.info("Split into %d chunks (avg %.0f words per chunk)", total_chunks, avg_chunk_size) @@ -460,7 +459,6 @@ class AudiobookConverter: logger.warning("Only %d/%d chunks succeeded. Proceeding with partial audiobook.", successful_chunks, total_chunks) - # Combine chunks (only the successful ones) success = audio.combine_chunks(total_chunks, output_path, chunk_results=results, speed=speed, output_format=output_format, intermediate=chapter is not None, @@ -504,7 +502,7 @@ class AudiobookConverter: else config.QWEN_API_URL) print(f"Qwen API endpoint: {api_url}") print(f"Voice mode: {self.voice_mode}") - print("Model size: 1.7B (always)") + print(f"Model size: {MODEL_SIZE} (always)") if self.voice_mode == VOICE_MODE_CUSTOM: print(f"Speaker: {config.SPEAKER}") print(f"Language: {self.language}") @@ -525,7 +523,6 @@ class AudiobookConverter: run_start = time.time() self._print_banner() - # Check for books book_files = sorted( f for f in BOOKS_FOLDER.iterdir() if f.is_file() and f.suffix.lower() in SUPPORTED_FORMATS @@ -571,7 +568,6 @@ class AudiobookConverter: print(f"[INFO] Converting {len(planned)} of {len(book_files)} book(s)") - # Convert each book results = {} for book_file, output_name in planned: try: @@ -585,7 +581,6 @@ class AudiobookConverter: logger.error("Unexpected error: %s", exc) results[book_file.name] = False - # Print summary successful = sum(results.values()) total = len(results) diff --git a/tests/test_audio.py b/tests/test_audio.py index c043766..c127311 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -175,23 +175,6 @@ class CollectChunkFilesTests(unittest.TestCase): self.assertEqual(files, [present]) self.assertEqual(missing, [2, 3]) - def test_glob_fallback_without_results(self): - with tempfile.TemporaryDirectory() as tmp: - chunks_dir = Path(tmp) - (chunks_dir / "chunk_0002.wav").write_bytes(b"audio") - (chunks_dir / "chunk_0001.wav").write_bytes(b"audio") - - original = audio.CHUNKS_FOLDER - audio.CHUNKS_FOLDER = chunks_dir - try: - files, missing = _collect_chunk_files(3) - finally: - audio.CHUNKS_FOLDER = original - - self.assertEqual(files, [chunks_dir / "chunk_0001.wav", - chunks_dir / "chunk_0002.wav"]) - self.assertEqual(missing, [3]) - class BuildM4bChaptersCommandTests(unittest.TestCase): def setUp(self): diff --git a/tests/test_converter.py b/tests/test_converter.py index 737fc06..20ebabc 100644 --- a/tests/test_converter.py +++ b/tests/test_converter.py @@ -48,13 +48,12 @@ class ConfigurationValidationTests(unittest.TestCase): def test_language_defaults_to_config(self): with patch("converter.converter.QwenTTSClient") as mock_tts: AudiobookConverter() - self.assertEqual(mock_tts.call_args.kwargs["language"], "English") + self.assertEqual(mock_tts.call_args.kwargs["language"], config.LANGUAGE) def test_output_format_defaults_to_config(self): with patch("converter.converter.QwenTTSClient"): converter = AudiobookConverter() self.assertEqual(converter.output_format, config.AUDIO_FORMAT) - self.assertEqual(config.AUDIO_FORMAT, "m4b") def test_language_normalized_before_tts_client(self): with patch("converter.converter.QwenTTSClient") as mock_tts: diff --git a/tests/test_extractors.py b/tests/test_extractors.py index d604351..ae1794c 100644 --- a/tests/test_extractors.py +++ b/tests/test_extractors.py @@ -153,6 +153,10 @@ class ExtractBookTests(unittest.TestCase): self.assertEqual(len(book.sections), 1) def test_epub_metadata_harvested(self): + try: + import ebooklib # noqa: F401 + except ImportError: + self.skipTest("ebooklib not installed") from converter.extractors import extract_book with tempfile.TemporaryDirectory() as tmp: diff --git a/tests/test_tts.py b/tests/test_tts.py index afaa2e3..64b4846 100644 --- a/tests/test_tts.py +++ b/tests/test_tts.py @@ -201,7 +201,6 @@ class PayloadLanguageTests(unittest.TestCase): kwargs = client.clone_client.predict.call_args.kwargs self.assertEqual(kwargs["model_size"], tts.MODEL_SIZE) self.assertEqual(kwargs["seed"], config.SEED) - self.assertNotIn("max_chunk_chars", kwargs) class FasterTTSClientHealthTests(unittest.TestCase): |
