pypodlib 0.1.0__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.
- pypodlib-0.1.0/.gitignore +29 -0
- pypodlib-0.1.0/LICENSE +26 -0
- pypodlib-0.1.0/PKG-INFO +124 -0
- pypodlib-0.1.0/README.md +94 -0
- pypodlib-0.1.0/pyproject.toml +85 -0
- pypodlib-0.1.0/src/pypodlib/__init__.py +45 -0
- pypodlib-0.1.0/src/pypodlib/api.py +939 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_parser/__init__.py +3 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_parser/chunk_parser.py +66 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_parser/constants.py +11 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_parser/mhfd_parser.py +61 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_parser/mhii_parser.py +60 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_parser/mhli_parser.py +16 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_parser/mhni_parser.py +47 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_parser/mhod_parser.py +59 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_parser/mhsd_parser.py +16 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_parser/parser.py +15 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_shared/__init__.py +2 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_shared/binary.py +66 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_shared/constants.py +72 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_shared/ithmb_paths.py +27 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_shared/mhlf.py +47 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_shared/mhni.py +184 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_shared/mhod.py +72 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_writer/__init__.py +77 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_writer/art_extractor.py +319 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_writer/artwork_types.py +115 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_writer/artwork_writer.py +1482 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_writer/artworkdb_chunks.py +475 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_writer/ithmb_codecs.py +723 -0
- pypodlib-0.1.0/src/pypodlib/artworkdb_writer/rgb565.py +227 -0
- pypodlib-0.1.0/src/pypodlib/device/__init__.py +158 -0
- pypodlib-0.1.0/src/pypodlib/device/artwork.py +184 -0
- pypodlib-0.1.0/src/pypodlib/device/artwork_presets.py +119 -0
- pypodlib-0.1.0/src/pypodlib/device/authority.py +764 -0
- pypodlib-0.1.0/src/pypodlib/device/bootstrap.py +210 -0
- pypodlib-0.1.0/src/pypodlib/device/capabilities.py +737 -0
- pypodlib-0.1.0/src/pypodlib/device/checksum.py +41 -0
- pypodlib-0.1.0/src/pypodlib/device/diagnostic_log.py +180 -0
- pypodlib-0.1.0/src/pypodlib/device/dump.py +496 -0
- pypodlib-0.1.0/src/pypodlib/device/durability.py +431 -0
- pypodlib-0.1.0/src/pypodlib/device/eject.py +1153 -0
- pypodlib-0.1.0/src/pypodlib/device/filesystem.py +181 -0
- pypodlib-0.1.0/src/pypodlib/device/filesystem_profile.py +633 -0
- pypodlib-0.1.0/src/pypodlib/device/images.py +409 -0
- pypodlib-0.1.0/src/pypodlib/device/info.py +2752 -0
- pypodlib-0.1.0/src/pypodlib/device/linux_identity.py +557 -0
- pypodlib-0.1.0/src/pypodlib/device/linux_integration.py +370 -0
- pypodlib-0.1.0/src/pypodlib/device/lookup.py +204 -0
- pypodlib-0.1.0/src/pypodlib/device/metadata_write.py +201 -0
- pypodlib-0.1.0/src/pypodlib/device/models.py +715 -0
- pypodlib-0.1.0/src/pypodlib/device/path_safety.py +140 -0
- pypodlib-0.1.0/src/pypodlib/device/recovery.py +147 -0
- pypodlib-0.1.0/src/pypodlib/device/scanner.py +2653 -0
- pypodlib-0.1.0/src/pypodlib/device/storage_safety.py +104 -0
- pypodlib-0.1.0/src/pypodlib/device/sysinfo.py +528 -0
- pypodlib-0.1.0/src/pypodlib/device/usb_backend.py +132 -0
- pypodlib-0.1.0/src/pypodlib/device/virtual.py +490 -0
- pypodlib-0.1.0/src/pypodlib/device/virtual_identity.py +50 -0
- pypodlib-0.1.0/src/pypodlib/device/vpd_iokit.py +617 -0
- pypodlib-0.1.0/src/pypodlib/device/vpd_libusb.py +1464 -0
- pypodlib-0.1.0/src/pypodlib/device/vpd_linux.py +256 -0
- pypodlib-0.1.0/src/pypodlib/device/vpd_usb_control.py +211 -0
- pypodlib-0.1.0/src/pypodlib/device/vpd_windows.py +311 -0
- pypodlib-0.1.0/src/pypodlib/device/write_guard.py +553 -0
- pypodlib-0.1.0/src/pypodlib/device/write_readiness.py +208 -0
- pypodlib-0.1.0/src/pypodlib/infrastructure/__init__.py +1 -0
- pypodlib-0.1.0/src/pypodlib/infrastructure/media_folders.py +180 -0
- pypodlib-0.1.0/src/pypodlib/infrastructure/settings_paths.py +83 -0
- pypodlib-0.1.0/src/pypodlib/infrastructure/settings_persistence.py +135 -0
- pypodlib-0.1.0/src/pypodlib/infrastructure/settings_runtime.py +526 -0
- pypodlib-0.1.0/src/pypodlib/infrastructure/settings_schema.py +345 -0
- pypodlib-0.1.0/src/pypodlib/infrastructure/settings_secrets.py +127 -0
- pypodlib-0.1.0/src/pypodlib/infrastructure/theme_catalog.py +371 -0
- pypodlib-0.1.0/src/pypodlib/infrastructure/theme_renderer.py +1109 -0
- pypodlib-0.1.0/src/pypodlib/infrastructure/theme_runtime.py +28 -0
- pypodlib-0.1.0/src/pypodlib/infrastructure/version.py +18 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/__init__.py +22 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/_parsing.py +120 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/artwork_links.py +108 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/byte_walk.py +586 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/chunk_parser.py +203 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/exceptions.py +59 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/forensics.py +851 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/ipod_library.py +174 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/mhbd_parser.py +43 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/mhia_parser.py +31 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/mhii_parser.py +34 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/mhip_parser.py +31 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/mhit_parser.py +34 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/mhod_parser.py +674 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/mhsd_parser.py +48 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/mhyp_parser.py +50 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/otg.py +187 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/parser.py +140 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_parser/playcounts.py +282 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/__init__.py +32 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/album_identity.py +93 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/constants.py +393 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/device_time.py +250 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/extraction.py +173 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/field_base.py +478 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/mhbd_defs.py +57 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/mhia_defs.py +39 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/mhii_defs.py +18 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/mhip_defs.py +33 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/mhit_defs.py +237 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/mhod_defs.py +811 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/mhsd_defs.py +15 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/mhyp_defs.py +58 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/playlist_hierarchy.py +285 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/playlist_kinds.py +41 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/playlist_lifecycle.py +55 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_shared/playlist_properties.py +193 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_writer/__init__.py +146 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_writer/hash58.py +279 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_writer/hash72.py +496 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_writer/hashab.py +290 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_writer/mhbd_writer.py +1588 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_writer/mhip_writer.py +150 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_writer/mhit_writer.py +511 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_writer/mhla_writer.py +211 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_writer/mhli_writer.py +141 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_writer/mhlp_writer.py +261 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_writer/mhlt_writer.py +52 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_writer/mhod52_writer.py +344 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_writer/mhod_spl_writer.py +448 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_writer/mhod_writer.py +589 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_writer/mhsd_writer.py +98 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_writer/mhyp_writer.py +641 -0
- pypodlib-0.1.0/src/pypodlib/itunesdb_writer/wasm/calcHashAB.wasm +0 -0
- pypodlib-0.1.0/src/pypodlib/sqlitedb_writer/__init__.py +21 -0
- pypodlib-0.1.0/src/pypodlib/sqlitedb_writer/_helpers.py +62 -0
- pypodlib-0.1.0/src/pypodlib/sqlitedb_writer/cbk_writer.py +183 -0
- pypodlib-0.1.0/src/pypodlib/sqlitedb_writer/dynamic_writer.py +123 -0
- pypodlib-0.1.0/src/pypodlib/sqlitedb_writer/extras_writer.py +84 -0
- pypodlib-0.1.0/src/pypodlib/sqlitedb_writer/genius_writer.py +56 -0
- pypodlib-0.1.0/src/pypodlib/sqlitedb_writer/library_writer.py +1187 -0
- pypodlib-0.1.0/src/pypodlib/sqlitedb_writer/locations_writer.py +145 -0
- pypodlib-0.1.0/src/pypodlib/sqlitedb_writer/sqlite_writer.py +310 -0
- pypodlib-0.1.0/src/pypodlib/sync/__init__.py +182 -0
- pypodlib-0.1.0/src/pypodlib/sync/_db_io.py +580 -0
- pypodlib-0.1.0/src/pypodlib/sync/_formats.py +79 -0
- pypodlib-0.1.0/src/pypodlib/sync/_playlist_builder.py +645 -0
- pypodlib-0.1.0/src/pypodlib/sync/_track_conversion.py +462 -0
- pypodlib-0.1.0/src/pypodlib/sync/album_chapters.py +1185 -0
- pypodlib-0.1.0/src/pypodlib/sync/audio_fingerprint.py +517 -0
- pypodlib-0.1.0/src/pypodlib/sync/backup_manager.py +3762 -0
- pypodlib-0.1.0/src/pypodlib/sync/capability_filter.py +61 -0
- pypodlib-0.1.0/src/pypodlib/sync/contracts.py +601 -0
- pypodlib-0.1.0/src/pypodlib/sync/core/__init__.py +27 -0
- pypodlib-0.1.0/src/pypodlib/sync/core/context.py +115 -0
- pypodlib-0.1.0/src/pypodlib/sync/core/engine.py +283 -0
- pypodlib-0.1.0/src/pypodlib/sync/core/models.py +141 -0
- pypodlib-0.1.0/src/pypodlib/sync/database_commit.py +251 -0
- pypodlib-0.1.0/src/pypodlib/sync/dependency_manager.py +352 -0
- pypodlib-0.1.0/src/pypodlib/sync/existing_track_matcher.py +268 -0
- pypodlib-0.1.0/src/pypodlib/sync/fingerprint_diff_engine.py +2132 -0
- pypodlib-0.1.0/src/pypodlib/sync/integrity.py +333 -0
- pypodlib-0.1.0/src/pypodlib/sync/ipod_track_paths.py +272 -0
- pypodlib-0.1.0/src/pypodlib/sync/itunes_prefs.py +666 -0
- pypodlib-0.1.0/src/pypodlib/sync/lastfm_scrobbler.py +482 -0
- pypodlib-0.1.0/src/pypodlib/sync/lb_scrobbler.py +583 -0
- pypodlib-0.1.0/src/pypodlib/sync/mapping.py +604 -0
- pypodlib-0.1.0/src/pypodlib/sync/path_identity.py +34 -0
- pypodlib-0.1.0/src/pypodlib/sync/pc_library.py +1770 -0
- pypodlib-0.1.0/src/pypodlib/sync/photos.py +2706 -0
- pypodlib-0.1.0/src/pypodlib/sync/plan_validator.py +595 -0
- pypodlib-0.1.0/src/pypodlib/sync/planning_stages.py +229 -0
- pypodlib-0.1.0/src/pypodlib/sync/playlist_parser.py +355 -0
- pypodlib-0.1.0/src/pypodlib/sync/quick_writes.py +384 -0
- pypodlib-0.1.0/src/pypodlib/sync/review_selection.py +254 -0
- pypodlib-0.1.0/src/pypodlib/sync/rockbox_metadata.py +1341 -0
- pypodlib-0.1.0/src/pypodlib/sync/scrobble_plan.py +43 -0
- pypodlib-0.1.0/src/pypodlib/sync/source_identity.py +255 -0
- pypodlib-0.1.0/src/pypodlib/sync/spl_evaluator.py +571 -0
- pypodlib-0.1.0/src/pypodlib/sync/sync_executor.py +4673 -0
- pypodlib-0.1.0/src/pypodlib/sync/sync_playlist_files.py +191 -0
- pypodlib-0.1.0/src/pypodlib/sync/sync_playlist_planner.py +204 -0
- pypodlib-0.1.0/src/pypodlib/sync/track_identity.py +194 -0
- pypodlib-0.1.0/src/pypodlib/sync/transcode_cache.py +829 -0
- pypodlib-0.1.0/src/pypodlib/sync/transcoder.py +2292 -0
- pypodlib-0.1.0/src/pypodlib/sync/unknown_metadata.py +307 -0
- pypodlib-0.1.0/src/pypodlib/sync_progress_stages.py +295 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
|
|
9
|
+
# MkDocs build output
|
|
10
|
+
site/
|
|
11
|
+
|
|
12
|
+
# Virtual environments
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
|
|
16
|
+
# Tooling
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.mypy_cache/
|
|
19
|
+
.ruff_cache/
|
|
20
|
+
.coverage
|
|
21
|
+
htmlcov/
|
|
22
|
+
|
|
23
|
+
# Editor / OS
|
|
24
|
+
.idea/
|
|
25
|
+
.vscode/
|
|
26
|
+
.DS_Store
|
|
27
|
+
|
|
28
|
+
# Local build/install flags
|
|
29
|
+
*.local
|
pypodlib-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 pyPodLib contributors
|
|
4
|
+
|
|
5
|
+
pyPodLib is derived from the iOpenPod project
|
|
6
|
+
(https://github.com/TheRealSavi/iOpenPod), Copyright (c) John Gibbons,
|
|
7
|
+
licensed under the MIT License, which grants permission to copy, modify,
|
|
8
|
+
merge, publish, distribute, sublicense, and/or sell copies of the Software.
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
pypodlib-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pypodlib
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Communicate with iPod Classic/Nano/Mini devices — detect and identify the device, read and write the iTunesDB library, and sync media — without iTunes.
|
|
5
|
+
Project-URL: Homepage, https://la22e.github.io/pyPodLib/
|
|
6
|
+
Project-URL: Repository, https://github.com/La22e/pyPodLib
|
|
7
|
+
Author: La22e
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: ipod,ipod-classic,ipod-manager,ipod-sync,itunes,itunes-alternative,itunesdb,music-library,music-sync
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
19
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Players
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: libusb-package>=1.0.30.0
|
|
22
|
+
Requires-Dist: pycryptodome>=3.20.0
|
|
23
|
+
Requires-Dist: pyusb>=1.3.1
|
|
24
|
+
Requires-Dist: wasmtime>=30.0.0
|
|
25
|
+
Provides-Extra: artwork
|
|
26
|
+
Requires-Dist: mutagen>=1.47.0; extra == 'artwork'
|
|
27
|
+
Requires-Dist: numpy>=2.0.0; extra == 'artwork'
|
|
28
|
+
Requires-Dist: pillow>=10.0.0; extra == 'artwork'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# pyPodLib
|
|
32
|
+
|
|
33
|
+
Read, write, and sync iPod Classic/Nano/Mini libraries from Python — no
|
|
34
|
+
iTunes required.
|
|
35
|
+
|
|
36
|
+
pyPodLib is a pure-Python library extracted from
|
|
37
|
+
[iOpenPod](https://github.com/TheRealSavi/iOpenPod) (MIT, © John Gibbons).
|
|
38
|
+
It keeps the battle-tested iTunesDB / iTunesCDB parsing and writing engine,
|
|
39
|
+
device detection, and sync logic, and exposes it as a headless library.
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
|
|
43
|
+
- **Device detection** — scan for connected iPods and identify model,
|
|
44
|
+
generation, capacity, colour, serial, and checksum type (`scan_ipods`,
|
|
45
|
+
`identify_ipod_at_path`).
|
|
46
|
+
- **Read** — parse the full library: tracks, user playlists, podcast
|
|
47
|
+
playlists, smart playlists (dataset 2/3/5), play counts, artwork refs.
|
|
48
|
+
- **Write** — save edits back with the correct per-device signature
|
|
49
|
+
(HASH58 via FireWire GUID, HASH72 via AES, HASHAB via WebAssembly,
|
|
50
|
+
NONE for pre-2007 devices), plus SQLite DBs for Nano 6G/7G and
|
|
51
|
+
iTunesPrefs protection.
|
|
52
|
+
- **Sync** — plan and execute media sync, with optional artwork and
|
|
53
|
+
transcode support.
|
|
54
|
+
- **Testable without hardware** — create "virtual iPods" (a folder
|
|
55
|
+
identity + seeded database) for round-trip tests
|
|
56
|
+
(`pypodlib.device.create_virtual_ipod`).
|
|
57
|
+
|
|
58
|
+
## Install
|
|
59
|
+
|
|
60
|
+
```console
|
|
61
|
+
pip install pypodlib # core (device + library read/write)
|
|
62
|
+
pip install 'pypodlib[artwork]' # + embedded-art extraction/encoding
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Runtime dependencies: `pyusb` / `libusb-package` (USB identification),
|
|
66
|
+
`pycryptodome` (HASH72 signing), `wasmtime` (HASHAB signing).
|
|
67
|
+
|
|
68
|
+
## Documentation
|
|
69
|
+
|
|
70
|
+
Full documentation: [la22e.github.io/pyPodLib](https://la22e.github.io/pyPodLib/)
|
|
71
|
+
|
|
72
|
+
## Quick start
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
import pypodlib
|
|
76
|
+
|
|
77
|
+
ipod = pypodlib.connect("/media/user/IPOD") # or pypodlib.scan_ipods()[0]
|
|
78
|
+
print(ipod.model_number, ipod.generation, ipod.capacity)
|
|
79
|
+
|
|
80
|
+
lib = ipod.library()
|
|
81
|
+
print(len(lib.tracks), "tracks")
|
|
82
|
+
|
|
83
|
+
track = lib.tracks[0]
|
|
84
|
+
track.title = "New Title"
|
|
85
|
+
track.rating = 80 # 4 stars
|
|
86
|
+
ipod.save() # writes iTunesDB + signature
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Low-level access
|
|
90
|
+
|
|
91
|
+
Every layer stays importable:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from pypodlib.device import scan_for_ipods, identify_ipod_at_path
|
|
95
|
+
from pypodlib.itunesdb_parser import parse_itunesdb, load_ipod_library
|
|
96
|
+
from pypodlib.itunesdb_writer import write_itunesdb, detect_checksum_type
|
|
97
|
+
from pypodlib.sync._db_io import read_existing_database, write_database
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Virtual iPods (no hardware)
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from pypodlib.device import create_virtual_ipod, available_virtual_ipod_models
|
|
104
|
+
|
|
105
|
+
import tempfile
|
|
106
|
+
root = tempfile.mkdtemp()
|
|
107
|
+
info = create_virtual_ipod(root, "MC297") # iPod Classic 7th Gen (HASH58)
|
|
108
|
+
ipod = pypodlib.connect(root)
|
|
109
|
+
ipod.library()
|
|
110
|
+
ipod.save() # round-trips through real writer
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Limitations
|
|
114
|
+
|
|
115
|
+
- Requires a mounted iPod volume (or a virtual iPod root) — USB enumeration
|
|
116
|
+
is used for identification only.
|
|
117
|
+
- Photo sync and embedded-art encoding need the `artwork` extra
|
|
118
|
+
(`numpy`, `Pillow`, `mutagen`).
|
|
119
|
+
- Video/ffmpeg transcoding requires an `ffmpeg`/`ffprobe` binary.
|
|
120
|
+
- Smart playlists are re-evaluated on save (matching iTunes behaviour).
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
|
|
124
|
+
MIT. pyPodLib is derived from iOpenPod (MIT, © John Gibbons).
|
pypodlib-0.1.0/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# pyPodLib
|
|
2
|
+
|
|
3
|
+
Read, write, and sync iPod Classic/Nano/Mini libraries from Python — no
|
|
4
|
+
iTunes required.
|
|
5
|
+
|
|
6
|
+
pyPodLib is a pure-Python library extracted from
|
|
7
|
+
[iOpenPod](https://github.com/TheRealSavi/iOpenPod) (MIT, © John Gibbons).
|
|
8
|
+
It keeps the battle-tested iTunesDB / iTunesCDB parsing and writing engine,
|
|
9
|
+
device detection, and sync logic, and exposes it as a headless library.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Device detection** — scan for connected iPods and identify model,
|
|
14
|
+
generation, capacity, colour, serial, and checksum type (`scan_ipods`,
|
|
15
|
+
`identify_ipod_at_path`).
|
|
16
|
+
- **Read** — parse the full library: tracks, user playlists, podcast
|
|
17
|
+
playlists, smart playlists (dataset 2/3/5), play counts, artwork refs.
|
|
18
|
+
- **Write** — save edits back with the correct per-device signature
|
|
19
|
+
(HASH58 via FireWire GUID, HASH72 via AES, HASHAB via WebAssembly,
|
|
20
|
+
NONE for pre-2007 devices), plus SQLite DBs for Nano 6G/7G and
|
|
21
|
+
iTunesPrefs protection.
|
|
22
|
+
- **Sync** — plan and execute media sync, with optional artwork and
|
|
23
|
+
transcode support.
|
|
24
|
+
- **Testable without hardware** — create "virtual iPods" (a folder
|
|
25
|
+
identity + seeded database) for round-trip tests
|
|
26
|
+
(`pypodlib.device.create_virtual_ipod`).
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```console
|
|
31
|
+
pip install pypodlib # core (device + library read/write)
|
|
32
|
+
pip install 'pypodlib[artwork]' # + embedded-art extraction/encoding
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Runtime dependencies: `pyusb` / `libusb-package` (USB identification),
|
|
36
|
+
`pycryptodome` (HASH72 signing), `wasmtime` (HASHAB signing).
|
|
37
|
+
|
|
38
|
+
## Documentation
|
|
39
|
+
|
|
40
|
+
Full documentation: [la22e.github.io/pyPodLib](https://la22e.github.io/pyPodLib/)
|
|
41
|
+
|
|
42
|
+
## Quick start
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import pypodlib
|
|
46
|
+
|
|
47
|
+
ipod = pypodlib.connect("/media/user/IPOD") # or pypodlib.scan_ipods()[0]
|
|
48
|
+
print(ipod.model_number, ipod.generation, ipod.capacity)
|
|
49
|
+
|
|
50
|
+
lib = ipod.library()
|
|
51
|
+
print(len(lib.tracks), "tracks")
|
|
52
|
+
|
|
53
|
+
track = lib.tracks[0]
|
|
54
|
+
track.title = "New Title"
|
|
55
|
+
track.rating = 80 # 4 stars
|
|
56
|
+
ipod.save() # writes iTunesDB + signature
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Low-level access
|
|
60
|
+
|
|
61
|
+
Every layer stays importable:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from pypodlib.device import scan_for_ipods, identify_ipod_at_path
|
|
65
|
+
from pypodlib.itunesdb_parser import parse_itunesdb, load_ipod_library
|
|
66
|
+
from pypodlib.itunesdb_writer import write_itunesdb, detect_checksum_type
|
|
67
|
+
from pypodlib.sync._db_io import read_existing_database, write_database
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Virtual iPods (no hardware)
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from pypodlib.device import create_virtual_ipod, available_virtual_ipod_models
|
|
74
|
+
|
|
75
|
+
import tempfile
|
|
76
|
+
root = tempfile.mkdtemp()
|
|
77
|
+
info = create_virtual_ipod(root, "MC297") # iPod Classic 7th Gen (HASH58)
|
|
78
|
+
ipod = pypodlib.connect(root)
|
|
79
|
+
ipod.library()
|
|
80
|
+
ipod.save() # round-trips through real writer
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Limitations
|
|
84
|
+
|
|
85
|
+
- Requires a mounted iPod volume (or a virtual iPod root) — USB enumeration
|
|
86
|
+
is used for identification only.
|
|
87
|
+
- Photo sync and embedded-art encoding need the `artwork` extra
|
|
88
|
+
(`numpy`, `Pillow`, `mutagen`).
|
|
89
|
+
- Video/ffmpeg transcoding requires an `ffmpeg`/`ffprobe` binary.
|
|
90
|
+
- Smart playlists are re-evaluated on save (matching iTunes behaviour).
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
MIT. pyPodLib is derived from iOpenPod (MIT, © John Gibbons).
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pypodlib"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Communicate with iPod Classic/Nano/Mini devices — detect and identify the device, read and write the iTunesDB library, and sync media — without iTunes."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "La22e"},
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"ipod", "ipod-classic", "itunes", "itunesdb", "music-sync",
|
|
17
|
+
"ipod-manager", "music-library", "ipod-sync", "itunes-alternative",
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 3 - Alpha",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Operating System :: OS Independent",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Topic :: Multimedia :: Sound/Audio",
|
|
28
|
+
"Topic :: Multimedia :: Sound/Audio :: Players",
|
|
29
|
+
]
|
|
30
|
+
dependencies = [
|
|
31
|
+
"pyusb>=1.3.1",
|
|
32
|
+
"libusb-package>=1.0.30.0",
|
|
33
|
+
"pycryptodome>=3.20.0",
|
|
34
|
+
"wasmtime>=30.0.0",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
artwork = [
|
|
39
|
+
"numpy>=2.0.0",
|
|
40
|
+
"pillow>=10.0.0",
|
|
41
|
+
"mutagen>=1.47.0",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.urls]
|
|
45
|
+
Homepage = "https://la22e.github.io/pyPodLib/"
|
|
46
|
+
Repository = "https://github.com/La22e/pyPodLib"
|
|
47
|
+
|
|
48
|
+
[dependency-groups]
|
|
49
|
+
dev = [
|
|
50
|
+
"mypy>=1.15.0",
|
|
51
|
+
"pytest>=8.3.5",
|
|
52
|
+
"ruff>=0.11.7",
|
|
53
|
+
"mkdocs>=1.6",
|
|
54
|
+
"mkdocs-material>=9.5",
|
|
55
|
+
"mkdocstrings[python]>=0.28",
|
|
56
|
+
"mkdocs-gen-files",
|
|
57
|
+
"mkdocs-literate-nav",
|
|
58
|
+
"mkdocs-section-index",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[tool.hatch.build.targets.wheel]
|
|
62
|
+
packages = ["src/pypodlib"]
|
|
63
|
+
include = [
|
|
64
|
+
"/src/pypodlib/**/*.py",
|
|
65
|
+
"/src/pypodlib/itunesdb_writer/wasm/calcHashAB.wasm",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
[tool.hatch.build.targets.sdist]
|
|
69
|
+
include = [
|
|
70
|
+
"/src",
|
|
71
|
+
"/README.md",
|
|
72
|
+
"/LICENSE",
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
[tool.pytest.ini_options]
|
|
76
|
+
addopts = "-p no:cacheprovider"
|
|
77
|
+
testpaths = ["tests"]
|
|
78
|
+
pythonpath = ["."]
|
|
79
|
+
|
|
80
|
+
[tool.ruff]
|
|
81
|
+
line-length = 320
|
|
82
|
+
target-version = "py311"
|
|
83
|
+
|
|
84
|
+
[tool.ruff.lint]
|
|
85
|
+
select = ["B", "E", "F", "I", "UP"]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pypodlib — communicate with iPod Classic/Nano/Mini devices from Python.
|
|
3
|
+
|
|
4
|
+
pyPodLib is a pure-Python library that reads and writes the iPod's on-disk
|
|
5
|
+
library (iTunesDB / iTunesCDB) without iTunes. It can:
|
|
6
|
+
|
|
7
|
+
- detect and identify a connected iPod (model, generation, capacity, colour)
|
|
8
|
+
- read tracks, playlists, podcasts, smart playlists and play counts
|
|
9
|
+
- write the database back with the correct per-device checksum signature
|
|
10
|
+
- copy media files onto the device and regenerate the database
|
|
11
|
+
|
|
12
|
+
Typical usage::
|
|
13
|
+
|
|
14
|
+
import pypodlib
|
|
15
|
+
|
|
16
|
+
ipod = pypodlib.connect("/media/user/IPOD") # or scan_ipods()[0]
|
|
17
|
+
print(ipod.model.family, ipod.model.generation)
|
|
18
|
+
|
|
19
|
+
lib = ipod.library()
|
|
20
|
+
for track in lib.tracks:
|
|
21
|
+
print(track.title, track.artist)
|
|
22
|
+
|
|
23
|
+
lib.tracks[0].rating = 5
|
|
24
|
+
ipod.save()
|
|
25
|
+
|
|
26
|
+
The low-level modules remain importable directly (``pypodlib.device``,
|
|
27
|
+
``pypodlib.itunesdb_parser``, ``pypodlib.itunesdb_writer``, ...).
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
from .api import IPod, Library, Playlist, Track, connect, library_from_path, scan_ipods
|
|
33
|
+
|
|
34
|
+
__version__ = "0.1.0"
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
"__version__",
|
|
38
|
+
"connect",
|
|
39
|
+
"scan_ipods",
|
|
40
|
+
"library_from_path",
|
|
41
|
+
"IPod",
|
|
42
|
+
"Library",
|
|
43
|
+
"Track",
|
|
44
|
+
"Playlist",
|
|
45
|
+
]
|