diff options
| author | historia <historiavg@proton.me> | 2026-08-26 02:25:55 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-08-26 02:25:55 -0400 |
| commit | 8b5c8697740ff415cf7f1d03c9fb5a8c8851d420 (patch) | |
| tree | 28c0323c54c896af5f89fb34b89a62e0fe0df291 /app/backends/audiocpp/configsync.py | |
| parent | acbd9ff2c91182d96c57ffb57bee6e9b3fcbcbd4 (diff) | |
| download | tts-audiobook-generator-8b5c8697740ff415cf7f1d03c9fb5a8c8851d420.tar.gz | |
refactor: audiocpp.py setup flow
Diffstat (limited to 'app/backends/audiocpp/configsync.py')
| -rw-r--r-- | app/backends/audiocpp/configsync.py | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/app/backends/audiocpp/configsync.py b/app/backends/audiocpp/configsync.py new file mode 100644 index 0000000..0a161c0 --- /dev/null +++ b/app/backends/audiocpp/configsync.py @@ -0,0 +1,163 @@ +"""Keep app/converter/config.py and server.json in sync with setup choices.""" + +import json +import re +import urllib.parse +from pathlib import Path +from typing import Optional + +from backends import common +from backends.common import CONFIG_PATH, url_with_port +from converter import config +from . import build +from .constants import FALLBACK_PORT + +def config_port() -> int: + """Return the port of AUDIOCPP_API_URL in app/converter/config.py.""" + try: + return urllib.parse.urlsplit(config.AUDIOCPP_API_URL).port or FALLBACK_PORT + except ValueError: + return FALLBACK_PORT + + +def update_config_api_url_port(port: int, config_path: Optional[Path] = None) -> bool: + """Rewrite the port inside AUDIOCPP_API_URL in app/converter/config.py. + + Reads the configured URL from the file (not from the imported module, + which a long hub session can leave behind), swaps its port for PORT, + and writes it back through ``common.update_config_value`` so the + imported module mirrors the change immediately. Returns True when the + file now holds the new URL. + """ + path = Path(config_path) if config_path is not None else CONFIG_PATH + try: + text = path.read_text(encoding="utf-8") + except OSError: + return False + match = re.search(r'(?m)^\s*AUDIOCPP_API_URL\s*=\s*"([^"]*)"', text) + if not match: + return False + return common.update_config_value("AUDIOCPP_API_URL", + url_with_port(match.group(1), port), + config_path=path) + + +def update_server_config_port(port: int) -> bool: + """Rewrite the 'port' in the audio.cpp checkout's server.json. + + Loads ``<checkout>/server.json``, sets its ``port`` to PORT, and + rewrites it with the same ``json.dump`` formatting the wizard uses. + Returns True when the file now carries PORT (a no-op when it already + does), and False when there is no checkout/server.json or the file + cannot be read or written. + """ + checkout = build.find_local_checkout() + if checkout is None: + return False + server_json = checkout / "server.json" + if not server_json.exists(): + return False + try: + data = json.loads(server_json.read_text(encoding="utf-8")) + except (OSError, ValueError): + return False + if not isinstance(data, dict): + return False + if data.get("port") == port: + return True + data["port"] = port + try: + with server_json.open("w", encoding="utf-8") as handle: + json.dump(data, handle, indent=2, ensure_ascii=False) + handle.write("\n") + except OSError: + return False + return True + + +def update_config_model_ids(model_id: str, + clone_model_id: Optional[str] = None, + config_path: Optional[Path] = None) -> bool: + """Rewrite AUDIOCPP_MODEL_ID (and AUDIOCPP_CLONE_MODEL_ID when given). + + Goes through ``common.update_config_value`` so the imported config + module mirrors the change immediately. Returns True when every named + key now holds its value in the file. + """ + path = Path(config_path) if config_path is not None else CONFIG_PATH + ok = common.update_config_value("AUDIOCPP_MODEL_ID", model_id, + config_path=path) + if clone_model_id is not None: + ok = common.update_config_value("AUDIOCPP_CLONE_MODEL_ID", + clone_model_id, + config_path=path) and ok + return ok + + +def _apply_port_sync(port: int, accepted: bool) -> None: + """Write the port into app/converter/config.py, or report when declined.""" + if accepted: + if not update_config_api_url_port(port): + print(f"[WARNING] Could not update {CONFIG_PATH}; edit " + "AUDIOCPP_API_URL by hand so audiobook.py uses the " + "new port") + else: + print("[WARNING] Left AUDIOCPP_API_URL unchanged; audiobook.py " + f"will still use port {config_port()}") + + +def _offer_config_model_id_sync(model_id: str, accepted: Optional[bool]) -> None: + """Point app/converter/config.py at a single hosted model entry. + + The converter requests the model id configured in AUDIOCPP_MODEL_ID, + and single-model servers use the same id for the clone entry, so both + ids are rewritten together. ACCEPTED is True/False (apply/skip the + rewrite) or None when no single-entry sync applies (nothing to do). + """ + if config.AUDIOCPP_MODEL_ID == model_id \ + and config.AUDIOCPP_CLONE_MODEL_ID == model_id: + return + if accepted is None: + return + if accepted: + if not update_config_model_ids(model_id, model_id): + print(f"[WARNING] Could not update {CONFIG_PATH}; edit " + "AUDIOCPP_MODEL_ID and AUDIOCPP_CLONE_MODEL_ID by hand so " + "audiobook.py uses this model") + else: + print("[WARNING] Left the model ids unchanged; audiobook.py will " + f"still request model '{config.AUDIOCPP_MODEL_ID}'") + + +def update_server_backend(backend: str) -> bool: + """Rewrite the 'backend' in the checkout's server.json, or True when none. + + Sets ``backend`` to BACKEND in ``<checkout>/server.json`` (same + ``json.dump`` formatting as the wizard). Returns True when the file now + carries BACKEND, when there is no server.json (nothing to sync), or when + it already does; False when the file exists but cannot be read/written. + """ + checkout = build.find_local_checkout() + if checkout is None: + return True + server_json = checkout / "server.json" + if not server_json.exists(): + return True + try: + data = json.loads(server_json.read_text(encoding="utf-8")) + except (OSError, ValueError): + return False + if not isinstance(data, dict): + return False + if data.get("backend") == backend: + return True + data["backend"] = backend + try: + with server_json.open("w", encoding="utf-8") as handle: + json.dump(data, handle, indent=2, ensure_ascii=False) + handle.write("\n") + except OSError: + return False + return True + + |
