discstation 0.1.37 → 0.1.38
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/arduino/DiscStation/DiscStation.ino +21 -12
- package/install.sh +4 -0
- package/package.json +1 -1
- package/src/discstation.py +38 -22
|
@@ -221,6 +221,8 @@ String discLine = "Disc: none";
|
|
|
221
221
|
String line1 = "Status: READY";
|
|
222
222
|
String line2 = "";
|
|
223
223
|
String line3 = "";
|
|
224
|
+
bool trayOpen = false; // host says the tray is open (eject wait / STANDBY:Tray open) - EJECT button then closes it
|
|
225
|
+
bool waitingIsTray = false; // the WAITING screen is the eject "close tray" wait, not the pre-burn confirm
|
|
224
226
|
String waitingLine1 = "";
|
|
225
227
|
String waitingLine2 = "";
|
|
226
228
|
int progressPercent = -1;
|
|
@@ -456,7 +458,7 @@ void drawWaiting() {
|
|
|
456
458
|
printUpper(waitingLine2);
|
|
457
459
|
|
|
458
460
|
display.setCursor(2, 49);
|
|
459
|
-
display.print("SHORT // GO");
|
|
461
|
+
display.print(waitingIsTray ? "EJECT BTN // CLOSE" : "SHORT // GO");
|
|
460
462
|
|
|
461
463
|
display.display();
|
|
462
464
|
}
|
|
@@ -774,6 +776,7 @@ void parseMessage(String msg) {
|
|
|
774
776
|
Out.println(msg);
|
|
775
777
|
|
|
776
778
|
if (msg.startsWith("HOME:")) {
|
|
779
|
+
trayOpen = false;
|
|
777
780
|
drawHome();
|
|
778
781
|
|
|
779
782
|
} else if (msg.startsWith("DISC:")) {
|
|
@@ -802,6 +805,7 @@ void parseMessage(String msg) {
|
|
|
802
805
|
if (uiState == UI_HOME) drawHome();
|
|
803
806
|
|
|
804
807
|
} else if (msg.startsWith("STANDBY:")) {
|
|
808
|
+
trayOpen = msg.substring(8) == "Tray open";
|
|
805
809
|
drawStandby();
|
|
806
810
|
|
|
807
811
|
} else if (msg.startsWith("TITLE:")) {
|
|
@@ -855,8 +859,13 @@ void parseMessage(String msg) {
|
|
|
855
859
|
} else if (msg.startsWith("WAITING:")) {
|
|
856
860
|
returnToHomeAt = 0;
|
|
857
861
|
String rest = msg.substring(8);
|
|
862
|
+
waitingIsTray = rest.indexOf("close tray") >= 0;
|
|
863
|
+
if (waitingIsTray) trayOpen = true;
|
|
858
864
|
int slash = rest.indexOf('/');
|
|
859
|
-
if (
|
|
865
|
+
if (waitingIsTray) {
|
|
866
|
+
waitingLine1 = "TRAY OPEN";
|
|
867
|
+
waitingLine2 = "";
|
|
868
|
+
} else if (slash >= 0) {
|
|
860
869
|
waitingLine1 = "Data: " + rest.substring(0, slash);
|
|
861
870
|
waitingLine1.trim();
|
|
862
871
|
waitingLine2 = "Disc: " + rest.substring(slash + 1);
|
|
@@ -989,14 +998,6 @@ void handleSelectPress(bool longPress) {
|
|
|
989
998
|
drawStatus();
|
|
990
999
|
}
|
|
991
1000
|
|
|
992
|
-
} else if (uiState == UI_STANDBY) {
|
|
993
|
-
Out.println("EJECT");
|
|
994
|
-
line1 = "Ejecting...";
|
|
995
|
-
line2 = "";
|
|
996
|
-
line3 = "";
|
|
997
|
-
returnToHomeAt = millis() + 5000;
|
|
998
|
-
drawStatus();
|
|
999
|
-
|
|
1000
1001
|
} else if (uiState == UI_STATUS && line1 == "Ejecting...") {
|
|
1001
1002
|
// Manual escape if stuck on eject screen with no app response
|
|
1002
1003
|
drawStandby();
|
|
@@ -1011,7 +1012,7 @@ void handleSelectPress(bool longPress) {
|
|
|
1011
1012
|
if (longPress) {
|
|
1012
1013
|
Out.println("CANCEL");
|
|
1013
1014
|
drawHome();
|
|
1014
|
-
} else {
|
|
1015
|
+
} else if (!waitingIsTray) { // tray close moved to the EJECT button
|
|
1015
1016
|
Out.println("CONFIRM");
|
|
1016
1017
|
line1 = "Confirmed!";
|
|
1017
1018
|
line2 = "";
|
|
@@ -1144,7 +1145,15 @@ void handleEncoderCCW() {
|
|
|
1144
1145
|
void handleEjectButton() {
|
|
1145
1146
|
if (wakeDisplay()) { lastInputTime = millis(); return; }
|
|
1146
1147
|
lastInputTime = millis();
|
|
1147
|
-
if (
|
|
1148
|
+
if (trayOpen && (uiState == UI_STANDBY || uiState == UI_HOME || (uiState == UI_WAITING && waitingIsTray))) {
|
|
1149
|
+
Out.println("CONFIRM"); // host closes the tray
|
|
1150
|
+
trayOpen = false;
|
|
1151
|
+
line1 = "Closing tray...";
|
|
1152
|
+
line2 = "";
|
|
1153
|
+
line3 = "";
|
|
1154
|
+
returnToHomeAt = millis() + 5000;
|
|
1155
|
+
drawStatus();
|
|
1156
|
+
} else if (uiState == UI_HOME || uiState == UI_STANDBY || uiState == UI_DISCONNECTED || uiState == UI_PLAY) {
|
|
1148
1157
|
Out.println("EJECT");
|
|
1149
1158
|
line1 = "Ejecting...";
|
|
1150
1159
|
line2 = "";
|
package/install.sh
CHANGED
|
@@ -52,6 +52,10 @@ if [[ ! -f "$CONFIG_DIR/server.crt" || ! -f "$CONFIG_DIR/server.key" ]]; then
|
|
|
52
52
|
fi
|
|
53
53
|
|
|
54
54
|
sudo usermod -aG dialout,cdrom "$USER" || true
|
|
55
|
+
# Don't let the kernel re-close an ejected tray whenever some process (a desktop
|
|
56
|
+
# volume monitor, a disc probe) opens the drive.
|
|
57
|
+
echo 'dev.cdrom.autoclose = 0' | sudo tee /etc/sysctl.d/90-discstation.conf >/dev/null \
|
|
58
|
+
&& sudo sysctl -q -w dev.cdrom.autoclose=0 || true
|
|
55
59
|
mkdir -p "$HOME/.config/systemd/user"
|
|
56
60
|
cp "$ROOT_DIR/systemd/discstation.service" "$HOME/.config/systemd/user/discstation.service"
|
|
57
61
|
systemctl --user daemon-reload
|
package/package.json
CHANGED
package/src/discstation.py
CHANGED
|
@@ -1002,30 +1002,41 @@ def eject_disc(ser, device):
|
|
|
1002
1002
|
safe_send(ser, "ERROR:Eject failed")
|
|
1003
1003
|
safe_send(ser, "STANDBY:Insert disc")
|
|
1004
1004
|
return ok
|
|
1005
|
-
|
|
1005
|
+
# The kernel closes the tray again (dev.cdrom.autoclose) when anything opens
|
|
1006
|
+
# the drive while it's out, and every disc probe (blkid/lsdvd/wodim/mediainfo,
|
|
1007
|
+
# driven by the background poll and by the web/phone /disc-info requests)
|
|
1008
|
+
# does exactly that. So: mark the tray open BEFORE the eject command runs (new
|
|
1009
|
+
# probes now bail out) and hold _detect_lock so any probe already in flight
|
|
1010
|
+
# finishes first - otherwise a probe landing inside the few seconds the eject
|
|
1011
|
+
# takes shuts the tray right after it opens.
|
|
1012
|
+
with _detect_lock:
|
|
1013
|
+
_tray_open = True
|
|
1014
|
+
_tray_open_since = time.monotonic()
|
|
1015
|
+
subprocess.run(["sync"], timeout=5)
|
|
1006
1016
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1017
|
+
subprocess.run(["sg_raw", device, "1e", "00", "00", "00", "00", "00"],
|
|
1018
|
+
timeout=5, capture_output=True)
|
|
1009
1019
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
if ok:
|
|
1013
|
-
break
|
|
1014
|
-
try:
|
|
1015
|
-
r = subprocess.run(cmd, timeout=10, capture_output=True)
|
|
1016
|
-
ok = r.returncode == 0
|
|
1020
|
+
ok = False
|
|
1021
|
+
for cmd in (["eject", device], ["sg_raw", device, "1b", "00", "00", "00", "02", "00"]):
|
|
1017
1022
|
if ok:
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1023
|
+
break
|
|
1024
|
+
try:
|
|
1025
|
+
r = subprocess.run(cmd, timeout=10, capture_output=True)
|
|
1026
|
+
ok = r.returncode == 0
|
|
1027
|
+
if ok:
|
|
1028
|
+
print(f"{cmd[0]} eject ok")
|
|
1029
|
+
else:
|
|
1030
|
+
err = (r.stderr or r.stdout or b"failed").decode(errors="ignore").strip()[:40]
|
|
1031
|
+
print(f"{cmd[0]} eject failed: {err}")
|
|
1032
|
+
except Exception as e:
|
|
1033
|
+
print(f"{cmd[0]} eject error: {e}")
|
|
1034
|
+
if not ok:
|
|
1035
|
+
_tray_open = False
|
|
1024
1036
|
|
|
1025
1037
|
if ok:
|
|
1026
|
-
_tray_open = True
|
|
1027
1038
|
_tray_open_since = time.monotonic()
|
|
1028
|
-
safe_send(ser, "WAITING:Press
|
|
1039
|
+
safe_send(ser, "WAITING:Press EJECT/to close tray")
|
|
1029
1040
|
# WAITING: isn't in _record_web_status()'s prefix whitelist, so the
|
|
1030
1041
|
# send above never reaches the web page - _tray_open has to be
|
|
1031
1042
|
# published explicitly here, same as DISC: gets its own dedicated
|
|
@@ -1064,7 +1075,7 @@ def eject_disc(ser, device):
|
|
|
1064
1075
|
continue
|
|
1065
1076
|
if line == "PONG":
|
|
1066
1077
|
continue
|
|
1067
|
-
if line
|
|
1078
|
+
if line in ("CONFIRM", "EJECT"): # EJECT button doubles as close while the tray is out
|
|
1068
1079
|
print("Closing tray...")
|
|
1069
1080
|
safe_send(ser, "STATUS:Closing tray...")
|
|
1070
1081
|
for close_cmd in (
|
|
@@ -1265,7 +1276,7 @@ def _refresh_udev(device):
|
|
|
1265
1276
|
def udev_cdrom_properties(device, refresh=False):
|
|
1266
1277
|
if discstation_host.system_name() != "linux":
|
|
1267
1278
|
return discstation_host.media_properties(device)
|
|
1268
|
-
overlay = _refresh_udev(device) if refresh else {}
|
|
1279
|
+
overlay = _refresh_udev(device) if refresh and not _tray_open else {}
|
|
1269
1280
|
properties = _udev_props_via_pyudev(device)
|
|
1270
1281
|
if properties is None:
|
|
1271
1282
|
# pyudev unavailable — fall back to parsing `udevadm info` output.
|
|
@@ -1306,7 +1317,7 @@ def _device_present(device):
|
|
|
1306
1317
|
|
|
1307
1318
|
|
|
1308
1319
|
def is_blank_disc(device):
|
|
1309
|
-
if not device:
|
|
1320
|
+
if not device or _tray_open: # probing an open tray re-closes it
|
|
1310
1321
|
return False
|
|
1311
1322
|
if not _device_present(device):
|
|
1312
1323
|
return False
|
|
@@ -1357,7 +1368,7 @@ def is_blank_disc(device):
|
|
|
1357
1368
|
|
|
1358
1369
|
|
|
1359
1370
|
def is_rewritable_disc(device):
|
|
1360
|
-
if not device:
|
|
1371
|
+
if not device or _tray_open: # probing an open tray re-closes it
|
|
1361
1372
|
return False
|
|
1362
1373
|
"""Return whether the inserted medium can be overwritten."""
|
|
1363
1374
|
if not _device_present(device):
|
|
@@ -1714,6 +1725,8 @@ def _maybe_reset_stuck_drive(device, failed_probes):
|
|
|
1714
1725
|
|
|
1715
1726
|
|
|
1716
1727
|
def _detect_disc_locked(device, settle, budget):
|
|
1728
|
+
if _tray_open: # an eject started while we waited for the lock
|
|
1729
|
+
return _disc_info(False, "none")
|
|
1717
1730
|
deadline = time.monotonic() + (DISC_DETECT_BUDGET if budget is None else budget)
|
|
1718
1731
|
props = udev_cdrom_properties(device, refresh=True)
|
|
1719
1732
|
|
|
@@ -4138,6 +4151,9 @@ def station_loop(ser):
|
|
|
4138
4151
|
print(f"Menu: {line.split(':', 1)[1]}")
|
|
4139
4152
|
continue
|
|
4140
4153
|
|
|
4154
|
+
if line == "EJECT" and _tray_open:
|
|
4155
|
+
line = "CONFIRM" # EJECT is a toggle: with the tray already out, close it
|
|
4156
|
+
|
|
4141
4157
|
if line == "EJECT":
|
|
4142
4158
|
try:
|
|
4143
4159
|
device = discstation_burn.disc_device()
|