ipodsync 0.1.8__tar.gz → 0.1.10__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 (43) hide show
  1. {ipodsync-0.1.8 → ipodsync-0.1.10}/PKG-INFO +5 -3
  2. {ipodsync-0.1.8 → ipodsync-0.1.10}/README.md +4 -2
  3. {ipodsync-0.1.8 → ipodsync-0.1.10}/pyproject.toml +1 -1
  4. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/__init__.py +1 -1
  5. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/cli.py +25 -6
  6. {ipodsync-0.1.8 → ipodsync-0.1.10}/.github/workflows/ci.yml +0 -0
  7. {ipodsync-0.1.8 → ipodsync-0.1.10}/.github/workflows/publish.yml +0 -0
  8. {ipodsync-0.1.8 → ipodsync-0.1.10}/.gitignore +0 -0
  9. {ipodsync-0.1.8 → ipodsync-0.1.10}/LICENSE +0 -0
  10. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/__main__.py +0 -0
  11. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/_art_templates.py +0 -0
  12. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/_hashab_gen.py +0 -0
  13. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/_hashab_gen2.py +0 -0
  14. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/_hashab_gen_rt.py +0 -0
  15. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/_hashab_py.py +0 -0
  16. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/_hashab_tables.bin +0 -0
  17. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/_hashab_tables.py +0 -0
  18. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/_lib_templates.py +0 -0
  19. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/artwork_writer.py +0 -0
  20. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/cbk.py +0 -0
  21. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/export.py +0 -0
  22. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/hashab.py +0 -0
  23. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/importer.py +0 -0
  24. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/library.py +0 -0
  25. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/models.py +0 -0
  26. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/sysinfo.py +0 -0
  27. {ipodsync-0.1.8 → ipodsync-0.1.10}/src/ipodsync/transport.py +0 -0
  28. {ipodsync-0.1.8 → ipodsync-0.1.10}/tests/data/ArtworkDB +0 -0
  29. {ipodsync-0.1.8 → ipodsync-0.1.10}/tests/data/Locations.itdb +0 -0
  30. {ipodsync-0.1.8 → ipodsync-0.1.10}/tests/data/Locations.itdb.cbk +0 -0
  31. {ipodsync-0.1.8 → ipodsync-0.1.10}/tests/data/hashab-traces.json +0 -0
  32. {ipodsync-0.1.8 → ipodsync-0.1.10}/tests/data/hashab-vectors.json +0 -0
  33. {ipodsync-0.1.8 → ipodsync-0.1.10}/tests/test_artwork_compact.py +0 -0
  34. {ipodsync-0.1.8 → ipodsync-0.1.10}/tests/test_artwork_writer.py +0 -0
  35. {ipodsync-0.1.8 → ipodsync-0.1.10}/tests/test_cbk.py +0 -0
  36. {ipodsync-0.1.8 → ipodsync-0.1.10}/tests/test_gc_orphans.py +0 -0
  37. {ipodsync-0.1.8 → ipodsync-0.1.10}/tests/test_hashab.py +0 -0
  38. {ipodsync-0.1.8 → ipodsync-0.1.10}/tests/test_hashab_port.py +0 -0
  39. {ipodsync-0.1.8 → ipodsync-0.1.10}/tools/build_gen2.py +0 -0
  40. {ipodsync-0.1.8 → ipodsync-0.1.10}/tools/gen_art_templates.py +0 -0
  41. {ipodsync-0.1.8 → ipodsync-0.1.10}/tools/gen_hashab_tables.py +0 -0
  42. {ipodsync-0.1.8 → ipodsync-0.1.10}/tools/gen_lib_templates.py +0 -0
  43. {ipodsync-0.1.8 → ipodsync-0.1.10}/tools/transpile_hashab.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ipodsync
3
- Version: 0.1.8
3
+ Version: 0.1.10
4
4
  Summary: Upload music to an iPod nano 6G/7G without iTunes: SQLite library, hashAB signing, cover art (pure-Python).
