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
|