aboutsummaryrefslogtreecommitdiff
path: root/audiobook.py
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-08-25 16:40:30 -0400
committerhistoria <historiavg@proton.me>2026-08-25 16:40:30 -0400
commit867866f131b0b6c76c54272791e7f7dea01db990 (patch)
treeb57ecdd66eeaf7ad73742d2f2bbe15d3a5498fa3 /audiobook.py
parentfca3431721a55277f139efc83df2438207917448 (diff)
downloadtts-audiobook-generator-867866f131b0b6c76c54272791e7f7dea01db990.tar.gz
feat: better tui menu option gating for models that support custom voices (qwen) and models that do not support instructions
Diffstat (limited to 'audiobook.py')
-rwxr-xr-xaudiobook.py26
1 files changed, 24 insertions, 2 deletions
diff --git a/audiobook.py b/audiobook.py
index 9bdad19..b4cd89d 100755
--- a/audiobook.py
+++ b/audiobook.py
@@ -59,6 +59,7 @@ def convert(backend: str = None, voice: str = None, clone: str = None,
model_id: str = None, instructions: str = None,
request_options: dict = None, input_dir: Path = None,
output_dir: Path = None, api_url: str = None,
+ speaker: str = None,
progress=None, cancel=None, confirm=None,
book_files=None, planned=None) -> int:
"""Run one conversion pass with explicit options (used by the CLI and hub).
@@ -101,7 +102,7 @@ def convert(backend: str = None, voice: str = None, clone: str = None,
book_files, planned = AudiobookConverter.preflight_overwrites(
backend=backend, voice=voice, voice_mode=voice_mode,
voice_clone_ref_audio=clone, output_format=output_format,
- instructions=instructions, confirm=confirm,
+ instructions=instructions, speaker=speaker, confirm=confirm,
)
if not book_files:
print("[INFO] Nothing to convert. Add a .txt, .pdf, or .epub file "
@@ -120,6 +121,7 @@ def convert(backend: str = None, voice: str = None, clone: str = None,
language=language, backend=backend, voice=voice, debug=debug,
model_id=model_id, instructions=instructions,
request_options=request_options, api_url=api_url,
+ speaker=speaker,
progress=progress, cancel=cancel,
)
converter._book_files = book_files
@@ -247,11 +249,21 @@ Examples:
"a key in the server's voices.json ('default' when it was started "
"with --ref-audio). audiocpp: a voice_preset or voice_dir entry "
"(cloning); required for audio.cpp families without built-in "
- "speakers (everything except Qwen3-TTS CustomVoice). Not used by "
+ "speakers (everything except Qwen3-TTS CustomVoice). When "
+ "AUDIOCPP_CLONE_MODEL_ID names a second server entry (typically "
+ "the Qwen Base model), --voice reroutes to it. Not used by "
"the qwen backend (use app/converter/config.py SPEAKER or --clone "
"there).")
)
parser.add_argument(
+ "--speaker", type=str, default=None, metavar="NAME",
+ help=("audiocpp only: a built-in Qwen3-TTS CustomVoice speaker name "
+ "(e.g. Vivian, Ryan, Uncle Fu). Selects speaker mode on the "
+ "CustomVoice model entry; mutually exclusive with --voice. "
+ "Without --voice/--speaker, a CustomVoice entry defaults to "
+ "the SPEAKER in app/converter/config.py.")
+ )
+ parser.add_argument(
"--debug", action="store_true",
help=("Troubleshooting mode: dump each chunk's raw audio and the exact text "
"sent for it under the debug/ folder (organized per book and chapter), "
@@ -326,6 +338,11 @@ Examples:
"uses a voice configured on the server (voice_presets or "
"voice_dir in its config); select it with --voice (see README)")
args.clone = None
+ if args.voice and args.speaker:
+ parser.error("--voice and --speaker are mutually exclusive with "
+ "--backend audiocpp: --voice selects a server-side "
+ "preset (cloning), --speaker a built-in CustomVoice "
+ "speaker (see README)")
if args.transcription or args.no_transcription:
print("[WARNING] --transcription/--no-transcription are ignored with "
"--backend audiocpp: the reference transcript is configured on "
@@ -356,6 +373,10 @@ Examples:
parser.error("--model requires --backend audiocpp; it selects an "
"audio.cpp server model entry id")
+ if args.speaker is not None and args.backend != BACKEND_AUDIOCPP:
+ parser.error("--speaker requires --backend audiocpp; it selects a "
+ "built-in Qwen3-TTS CustomVoice speaker")
+
if args.instructions is not None and args.backend != BACKEND_AUDIOCPP:
parser.error("--instructions requires --backend audiocpp; it is "
"sent as the audio.cpp request's instructions field")
@@ -390,6 +411,7 @@ Examples:
request_options=request_options,
input_dir=args.input, output_dir=args.output,
api_url=api_url,
+ speaker=args.speaker,
))