ipodsync 0.1.1__tar.gz → 0.1.2__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.
- {ipodsync-0.1.1 → ipodsync-0.1.2}/PKG-INFO +29 -4
- {ipodsync-0.1.1 → ipodsync-0.1.2}/README.md +23 -3
- {ipodsync-0.1.1 → ipodsync-0.1.2}/pyproject.toml +6 -1
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/__init__.py +1 -1
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/cli.py +50 -32
- {ipodsync-0.1.1 → ipodsync-0.1.2}/.github/workflows/ci.yml +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/.github/workflows/publish.yml +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/.gitignore +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/LICENSE +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/__main__.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/_art_templates.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/_hashab_gen.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/_hashab_gen2.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/_hashab_gen_rt.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/_hashab_py.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/_hashab_tables.bin +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/_hashab_tables.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/_lib_templates.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/artwork_writer.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/cbk.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/export.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/hashab.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/importer.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/library.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/models.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/sysinfo.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/src/ipodsync/transport.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/tests/data/ArtworkDB +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/tests/data/Locations.itdb +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/tests/data/Locations.itdb.cbk +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/tests/data/hashab-traces.json +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/tests/data/hashab-vectors.json +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/tests/test_artwork_writer.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/tests/test_cbk.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/tests/test_hashab.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/tests/test_hashab_port.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/tools/build_gen2.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/tools/gen_art_templates.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/tools/gen_hashab_tables.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/tools/gen_lib_templates.py +0 -0
- {ipodsync-0.1.1 → ipodsync-0.1.2}/tools/transpile_hashab.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ipodsync
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
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
|
|
@@ -17,6 +17,11 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
17
17
|
Classifier: Operating System :: MacOS
|
|
18
18
|
Classifier: Operating System :: POSIX :: Linux
|
|
19
19
|
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
25
|
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
21
26
|
Requires-Python: >=3.9
|
|
22
27
|
Requires-Dist: mutagen>=1.47
|
|
@@ -28,8 +33,8 @@ Description-Content-Type: text/markdown
|
|
|
28
33
|
# ipodsync
|
|
29
34
|
|
|
30
35
|
[](https://github.com/buldiei/ipodsync/actions/workflows/ci.yml)
|
|
31
|
-
[](https://pypi.org/project/ipodsync/)
|
|
32
|
-
[](https://pypi.org/project/ipodsync/)
|
|
36
|
+
[](https://pypi.org/project/ipodsync/)
|
|
37
|
+
[](https://pypi.org/project/ipodsync/)
|
|
33
38
|
[](LICENSE)
|
|
34
39
|
|
|
35
40
|
Upload music to an **iPod nano 6G/7G** without iTunes/Apple Music — straight from
|
|
@@ -54,9 +59,29 @@ edits its SQLite library, signs it (hashAB) and **generates cover art itself**.
|
|
|
54
59
|
|
|
55
60
|
## Install
|
|
56
61
|
|
|
62
|
+
`ipodsync` is a command-line tool, so install it with **pipx** — it lands in an
|
|
63
|
+
isolated environment while the `ipodsync` command stays available everywhere:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pipx install ipodsync
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
No pipx yet? `brew install pipx` (macOS) or `sudo apt install pipx` (Debian/Ubuntu),
|
|
70
|
+
then `pipx ensurepath`.
|
|
71
|
+
|
|
72
|
+
<details>
|
|
73
|
+
<summary><code>pip install ipodsync</code> fails with <code>externally-managed-environment</code>?</summary>
|
|
74
|
+
|
|
75
|
+
On modern Debian/Ubuntu (PEP 668) the system Python is locked, so a plain
|
|
76
|
+
`pip install` into it is refused — that's an OS guard, not an ipodsync issue.
|
|
77
|
+
Use `pipx` (above), or a virtualenv:
|
|
78
|
+
|
|
57
79
|
```bash
|
|
58
|
-
|
|
80
|
+
python3 -m venv ~/.venvs/ipodsync
|
|
81
|
+
~/.venvs/ipodsync/bin/pip install ipodsync
|
|
82
|
+
~/.venvs/ipodsync/bin/ipodsync --help
|
|
59
83
|
```
|
|
84
|
+
</details>
|
|
60
85
|
|
|
61
86
|
Requires Python ≥ 3.9. Pure-Python — no compiler or native library needed.
|
|
62
87
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# ipodsync
|
|
2
2
|
|
|
3
3
|
[](https://github.com/buldiei/ipodsync/actions/workflows/ci.yml)
|
|
4
|
-
[](https://pypi.org/project/ipodsync/)
|
|
5
|
-
[](https://pypi.org/project/ipodsync/)
|
|
4
|
+
[](https://pypi.org/project/ipodsync/)
|
|
5
|
+
[](https://pypi.org/project/ipodsync/)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
8
|
Upload music to an **iPod nano 6G/7G** without iTunes/Apple Music — straight from
|
|
@@ -27,9 +27,29 @@ edits its SQLite library, signs it (hashAB) and **generates cover art itself**.
|
|
|
27
27
|
|
|
28
28
|
## Install
|
|
29
29
|
|
|
30
|
+
`ipodsync` is a command-line tool, so install it with **pipx** — it lands in an
|
|
31
|
+
isolated environment while the `ipodsync` command stays available everywhere:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pipx install ipodsync
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
No pipx yet? `brew install pipx` (macOS) or `sudo apt install pipx` (Debian/Ubuntu),
|
|
38
|
+
then `pipx ensurepath`.
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary><code>pip install ipodsync</code> fails with <code>externally-managed-environment</code>?</summary>
|
|
42
|
+
|
|
43
|
+
On modern Debian/Ubuntu (PEP 668) the system Python is locked, so a plain
|
|
44
|
+
`pip install` into it is refused — that's an OS guard, not an ipodsync issue.
|
|
45
|
+
Use `pipx` (above), or a virtualenv:
|
|
46
|
+
|
|
30
47
|
```bash
|
|
31
|
-
|
|
48
|
+
python3 -m venv ~/.venvs/ipodsync
|
|
49
|
+
~/.venvs/ipodsync/bin/pip install ipodsync
|
|
50
|
+
~/.venvs/ipodsync/bin/ipodsync --help
|
|
32
51
|
```
|
|
52
|
+
</details>
|
|
33
53
|
|
|
34
54
|
Requires Python ≥ 3.9. Pure-Python — no compiler or native library needed.
|
|
35
55
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ipodsync"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
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"
|
|
@@ -19,6 +19,11 @@ classifiers = [
|
|
|
19
19
|
"Operating System :: MacOS",
|
|
20
20
|
"Operating System :: POSIX :: Linux",
|
|
21
21
|
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
22
27
|
"Topic :: Multimedia :: Sound/Audio",
|
|
23
28
|
]
|
|
24
29
|
dependencies = [
|
|
@@ -256,39 +256,57 @@ def cmd_cover(ipod, args):
|
|
|
256
256
|
|
|
257
257
|
|
|
258
258
|
def main() -> int:
|
|
259
|
-
ap = argparse.ArgumentParser(
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
sub.add_parser("
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
259
|
+
ap = argparse.ArgumentParser(
|
|
260
|
+
prog="ipodsync",
|
|
261
|
+
description="Upload music to an iPod nano 6G/7G without iTunes (macOS + Linux). "
|
|
262
|
+
"The iPod mounts as a plain volume; ipodsync edits its SQLite library, "
|
|
263
|
+
"signs it (hashAB) and writes cover art.",
|
|
264
|
+
epilog="Set IPODSYNC_MOUNT=/path to point at the iPod explicitly. "
|
|
265
|
+
"add/rm/cover back up the library before editing. Run "
|
|
266
|
+
"`ipodsync <command> -h` for per-command help.")
|
|
267
|
+
sub = ap.add_subparsers(dest="cmd", required=True, metavar="<command>")
|
|
268
|
+
|
|
269
|
+
sub.add_parser("list", help="show tracks on the iPod")
|
|
270
|
+
|
|
271
|
+
pe = sub.add_parser("export", help="download tracks from the iPod (read-only)")
|
|
272
|
+
pe.add_argument("dest", help="destination directory")
|
|
273
|
+
pe.add_argument("--pid", type=int, help="export only this track (by pid)")
|
|
274
|
+
pe.add_argument("--by-album", action="store_true", help="lay out as Artist/Album/")
|
|
275
|
+
pe.add_argument("--no-tag", action="store_true", help="don't write ID3/MP4 tags")
|
|
276
|
+
|
|
277
|
+
pr = sub.add_parser("rm", help="remove a track from the iPod")
|
|
278
|
+
pr.add_argument("pid", type=int, help="track pid (see `list`)")
|
|
279
|
+
pr.add_argument("--delete-file", action="store_true", help="also delete the audio file")
|
|
280
|
+
|
|
281
|
+
pa = sub.add_parser("add", help="upload an MP3 (cover attached automatically)")
|
|
282
|
+
pa.add_argument("file", help="path to the MP3 to upload")
|
|
283
|
+
pa.add_argument("--title", help="override the title tag")
|
|
284
|
+
pa.add_argument("--artist", help="override the artist tag")
|
|
285
|
+
pa.add_argument("--album", help="override the album tag")
|
|
286
|
+
pa.add_argument("--no-cover", action="store_true", help="don't attach the embedded cover")
|
|
287
|
+
|
|
288
|
+
sub.add_parser("status", help="report ready / no-access / not-connected")
|
|
289
|
+
|
|
290
|
+
pw = sub.add_parser("wait", help="block until an iPod is ready")
|
|
291
|
+
pw.add_argument("--timeout", type=float, default=120, help="seconds to wait (0 = forever)")
|
|
292
|
+
pw.add_argument("--interval", type=float, default=2, help="poll interval, seconds")
|
|
293
|
+
|
|
294
|
+
sub.add_parser("playlists", help="list playlists")
|
|
295
|
+
pc = sub.add_parser("pl-create", help="create a playlist")
|
|
296
|
+
pc.add_argument("name", help="playlist name")
|
|
297
|
+
ppa = sub.add_parser("pl-add", help="add tracks to a playlist")
|
|
298
|
+
ppa.add_argument("playlist", type=int, help="playlist pid")
|
|
299
|
+
ppa.add_argument("track", type=int, nargs="+", help="track pid(s)")
|
|
300
|
+
ppr = sub.add_parser("pl-rm", help="remove a track from a playlist")
|
|
301
|
+
ppr.add_argument("playlist", type=int, help="playlist pid")
|
|
302
|
+
ppr.add_argument("track", type=int, help="track pid")
|
|
303
|
+
ppd = sub.add_parser("pl-del", help="delete a playlist")
|
|
304
|
+
ppd.add_argument("playlist", type=int, help="playlist pid")
|
|
305
|
+
|
|
306
|
+
pcv = sub.add_parser("cover", help="attach a cover to an existing track")
|
|
307
|
+
pcv.add_argument("pid", type=int, help="track pid")
|
|
291
308
|
pcv.add_argument("--image", help="cover image file (otherwise the track's APIC is used)")
|
|
309
|
+
|
|
292
310
|
args = ap.parse_args()
|
|
293
311
|
|
|
294
312
|
# commands that don't require a ready iPod
|
|
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
|
|
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
|