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.
- {flashpod-0.2.2 → flashpod-0.2.4}/PKG-INFO +1 -1
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/ipod_flash.py +57 -12
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/platform/macos.py +19 -19
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/platform/windows.py +33 -20
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod.egg-info/PKG-INFO +1 -1
- {flashpod-0.2.2 → flashpod-0.2.4}/pyproject.toml +1 -1
- {flashpod-0.2.2 → flashpod-0.2.4}/LICENSE +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/README.md +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/__init__.py +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/__main__.py +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/cli.py +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/contrib/99-flashpod-firewire-ipod.rules +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/fat32.py +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/fatfs.py +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/firmware/firmware.json +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/itunesdb.py +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/platform/__init__.py +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/platform/base.py +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/platform/linux.py +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod/resources.py +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod.egg-info/SOURCES.txt +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod.egg-info/dependency_links.txt +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod.egg-info/entry_points.txt +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod.egg-info/requires.txt +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/flashpod.egg-info/top_level.txt +0 -0
- {flashpod-0.2.2 → flashpod-0.2.4}/setup.cfg +0 -0
|
@@ -397,10 +397,53 @@ def device_mountpoints(dev):
|
|
|
397
397
|
# ----------------------------------------------------------------------------
|
|
398
398
|
# Interactive selection + confirmation
|
|
399
399
|
# ----------------------------------------------------------------------------
|
|
400
|
-
def
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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):
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "flashpod"
|
|
7
|
-
version = "0.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
|
|
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
|