ipodsync 0.1.3__tar.gz → 0.1.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.
- {ipodsync-0.1.3 → ipodsync-0.1.4}/PKG-INFO +1 -1
- {ipodsync-0.1.3 → ipodsync-0.1.4}/pyproject.toml +1 -1
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/__init__.py +1 -1
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/artwork_writer.py +19 -9
- {ipodsync-0.1.3 → ipodsync-0.1.4}/tests/test_artwork_writer.py +14 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/.github/workflows/ci.yml +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/.github/workflows/publish.yml +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/.gitignore +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/LICENSE +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/README.md +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/__main__.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/_art_templates.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/_hashab_gen.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/_hashab_gen2.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/_hashab_gen_rt.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/_hashab_py.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/_hashab_tables.bin +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/_hashab_tables.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/_lib_templates.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/cbk.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/cli.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/export.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/hashab.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/importer.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/library.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/models.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/sysinfo.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/src/ipodsync/transport.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/tests/data/ArtworkDB +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/tests/data/Locations.itdb +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/tests/data/Locations.itdb.cbk +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/tests/data/hashab-traces.json +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/tests/data/hashab-vectors.json +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/tests/test_cbk.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/tests/test_hashab.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/tests/test_hashab_port.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/tools/build_gen2.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/tools/gen_art_templates.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/tools/gen_hashab_tables.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/tools/gen_lib_templates.py +0 -0
- {ipodsync-0.1.3 → ipodsync-0.1.4}/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.4
|
|
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
|
|
@@ -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.4"
|
|
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"
|
|
@@ -58,20 +58,30 @@ FIRST_IMAGE_ID = 101
|
|
|
58
58
|
|
|
59
59
|
# --- encoding a cover to RGB565-LE ----------------------------------------------
|
|
60
60
|
def to_rgb565(image_bytes: bytes, width: int, height: int, slot: int) -> bytes:
|
|
61
|
-
"""Resize the cover to width×height, pack into RGB565-LE
|
|
61
|
+
"""Resize the cover to width×height, pack into RGB565-LE.
|
|
62
|
+
|
|
63
|
+
Each row is padded to the device's row stride (`slot // height`), matching
|
|
64
|
+
iTunes: e.g. F1016 is 57×57 pixels but stored with a 116-byte (58 px) stride —
|
|
65
|
+
2 zero bytes at the end of every row. Getting this wrong shears the thumbnail.
|
|
66
|
+
"""
|
|
62
67
|
from PIL import Image
|
|
63
68
|
|
|
64
69
|
im = Image.open(io.BytesIO(image_bytes)).convert("RGB").resize(
|
|
65
70
|
(width, height), Image.LANCZOS)
|
|
66
71
|
rgb = im.tobytes() # width*height*3
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
for
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
stride = slot // height # bytes per row the device reads
|
|
73
|
+
out = bytearray()
|
|
74
|
+
for y in range(height):
|
|
75
|
+
row = bytearray(stride) # zero-padded to the stride
|
|
76
|
+
base, j = y * width * 3, 0
|
|
77
|
+
for _ in range(width):
|
|
78
|
+
v = ((rgb[base] >> 3) << 11) | ((rgb[base + 1] >> 2) << 5) | (rgb[base + 2] >> 3)
|
|
79
|
+
row[j] = v & 0xFF
|
|
80
|
+
row[j + 1] = v >> 8
|
|
81
|
+
base += 3
|
|
82
|
+
j += 2
|
|
83
|
+
out += row
|
|
84
|
+
if len(out) < slot: # safety (slot == height*stride already)
|
|
75
85
|
out.extend(b"\x00" * (slot - len(out)))
|
|
76
86
|
return bytes(out)
|
|
77
87
|
|
|
@@ -137,3 +137,17 @@ def test_row_or_template_fallback_on_empty():
|
|
|
137
137
|
got["title"] = "x"
|
|
138
138
|
assert library._EMPTY_TEMPLATES["item"].get("title") != "x"
|
|
139
139
|
c.close()
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def test_f1016_row_stride_padding():
|
|
143
|
+
"""F1016 is 57×57 pixels but stored with a 116-byte (58 px) row stride — two
|
|
144
|
+
zero bytes at the end of every row, like iTunes. Wrong stride shears the thumb."""
|
|
145
|
+
from PIL import Image
|
|
146
|
+
import io
|
|
147
|
+
buf = io.BytesIO()
|
|
148
|
+
Image.new("RGB", (80, 80), (200, 30, 30)).save(buf, format="PNG")
|
|
149
|
+
data = aw.to_rgb565(buf.getvalue(), 57, 57, 6612)
|
|
150
|
+
assert len(data) == 6612
|
|
151
|
+
stride = 6612 // 57 # 116
|
|
152
|
+
for y in range(57):
|
|
153
|
+
assert data[y * stride + 114:y * stride + 116] == b"\x00\x00", f"row {y}"
|
|
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
|