5
5
  Project-URL: Homepage, https://github.com/buldiei/ipodsync
6
6
  Project-URL: Repository, https://github.com/buldiei/ipodsync
@@ -97,9 +97,11 @@ ipodsync add "Song.mp3" # + cover auto
97
97
  ipodsync add a.mp3 b.mp3 c.mp3 # several at once (one library write)
98
98
  ipodsync add -f ~/Music/album # a whole folder (recursively)
99
99
  ipodsync add "Song.mp3" --no-cover
100
- ipodsync export ~/Music/ipod --by-album
100
+ ipodsync export ~/Music/ipod --by-album # download everything, in Artist/Album/ folders
101
+ ipodsync export ~/Music/ipod --album "Breakdown" # download one album (or --artist NAME / --pid PID)
101
102
  ipodsync cover 123456789 --image cover.jpg
102
- ipodsync rm 123456789 --delete-file
103
+ ipodsync rm 123456789 # removes the track and its audio file
104
+ ipodsync rm 123456789 --keep-file # …but leave the audio file on the iPod
103
105
  ipodsync -b add "Song.mp3" # -b: back up the library first (off by default)
104
106
  ```
105
107
 
@@ -65,9 +65,11 @@ ipodsync add "Song.mp3" # + cover auto
65
65
  ipodsync add a.mp3 b.mp3 c.mp3 # several at once (one library write)
66
66
  ipodsync add -f ~/Music/album # a whole folder (recursively)
67
67
  ipodsync add "Song.mp3" --no-cover
68
- ipodsync export ~/Music/ipod --by-album
68
+ ipodsync export ~/Music/ipod --by-album # download everything, in Artist/Album/ folders
69
+ ipodsync export ~/Music/ipod --album "Breakdown" # download one album (or --artist NAME / --pid PID)
69
70
  ipodsync cover 123456789 --image cover.jpg
70
- ipodsync rm 123456789 --delete-file
71
+ ipodsync rm 123456789 # removes the track and its audio file
72
+ ipodsync rm 123456789 --keep-file # …but leave the audio file on the iPod
71
73
  ipodsync -b add "Song.mp3" # -b: back up the library first (off by default)
72
74
  ```
73
75
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ipodsync"
7
- version = "0.1.8"
7
+ version = "0.1.10"
8
8
  description = "Upload music to an iPod nano 6G/7G without iTunes: SQLite library, hashAB signing, cover art (pure-Python)."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -13,7 +13,7 @@ from .library import ItlpLibrary
13
13
  from .models import Track
14
14
  from .transport import IPod, IPodNotFound, find_ipod
15
15
 
16
- __version__ = "0.1.8"
16
+ __version__ = "0.1.10"
17
17
 
