discstation 0.1.6 → 0.1.8

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "discstation",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "DiscStation optical-media appliance host — one cross-OS installer",
5
5
  "bin": {
6
6
  "discstation-setup": "scripts/setup.mjs"
@@ -1046,6 +1046,9 @@ def is_rewritable_disc(device):
1046
1046
  "",
1047
1047
  )
1048
1048
  return "rw" in media_line
1049
+ # Non-Linux: the RW signal comes from the ID_CDROM_MEDIA_*_RW keys above,
1050
+ # which discstation_host.media_properties() tags from the drutil/diskutil
1051
+ # media type.
1049
1052
  return False
1050
1053
 
1051
1054
 
@@ -1110,6 +1113,8 @@ def drive_status(device):
1110
1113
  does not disturb the tray. Never raises."""
1111
1114
  if not device:
1112
1115
  return "unknown"
1116
+ if discstation_host.system_name() != "linux":
1117
+ return discstation_host.drive_status()
1113
1118
  try:
1114
1119
  fd = os.open(device, os.O_RDONLY | os.O_NONBLOCK)
1115
1120
  except OSError:
@@ -1246,6 +1251,12 @@ def _classify_disc(device, props, failed, deadline):
1246
1251
  print(f"Disc inspection failed: {e}")
1247
1252
  kind = kind or "data_disc"
1248
1253
  return _disc_info(True, kind, web_type=_web_type_for(kind, 0))
1254
+ if props.get("ID_CDROM_MEDIA_STATE") == "blank":
1255
+ try:
1256
+ cap = discstation_burn.disc_capacity_bytes(device) or 0
1257
+ except Exception:
1258
+ cap = 0
1259
+ return _disc_info(True, "blank", capacity_bytes=cap, web_type="BLANK")
1249
1260
  return _disc_info(True, "unknown", web_type="UNKNOWN", failed_probes=failed)
1250
1261
 
1251
1262
  # Fast path for a blank disc: cdrom_id reports this reliably in ~20ms, and
@@ -144,6 +144,38 @@ def disc_device():
144
144
  raise FileNotFoundError("No optical disc drive found; set DISC_DEVICE explicitly")
145
145
 
146
146
 
147
+ def drive_status():
148
+ """Non-Linux equivalent of the CDROM_DRIVE_STATUS ioctl.
149
+ Returns 'disc' | 'no_disc' | 'unknown'. macOS: parse `drutil status`."""
150
+ if system_name() != "darwin":
151
+ return "unknown"
152
+ try:
153
+ r = subprocess.run(["/usr/bin/drutil", "status"], capture_output=True,
154
+ text=True, check=False, timeout=4)
155
+ except (OSError, subprocess.TimeoutExpired):
156
+ return "unknown"
157
+ text = (r.stdout + r.stderr).lower()
158
+ if "no media" in text or "media is not present" in text:
159
+ return "no_disc"
160
+ if "name: /dev/disk" in text or "type: cd" in text or "type: dvd" in text \
161
+ or "type: bd" in text:
162
+ return "disc"
163
+ return "unknown"
164
+
165
+
166
+ def _tag_rewritable(props, media_type):
167
+ """Set the ID_CDROM_MEDIA_*_RW key that discstation.is_rewritable_disc reads,
168
+ from a lowercased media-type string (drutil 'Type:' or diskutil 'Optical
169
+ Media Type'). ponytail: substring match — same heuristic as the rest of this
170
+ module."""
171
+ if "cd-rw" in media_type or "cdrw" in media_type:
172
+ props["ID_CDROM_MEDIA_CD_RW"] = "1"
173
+ elif "dvd-rw" in media_type or "dvd-ram" in media_type:
174
+ props["ID_CDROM_MEDIA_DVD_RW"] = "1"
175
+ elif "dvd+rw" in media_type or "bd-re" in media_type:
176
+ props["ID_CDROM_MEDIA_DVD_PLUS_RW"] = "1"
177
+
178
+
147
179
  def _drutil_media_properties():
148
180
  result = subprocess.run(["/usr/bin/drutil", "status"], capture_output=True, text=True, check=False, timeout=3)
149
181
  text = result.stdout + result.stderr
@@ -151,12 +183,16 @@ def _drutil_media_properties():
151
183
  if result.returncode != 0 or "no media" in lowered:
152
184
  return {}
153
185
  props = {"ID_CDROM": "1", "ID_CDROM_MEDIA": "1"}
154
- if "space used:" in lowered and "00:00:00" in lowered.split("space used:", 1)[1][:24]:
186
+ blank = "space used:" in lowered and "00:00:00" in lowered.split("space used:", 1)[1][:24]
187
+ if blank:
155
188
  props["ID_CDROM_MEDIA_STATE"] = "blank"
156
- if "type: cd" in lowered:
157
- props["ID_CDROM_MEDIA_TYPE"] = "audio"
158
- elif "type: dvd" in lowered:
189
+ match = re.search(r"type:\s*([a-z0-9+\-]+)", lowered)
190
+ media_type = match.group(1) if match else ""
191
+ if media_type.startswith("dvd") or media_type.startswith("bd"):
159
192
  props["ID_CDROM_MEDIA_TYPE"] = "dvd"
193
+ elif media_type.startswith("cd") and not blank and "-r" not in media_type:
194
+ props["ID_CDROM_MEDIA_TYPE"] = "audio"
195
+ _tag_rewritable(props, media_type)
160
196
  return props
161
197
 
162
198
 
@@ -168,7 +204,7 @@ def media_properties(device):
168
204
  except subprocess.TimeoutExpired:
169
205
  return _drutil_media_properties()
170
206
  if disk.returncode != 0:
171
- return {}
207
+ return _drutil_media_properties()
172
208
  fields = {}
173
209
  for line in disk.stdout.splitlines():
174
210
  if ":" in line:
@@ -176,7 +212,7 @@ def media_properties(device):
176
212
  fields[key.strip()] = value.strip()
177
213
  optical = fields.get("Optical Media Type", "")
178
214
  if not optical and not fields.get("Device / Media Name"):
179
- return {}
215
+ return _drutil_media_properties()
180
216
  props = {"ID_CDROM": "1", "ID_CDROM_MEDIA": "1"}
181
217
  label = fields.get("Volume Name", "")
182
218
  filesystem = fields.get("Type (Bundle)") or fields.get("File System Personality")
@@ -204,8 +240,9 @@ def media_properties(device):
204
240
  optical = optical.lower()
205
241
  if "dvd+r dl" in optical or "dvd-r dl" in optical:
206
242
  props["ID_CDROM_MEDIA_DVD_PLUS_R_DL"] = "1"
207
- elif "dvd+r" in optical or "dvd-r" in optical:
243
+ elif ("dvd+r" in optical or "dvd-r" in optical) and "rw" not in optical:
208
244
  props["ID_CDROM_MEDIA_DVD_PLUS_R"] = "1"
245
+ _tag_rewritable(props, optical)
209
246
  return props
210
247
  return {}
211
248