Fix UI freeze on iPod mount/track-load and add content_hash/sync-metadata

- Move mount_device, get_device_info, and track scanning to background
  WorkerThread (new 'mount_and_load' task type) so the UI stays responsive
- Fix DeviceMonitor auto-poll to run detect_devices() in a background thread
- Refactor _on_device_detection_finished and _on_mount_clicked to use the
  new worker; remove synchronous _set_device_mounted
- Avoid double scan (get_track_count + get_all_tracks) — scan once in worker
- Add content_hash fingerprint to _scan_ipod_files and library_cache for
  track matching
- Add update_track_metadata / _write_databases_from_tracks with artwork
  override support in Nano7Database
- Add 'Sync Metadata' button and context menu action in LibraryTab
This commit is contained in:
Maksim Totmin
2026-06-01 12:37:49 +07:00
parent 25bb8105ac
commit 3bfb0ebbfe
6 changed files with 437 additions and 74 deletions
+28 -9
View File
@@ -1,13 +1,25 @@
import logging
from typing import Set
from PyQt6.QtCore import QObject, QTimer, pyqtSignal
from PyQt6.QtCore import QObject, QTimer, QThread, pyqtSignal
from ipod_device import IPodDevice
logger = logging.getLogger(__name__)
class _DetectWorker(QThread):
finished = pyqtSignal(object)
def run(self):
try:
devices = IPodDevice().detect_devices()
self.finished.emit(devices)
except Exception:
logger.exception("Device detection failed")
self.finished.emit([])
class DeviceMonitor(QObject):
state_changed = pyqtSignal(list)
@@ -17,6 +29,7 @@ class DeviceMonitor(QObject):
self.timer = QTimer(self)
self.timer.timeout.connect(self._poll)
self._device_ids: Set[str] = set()
self._poll_worker = None
def start(self, interval_ms: int = 3000):
self._device_ids.clear()
@@ -25,14 +38,20 @@ class DeviceMonitor(QObject):
def stop(self):
self.timer.stop()
if self._poll_worker and self._poll_worker.isRunning():
self._poll_worker.quit()
self._poll_worker.wait(3000)
self._device_ids.clear()
def _poll(self):
try:
devices = IPodDevice().detect_devices()
current_ids = {d["id"] for d in devices}
if current_ids != self._device_ids:
self._device_ids = current_ids
self.state_changed.emit(devices)
except Exception:
logger.exception("DeviceMonitor poll failed")
if self._poll_worker and self._poll_worker.isRunning():
return
self._poll_worker = _DetectWorker()
self._poll_worker.finished.connect(self._on_devices_detected)
self._poll_worker.start()
def _on_devices_detected(self, devices):
current_ids = {d["id"] for d in devices}
if current_ids != self._device_ids:
self._device_ids = current_ids
self.state_changed.emit(devices)