discstation 0.1.2 → 0.1.6
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 +22 -2
- package/src/discstation_host.py +19 -5
package/package.json
CHANGED
package/src/discstation.py
CHANGED
|
@@ -918,6 +918,8 @@ _tray_open_since = 0.0 # time.monotonic() of the last OLED-initiated eject
|
|
|
918
918
|
|
|
919
919
|
|
|
920
920
|
def _tray_closed_with_disc(device):
|
|
921
|
+
if not device:
|
|
922
|
+
return False
|
|
921
923
|
global _tray_open
|
|
922
924
|
if not Path(device).exists():
|
|
923
925
|
return False
|
|
@@ -932,6 +934,8 @@ def _tray_closed_with_disc(device):
|
|
|
932
934
|
|
|
933
935
|
|
|
934
936
|
def disc_present(device):
|
|
937
|
+
if not device:
|
|
938
|
+
return False
|
|
935
939
|
if _tray_closed_with_disc(device):
|
|
936
940
|
return True
|
|
937
941
|
if _tray_open:
|
|
@@ -968,6 +972,8 @@ def disc_present(device):
|
|
|
968
972
|
|
|
969
973
|
|
|
970
974
|
def is_blank_disc(device):
|
|
975
|
+
if not device:
|
|
976
|
+
return False
|
|
971
977
|
if not Path(device).exists():
|
|
972
978
|
return False
|
|
973
979
|
|
|
@@ -1017,6 +1023,8 @@ def is_blank_disc(device):
|
|
|
1017
1023
|
|
|
1018
1024
|
|
|
1019
1025
|
def is_rewritable_disc(device):
|
|
1026
|
+
if not device:
|
|
1027
|
+
return False
|
|
1020
1028
|
"""Return whether the inserted medium can be overwritten."""
|
|
1021
1029
|
if not Path(device).exists():
|
|
1022
1030
|
return False
|
|
@@ -1100,6 +1108,8 @@ def drive_status(device):
|
|
|
1100
1108
|
Returns 'disc' | 'no_disc' | 'open' | 'loading' | 'unknown'. Single ioctl on
|
|
1101
1109
|
an O_NONBLOCK fd — does not consult the (stale on this USB bridge) udev db and
|
|
1102
1110
|
does not disturb the tray. Never raises."""
|
|
1111
|
+
if not device:
|
|
1112
|
+
return "unknown"
|
|
1103
1113
|
try:
|
|
1104
1114
|
fd = os.open(device, os.O_RDONLY | os.O_NONBLOCK)
|
|
1105
1115
|
except OSError:
|
|
@@ -1118,6 +1128,8 @@ def drive_status(device):
|
|
|
1118
1128
|
|
|
1119
1129
|
def _media_quick_state(device, props):
|
|
1120
1130
|
"""Fast, cheap read of whether a disc is loaded. No long probes."""
|
|
1131
|
+
if not device:
|
|
1132
|
+
return "empty"
|
|
1121
1133
|
if _tray_open:
|
|
1122
1134
|
return "empty" # deliberate OLED eject — do not poke the drive
|
|
1123
1135
|
st = drive_status(device)
|
|
@@ -1402,6 +1414,8 @@ def detect_disc(device, settle=True, budget=None, force=False):
|
|
|
1402
1414
|
disc_status_line / disc_title / menu_items_for_disc in one refresh burst do a
|
|
1403
1415
|
single probe. `settle=False` skips the post-insert wait (used by the web
|
|
1404
1416
|
endpoint, which can just poll again)."""
|
|
1417
|
+
if not device:
|
|
1418
|
+
return _disc_info(False, "none")
|
|
1405
1419
|
if _tray_open:
|
|
1406
1420
|
# Tray was deliberately ejected from the OLED — report "no disc" without
|
|
1407
1421
|
# touching the drive (cdrom_id / wodim / open() would re-close the tray).
|
|
@@ -3654,7 +3668,10 @@ def rip_audio_cd(ser, device, artist_hint=None, album_hint=None):
|
|
|
3654
3668
|
def station_loop(ser, url, artist_hint=None, album_hint=None):
|
|
3655
3669
|
global _last_burn_result, _last_burn_result_time, _tray_open, _tray_open_since
|
|
3656
3670
|
discstation_burn.cleanup_old_jobs()
|
|
3657
|
-
|
|
3671
|
+
try:
|
|
3672
|
+
device = discstation_burn.disc_device()
|
|
3673
|
+
except Exception:
|
|
3674
|
+
device = None # empty drive (e.g. macOS after an eject) — keep the loop alive
|
|
3658
3675
|
|
|
3659
3676
|
for _ in range(50):
|
|
3660
3677
|
line = read_serial_line(ser, timeout=0.2)
|
|
@@ -3708,7 +3725,10 @@ def station_loop(ser, url, artist_hint=None, album_hint=None):
|
|
|
3708
3725
|
_disc_poll_start = 0
|
|
3709
3726
|
|
|
3710
3727
|
def _poll_disc():
|
|
3711
|
-
|
|
3728
|
+
try:
|
|
3729
|
+
current_device = discstation_burn.disc_device()
|
|
3730
|
+
except Exception:
|
|
3731
|
+
return "Disc: none", None # no drive/media visible right now
|
|
3712
3732
|
return disc_status_line(current_device), current_device
|
|
3713
3733
|
|
|
3714
3734
|
def apply_disc_line(new_line):
|
package/src/discstation_host.py
CHANGED
|
@@ -96,15 +96,25 @@ def _mac_optical_device():
|
|
|
96
96
|
return f"/dev/{match.group(1)}" if match else None
|
|
97
97
|
|
|
98
98
|
|
|
99
|
+
_last_disc_device = None
|
|
100
|
+
|
|
101
|
+
|
|
99
102
|
def disc_device():
|
|
103
|
+
global _last_disc_device
|
|
100
104
|
system = system_name()
|
|
101
105
|
override = os.environ.get("DISC_DEVICE") or os.environ.get("DVD_DEVICE")
|
|
102
106
|
if system == "darwin":
|
|
103
107
|
detected = _mac_optical_device()
|
|
104
108
|
if detected:
|
|
109
|
+
_last_disc_device = detected
|
|
105
110
|
return detected
|
|
106
|
-
if override
|
|
111
|
+
if override:
|
|
107
112
|
return override
|
|
113
|
+
# macOS: the /dev/diskN node only exists while media is loaded. Once a
|
|
114
|
+
# disc is ejected there is nothing to detect — keep returning the last
|
|
115
|
+
# known node so the caller can still probe it (it just reports no media).
|
|
116
|
+
if _last_disc_device:
|
|
117
|
+
return _last_disc_device
|
|
108
118
|
elif override:
|
|
109
119
|
return override
|
|
110
120
|
|
|
@@ -364,10 +374,14 @@ def eject_device(device, close=False):
|
|
|
364
374
|
command.append("-t")
|
|
365
375
|
command.append(device)
|
|
366
376
|
elif system == "darwin":
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
if
|
|
370
|
-
commands
|
|
377
|
+
# `drutil tray open` is a no-op on slot-load drives; `drutil eject`
|
|
378
|
+
# works on both. `diskutil eject` is the fallback for a mounted disc.
|
|
379
|
+
if close:
|
|
380
|
+
commands = [["/usr/bin/drutil", "tray", "close"]]
|
|
381
|
+
else:
|
|
382
|
+
commands = [["/usr/bin/drutil", "eject"]]
|
|
383
|
+
if device:
|
|
384
|
+
commands.append(["/usr/sbin/diskutil", "eject", device])
|
|
371
385
|
for command in commands:
|
|
372
386
|
try:
|
|
373
387
|
if subprocess.run(command, capture_output=True, text=True, timeout=10).returncode == 0:
|