diff options
| author | historia <historiavg@proton.me> | 2026-08-18 19:32:48 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-08-18 19:32:48 -0400 |
| commit | 7e8bf663a65111fb648b01d9f45ad08eddbf2515 (patch) | |
| tree | 83a979f15d8c7d532c3cf2ffaabac927753e27b4 /converter/converter.py | |
| parent | abc610097fe3a6ff1c1282f728fb0d75440f6ba8 (diff) | |
| download | tts-audiobook-generator-7e8bf663a65111fb648b01d9f45ad08eddbf2515.tar.gz | |
feat: include narrator in output filenames
Diffstat (limited to 'converter/converter.py')
| -rw-r--r-- | converter/converter.py | 22 |
1 files changed, 18 insertions, 4 deletions
diff --git a/converter/converter.py b/converter/converter.py index 29d3bc4..4d20083 100644 --- a/converter/converter.py +++ b/converter/converter.py @@ -13,7 +13,7 @@ from typing import Dict, List, Optional, Tuple from . import audio, chunking, config, cover, extractors from .audio import TrackMeta -from .tts import QwenTTSClient, normalize_language +from .tts import QwenTTSClient, normalize_language, speaker_display_name logger = logging.getLogger(__name__) @@ -132,11 +132,24 @@ class AudiobookConverter: ) @staticmethod - def _sanitize_filename(name: str) -> str: + def _sanitize_filename(name: str, fallback: str = "chapter") -> str: """Make a chapter title safe to use as part of a file name.""" cleaned = re.sub(r'[\\/:*?"<>|]', " ", name) cleaned = re.sub(r"\s+", " ", cleaned).strip().strip(".") - return cleaned[:80] or "chapter" + return cleaned[:80] or fallback + + def _narrator_tag(self) -> str: + """Narrator name used in output file names. + + Custom voice mode uses the built-in speaker's display name; voice + clone mode uses the reference audio file's stem. Spaces become + underscores (e.g. "Uncle Fu" -> "Uncle_Fu"). + """ + if self.voice_mode == config.VOICE_MODE_CLONE: + narrator = Path(self.voice_clone_ref_audio).stem + else: + narrator = speaker_display_name() + return self._sanitize_filename(narrator, fallback="narrator").replace(" ", "_") def convert_book(self, file_path: Path, output_name: Optional[str] = None) -> bool: """Convert a single book to one or more audiobook files.""" @@ -155,7 +168,7 @@ class AudiobookConverter: logger.error("No text extracted") return False - stem = output_name or file_path.stem + stem = output_name or f"{file_path.stem}_{self._narrator_tag()}" # Cover art: generated once per book. Named with the chunk_ # prefix so cleanup_chunks() removes it with the other scratch @@ -416,6 +429,7 @@ class AudiobookConverter: output_name = book_file.stem if stem_counts[book_file.stem] > 1: output_name = f"{book_file.stem}_{book_file.suffix.lstrip('.')}" + output_name = f"{output_name}_{self._narrator_tag()}" existing = find_existing_outputs(output_name, self.output_format) if existing and not prompt_overwrite(existing, output_name): print(f"[INFO] Skipping {book_file.name} (existing output kept)") |
