From c02d66b2d3221c0c5f5e8f2cb2ae218f1e325a0a Mon Sep 17 00:00:00 2001 From: historia Date: Mon, 24 Aug 2026 01:57:13 -0400 Subject: feat: manage venv for all backends --- backends/__init__.py | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) (limited to 'backends/__init__.py') diff --git a/backends/__init__.py b/backends/__init__.py index 629551e..6a23ab7 100644 --- a/backends/__init__.py +++ b/backends/__init__.py @@ -5,8 +5,15 @@ its setup wizard, its status detection, and the launch command it prints once configured. This package aggregates them into a single registry so ``audiobook.py``'s TUI hub and future tools can iterate backends without hardcoding their names: ``backends.detect_all()`` reports which are set -up (and whether their server is currently running), and -``backends.REGISTRY`` drives the hub's setup/configure menus. +up (and whether their server is currently running), and the registry +drives the hub's setup/configure menus. + +The registry is built lazily on the first call to ``get``/``detect_all``/ +``detect`` (not at package import time), because the backend modules pull +in ``converter.tts`` and its third-party dependencies, which are only +available inside the managed venv that ``audiobook.py`` bootstraps before +importing them. ``backends.envs`` is imported during that bootstrap, so +importing this package must stay cheap and dependency-free. Adding a backend: create ``backends/.py`` exposing ``detect() -> BackendStatus``, ``run_tui() -> int`` and @@ -15,10 +22,25 @@ Adding a backend: create ``backends/.py`` exposing automatically. """ +import shlex from dataclasses import dataclass, field from typing import Callable, List, Optional +@dataclass +class ServerSpec: + """One launchable server process for a backend. + + A backend may expose more than one server (qwen runs CustomVoice and Base + on separate ports). ARGV is the exact command line the hub spawns (using + the managed venv's absolute binaries, so no shell activation is needed); + URL is the endpoint ``common.server_running`` probes to decide readiness. + """ + name: str + url: str + argv: List[str] + + @dataclass class BackendStatus: """How far a backend is set up, plus the command to start it. @@ -29,8 +51,10 @@ class BackendStatus: points at the right port). RUNNING means an external server is currently accepting connections on the configured port (probed by ``backends.common.server_running``). DETAILS are short status lines for - the hub. LAUNCH_HINT is the exact command the user runs to start the - server. + the hub. LAUNCH_HINT is the human-readable command(s) the user runs to + start the server, derived from SERVERS by ``format_launch_hint``. + SERVERS is the machine-usable list of server processes the hub can + start/stop (empty when the backend is not yet configured). """ key: str label: str @@ -39,6 +63,7 @@ class BackendStatus: running: bool = False details: List[str] = field(default_factory=list) launch_hint: str = "" + servers: List[ServerSpec] = field(default_factory=list) @property def ready(self) -> bool: @@ -46,6 +71,11 @@ class BackendStatus: return self.installed and self.configured +def format_launch_hint(servers: List[ServerSpec]) -> str: + """Join a backend's server argvs into a copy-pasteable launch hint.""" + return " ; ".join(shlex.join(s.argv) for s in servers) + + @dataclass class ConfigureAction: """A per-backend "configure" menu entry (e.g. "New server.json").""" @@ -114,6 +144,3 @@ def detect(key: str) -> Optional[BackendStatus]: """Detect a single backend by key.""" info = get(key) return info.detect() if info is not None else None - - -_build_registry() -- cgit v1.2.3