diff --git a/src/ipod_nano7_db.py b/src/ipod_nano7_db.py index 1f635c3..bd25c03 100644 --- a/src/ipod_nano7_db.py +++ b/src/ipod_nano7_db.py @@ -35,6 +35,8 @@ from typing import Any, Optional from mutagen import File as MutagenFile from mutagen.easyid3 import EasyID3 +from artwork import prepare_artwork_for_track, ArtworkEntry, write_artworkdb + _IOP_ROOT = "/tmp/iOpenPod" _IOP_CACHE: dict[str, Any] = {} _IOP_READY = False @@ -188,7 +190,8 @@ def _read_audio_tags(filepath: str) -> dict[str, Any]: def _scanned_tracks_to_iop(mountpoint: str, - scanned: list[dict]) -> list: + scanned: list[dict], + artwork_map: dict[str, tuple[int, int, int]] | None = None) -> list: """Convert our scanned track dicts to iOpenPod TrackInfo objects.""" IopTrackInfo = _import_iop("iTunesDB_Writer.mhit_writer").TrackInfo tracks: list = [] @@ -201,6 +204,12 @@ def _scanned_tracks_to_iop(mountpoint: str, location = f":iPod_Control:Music:{rel.replace('/', ':')}" size = os.path.getsize(fpath) + art_count = 0 + art_size = 0 + mhii_link = 0 + if artwork_map and fpath in artwork_map: + mhii_link, art_count, art_size = artwork_map[fpath] + t = IopTrackInfo( title=s.get("title") or base, location=location, @@ -219,9 +228,9 @@ def _scanned_tracks_to_iop(mountpoint: str, total_tracks=s.get("track_count", 0), disc_number=s.get("disc_number", 0), total_discs=s.get("disc_count", 0), - artwork_count=s.get("art_count", 0), - artwork_size=s.get("art_size", 0), - mhii_link=s.get("mhii_link", 0), + artwork_count=art_count, + artwork_size=art_size, + mhii_link=mhii_link, ) tracks.append(t) return tracks @@ -487,11 +496,58 @@ class Nano7Database: self._write_empty_databases() return - # 2. Convert to iOpenPod TrackInfo - tracks = _scanned_tracks_to_iop(self.mountpoint, scanned) + # 2. Prepare artwork for all tracks + artwork_map: dict[str, tuple[int, int, int]] = {} + art_entries: list[ArtworkEntry] = [] + art_hash_to_img_id: dict[str, int] = {} + next_img_id = 100 + + for idx, s in enumerate(scanned): + fpath = s["file_path"] + try: + art_result = prepare_artwork_for_track(fpath) + except Exception: + art_result = None + if art_result is not None: + art_hash, formats = art_result + art_count = len(formats) + total_size = sum(p.size for p in formats.values()) + + if art_hash not in art_hash_to_img_id: + img_id = next_img_id + next_img_id += 1 + art_hash_to_img_id[art_hash] = img_id + art_entries.append(ArtworkEntry( + img_id=img_id, + db_track_id=idx, + art_hash=art_hash, + src_img_size=total_size, + formats=formats, + db_track_ids=[idx], + )) + else: + img_id = art_hash_to_img_id[art_hash] + for entry in art_entries: + if entry.img_id == img_id: + if idx not in entry.db_track_ids: + entry.db_track_ids.append(idx) + break + + artwork_map[fpath] = (img_id, art_count, total_size) + + if art_entries: + try: + write_artworkdb(self.mountpoint, art_entries) + logger.info("ArtworkDB written: %d unique covers, %d total tracks", + len(art_entries), len(artwork_map)) + except Exception as e: + logger.warning("ArtworkDB write failed: %s", e) + + # 3. Convert to iOpenPod TrackInfo + tracks = _scanned_tracks_to_iop(self.mountpoint, scanned, artwork_map) logger.info("Built %d iOpenPod TrackInfo objects", len(tracks)) - # 3. Determine capabilities for this device + # 4. Determine capabilities for this device ipod_device = _import_iop("ipod_device") capabilities = None try: @@ -505,7 +561,7 @@ class Nano7Database: except Exception as e: logger.debug("Capabilities detection failed: %s", e) - # 4. Generate and write binary iTunesDB first (to get db_pid) + # 5. Generate and write binary iTunesDB first (to get db_pid) try: firewire_id = ipod_device.get_firewire_id(self.mountpoint) except Exception: @@ -525,7 +581,7 @@ class Nano7Database: return logger.info("iTunesDB written (%d tracks)", len(tracks)) - # 5. Extract db_pid from the binary DB for SQLite cross-reference + # 6. Extract db_pid from the binary DB for SQLite cross-reference db_pid = 0 try: ipod_dev = _import_iop("ipod_device") @@ -539,7 +595,7 @@ class Nano7Database: except Exception as e: logger.warning("Could not extract db_pid: %s", e) - # 6. Write SQLite databases (Nano 6G/7G require this) + # 7. Write SQLite databases (Nano 6G/7G require this) try: write_sqlite_databases = _import_iop("SQLiteDB_Writer").write_sqlite_databases sqlite_ok = write_sqlite_databases(