From 5bfbdcb5765fd4eb57d13c67169bb3c2706ead75 Mon Sep 17 00:00:00 2001 From: historia Date: Sun, 23 Aug 2026 23:48:25 -0400 Subject: feat: audiobook.py tui: convert, modify, or install backends --- backends/__init__.py | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 backends/__init__.py (limited to 'backends/__init__.py') diff --git a/backends/__init__.py b/backends/__init__.py new file mode 100644 index 0000000..9203143 --- /dev/null +++ b/backends/__init__.py @@ -0,0 +1,114 @@ +"""Registry of the TTS backends the audiobook generator can talk to. + +Each backend (audio.cpp, qwen, faster) lives in its own module and owns +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. + +Adding a backend: create ``backends/.py`` exposing +``detect() -> BackendStatus``, ``run_tui() -> int`` and +``modify_actions: list[ModifyAction]``, then append a ``BackendInfo`` in +``_build_registry`` below. ``audiobook.py`` and the hub pick it up +automatically. +""" + +from dataclasses import dataclass, field +from typing import Callable, List, Optional + + +@dataclass +class BackendStatus: + """How far a backend is set up, plus the command to start it. + + 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. + """ + key: str + label: str + installed: bool + configured: bool + details: List[str] = field(default_factory=list) + launch_hint: str = "" + + @property + def ready(self) -> bool: + """True when the backend is installed and configured for use.""" + return self.installed and self.configured + + +@dataclass +class ModifyAction: + """A per-backend "modify" menu entry (e.g. "New server.json").""" + label: str + run: Callable[[], int] + + +@dataclass +class BackendInfo: + """One registry entry: identity, detector, setup wizard, modify menu.""" + key: str + label: str + detect: Callable[[], BackendStatus] + setup_tui: Callable[[], int] + modify_actions: List[ModifyAction] = field(default_factory=list) + + +REGISTRY: List[BackendInfo] = [] +_BY_KEY: dict = {} + + +def _build_registry() -> None: + """Import the backend modules and wire up REGISTRY (once).""" + if REGISTRY: + return + from . import audiocpp, faster, qwen + + REGISTRY.append(BackendInfo( + key="audiocpp", + label="audio.cpp", + detect=audiocpp.detect, + setup_tui=audiocpp.run_tui, + modify_actions=audiocpp.modify_actions, + )) + REGISTRY.append(BackendInfo( + key="qwen", + label="Qwen3-TTS (demo server)", + detect=qwen.detect, + setup_tui=qwen.run_tui, + modify_actions=qwen.modify_actions, + )) + REGISTRY.append(BackendInfo( + key="faster", + label="faster-qwen3-tts", + detect=faster.detect, + setup_tui=faster.run_tui, + modify_actions=faster.modify_actions, + )) + for info in REGISTRY: + _BY_KEY[info.key] = info + + +def get(key: str) -> Optional[BackendInfo]: + """Return the registry entry for KEY, or None.""" + _build_registry() + return _BY_KEY.get(key) + + +def detect_all() -> List[BackendStatus]: + """Detect every registered backend's status, in registry order.""" + _build_registry() + return [info.detect() for info in REGISTRY] + + +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