diff options
| author | historia <historiavg@proton.me> | 2026-08-20 23:50:37 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-08-20 23:50:37 -0400 |
| commit | 38c8fdcba7ce54ad0ad76be9ef0748df1c55ebc1 (patch) | |
| tree | 911e031ee0e4b902fcd3954df3838a920416e8a3 /tools | |
| parent | 5c3df0a434059bd0d541bda35a51e49e3c44dd55 (diff) | |
| download | tts-audiobook-generator-38c8fdcba7ce54ad0ad76be9ef0748df1c55ebc1.tar.gz | |
feat: make_audiocpp_server_json.py takes an argument. remove chunk wording with audiocpp backend.
Diffstat (limited to 'tools')
| -rwxr-xr-x | tools/make_audiocpp_server_json.py | 88 |
1 files changed, 42 insertions, 46 deletions
diff --git a/tools/make_audiocpp_server_json.py b/tools/make_audiocpp_server_json.py index 1273e43..d24f895 100755 --- a/tools/make_audiocpp_server_json.py +++ b/tools/make_audiocpp_server_json.py @@ -14,28 +14,30 @@ v3 TTS 4B, VoxCPM2-2B, and IndexTTS-2 / 2.5 (see the "Option 4" section of the README). The converter works with other audio.cpp TTS families too; host them by writing server.json by hand. -Reference .wav files for voice cloning (a directory argument or an -interactive prompt) are transcribed with a local Whisper backend -(faster_whisper or whisper) and added as voice_presets on the cloning -model entry. +Reference .wav files for voice cloning (the required WAV_DIR argument) +are transcribed with a local Whisper backend (faster_whisper or whisper) +and added as voice_presets on the cloning model entry. Every value can also be supplied as a command-line flag; anything missing is asked interactively with the default shown in brackets. Pressing Enter -accepts the default, so running the tool with no arguments and pressing -Enter through produces a server.json hosting both Qwen3-TTS models on -127.0.0.1:8080 with the cuda backend. +accepts the default. Usage: - python tools/make_audiocpp_server_json.py [WAV_DIR] [--output PATH] + python tools/make_audiocpp_server_json.py WAV_DIR [--output PATH] [--family {qwen3_tts,higgs_audio_tts,voxcpm2,index_tts2,index_tts2_5}] [--model-id ID] [--model-path PATH] [--host HOST] [--port PORT] [--models {both,custom,clone}] [--backend {cuda,vulkan,hip,cpu}] [--lazy-load] [--whisper-model NAME] [--force] + +WAV_DIR is required: a directory of .wav reference files used as voice +cloning presets. It is checked up front and reported with its resolved +absolute path if it does not exist. """ import argparse import json +import os import re import sys import urllib.parse @@ -108,6 +110,19 @@ FAMILY_KEYS = tuple(entry["key"] for entry in FAMILY_ENTRIES) FAMILY_BY_KEY = {entry["key"]: entry for entry in FAMILY_ENTRIES} +def resolve_wav_dir_arg(value: str) -> Path: + """Normalize a user-supplied wav directory argument. + + Strips surrounding quotes (a common copy-paste artifact), expands a + leading ``~``, and resolves the result to an absolute path so relative + paths are always validated against the current working directory. + """ + cleaned = value.strip() + if len(cleaned) >= 2 and cleaned[0] == cleaned[-1] and cleaned[0] in "\"'": + cleaned = cleaned[1:-1] + return Path(os.path.expanduser(cleaned)).resolve() + + def find_wav_files(input_dir: Path) -> list: """Return the .wav files in INPUT_DIR, sorted alphabetically by name.""" return sorted( @@ -241,23 +256,6 @@ def ask_distinct_clone_id(primary_id: str) -> str: f"equal to '{primary_id}'.") -def ask_wav_dir() -> Optional[Path]: - """Prompt for a directory of .wav clone references; Enter skips.""" - while True: - try: - answer = input("Directory with .wav files to clone " - "(Enter to skip): ").strip() - except EOFError: - return None - if not answer: - return None - path = Path(answer) - if path.is_dir(): - return path - print(f"[WARNING] {answer} is not a directory; try again " - "(or press Enter to skip).") - - def config_port() -> int: """Return the port of AUDIOCPP_API_URL in converter/config.py.""" try: @@ -452,27 +450,20 @@ def _ask_host_port_backend_lazy(args: argparse.Namespace def _collect_voice_presets(args: argparse.Namespace, include_clone: bool) -> Dict[str, dict]: - """Resolve the clone-reference wav directory and transcribe it. + """Transcribe the wav directory into the voice_presets mapping. - Returns the voice_presets mapping (empty when no wavs were given or - found). Cloning entries only: a run without any cloning model ignores - the wav directory entirely. + Returns the voice_presets mapping (empty when no wavs were found). + Cloning entries only: a run without any cloning model ignores the wav + directory entirely. """ - wav_dir: Optional[Path] = None - if args.input_dir is not None: - if include_clone: - wav_dir = args.input_dir - else: - print(f"[WARNING] Ignoring {args.input_dir}: no cloning model " - "selected, so voice presets are not used") - elif include_clone: - wav_dir = ask_wav_dir() - if wav_dir is None: + if not include_clone: + print(f"[WARNING] Ignoring {args.input_dir}: no cloning model " + "selected, so voice presets are not used") return {} - wav_files = find_wav_files(wav_dir) + wav_files = find_wav_files(args.input_dir) if not wav_files: - print(f"[WARNING] No .wav files found in {wav_dir}; writing the " + print(f"[WARNING] No .wav files found in {args.input_dir}; writing the " "config without voice presets") return {} if whisper_backend_available() is None: @@ -512,9 +503,9 @@ def main() -> int: parser = argparse.ArgumentParser( description="Generate a server.json for the audio.cpp audiocpp_server " "hosting a TTS model used by this converter.") - parser.add_argument("input_dir", type=Path, nargs="?", default=None, - help="Optional directory with .wav reference files " - "to add as voice cloning presets") + parser.add_argument("input_dir", type=resolve_wav_dir_arg, metavar="WAV_DIR", + help="Directory with .wav reference files to add as " + "voice cloning presets (required)") parser.add_argument("--output", type=Path, default=Path("server.json"), help="Output path for server.json (default: " "server.json in the current directory)") @@ -551,8 +542,13 @@ def main() -> int: help="Overwrite the output file without prompting") args = parser.parse_args() - if args.input_dir is not None and not args.input_dir.is_dir(): - parser.error(f"WAV directory not found: {args.input_dir}") + if not args.input_dir.is_dir(): + parser.error( + f"WAV directory not found: {args.input_dir}\n" + f" (resolved from the current working directory: " + f"{Path.cwd()})\n" + " WAV_DIR must be a directory containing the .wav " + "reference files to use as voice cloning presets") if args.output.exists() and not args.force \ and not prompt_overwrite(args.output): |
