diff options
| author | historia <historiavg@proton.me> | 2026-08-24 06:04:38 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-08-24 06:04:38 -0400 |
| commit | 4db8ea7a63107297450819d227497ebbb121ff38 (patch) | |
| tree | 250b646195f98a4b4c42b04589c0ca8951d13fbb /app/backends | |
| parent | 4f3d16e9d532efd6a6bda283925efeb8c93b51c7 (diff) | |
| download | tts-audiobook-generator-4db8ea7a63107297450819d227497ebbb121ff38.tar.gz | |
fix: don't expect/use local config for remote servers
Diffstat (limited to 'app/backends')
| -rwxr-xr-x | app/backends/audiocpp.py | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/app/backends/audiocpp.py b/app/backends/audiocpp.py index 673d344..a8c5568 100755 --- a/app/backends/audiocpp.py +++ b/app/backends/audiocpp.py @@ -35,6 +35,7 @@ import re import subprocess import sys import urllib.parse +import urllib.request from pathlib import Path from typing import Callable, Dict, List, Optional, Set, Tuple @@ -1301,6 +1302,57 @@ def find_local_checkout() -> Optional[Path]: return None +def fetch_server_models(api_url: str) -> Optional[List[Dict[str, str]]]: + """List a running audiocpp_server's model entries via GET /v1/models. + + Returns ``[{id, family, task}, ...]`` — the same shape the converter's + client resolves at startup — or None when URL does not answer with a + valid document (wrong server, still starting, older audio.cpp). Used by + the hub to drive the convert menus against a remote server that has no + local server.json describing it. + """ + try: + with urllib.request.urlopen( + f"{api_url.rstrip('/')}/v1/models", timeout=10) as response: + payload = json.loads(response.read().decode("utf-8")) + except (OSError, ValueError): + # URLError/HTTPError/socket errors are OSErrors; a non-JSON body is + # a ValueError. Anything else means "not an audiocpp_server". + return None + entries = payload.get("data") if isinstance(payload, dict) else None + models: List[Dict[str, str]] = [] + for entry in entries or []: + if isinstance(entry, dict) and entry.get("id"): + models.append({ + "id": str(entry["id"]), + "family": str(entry.get("family") or ""), + "task": str(entry.get("task") or ""), + }) + return models + + +def fetch_server_voices(api_url: str, model_id: str) -> Optional[List[str]]: + """List a running audiocpp_server's voices for MODEL_ID. + + Queries ``GET /v1/audio/voices?model=<id>`` — the endpoint the converter + validates ``--voice`` against — and returns its voice-name list, or None + when the server cannot be queried. Lets the hub offer a remote server's + voices without reading its configuration locally. + """ + query = urllib.parse.urlencode({"model": model_id}) + try: + with urllib.request.urlopen( + f"{api_url.rstrip('/')}/v1/audio/voices?{query}", + timeout=10) as response: + payload = json.loads(response.read().decode("utf-8")) + except (OSError, ValueError): + return None + voices = payload.get("voices") if isinstance(payload, dict) else None + if not isinstance(voices, list): + return None + return [str(voice) for voice in voices] + + def find_audiocpp_server_bin(audiocpp_dir: Path) -> Optional[Path]: """Return the built audiocpp_server binary, or None when not built. |
