fix: reliable play count sync with auto-sync + deferred iTunesDB commit
Stops double-counting when iPod Nano 7G regenerates Play Counts file after a DB write (firmware quirk, new mtime with stale data). Architecture: - Mount: only max(cache, base) — never reads Play Counts delta - 2s after mount → auto-sync worker: reads delta, subtracts stored (persistent guard), applies effective to cache, defers DB write - iTunesDB commit deferred to next stable mount (checks W_OK first) - read_play_stats() uses 3s retry + read_play_counts_delta() fallback (filesystem-based PC parsing, independent of iTunesDB I/O errors) Changes: - ipod_nano7_db.py: sync_itunescdb() returns bool; new read_play_counts_delta() method; get_all_tracks() no longer adds delta; _merge_play_stats() drops delta addition; suppress iOpenPod ERROR logs to CRITICAL - library_cache.py: schema v2 with playcounts_log table + get/set_last_synced_delta, migration from v1 - worker.py: session guards (_processed_fwids, _auto_synced_fwids, _pending_commit_fwids); auto_sync_playcounts task; deferred commit in mount - app.py: QTimer.singleShot(2000) after mount for auto-sync - ipod_tab.py: _on_auto_state_changed rewritten (no unmount, mp guard) - library_tab.py: refresh_play_counts_from_cache() method
This commit is contained in:
@@ -685,6 +685,52 @@ class LibraryTab(QWidget):
|
||||
self._restore_playing_row()
|
||||
self._build_album_grid()
|
||||
|
||||
def refresh_play_counts_from_cache(self):
|
||||
"""Reload play-count values from the persistent cache without a
|
||||
full library rescan.
|
||||
|
||||
Called after ``mount_and_load`` completes — the cache already
|
||||
holds merged iPod + local play stats, but the table was
|
||||
rendered before the mount and displays stale values.
|
||||
"""
|
||||
cache = get_library_cache()
|
||||
updated = 0
|
||||
|
||||
ready_index = {t["path"]: i for i, t in enumerate(self.library_ready)}
|
||||
|
||||
for row in range(self.library_table.rowCount()):
|
||||
item_0 = self.library_table.item(row, 0)
|
||||
if item_0 is None:
|
||||
continue
|
||||
data = item_0.data(Qt.ItemDataRole.UserRole)
|
||||
if data is None:
|
||||
continue
|
||||
_is_ready, track = data
|
||||
if not _is_ready:
|
||||
continue
|
||||
path = track.get("path", "")
|
||||
if not path:
|
||||
continue
|
||||
entry = cache.get(path)
|
||||
if entry is None:
|
||||
continue
|
||||
|
||||
new_pc = entry.get("play_count", 0)
|
||||
old_pc = track.get("play_count", 0)
|
||||
if new_pc != old_pc:
|
||||
track["play_count"] = new_pc
|
||||
pc_cell = self.library_table.item(row, 7)
|
||||
if pc_cell:
|
||||
pc_cell.setText(str(new_pc))
|
||||
|
||||
idx = ready_index.get(path)
|
||||
if idx is not None:
|
||||
self.library_ready[idx]["play_count"] = new_pc
|
||||
updated += 1
|
||||
|
||||
if updated:
|
||||
logger.debug("Refreshed play counts for %d tracks from cache", updated)
|
||||
|
||||
def _restore_playing_row(self):
|
||||
idx = self.current_playback_index
|
||||
if idx < 0:
|
||||
|
||||
Reference in New Issue
Block a user