aboutsummaryrefslogtreecommitdiff
path: root/app/backends/audiocpp/configsync.py
blob: bce8142f0f8ac7d5814092a4b6c8b84f05c4594e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
"""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 _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 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