From b1391b9466acd2b987fbfb0bde29632741c98b93 Mon Sep 17 00:00:00 2001 From: Broque Thomas Date: Fri, 21 Nov 2025 15:29:25 -0800 Subject: [PATCH] add failed scan items to wishlist. --- services/sync_service.py | 59 ++++++++++++++++++++++++++++++++++++---- ui/pages/sync.py | 12 ++++++-- webui/static/script.js | 11 +++++++- 3 files changed, 74 insertions(+), 8 deletions(-) diff --git a/services/sync_service.py b/services/sync_service.py index 9b9a8c87..37f66657 100644 --- a/services/sync_service.py +++ b/services/sync_service.py @@ -22,7 +22,8 @@ class SyncResult: failed_tracks: int sync_time: datetime errors: List[str] - + wishlist_added_count: int = 0 + @property def success_rate(self) -> float: if self.total_tracks == 0: @@ -252,7 +253,53 @@ class PlaylistSyncService: total_tracks=total_tracks, matched_tracks=len(matched_tracks), failed_tracks=failed_tracks) - + + # Auto-add unmatched tracks to wishlist + wishlist_added_count = 0 + if unmatched_tracks: + try: + from core.wishlist_service import get_wishlist_service + wishlist_service = get_wishlist_service() + + logger.info(f"Auto-adding {len(unmatched_tracks)} unmatched tracks to wishlist") + + for match_result in unmatched_tracks: + spotify_track = match_result.spotify_track + + # Convert SpotifyTrack to dict format expected by wishlist service + spotify_track_data = { + 'id': spotify_track.id, + 'name': spotify_track.name, + 'artists': [{'name': a} if isinstance(a, str) else a for a in spotify_track.artists], + 'album': {'name': spotify_track.album}, + 'duration_ms': spotify_track.duration_ms, + 'popularity': getattr(spotify_track, 'popularity', 0), + 'preview_url': getattr(spotify_track, 'preview_url', None), + 'external_urls': getattr(spotify_track, 'external_urls', {}) + } + + # Add to wishlist with source context + success = wishlist_service.add_spotify_track_to_wishlist( + spotify_track_data=spotify_track_data, + failure_reason='Missing from media server after sync', + source_type='playlist', + source_context={ + 'playlist_name': playlist.name, + 'playlist_id': playlist.id, + 'sync_type': 'automatic_sync', + 'timestamp': datetime.now().isoformat() + } + ) + + if success: + wishlist_added_count += 1 + + logger.info(f"Successfully added {wishlist_added_count}/{len(unmatched_tracks)} tracks to wishlist") + + except Exception as e: + logger.warning(f"Failed to auto-add tracks to wishlist: {e}") + # Don't fail the sync if wishlist add fails + result = SyncResult( playlist_name=playlist.name, total_tracks=len(playlist.tracks), @@ -261,9 +308,10 @@ class PlaylistSyncService: downloaded_tracks=downloaded_tracks, failed_tracks=failed_tracks, sync_time=datetime.now(), - errors=errors + errors=errors, + wishlist_added_count=wishlist_added_count ) - + logger.info(f"Sync completed: {result.success_rate:.1f}% success rate") return result @@ -437,7 +485,8 @@ class PlaylistSyncService: downloaded_tracks=0, failed_tracks=0, sync_time=datetime.now(), - errors=errors + errors=errors, + wishlist_added_count=0 ) def get_sync_preview(self, playlist_name: str) -> Dict[str, Any]: diff --git a/ui/pages/sync.py b/ui/pages/sync.py index 03bd6d37..3b217fe1 100644 --- a/ui/pages/sync.py +++ b/ui/pages/sync.py @@ -3750,10 +3750,18 @@ class SyncPage(QWidget): # Show toast notification for sync completion if hasattr(self, 'toast_manager') and self.toast_manager: + wishlist_count = getattr(result, 'wishlist_added_count', 0) + if result.failed_tracks > 0: - self.toast_manager.show_toast(f"Sync completed: {result.matched_tracks}/{result.total_tracks} tracks added, {result.failed_tracks} failed", ToastType.WARNING) + msg = f"Sync completed: {result.matched_tracks}/{result.total_tracks} tracks added, {result.failed_tracks} failed" + if wishlist_count > 0: + msg += f". {wishlist_count} track{'s' if wishlist_count > 1 else ''} added to wishlist" + self.toast_manager.show_toast(msg, ToastType.WARNING) else: - self.toast_manager.show_toast(f"Sync completed: {result.matched_tracks} tracks added to queue", ToastType.SUCCESS) + msg = f"Sync completed: {result.matched_tracks} tracks added to queue" + if wishlist_count > 0: + msg += f". {wishlist_count} missing track{'s' if wishlist_count > 1 else ''} added to wishlist" + self.toast_manager.show_toast(msg, ToastType.SUCCESS) # Continue sequential sync if in progress if self.is_sequential_syncing: diff --git a/webui/static/script.js b/webui/static/script.js index ea5fa36b..d9670318 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -6283,7 +6283,16 @@ function updateCardToDefault(playlistId, finalState = null) { if (finalState.status === 'finished') { statusEl.textContent = `Synced: Just now`; statusEl.className = 'playlist-card-status status-synced'; - showToast(`Sync complete for "${card.querySelector('.playlist-card-name').textContent}"`, 'success'); + + // Check if any tracks were added to wishlist + const wishlistCount = finalState.progress?.wishlist_added_count || finalState.result?.wishlist_added_count || 0; + const playlistName = card.querySelector('.playlist-card-name').textContent; + + if (wishlistCount > 0) { + showToast(`Sync complete for "${playlistName}". Added ${wishlistCount} missing track${wishlistCount > 1 ? 's' : ''} to wishlist.`, 'success'); + } else { + showToast(`Sync complete for "${playlistName}"`, 'success'); + } } else { statusEl.textContent = `Sync Failed`; statusEl.className = 'playlist-card-status status-needs-sync'; // Or a new error class