aboutsummaryrefslogtreecommitdiff
path: root/converter
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-08-18 03:13:14 -0400
committerhistoria <historiavg@proton.me>2026-08-18 03:13:14 -0400
commit86d2eb8d789f82dd8e56dd0ff53933152ba94e6b (patch)
tree4a19f0c77d7600d7c060cfe6238f30e1829f4660 /converter
parentde5b2d9ca7302ade711d9dfb32b2fe8047f55cc2 (diff)
downloadtts-audiobook-generator-86d2eb8d789f82dd8e56dd0ff53933152ba94e6b.tar.gz
feat: prompt before overwriting existing books
Diffstat (limited to 'converter')
-rw-r--r--converter/converter.py63
1 files changed, 61 insertions, 2 deletions
diff --git a/converter/converter.py b/converter/converter.py
index c0e97e4..a58fc33 100644
--- a/converter/converter.py
+++ b/converter/converter.py
@@ -1,5 +1,6 @@
"""Orchestrates book-to-audiobook conversion."""
+import glob
import logging
import re
import sys
@@ -39,6 +40,48 @@ def setup_directories() -> None:
Path(directory).mkdir(parents=True, exist_ok=True)
+def find_existing_outputs(output_name: str, output_format: str) -> List[Path]:
+ """Return existing output files that a conversion would overwrite.
+
+ Multi-section books (e.g. EPUB chapters) and speed-adjusted copies are
+ named ``{name}_suffix.{ext}``; exact chapter file names are only known
+ after text extraction, so any file matching that pattern counts.
+ """
+ folder = config.AUDIOBOOKS_FOLDER
+ existing: List[Path] = []
+ primary = folder / f"{output_name}.{output_format}"
+ if primary.exists():
+ existing.append(primary)
+ existing.extend(sorted(
+ folder.glob(f"{glob.escape(output_name)}_*.{output_format}")))
+ return existing
+
+
+def prompt_overwrite(existing: List[Path], output_name: str) -> bool:
+ """Ask whether to reconvert a book whose output files already exist.
+
+ All overwrite questions are asked before any conversion starts so the
+ rest of the run is unattended. Returns False when no interactive input
+ is available (stdin closed), keeping existing files safe.
+ """
+ if len(existing) == 1:
+ message = f"{existing[0].name} already exists. Convert anyway and overwrite it?"
+ else:
+ message = (f"{len(existing)} output files for '{output_name}' already exist "
+ f"(e.g. {existing[0].name}). Convert anyway and overwrite them?")
+ while True:
+ try:
+ answer = input(f"{message} (y/n): ").strip().lower()
+ except EOFError:
+ print("\n[WARNING] No interactive input available; keeping existing output")
+ return False
+ if answer in ("y", "yes"):
+ return True
+ if answer in ("n", "no"):
+ return False
+ print("Please answer 'y' or 'n'.")
+
+
class AudiobookConverter:
"""Audiobook converter using the Qwen TTS API."""
@@ -336,12 +379,28 @@ class AudiobookConverter:
# Avoid output collisions when two books share a stem (e.g. dune.txt + dune.epub).
stem_counts: Dict[str, int] = Counter(book_file.stem for book_file in book_files)
- # Convert each book
- results = {}
+ # Ask every overwrite question up front, before any conversion
+ # starts, so the rest of the run is unattended.
+ planned: List[Tuple[Path, str]] = []
for book_file in book_files:
output_name = book_file.stem
if stem_counts[book_file.stem] > 1:
output_name = f"{book_file.stem}_{book_file.suffix.lstrip('.')}"
+ 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)")
+ continue
+ planned.append((book_file, output_name))
+
+ if not planned:
+ print("[INFO] Nothing to convert (all books skipped)")
+ return True
+
+ print(f"[INFO] Converting {len(planned)} of {len(book_files)} book(s)")
+
+ # Convert each book
+ results = {}
+ for book_file, output_name in planned:
try:
success = self.convert_book(book_file, output_name=output_name)
results[book_file.name] = success