discstation 0.1.17 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/discstation.py +13 -3
- package/src/discstation_burn.py +21 -9
- package/src/static/app.js +2 -1
- package/src/static/index.html +2 -2
package/package.json
CHANGED
package/src/discstation.py
CHANGED
|
@@ -53,6 +53,8 @@ _last_upload_label = None
|
|
|
53
53
|
_web_status = "READY"
|
|
54
54
|
_web_progress = -1
|
|
55
55
|
_web_progress_active = False
|
|
56
|
+
_operation_active = False # a burn/rip/play flow is holding the drive
|
|
57
|
+
_last_disc_info = {"disc_present": False, "capacity_bytes": 0, "capacity_gb": 0, "type": "none"}
|
|
56
58
|
_active_ser = None
|
|
57
59
|
STATIC_DIR = Path(__file__).resolve().parent / "static"
|
|
58
60
|
|
|
@@ -146,6 +148,10 @@ class _WebHandler(http.server.BaseHTTPRequestHandler):
|
|
|
146
148
|
self._respond(200, f'{len(files)} file(s) uploaded ({size_str}). Select BURN DATA on remote.')
|
|
147
149
|
|
|
148
150
|
def _serve_disc_info(self):
|
|
151
|
+
if _operation_active:
|
|
152
|
+
# a burn/rip/play holds the drive — don't probe it, serve last-known.
|
|
153
|
+
self._respond(200, json.dumps({**_last_disc_info, "busy": True}), "application/json")
|
|
154
|
+
return
|
|
149
155
|
info = {"disc_present": False, "capacity_bytes": 0, "capacity_gb": 0, "type": "none"}
|
|
150
156
|
try:
|
|
151
157
|
device = discstation_burn.disc_device()
|
|
@@ -163,6 +169,7 @@ class _WebHandler(http.server.BaseHTTPRequestHandler):
|
|
|
163
169
|
info["label"] = di.label
|
|
164
170
|
except Exception as e:
|
|
165
171
|
print(f"Disc info error: {e}")
|
|
172
|
+
_last_disc_info.update(info)
|
|
166
173
|
self._respond(200, json.dumps(info), "application/json")
|
|
167
174
|
|
|
168
175
|
def _serve_sse(self):
|
|
@@ -212,7 +219,7 @@ class _WebHandler(http.server.BaseHTTPRequestHandler):
|
|
|
212
219
|
def _serve_sw(self):
|
|
213
220
|
sw = '''self.addEventListener('install', e => {
|
|
214
221
|
self.skipWaiting();
|
|
215
|
-
caches.open('discstation-
|
|
222
|
+
caches.open('discstation-v8').then(c => c.addAll(['/','/static/style.css?v=8','/static/app.js?v=8']));
|
|
216
223
|
});
|
|
217
224
|
self.addEventListener('activate', e => e.waitUntil(clients.claim()));
|
|
218
225
|
self.addEventListener('fetch', e => {
|
|
@@ -221,7 +228,7 @@ self.addEventListener('fetch', e => {
|
|
|
221
228
|
if (path === '/' || path.startsWith('/static/')) {
|
|
222
229
|
e.respondWith(fetch(e.request).then(r => {
|
|
223
230
|
const copy = r.clone();
|
|
224
|
-
caches.open('discstation-
|
|
231
|
+
caches.open('discstation-v8').then(c => c.put(e.request, copy));
|
|
225
232
|
return r;
|
|
226
233
|
}).catch(() => caches.match(e.request)));
|
|
227
234
|
} else {
|
|
@@ -3731,7 +3738,7 @@ def rip_audio_cd(ser, device, artist_hint=None, album_hint=None):
|
|
|
3731
3738
|
|
|
3732
3739
|
|
|
3733
3740
|
def station_loop(ser, url, artist_hint=None, album_hint=None):
|
|
3734
|
-
global _last_burn_result, _last_burn_result_time, _tray_open, _tray_open_since
|
|
3741
|
+
global _last_burn_result, _last_burn_result_time, _tray_open, _tray_open_since, _operation_active
|
|
3735
3742
|
discstation_burn.cleanup_old_jobs()
|
|
3736
3743
|
try:
|
|
3737
3744
|
device = discstation_burn.disc_device()
|
|
@@ -3934,6 +3941,7 @@ def station_loop(ser, url, artist_hint=None, album_hint=None):
|
|
|
3934
3941
|
# The user picked a mode — they want to act on a disc, so the drive is
|
|
3935
3942
|
# fair game again even if it was ejected from the OLED earlier.
|
|
3936
3943
|
_tray_open = False
|
|
3944
|
+
_operation_active = True # stop /disc-info probing the drive during the flow
|
|
3937
3945
|
try:
|
|
3938
3946
|
if mode == "BURN":
|
|
3939
3947
|
burn_flow(ser, url)
|
|
@@ -3964,6 +3972,8 @@ def station_loop(ser, url, artist_hint=None, album_hint=None):
|
|
|
3964
3972
|
_last_burn_result = f"ERROR: {e}"
|
|
3965
3973
|
print(f"Error in {mode}: {e}")
|
|
3966
3974
|
time.sleep(4)
|
|
3975
|
+
finally:
|
|
3976
|
+
_operation_active = False
|
|
3967
3977
|
|
|
3968
3978
|
_last_burn_result_time = time.time()
|
|
3969
3979
|
refresh_main_menu(ser)
|
package/src/discstation_burn.py
CHANGED
|
@@ -1274,22 +1274,31 @@ def _run_growisofs(ser, growisofs_cmd, log_path, device=None):
|
|
|
1274
1274
|
|
|
1275
1275
|
def _run_hdiutil_burn(ser, image_path, device=None):
|
|
1276
1276
|
"""Burn a pre-built ISO on macOS via `hdiutil burn -puppetstrings`, streaming
|
|
1277
|
-
its PERCENT: lines to the ESP32."""
|
|
1277
|
+
its PERCENT: lines to the ESP32 for both the write and verify passes."""
|
|
1278
1278
|
send(ser, "STATUS:Burning image...")
|
|
1279
1279
|
send(ser, "PROGRESS:0%")
|
|
1280
1280
|
cmd = discstation_host.iso_burn_command(device or disc_device(), image_path)
|
|
1281
|
+
if discstation_host.system_name() == "darwin":
|
|
1282
|
+
# hdiutil block-buffers stdout to a pipe -> no progress until it exits.
|
|
1283
|
+
# Run it under a pty (script relays the child's exit status verbatim).
|
|
1284
|
+
cmd = ["/usr/bin/script", "-q", "/dev/null", *cmd]
|
|
1281
1285
|
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
|
|
1282
1286
|
out_lines = []
|
|
1283
1287
|
last_pct = -1
|
|
1288
|
+
phase = "burn"
|
|
1284
1289
|
try:
|
|
1285
1290
|
for line in iter_proc_or_cancel(proc, ser):
|
|
1286
1291
|
out_lines.append(line)
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1292
|
+
low = line.lower()
|
|
1293
|
+
m = re.search(r"PERCENT:(-?[\d.]+)", line)
|
|
1294
|
+
pct = int(float(m.group(1))) if m else None
|
|
1295
|
+
if phase == "burn" and ("verif" in low or (pct is not None and last_pct > 90 and pct < 5)):
|
|
1296
|
+
phase = "verify"
|
|
1297
|
+
last_pct = -1
|
|
1298
|
+
send(ser, "STATUS:Verifying...")
|
|
1299
|
+
if pct is not None and 0 <= pct <= 100 and pct != last_pct:
|
|
1300
|
+
last_pct = pct
|
|
1301
|
+
send(ser, f"PROGRESS:{min(pct, 99)}%")
|
|
1293
1302
|
except (KeyboardInterrupt, SystemExit):
|
|
1294
1303
|
stop_process(proc)
|
|
1295
1304
|
raise
|
|
@@ -1300,8 +1309,11 @@ def _run_hdiutil_burn(ser, image_path, device=None):
|
|
|
1300
1309
|
safe_send(ser, "PROGRESS:100%")
|
|
1301
1310
|
try:
|
|
1302
1311
|
discstation_host.eject_device(device or disc_device())
|
|
1303
|
-
except Exception
|
|
1304
|
-
|
|
1312
|
+
except Exception:
|
|
1313
|
+
try:
|
|
1314
|
+
discstation_host.eject_device(None) # device-less `drutil eject`
|
|
1315
|
+
except Exception as e:
|
|
1316
|
+
print(f"Disc eject skipped: {e}")
|
|
1305
1317
|
|
|
1306
1318
|
|
|
1307
1319
|
def burn(ser, dvd_dir, disc_label, speed=None, is_dual_layer=False):
|
package/src/static/app.js
CHANGED
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
try {
|
|
78
78
|
const response = await fetch("/disc-info", { cache: "no-store" });
|
|
79
79
|
const info = await response.json();
|
|
80
|
+
if (info.busy) return; // burn/rip in progress — keep current
|
|
80
81
|
state.discBytes = Number(info.capacity_bytes || 0);
|
|
81
82
|
state.discType = info.type || "none";
|
|
82
83
|
renderSelection();
|
|
@@ -281,7 +282,7 @@
|
|
|
281
282
|
setInterval(loadDiscInfo, 15000);
|
|
282
283
|
}
|
|
283
284
|
|
|
284
|
-
if ("serviceWorker" in navigator) navigator.serviceWorker.register("/sw.js?v=
|
|
285
|
+
if ("serviceWorker" in navigator) navigator.serviceWorker.register("/sw.js?v=8").catch(() => {});
|
|
285
286
|
window.addEventListener("beforeinstallprompt", (event) => {
|
|
286
287
|
event.preventDefault();
|
|
287
288
|
const button = document.createElement("button");
|
package/src/static/index.html
CHANGED
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
</footer>
|
|
106
106
|
</div>
|
|
107
107
|
<div id="install-slot"></div>
|
|
108
|
-
<script>if ("serviceWorker" in navigator) navigator.serviceWorker.register("/sw.js?v=
|
|
109
|
-
<script src="/static/app.js?v=
|
|
108
|
+
<script>if ("serviceWorker" in navigator) navigator.serviceWorker.register("/sw.js?v=8").catch(() => {});</script>
|
|
109
|
+
<script src="/static/app.js?v=8" defer></script>
|
|
110
110
|
</body>
|
|
111
111
|
</html>
|