diff options
| author | historia <historiavg@proton.me> | 2026-08-24 00:41:52 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-08-24 00:41:52 -0400 |
| commit | 194c63e4d11e6de9792a736a7b99788f1db78741 (patch) | |
| tree | 0c7eca8bee825b8d5c32f4c64e1d87da793caedb /backends | |
| parent | 5bfbdcb5765fd4eb57d13c67169bb3c2706ead75 (diff) | |
| download | tts-audiobook-generator-194c63e4d11e6de9792a736a7b99788f1db78741.tar.gz | |
feat: running process detection, menu gating
Diffstat (limited to 'backends')
| -rw-r--r-- | backends/__init__.py | 29 | ||||
| -rwxr-xr-x | backends/audiocpp.py | 19 | ||||
| -rw-r--r-- | backends/common.py | 25 | ||||
| -rwxr-xr-x | backends/faster.py | 17 | ||||
| -rw-r--r-- | backends/qwen.py | 17 |
5 files changed, 73 insertions, 34 deletions
diff --git a/backends/__init__.py b/backends/__init__.py index 9203143..629551e 100644 --- a/backends/__init__.py +++ b/backends/__init__.py @@ -5,11 +5,12 @@ 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 ``backends.REGISTRY`` drives the hub's setup/modify menus. +up (and whether their server is currently running), and +``backends.REGISTRY`` drives the hub's setup/configure menus. Adding a backend: create ``backends/<name>.py`` exposing ``detect() -> BackendStatus``, ``run_tui() -> int`` and -``modify_actions: list[ModifyAction]``, then append a ``BackendInfo`` in +``configure_actions: list[ConfigureAction]``, then append a ``BackendInfo`` in ``_build_registry`` below. ``audiobook.py`` and the hub pick it up automatically. """ @@ -25,13 +26,17 @@ class BackendStatus: INSTALLED means the backend itself is present (a cloned + built checkout, or a pip package). CONFIGURED means the supporting files are in place (a server.json / voices.json and a converter/config.py that - points at the right port). DETAILS are short status lines for the hub. - LAUNCH_HINT is the exact command the user runs to start the server. + 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. """ key: str label: str installed: bool configured: bool + running: bool = False details: List[str] = field(default_factory=list) launch_hint: str = "" @@ -42,20 +47,20 @@ class BackendStatus: @dataclass -class ModifyAction: - """A per-backend "modify" menu entry (e.g. "New server.json").""" +class ConfigureAction: + """A per-backend "configure" menu entry (e.g. "New server.json").""" label: str run: Callable[[], int] @dataclass class BackendInfo: - """One registry entry: identity, detector, setup wizard, modify menu.""" + """One registry entry: identity, detector, setup wizard, configure menu.""" key: str label: str detect: Callable[[], BackendStatus] setup_tui: Callable[[], int] - modify_actions: List[ModifyAction] = field(default_factory=list) + configure_actions: List[ConfigureAction] = field(default_factory=list) REGISTRY: List[BackendInfo] = [] @@ -73,21 +78,21 @@ def _build_registry() -> None: label="audio.cpp", detect=audiocpp.detect, setup_tui=audiocpp.run_tui, - modify_actions=audiocpp.modify_actions, + configure_actions=audiocpp.configure_actions, )) REGISTRY.append(BackendInfo( key="qwen", - label="Qwen3-TTS (demo server)", + label="qwen-tts", detect=qwen.detect, setup_tui=qwen.run_tui, - modify_actions=qwen.modify_actions, + configure_actions=qwen.configure_actions, )) REGISTRY.append(BackendInfo( key="faster", label="faster-qwen3-tts", detect=faster.detect, setup_tui=faster.run_tui, - modify_actions=faster.modify_actions, + configure_actions=faster.configure_actions, )) for info in REGISTRY: _BY_KEY[info.key] = info diff --git a/backends/audiocpp.py b/backends/audiocpp.py index b401366..57636a7 100755 --- a/backends/audiocpp.py +++ b/backends/audiocpp.py @@ -41,8 +41,8 @@ from typing import Callable, Dict, List, Optional, Set, Tuple # Allow running directly (python backends/audiocpp.py) from any cwd. sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) -import tui -from backends import BackendStatus, ModifyAction +from ui import tui +from backends import BackendStatus, ConfigureAction from backends import common from backends.common import ( CONFIG_PATH, @@ -1645,11 +1645,14 @@ def build_parser() -> argparse.ArgumentParser: def detect() -> BackendStatus: """Detect how far audio.cpp is set up, plus the command to start it.""" checkout = find_local_checkout() + # Probe the server first: it may be running externally even with no + # local checkout, and the status table should show that. + running = common.server_running(config.AUDIOCPP_API_URL) details: List[str] = [] launch = "" if checkout is None: return BackendStatus("audiocpp", "audio.cpp", installed=False, - configured=False, + configured=False, running=running, details=["not cloned — run setup to clone " "./audio.cpp"]) details.append(f"checkout: {checkout}") @@ -1670,13 +1673,13 @@ def detect() -> BackendStatus: else: details.append("no server.json — run setup to configure models") return BackendStatus("audiocpp", "audio.cpp", installed=built, - configured=configured, details=details, - launch_hint=launch) + configured=configured, running=running, + details=details, launch_hint=launch) -modify_actions: List[ModifyAction] = [ - ModifyAction("Reconfigure audio.cpp (models, voices, server.json)", - run_tui), +configure_actions: List[ConfigureAction] = [ + ConfigureAction("Reconfigure audio.cpp (models, voices, server.json)", + run_tui), ] diff --git a/backends/common.py b/backends/common.py index 2c6437f..d707fdc 100644 --- a/backends/common.py +++ b/backends/common.py @@ -141,6 +141,31 @@ def url_with_port(url: str, port: int) -> str: (parts.scheme or "http", f"{host}:{port}", parts.path, "", "")) +def server_running(url: str, timeout: float = 0.3) -> bool: + """True when something accepts TCP connections at URL's host:port. + + A protocol-agnostic socket connect: an HTTP TTS server that is up will + accept the connection (we do not need to speak HTTP to know it is + listening). Returns False on any parse or connection error, so a + misconfigured URL never blocks the hub — it just reports the backend + as not running. Used by each backend's ``detect()`` to set + ``BackendStatus.running``. + """ + import socket + try: + parts = urllib.parse.urlsplit(url) + host = parts.hostname or "127.0.0.1" + port = parts.port or (443 if (parts.scheme or "http") == "https" + else 80) + except ValueError: + return False + try: + with socket.create_connection((host, port), timeout=timeout): + return True + except OSError: + return False + + def update_config_value(key: str, value: str, config_path: Optional[Path] = None) -> bool: """Rewrite a ``KEY = "value"`` line in converter/config.py. diff --git a/backends/faster.py b/backends/faster.py index 4a2cc6f..71be050 100755 --- a/backends/faster.py +++ b/backends/faster.py @@ -25,8 +25,8 @@ from typing import List, Optional sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) -import tui -from backends import BackendStatus, ModifyAction +from ui import tui +from backends import BackendStatus, ConfigureAction from backends import common from backends.common import TTS_ROOT, find_wav_files, normalize_dir_arg from converter import config @@ -335,6 +335,7 @@ def detect() -> BackendStatus: cloned = _is_cloned() voices_json = _checkout() / "voices.json" configured = installed and cloned and voices_json.exists() + running = common.server_running(config.FASTER_API_URL) details: List[str] = [] details.append("pip: installed" if installed else "not installed — run setup to pip install") @@ -348,12 +349,12 @@ def detect() -> BackendStatus: f"--voices {voices_json} --port {_config_port()}") return BackendStatus("faster", "faster-qwen3-tts", installed=installed and cloned, - configured=configured, details=details, - launch_hint=launch) + configured=configured, running=running, + details=details, launch_hint=launch) def _run_voices_only_tui() -> int: - """Rebuild voices.json via the TUI (the "modify" action). + """Rebuild voices.json via the TUI (the "configure" action). Runs the same wizard but skips the pip/clone prerequisites so it goes straight to picking the .wav directory and writing voices.json. @@ -364,9 +365,9 @@ def _run_voices_only_tui() -> int: return run_tui(args) -modify_actions: List[ModifyAction] = [ - ModifyAction("Rebuild voices.json", _run_voices_only_tui), - ModifyAction("Reconfigure faster-qwen3-tts", run_tui), +configure_actions: List[ConfigureAction] = [ + ConfigureAction("Rebuild voices.json", _run_voices_only_tui), + ConfigureAction("Reconfigure faster-qwen3-tts", run_tui), ] diff --git a/backends/qwen.py b/backends/qwen.py index 48e1804..60f3bb6 100644 --- a/backends/qwen.py +++ b/backends/qwen.py @@ -22,8 +22,8 @@ from typing import List, Optional sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) -import tui -from backends import BackendStatus, ModifyAction +from ui import tui +from backends import BackendStatus, ConfigureAction from backends import common from converter import config @@ -209,6 +209,10 @@ def detect() -> BackendStatus: installed = _is_installed() custom_port = _config_port(config.QWEN_API_URL, DEFAULT_CUSTOM_PORT) clone_port = _config_port(config.CLONE_API_URL, DEFAULT_CLONE_PORT) + # Running when either server is up — CustomVoice (speaker mode) or Base + # (voice clone) each suffice for a conversion on their own. + running = (common.server_running(config.QWEN_API_URL) + or common.server_running(config.CLONE_API_URL)) details: List[str] = [] details.append("pip: installed" if installed else "not installed — run setup to pip install qwen-tts") @@ -218,13 +222,14 @@ def detect() -> BackendStatus: launch = (f"qwen-tts-demo {QWEN_CUSTOMVOICE_MODEL} --ip 127.0.0.1 " f"--port {custom_port} ; qwen-tts-demo {QWEN_BASE_MODEL} " f"--ip 127.0.0.1 --port {clone_port}") - return BackendStatus("qwen", "Qwen3-TTS (demo server)", + return BackendStatus("qwen", "qwen-tts", installed=installed, configured=installed, - details=details, launch_hint=launch) + running=running, details=details, + launch_hint=launch) -modify_actions: List[ModifyAction] = [ - ModifyAction("Reconfigure Qwen3-TTS (ports/speaker)", run_tui), +configure_actions: List[ConfigureAction] = [ + ConfigureAction("Reconfigure qwen-tts (ports/speaker)", run_tui), ] |
