fix: robust iOpenPod imports and sync error handling
_import_iop() now permanently evicts local ipod_device from sys.modules on first call (instead of restoring it), caches all iOpenPod modules, and avoids the spurious ModuleNotFoundError cascade on rapid calls. sync_itunescdb() wrapped in try/except — no longer propagates import/sync errors to add_track() callers. ipod_device.py fallback no longer duplicates files to /Music/ when add_track() already copied them to F00/.
This commit is contained in:
parent
4f7bd2709c
commit
0f12c4985f
@ -543,9 +543,10 @@ class IPodDevice:
|
||||
except ImportError:
|
||||
self.logger.warning("ipod_nano7_db not available, using simple file copy")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Nano7 database transfer failed: {e}, falling back to simple copy")
|
||||
self.logger.error(f"Nano7 database transfer failed: {e}")
|
||||
return False # Don't fall back — file was already copied to F00/
|
||||
|
||||
# Fallback: simple file copy (won't show in iPod interface)
|
||||
# Fallback: simple file copy — only on ImportError
|
||||
if relative_path:
|
||||
dest_dir = os.path.join(self.mount_point, relative_path)
|
||||
else:
|
||||
|
||||
@ -36,34 +36,43 @@ from mutagen import File as MutagenFile
|
||||
from mutagen.easyid3 import EasyID3
|
||||
|
||||
_IOP_ROOT = "/tmp/iOpenPod"
|
||||
_IOP_CACHE: dict[str, Any] = {}
|
||||
_IOP_READY = False
|
||||
|
||||
|
||||
def _import_iop(module_name: str):
|
||||
"""Import a module from iOpenPod, bypassing any locally-cached conflict.
|
||||
"""Import a module from iOpenPod, evicting our local ipod_device once.
|
||||
|
||||
main.py imports our local ``ipod_device`` first, which caches it in
|
||||
``sys.modules``. When ipod_nano7_db later tries ``from ipod_device
|
||||
import ChecksumType``, Python returns the cached *local* module
|
||||
(which lacks ChecksumType). This helper temporarily evicts the
|
||||
conflicting local modules, adds the iOpenPod root to sys.path, loads
|
||||
the requested module from there, then restores the path.
|
||||
main.py imports our local ``ipod_device`` early, caching it in
|
||||
``sys.modules``. On the FIRST call here we permanently evict the
|
||||
local module so iOpenPod's ``ipod_device`` takes its place in
|
||||
``sys.modules``. Our local classes (IPodDevice) were already
|
||||
bound by ``from ipod_device import IPodDevice`` in main.py before
|
||||
the eviction, so they stay alive through their own references.
|
||||
|
||||
Subsequent calls are served from a simple module cache.
|
||||
"""
|
||||
# Modules that may be cached as our local copies and need eviction.
|
||||
_conflicting = {"ipod_device"}
|
||||
_saved = {}
|
||||
for name in _conflicting & set(sys.modules):
|
||||
_saved[name] = sys.modules.pop(name)
|
||||
global _IOP_READY
|
||||
if module_name in _IOP_CACHE:
|
||||
return _IOP_CACHE[module_name]
|
||||
|
||||
if not _IOP_READY:
|
||||
# Evict local shadows ONCE so iOpenPod modules resolve correctly.
|
||||
sys.modules.pop("ipod_device", None)
|
||||
_IOP_READY = True
|
||||
logger.debug("iOpenPod boot: evicted local ipod_device")
|
||||
|
||||
had_iop = _IOP_ROOT in sys.path
|
||||
if not had_iop:
|
||||
sys.path.insert(0, _IOP_ROOT)
|
||||
|
||||
try:
|
||||
return importlib.import_module(module_name)
|
||||
mod = importlib.import_module(module_name)
|
||||
finally:
|
||||
if not had_iop:
|
||||
sys.path.remove(_IOP_ROOT)
|
||||
sys.modules.update(_saved)
|
||||
|
||||
_IOP_CACHE[module_name] = mod
|
||||
return mod
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -463,12 +472,13 @@ class Nano7Database:
|
||||
# ── sync: regenerate everything via iOpenPod ──────────────────────
|
||||
|
||||
def sync_itunescdb(self) -> None:
|
||||
"""Regenerate ALL databases using iOpenPod's writers.
|
||||
"""Regenerate ALL databases using iOpenPod's writers."""
|
||||
try:
|
||||
self._sync_itunescdb_impl()
|
||||
except Exception as e:
|
||||
logger.exception("iTunesDB sync failed: %s", e)
|
||||
|
||||
Scans all audio files on iPod, builds correct TrackInfo list,
|
||||
writes SQLite databases (Library/Locations/Dynamic/Extras/Genius),
|
||||
writes binary iTunesDB, writes iTunesCDB.ext and Locations.cbk.
|
||||
"""
|
||||
def _sync_itunescdb_impl(self) -> None:
|
||||
# 1. Scan all audio files on iPod
|
||||
scanned = self._scan_ipod_files()
|
||||
if not scanned:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user