From e66eb0e7d4342ae1c58e9bbd341843753be548f0 Mon Sep 17 00:00:00 2001 From: historia Date: Fri, 28 Aug 2026 17:16:54 -0400 Subject: feat: cli auto-starts and stops locally-managed servers if no --api-url is passed --- audiobook.py | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) (limited to 'audiobook.py') 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, )) -- cgit v1.2.3