18
18
  __all__ = [
19
19
  "find_ipod",
@@ -2,9 +2,9 @@
2
2
 
3
3
  ipodsync list # show tracks
4
4
  ipodsync export DEST [--by-album] [--no-tag] # download everything from the iPod
5
- ipodsync export DEST --pid PID # download a single track
5
+ ipodsync export DEST --album NAME # download one album (--artist / --pid also filter)
6
6
  ipodsync add FILE [--no-cover] # upload a track (+cover auto)
7
- ipodsync rm PID [--delete-file] # remove a track (+resign)
7
+ ipodsync rm PID [--keep-file] # remove a track + its file (+resign)
8
8
  ipodsync cover PID [--image IMG] # attach a cover to a track
9
9
 
10
10
  Export is read-only. add/rm/cover write to the database; pass -b to back the library
@@ -54,6 +54,17 @@ def cmd_export(ipod, args):
54
54
  tracks = lib.list_tracks()
55
55
  if args.pid is not None:
56
56
  tracks = [t for t in tracks if t["pid"] == args.pid]
57
+ if args.album:
58
+ q = args.album.casefold()
59
+ tracks = [t for t in tracks if q in (t["album"] or "").casefold()]
60
+ if args.artist:
61
+ q = args.artist.casefold()
62
+ tracks = [t for t in tracks if q in (t["artist"] or "").casefold()]
63
+ if not tracks:
64
+ filt = ", ".join(f"{k}={v!r}" for k, v in
65
+ (("pid", args.pid), ("album", args.album), ("artist", args.artist)) if v)
66
+ print(f"⚠️ no tracks match ({filt or 'empty library'}). Try `ipodsync list`.")
67
+ lib.close(); return
57
68
  dest = Path(args.dest)
58
69
  layout = "artist_album" if args.by_album else "flat"
59
70
  ok = 0
@@ -79,9 +90,12 @@ def cmd_rm(ipod, args):
79
90
  if args.backup:
80
91
  _backup_artwork(ipod, stamp)
81
92
 
93
+ if args.delete_file: # deprecated: deleting the file is the default now
94
+ print(" note: --delete-file is now the default; drop it, or use --keep-file to keep the file.")
95
+
82
96
  guid = read_firewire_guid(ipod.sysinfo_extended, ipod.sysinfo, mount=ipod.root)
83
97
  lib = ItlpLibrary(itlp)
84
- music = ipod.music_dir if args.delete_file else None
98
+ music = None if args.keep_file else ipod.music_dir # delete the file unless --keep-file
85
99
  lib.remove_track(args.pid, music_dir=music)
86
100
  lib.resign(guid)
87
101
  live = {t["pid"] for t in lib.list_tracks()} # remaining tracks, after removal
@@ -90,8 +104,8 @@ def cmd_rm(ipod, args):
90
104
  # drop the removed track's thumbnails from ArtworkDB + repack the .ithmb files
91
105
  stats = compact(ipod.control / "Artwork", live)
92
106
 
93
- print(f" Removed track pid {args.pid}"
94
- f"{' (+ deleted the file)' if args.delete_file else ''}. Eject the iPod.")
107
+ tail = " (kept the audio file)" if args.keep_file else " (+ deleted the file)"
108
+ print(f" Removed track pid {args.pid}{tail}. Eject the iPod.")
95
109
  if stats["removed"]:
96
110
  print(f" Reclaimed cover art for {stats['removed']} track"
97
111
  f"{'s' if stats['removed'] != 1 else ''}.")
@@ -330,12 +344,17 @@ def main() -> int:
330
344
  pe = sub.add_parser("export", help="download tracks from the iPod (read-only)")
331
345
  pe.add_argument("dest", help="destination directory")
332
346
  pe.add_argument("--pid", type=int, help="export only this track (by pid)")
347
+ pe.add_argument("--album", help="only tracks whose album contains this (case-insensitive)")
348
+ pe.add_argument("--artist", help="only tracks whose artist contains this (case-insensitive)")
333
349
  pe.add_argument("--by-album", action="store_true", help="lay out as Artist/Album/")
334
350
  pe.add_argument("--no-tag", action="store_true", help="don't write ID3/MP4 tags")
335
351
 
336
352
  pr = sub.add_parser("rm", help="remove a track from the iPod")
337
353
  pr.add_argument("pid", type=int, help="track pid (see `list`)")
338
- pr.add_argument("--delete-file", action="store_true", help="also delete the audio file")
354
+ pr.add_argument("--keep-file", action="store_true",
355
+ help="keep the audio file on the iPod (default: delete it too)")
356
+ pr.add_argument("--delete-file", action="store_true",
357
+ help=argparse.SUPPRESS) # deprecated: deletion is the default now
339
358
 
340
359
  pa = sub.add_parser("add", help="upload a track (or a whole folder); cover attached automatically")
341
360
  pa.add_argument("file", nargs="*", help="path(s) to the audio file(s) to upload")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes