aboutsummaryrefslogtreecommitdiff
path: root/audiobook.py
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-08-28 17:16:54 -0400
committerhistoria <historiavg@proton.me>2026-08-28 17:16:54 -0400
commite66eb0e7d4342ae1c58e9bbd341843753be548f0 (patch)
tree0ade3b71c86b59511d7653e450377a9366a30482 /audiobook.py
parent270fa60c01866c4431d540be960b6cd2bc2b9c44 (diff)
downloadtts-audiobook-generator-e66eb0e7d4342ae1c58e9bbd341843753be548f0.tar.gz
feat: cli auto-starts and stops locally-managed servers if no --api-url is passed
Diffstat (limited to 'audiobook.py')
-rwxr-xr-xaudiobook.py42
1 files changed, 40 insertions, 2 deletions
diff --git a/audiobook.py b/audiobook.py
index 5da78ae..36fc2df 100755
--- a/audiobook.py
+++ b/audiobook.py
@@ -9,6 +9,12 @@ process the input directory); pass flags to script a conversion directly.
directory (the two flag pairs are mutually exclusive). Edit
app/converter/config.py for server URLs and processing settings; the TTS
backend, model and voice are chosen per run with CLI flags.
+
+Without --api-url the CLI manages the server itself: it starts the
+backend's managed instance (the one installed via the TUI), converts, and
+stops it again — unless a server is already running at the configured
+endpoint, which is used as-is and left running. --api-url targets an
+externally-run server and never touches server state.
"""
import argparse
@@ -83,7 +89,7 @@ def convert(backend: str, voice: str = None, clone: str = None,
output_dir: Path = None, api_url: str = None,
input_file: Path = None, output_file: Path = None,
progress=None, cancel=None, confirm=None,
- book_files=None, planned=None) -> int:
+ book_files=None, planned=None, manage_server: bool = False) -> int:
"""Run one conversion pass with explicit options (used by the CLI and hub).
Returns the process exit code (0 on success, 1 on failure, 130 on
@@ -96,6 +102,15 @@ def convert(backend: str, voice: str = None, clone: str = None,
configured server URL for the selected backend (used by the hub's
"[remote]" entries and --api-url).
+ MANAGE_SERVER (the CLI, without --api-url) boots the backend's managed
+ server before converting — backends.managed.ensure_running — and stops
+ it again afterwards, but only when this run started it; a server
+ already answering at the configured endpoint is used as-is and left
+ running. It is skipped when API_URL is set (an external server is
+ never touched) and when nothing needs converting (the early returns
+ below run first, so an empty input folder never boots a server). The
+ hub leaves it False: its run view boots and stops the server itself.
+
INPUT_FILE converts a single book file instead of scanning the
input folder (the CLI validates it and resolves relative paths
against the project root). OUTPUT_FILE, which requires INPUT_FILE,
@@ -172,7 +187,18 @@ def convert(backend: str, voice: str = None, clone: str = None,
print("[INFO] Nothing to convert (all books skipped)")
return 0
+ # The CLI's server lifecycle (MANAGE_SERVER without API_URL): boot the
+ # managed instance before converting and shut it down when the run is
+ # over — success, failure, or Ctrl-C — via the finally below. A server
+ # already running is reused and left running (shutdown is a no-op for
+ # it), and a refused/failed boot (server.ok False) stops here.
+ server = None
try:
+ if manage_server and api_url is None:
+ from backends import managed
+ server = managed.ensure_running(backend, voice_mode)
+ if server is not None and not server.ok:
+ return 1
converter = AudiobookConverter(
voice_mode=voice_mode, voice_clone_ref_audio=clone,
voice_clone_ref_text=transcription,
@@ -201,6 +227,9 @@ def convert(backend: str, voice: str = None, clone: str = None,
if progress is not None:
progress({"kind": "error", "message": str(exc)})
return 1
+ finally:
+ if server is not None:
+ server.shutdown()
return 0 if ok else 1
@@ -240,6 +269,10 @@ Examples:
# No arguments, in a terminal: the full TUI (set up backends, convert).
python audiobook.py
+ # Without --api-url the CLI starts the backend's managed server (installed
+ # via the TUI), converts, and stops it again; a server already running at
+ # the configured endpoint is used as-is and left running.
+
# Use the audio.cpp audiocpp_server (a built-in speaker, or a server-side voice)
python audiobook.py --backend audiocpp --voice Vivian
@@ -401,7 +434,11 @@ Examples:
"endpoint for the selected backend. Accept a host:port "
"(e.g. 10.20.30.40:8080) or a full http(s):// URL. With "
"--backend qwen it overrides the endpoint for the active "
- "voice mode (custom or clone).")
+ "voice mode (custom or clone). Without it the CLI starts the "
+ "backend's managed server itself (when installed), converts, "
+ "and stops it again — unless a server is already running at "
+ "the configured endpoint, which is used as-is and left "
+ "running; an explicit --api-url never touches server state.")
)
args = parser.parse_args()
@@ -577,6 +614,7 @@ Examples:
input_dir=args.input, output_dir=args.output,
api_url=api_url,
input_file=input_file, output_file=output_file,
+ manage_server=True,
))