aboutsummaryrefslogtreecommitdiff
path: root/app/converter/converter.py
diff options
context:
space:
mode:
Diffstat (limited to 'app/converter/converter.py')
-rw-r--r--app/converter/converter.py33
1 files changed, 28 insertions, 5 deletions
diff --git a/app/converter/converter.py b/app/converter/converter.py
index bd5e477..a10a57d 100644
--- a/app/converter/converter.py
+++ b/app/converter/converter.py
@@ -212,6 +212,7 @@ class AudiobookConverter:
instructions: Optional[str] = None,
request_options: Optional[Dict[str, str]] = None,
api_url: Optional[str] = None,
+ unload_models: Optional[bool] = None,
progress: Optional[Callable[[dict], None]] = None,
cancel=None):
if speed <= 0:
@@ -243,6 +244,9 @@ class AudiobookConverter:
# them against the server-hosted model at connect time.
self.instructions = instructions
self.request_options = dict(request_options or {})
+ # audio.cpp only: force unloading previously-loaded server models
+ # at connect time (None follows the AUDIOCPP_UNLOAD_MODELS setting).
+ self.unload_models = unload_models
self._validate_configuration()
# Interactive reporting (the TUI run view): PROGRESS receives an
# event dict per state change and turns the clients' console prints
@@ -261,13 +265,15 @@ class AudiobookConverter:
# elsewhere. model_id picks the server entry per run
# (auto-selected on single-entry servers); instructions
# describe or style the voice, request_options pass
- # per-model controls through to the server.
+ # per-model controls through to the server. unload_models
+ # forces a pre-run model unload when not None ("All" runs).
self.tts = AudioCppTTSClient(chunks_dir=CHUNKS_FOLDER,
voice=voice, language=self.language,
model_id=model_id,
instructions=instructions,
request_options=self.request_options,
- api_url=api_url, quiet=quiet)
+ api_url=api_url, quiet=quiet,
+ unload_models=unload_models)
else:
# Qwen: the voice mode picks the request shape (built-in
# speaker, clone from a reference .wav, or a designed voice);
@@ -355,7 +361,6 @@ class AudiobookConverter:
voice_clone_ref_audio: Optional[str],
instructions: Optional[str] = None) -> str:
"""Narrator name used in output file names, without a server connection.
-
Custom voice mode uses the built-in speaker's display name; voice
clone mode uses the reference audio file's stem; the faster and
audiocpp backends use the server-side voice name (for audiocpp's
@@ -392,6 +397,19 @@ class AudiobookConverter:
return AudiobookConverter._sanitize_filename(
narrator, fallback="narrator").replace(" ", "_")
+ @staticmethod
+ def compute_model_tag(model_id: Optional[str]) -> str:
+ """Model id used in output file names, without a server connection.
+
+ "All (multiple generation)" runs name every output with the
+ generating model's id so the per-model files never collide
+ (e.g. ``dune_qwen3_tts_1_7b_base_q8_0_Vivian.m4b``). Pure (no I/O,
+ no server) so the pre-flight can compute the exact output names a
+ run would produce before spending time connecting to a TTS server.
+ """
+ return AudiobookConverter._sanitize_filename(
+ model_id or "", fallback="model").replace(" ", "_")
+
# ------------------------------------------------------------------
# Debug dumps (--debug)
# ------------------------------------------------------------------
@@ -804,6 +822,7 @@ class AudiobookConverter:
confirm: Optional[Callable[[str, bool], bool]] = None,
book_files: Optional[List[Path]] = None,
output_name: Optional[str] = None,
+ name_tag: Optional[str] = None,
) -> Tuple[List[Path], List[Tuple[Path, str]]]:
"""Discover books and ask every overwrite question up front.
@@ -822,7 +841,10 @@ class AudiobookConverter:
(a single --input-file book; still filtered to supported formats),
and OUTPUT_NAME overrides the computed output name with a verbatim
base name (--output-file's stem, no narrator tag or stem-collision
- suffix). Both default to the directory-scan behavior.
+ suffix). Both default to the directory-scan behavior. NAME_TAG, when
+ given, is inserted between the book stem and the narrator tag
+ ("All (multiple generation)" runs pass the sanitized model id, so
+ each model's outputs are named and planned separately).
"""
if book_files is None:
book_files = sorted(
@@ -854,7 +876,8 @@ class AudiobookConverter:
name = book_file.stem
if stem_counts[book_file.stem] > 1:
name = f"{book_file.stem}_{book_file.suffix.lstrip('.')}"
- names.append((book_file, f"{name}_{narrator_tag}"))
+ tag = f"{name_tag}_{narrator_tag}" if name_tag else narrator_tag
+ names.append((book_file, f"{name}_{tag}"))
# Ask every overwrite question up front, before any conversion
# starts, so the rest of the run is unattended.