flashpod 0.2.2__tar.gz → 0.2.4__tar.gz

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.
Files changed (26) hide show
  1. {flashpod-0.2.2 → flashpod-0.2.4}/PKG-INFO +1 -1
  2. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/ipod_flash.py +57 -12
  3. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/platform/macos.py +19 -19
  4. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/platform/windows.py +33 -20
  5. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod.egg-info/PKG-INFO +1 -1
  6. {flashpod-0.2.2 → flashpod-0.2.4}/pyproject.toml +1 -1
  7. {flashpod-0.2.2 → flashpod-0.2.4}/LICENSE +0 -0
  8. {flashpod-0.2.2 → flashpod-0.2.4}/README.md +0 -0
  9. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/__init__.py +0 -0
  10. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/__main__.py +0 -0
  11. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/cli.py +0 -0
  12. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/contrib/99-flashpod-firewire-ipod.rules +0 -0
  13. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/fat32.py +0 -0
  14. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/fatfs.py +0 -0
  15. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/firmware/firmware.json +0 -0
  16. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/itunesdb.py +0 -0
  17. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/platform/__init__.py +0 -0
  18. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/platform/base.py +0 -0
  19. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/platform/linux.py +0 -0
  20. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/resources.py +0 -0
  21. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod.egg-info/SOURCES.txt +0 -0
  22. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod.egg-info/dependency_links.txt +0 -0
  23. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod.egg-info/entry_points.txt +0 -0
  24. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod.egg-info/requires.txt +0 -0
  25. {flashpod-0.2.2 → flashpod-0.2.4}/flashpod.egg-info/top_level.txt +0 -0
  26. {flashpod-0.2.2 → flashpod-0.2.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flashpod
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Command-line iPod sync + card-flashing tooling for early FireWire-era iPods, pure Python
5
5
  Author: David Barnhart
6
6
  License-Expression: MIT
@@ -397,10 +397,53 @@ def device_mountpoints(dev):
397
397
  # ----------------------------------------------------------------------------
398
398
  # Interactive selection + confirmation
399
399
  # ----------------------------------------------------------------------------
400
- def choose_device():
401
- cands = list_candidates()
402
- if not cands:
403
- sys.exit(color("No removable/USB disks found. Plug in the card and retry.", C_RED))
400
+ def pick_device(scan, render, path_of, empty_msg):
401
+ """Interactive device chooser shared by every platform backend.
402
+
403
+ ``scan()`` returns the candidate list (called again on each refresh),
404
+ ``render(items)`` prints the table, ``path_of(item)`` yields the device path
405
+ to return — or ``None`` for an entry that exists but can't be written, such
406
+ as the empty slot of a multi-slot card reader. Rejecting it here is much
407
+ kinder than letting it through: with no media, every downstream call fails
408
+ obscurely ("could not determine size", access denied) and reads as a bug.
409
+
410
+ The loop accepts 'r' to re-scan. Without it, a reader plugged in — or a card
411
+ inserted — after the list was drawn stays invisible until you quit and start
412
+ the whole command over, which is a long way back when you've already picked
413
+ a model and firmware.
414
+ """
415
+ items = scan()
416
+ if not items:
417
+ sys.exit(color(empty_msg, C_RED))
418
+ render(items)
419
+ while True:
420
+ try:
421
+ sel = input(color("Select device number "
422
+ "('r' to rescan, 'q' to quit): ", C_CYN)).strip().lower()
423
+ except (EOFError, KeyboardInterrupt):
424
+ print(file=sys.stderr)
425
+ sys.exit("Aborted.")
426
+ if sel in ("q", "quit", ""):
427
+ sys.exit("Aborted.")
428
+ if sel in ("r", "refresh", "rescan"):
429
+ items = scan()
430
+ if not items:
431
+ print(color(" " + empty_msg, C_YEL), file=sys.stderr)
432
+ else:
433
+ render(items)
434
+ continue
435
+ if sel.isdigit() and int(sel) < len(items):
436
+ path = path_of(items[int(sel)])
437
+ if path:
438
+ return path
439
+ print(color(" that slot is empty — insert a card and press 'r' "
440
+ "to rescan, or pick another device", C_YEL),
441
+ file=sys.stderr)
442
+ continue
443
+ print(color(" invalid selection", C_RED), file=sys.stderr)
444
+
445
+
446
+ def _render_candidates(cands):
404
447
  print(color("\nAttached removable storage:\n", C_CYN), file=sys.stderr)
405
448
  # Multi-slot readers expose one /dev/sdX per slot, all with the same
406
449
  # identity strings; number the slots so they can be told apart.
@@ -442,13 +485,13 @@ def choose_device():
442
485
  if r["warn"]:
443
486
  print(color(" " * sub + "⚠ " + r["warn"], C_YEL), file=sys.stderr)
444
487
  print(file=sys.stderr)
445
- while True:
446
- sel = input(color("Select device number (or 'q' to quit): ", C_CYN)).strip()
447
- if sel.lower() in ("q", "quit", ""):
448
- sys.exit("Aborted.")
449
- if sel.isdigit() and int(sel) < len(cands):
450
- return "/dev/" + cands[int(sel)]["name"]
451
- print(color(" invalid selection", C_RED), file=sys.stderr)
488
+
489
+
490
+ def choose_device():
491
+ return pick_device(
492
+ list_candidates, _render_candidates,
493
+ lambda d: ("/dev/" + d["name"]) if int(d.get("size") or 0) else None,
494
+ "No removable/USB disks found. Plug in the card and retry.")
452
495
 
453
496
  def device_label(dev):
454
497
  """Short, typeable name for a device, used in the ERASE confirmation.
@@ -761,7 +804,9 @@ def flash(device=None, firmware=None,
761
804
 
762
805
  total_sectors = plat.device_sectors(dev)
763
806
  if total_sectors <= 0:
764
- sys.exit(color("could not determine size of %s" % dev, C_RED))
807
+ sys.exit(color("could not determine size of %s is a card inserted? "
808
+ "An empty slot in a multi-slot reader still appears as "
809
+ "a disk, but reports size 0." % dev, C_RED))
765
810
  confirm(dev, total_sectors, assume_yes or dry_run, lba48, max_data_sectors)
766
811
  plat.unmount_all(dev, dry_run)
767
812
  write_layout(dev, fw, total_sectors, dry_run, do_format, lba48, max_data_sectors)
@@ -73,26 +73,26 @@ class MacOSPlatform(Platform):
73
73
  def choose_device(self):
74
74
  from .. import ipod_flash
75
75
  color = ipod_flash.color
76
- disks = self._external_disks()
77
- if not disks:
78
- sys.exit(color("No external disks found. Plug in the card reader and retry.",
79
- ipod_flash.C_RED))
80
- print(color("\nAttached external disks:\n", ipod_flash.C_CYN), file=sys.stderr)
81
- for i, (d, info) in enumerate(disks):
76
+
77
+ def render(disks):
78
+ print(color("\nAttached external disks:\n", ipod_flash.C_CYN),
79
+ file=sys.stderr)
80
+ for i, (d, info) in enumerate(disks):
81
+ size = int(info.get("TotalSize") or info.get("Size") or 0)
82
+ name = (info.get("MediaName") or info.get("IORegistryEntryName")
83
+ or "").strip() or "disk"
84
+ print(" [%d] /dev/%-7s %10s %s" %
85
+ (i, d, ipod_flash.fmt_size(size), name), file=sys.stderr)
86
+ print(file=sys.stderr)
87
+
88
+ def to_path(item):
89
+ d, info = item
82
90
  size = int(info.get("TotalSize") or info.get("Size") or 0)
83
- name = (info.get("MediaName") or info.get("IORegistryEntryName")
84
- or "").strip() or "disk"
85
- print(" [%d] /dev/%-7s %10s %s" %
86
- (i, d, ipod_flash.fmt_size(size), name), file=sys.stderr)
87
- print(file=sys.stderr)
88
- while True:
89
- sel = input(color("Select device number (or 'q' to quit): ",
90
- ipod_flash.C_CYN)).strip()
91
- if sel.lower() in ("q", "quit", ""):
92
- sys.exit("Aborted.")
93
- if sel.isdigit() and int(sel) < len(disks):
94
- return "/dev/" + disks[int(sel)][0]
95
- print(color(" invalid selection", ipod_flash.C_RED), file=sys.stderr)
91
+ return ("/dev/" + d) if size else None # 0 = empty reader slot
92
+
93
+ return ipod_flash.pick_device(
94
+ self._external_disks, render, to_path,
95
+ "No external disks found. Plug in the card reader and retry.")
96
96
 
97
97
  def device_sectors(self, dev):
98
98
  # real file/image -> just its size
@@ -144,29 +144,42 @@ class WindowsPlatform(Platform):
144
144
  f[4].strip().lower() == "true" or f[5].strip().lower() == "true"))
145
145
  return disks
146
146
 
147
+ def _removable_disks(self):
148
+ """Removable/USB disks worth offering, falling back to every non-system
149
+ disk when nothing matches the removable bus types."""
150
+ disks = self._disks()
151
+ cands = [d for d in disks
152
+ if not d[4] and d[3] in ("USB", "SD", "MMC", "1394")]
153
+ return cands or [d for d in disks if not d[4]]
154
+
147
155
  def choose_device(self):
148
156
  from .. import ipod_flash
149
157
  color = ipod_flash.color
150
- cands = [d for d in self._disks()
151
- if not d[4] and d[3] in ("USB", "SD", "MMC", "1394")]
152
- if not cands:
153
- cands = [d for d in self._disks() if not d[4]]
154
- if not cands:
155
- sys.exit(color("No removable disks found. Plug in the card and retry.",
156
- ipod_flash.C_RED))
157
- print(color("\nAttached removable disks:\n", ipod_flash.C_CYN), file=sys.stderr)
158
- for i, (num, size, name, bus, _sys) in enumerate(cands):
159
- print(" [%d] \\\\.\\PhysicalDrive%-3d %10s %s (%s)" %
160
- (i, num, ipod_flash.fmt_size(size), name.strip(), bus), file=sys.stderr)
161
- print(file=sys.stderr)
162
- while True:
163
- sel = input(color("Select device number (or 'q' to quit): ",
164
- ipod_flash.C_CYN)).strip()
165
- if sel.lower() in ("q", "quit", ""):
166
- sys.exit("Aborted.")
167
- if sel.isdigit() and int(sel) < len(cands):
168
- return "\\\\.\\PhysicalDrive%d" % cands[int(sel)][0]
169
- print(color(" invalid selection", ipod_flash.C_RED), file=sys.stderr)
158
+
159
+ def render(cands):
160
+ print(color("\nAttached removable disks:\n", ipod_flash.C_CYN),
161
+ file=sys.stderr)
162
+ for i, (num, size, name, bus, _sys) in enumerate(cands):
163
+ print(" [%d] \\\\.\\PhysicalDrive%-3d %10s %s (%s)" %
164
+ (i, num, ipod_flash.fmt_size(size), name.strip(), bus),
165
+ file=sys.stderr)
166
+ if not size:
167
+ # A multi-slot reader shows one disk per slot; an empty one
168
+ # reports size 0 / "No Media". Say so, or it reads as a
169
+ # normal target and every later failure ("could not
170
+ # determine size", access denied) looks like a flashpod bug.
171
+ print(color(" no card inserted empty reader slot",
172
+ ipod_flash.C_YEL), file=sys.stderr)
173
+ print(file=sys.stderr)
174
+
175
+ def to_path(d):
176
+ if not d[1]:
177
+ return None # empty slot: rejected by pick_device
178
+ return "\\\\.\\PhysicalDrive%d" % d[0]
179
+
180
+ return ipod_flash.pick_device(
181
+ self._removable_disks, render, to_path,
182
+ "No removable disks found. Plug in the card and retry.")
170
183
 
171
184
  def device_sectors(self, dev):
172
185
  if os.path.isfile(dev):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flashpod
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Command-line iPod sync + card-flashing tooling for early FireWire-era iPods, pure Python
5
5
  Author: David Barnhart
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "flashpod"
7
- version = "0.2.2"
7
+ version = "0.2.4"
8
8
  description = "Command-line iPod sync + card-flashing tooling for early FireWire-era iPods, pure Python"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.6"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes