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:
+91
-1
@@ -214,6 +214,12 @@ class LibraryTab(QWidget):
|
||||
self.library_transfer_btn.clicked.connect(self._on_library_transfer_clicked)
|
||||
toolbar.addWidget(self.library_transfer_btn)
|
||||
|
||||
self.library_sync_btn = QPushButton("\uD83D\uDD04 Sync Metadata")
|
||||
self.library_sync_btn.setEnabled(False)
|
||||
self.library_sync_btn.setToolTip("Sync metadata and artwork for all tracks on iPod")
|
||||
self.library_sync_btn.clicked.connect(self._on_sync_metadata_clicked)
|
||||
toolbar.addWidget(self.library_sync_btn)
|
||||
|
||||
self.library_remove_btn = QPushButton("\uD83D\uDDD1\uFE0F Remove")
|
||||
self.library_remove_btn.setEnabled(False)
|
||||
self.library_remove_btn.clicked.connect(self._on_library_remove_selected)
|
||||
@@ -233,6 +239,7 @@ class LibraryTab(QWidget):
|
||||
self.library_add_btn,
|
||||
self.library_convert_btn,
|
||||
self.library_transfer_btn,
|
||||
self.library_sync_btn,
|
||||
self.library_remove_btn,
|
||||
self.library_edit_btn,
|
||||
refresh_btn,
|
||||
@@ -717,7 +724,9 @@ class LibraryTab(QWidget):
|
||||
has_ready = ready_count > 0
|
||||
has_source = source_count > 0
|
||||
|
||||
self.library_transfer_btn.setEnabled(has_ready and self._current_mount_point is not None)
|
||||
has_mount = self._current_mount_point is not None
|
||||
self.library_transfer_btn.setEnabled(has_ready and has_mount)
|
||||
self.library_sync_btn.setEnabled(has_ready and has_mount)
|
||||
self.library_convert_btn.setEnabled(has_source)
|
||||
self.library_remove_btn.setEnabled(len(all_tracks) > 0)
|
||||
self.library_edit_btn.setEnabled(len(all_tracks) > 0)
|
||||
@@ -984,6 +993,8 @@ class LibraryTab(QWidget):
|
||||
play_action.triggered.connect(lambda: self._play_track_from_index(row))
|
||||
|
||||
if is_ready and self._current_mount_point is not None:
|
||||
sync_action = menu.addAction("\uD83D\uDD04 Sync Metadata to iPod")
|
||||
sync_action.triggered.connect(self._on_sync_metadata_clicked)
|
||||
transfer_action = menu.addAction("\uD83D\uDCE4 Transfer to iPod")
|
||||
transfer_action.triggered.connect(self._on_library_transfer_clicked)
|
||||
else:
|
||||
@@ -1129,6 +1140,84 @@ class LibraryTab(QWidget):
|
||||
|
||||
self._cleanup_worker()
|
||||
|
||||
def _on_sync_metadata_clicked(self):
|
||||
selected_tracks = self._get_selected_ready_tracks()
|
||||
if not selected_tracks:
|
||||
QMessageBox.warning(self, "Error", "No tracks selected.")
|
||||
return
|
||||
|
||||
if not self._current_mount_point:
|
||||
QMessageBox.warning(self, "Error", "No iPod device mounted.")
|
||||
return
|
||||
|
||||
if self.worker_thread and self.worker_thread.isRunning():
|
||||
QMessageBox.information(self, "Info", "Please wait for the current task to finish")
|
||||
return
|
||||
|
||||
self.library_sync_btn.setEnabled(False)
|
||||
self.library_status.setText("Matching tracks on iPod...")
|
||||
self.library_progress.setValue(0)
|
||||
|
||||
from ipod_nano7_db import Nano7Database, content_hash as compute_content_hash
|
||||
|
||||
db = Nano7Database(self._current_mount_point)
|
||||
ipod_tracks = db.get_all_tracks()
|
||||
ipod_by_hash = {t.get("content_hash", ""): t for t in ipod_tracks if t.get("content_hash")}
|
||||
|
||||
sync_batch = []
|
||||
errors = []
|
||||
for track_info, local_path in selected_tracks:
|
||||
ch = compute_content_hash(local_path)
|
||||
ipod_track = ipod_by_hash.get(ch)
|
||||
if ipod_track is None:
|
||||
ipod_track = db.find_track_by_metadata(
|
||||
track_info.title, track_info.artist, track_info.album or "",
|
||||
)
|
||||
if ipod_track is None:
|
||||
errors.append(f"'{track_info.title}' — not found on iPod")
|
||||
continue
|
||||
|
||||
sync_batch.append({
|
||||
"ipod_pid": ipod_track["pid"],
|
||||
"local_path": local_path,
|
||||
"new_metadata": {
|
||||
"title": track_info.title,
|
||||
"artist": track_info.artist,
|
||||
"album": track_info.album or "",
|
||||
"track_number": track_info.track_number or 0,
|
||||
"genre": track_info.genre or "",
|
||||
},
|
||||
})
|
||||
|
||||
if not sync_batch:
|
||||
msg = "No matching tracks found on iPod"
|
||||
if errors:
|
||||
msg += ":\n" + "\n".join(errors[:5])
|
||||
QMessageBox.warning(self, "Sync Error", msg)
|
||||
return
|
||||
|
||||
if errors:
|
||||
logger.warning("Sync metadata skipped for %d unmatched track(s)", len(errors))
|
||||
|
||||
self.worker_thread = WorkerThread(
|
||||
task_type="sync_metadata",
|
||||
sync_batch=sync_batch,
|
||||
mount_point=self._current_mount_point,
|
||||
)
|
||||
self.worker_thread.progress_signal.connect(self._on_library_progress)
|
||||
self.worker_thread.finished_signal.connect(self._on_sync_metadata_finished)
|
||||
self.worker_thread.start()
|
||||
|
||||
def _on_sync_metadata_finished(self, success, message, result):
|
||||
self.library_sync_btn.setEnabled(True)
|
||||
if success:
|
||||
self.library_status.setText(message)
|
||||
QMessageBox.information(self, "Sync Complete", message)
|
||||
else:
|
||||
self.library_status.setText(f"Error: {message}")
|
||||
QMessageBox.warning(self, "Sync Error", message)
|
||||
self._cleanup_worker()
|
||||
|
||||
def _cleanup_worker(self):
|
||||
if self.worker_thread:
|
||||
self.worker_thread.wait(3000)
|
||||
@@ -1168,6 +1257,7 @@ class LibraryTab(QWidget):
|
||||
def on_device_unmounted(self):
|
||||
self._current_mount_point = None
|
||||
self.library_transfer_btn.setEnabled(False)
|
||||
self.library_sync_btn.setEnabled(False)
|
||||
self.library_progress.setVisible(False)
|
||||
self.library_status.setVisible(False)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user