discstation 0.1.13 → 0.1.15
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 +20 -2
- package/src/discstation_host.py +18 -6
package/package.json
CHANGED
package/src/discstation.py
CHANGED
|
@@ -571,6 +571,13 @@ def _record_web_status(msg):
|
|
|
571
571
|
elif msg.startswith("CANCELLED:"):
|
|
572
572
|
_web_status = msg[10:].strip() or "CANCELLED"
|
|
573
573
|
_web_progress_active = False
|
|
574
|
+
elif msg.startswith(("STANDBY:", "HOME:")):
|
|
575
|
+
# idle again (tray open, insert disc, back to the menu) — clear any
|
|
576
|
+
# lingering "Ejecting..." / progress state on the web UI.
|
|
577
|
+
text = msg.split(":", 1)[1].strip()
|
|
578
|
+
_web_status = "READY" if text in ("", "DiscStation", "Select mode", "Starting...") else text
|
|
579
|
+
_web_progress = -1
|
|
580
|
+
_web_progress_active = False
|
|
574
581
|
else:
|
|
575
582
|
return
|
|
576
583
|
_sse_publish(_status_snapshot())
|
|
@@ -2745,6 +2752,13 @@ def burn_data_flow(ser):
|
|
|
2745
2752
|
if not files_to_burn:
|
|
2746
2753
|
raise RuntimeError("No files to burn")
|
|
2747
2754
|
|
|
2755
|
+
# A single .iso (loose, a local path, or an upload folder holding just
|
|
2756
|
+
# the .iso) is written verbatim as a bootable image, not repackaged.
|
|
2757
|
+
if len(files_to_burn) == 1 and files_to_burn[0].is_dir():
|
|
2758
|
+
inner = [p for p in files_to_burn[0].iterdir() if p.is_file()]
|
|
2759
|
+
if len(inner) == 1 and inner[0].suffix.lower() == ".iso":
|
|
2760
|
+
files_to_burn = inner
|
|
2761
|
+
|
|
2748
2762
|
total_bytes = sum(f.stat().st_size for f in files_to_burn if f.is_file())
|
|
2749
2763
|
for d in files_to_burn:
|
|
2750
2764
|
if d.is_dir():
|
|
@@ -3829,9 +3843,13 @@ def station_loop(ser, url, artist_hint=None, album_hint=None):
|
|
|
3829
3843
|
apply_disc_line("Disc: reading...")
|
|
3830
3844
|
elif st == "disc":
|
|
3831
3845
|
_tray_open = False
|
|
3846
|
+
transient_words = ["none", "checking", "reading", "tray open"]
|
|
3847
|
+
if discstation_host.system_name() == "darwin":
|
|
3848
|
+
# macOS slot drives take a few seconds to mount; "unknown"
|
|
3849
|
+
# is a not-ready read, not a settled answer — keep re-polling.
|
|
3850
|
+
transient_words.append("unknown")
|
|
3832
3851
|
have_line = last_disc_line and not any(
|
|
3833
|
-
w in last_disc_line.lower()
|
|
3834
|
-
for w in ("none", "checking", "reading", "tray open"))
|
|
3852
|
+
w in last_disc_line.lower() for w in transient_words)
|
|
3835
3853
|
if not have_line and _disc_poll_future is None:
|
|
3836
3854
|
_disc_poll_start = now
|
|
3837
3855
|
last_disc_poll = now
|
package/src/discstation_host.py
CHANGED
|
@@ -295,23 +295,30 @@ def build_data_image(source_paths, output_path, label, video=False):
|
|
|
295
295
|
# xorriso's mkisofs emulation is the same lineage as Linux's
|
|
296
296
|
# genisoimage/growisofs; -dvd-video gives a set-top-compatible
|
|
297
297
|
# VIDEO_TS layout (the arg is the dir *containing* VIDEO_TS).
|
|
298
|
+
# xorriso's mkisofs emulation does NOT accept `-udf`; -dvd-video alone
|
|
299
|
+
# gives a DVD-Video-compliant ISO9660 layout set-top players read.
|
|
298
300
|
command = [tool("xorriso"), "-as", "mkisofs", "-V", label, "-o", str(output_path)]
|
|
299
301
|
if video:
|
|
300
|
-
command += ["-dvd-video",
|
|
302
|
+
command += ["-dvd-video", str(source_paths[0])]
|
|
301
303
|
else:
|
|
302
|
-
command += ["-iso-level", "3", "-J", "-R",
|
|
303
|
-
*[str(path) for path in source_paths]]
|
|
304
|
+
command += ["-iso-level", "3", "-J", "-R", *[str(path) for path in source_paths]]
|
|
304
305
|
else:
|
|
305
306
|
raise RuntimeError("Image building is only used by non-Linux optical backends")
|
|
306
|
-
subprocess.run(command,
|
|
307
|
+
result = subprocess.run(command, capture_output=True, text=True)
|
|
308
|
+
if result.returncode != 0:
|
|
309
|
+
lines = [ln.strip() for ln in (result.stderr or result.stdout).splitlines() if ln.strip()]
|
|
310
|
+
tail = next((ln for ln in reversed(lines) if "FAILURE" in ln.upper()),
|
|
311
|
+
lines[-1] if lines else f"exit {result.returncode}")
|
|
312
|
+
raise RuntimeError(f"xorriso: {tail[:200]}")
|
|
307
313
|
return output_path
|
|
308
314
|
|
|
309
315
|
|
|
310
316
|
def iso_burn_command(device, image_path):
|
|
311
317
|
system = system_name()
|
|
312
318
|
if system == "darwin":
|
|
313
|
-
# -puppetstrings emits machine-readable PERCENT: / MESSAGE: lines
|
|
314
|
-
|
|
319
|
+
# -puppetstrings emits machine-readable PERCENT: / MESSAGE: lines;
|
|
320
|
+
# -nosynth writes the image verbatim so a bootable ISO stays bootable.
|
|
321
|
+
return [tool("hdiutil"), "burn", "-puppetstrings", "-nosynth", str(image_path)]
|
|
315
322
|
if system == "windows":
|
|
316
323
|
return [tool("isoburn.exe"), "/Q", device, str(image_path)]
|
|
317
324
|
raise RuntimeError("ISO command requested on Linux; use growisofs backend")
|
|
@@ -424,6 +431,7 @@ def eject_device(device, close=False):
|
|
|
424
431
|
command.append("-t")
|
|
425
432
|
command.append(device)
|
|
426
433
|
elif system == "darwin":
|
|
434
|
+
global _last_disc_device
|
|
427
435
|
# `drutil tray open` is a no-op on slot-load drives; `drutil eject`
|
|
428
436
|
# works on both. `diskutil eject` is the fallback for a mounted disc.
|
|
429
437
|
if close:
|
|
@@ -435,6 +443,10 @@ def eject_device(device, close=False):
|
|
|
435
443
|
for command in commands:
|
|
436
444
|
try:
|
|
437
445
|
if subprocess.run(command, capture_output=True, text=True, timeout=10).returncode == 0:
|
|
446
|
+
if not close:
|
|
447
|
+
# the /dev/diskN node is gone now — don't let disc_device()
|
|
448
|
+
# hand back this stale node when the next disc goes in.
|
|
449
|
+
_last_disc_device = None
|
|
438
450
|
return True
|
|
439
451
|
except (OSError, subprocess.TimeoutExpired):
|
|
440
452
|
pass
|