aboutsummaryrefslogtreecommitdiff
path: root/backends/__init__.py
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-08-23 23:48:25 -0400
committerhistoria <historiavg@proton.me>2026-08-23 23:48:25 -0400
commit5bfbdcb5765fd4eb57d13c67169bb3c2706ead75 (patch)
treea07a27976f56a449e8c33641161553aa0989f5c2 /backends/__init__.py
parent07f7b351f2956b6c92761877c9a4314bcede3b6e (diff)
downloadtts-audiobook-generator-5bfbdcb5765fd4eb57d13c67169bb3c2706ead75.tar.gz
feat: audiobook.py tui: convert, modify, or install backends
Diffstat (limited to 'backends/__init__.py')
-rw-r--r--backends/__init__.py114
1 files changed, 114 insertions, 0 deletions
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/<name>.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()