From 7d21385ce9bf33dc19682a14291df598ca4d03a2 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Fri, 10 Apr 2026 11:04:21 -0700 Subject: [PATCH] Show which tracks failed to match in sync completion toast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a playlist sync has unmatched tracks sent to wishlist, the completion toast now shows the specific track names instead of just a count. Uses warning style so it stands out. The unmatched track list is included in the sync state result so it's available for both live status polling and notification history. Addresses #272 — silent sync failures where users couldn't tell which tracks out of 150+ failed to match their Plex library. --- web_server.py | 11 ++++++++++- webui/static/script.js | 6 +++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/web_server.py b/web_server.py index 16d1d9be..00d2f520 100644 --- a/web_server.py +++ b/web_server.py @@ -36657,12 +36657,21 @@ def _run_sync_task(playlist_id, playlist_name, tracks_json, automation_id=None, # Update final state on completion # Convert result to JSON-serializable dict (datetime/errors can't be emitted via SocketIO) - # Exclude match_details — large, not needed for live status, saved to DB separately + # Exclude match_details (large) but include a summary of unmatched tracks result_dict = { k: (v.isoformat() if hasattr(v, 'isoformat') else v) for k, v in result.__dict__.items() if k != 'match_details' } + # Include unmatched track names so the frontend can show which tracks failed + match_details = getattr(result, 'match_details', None) + if match_details: + unmatched_summary = [ + {'name': d.get('name', ''), 'artist': d.get('artist', ''), 'image_url': d.get('image_url', '')} + for d in match_details if d.get('status') == 'not_found' + ] + if unmatched_summary: + result_dict['unmatched_tracks'] = unmatched_summary with sync_lock: sync_states[playlist_id] = { "status": "finished", diff --git a/webui/static/script.js b/webui/static/script.js index 82f69186..ae4aedc0 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -16296,9 +16296,13 @@ function updateCardToDefault(playlistId, finalState = null) { // Check if any tracks were added to wishlist const wishlistCount = finalState.progress?.wishlist_added_count || finalState.result?.wishlist_added_count || 0; + const unmatchedTracks = finalState.progress?.unmatched_tracks || finalState.result?.unmatched_tracks || []; const playlistName = card.querySelector('.playlist-card-name').textContent; - if (wishlistCount > 0) { + if (wishlistCount > 0 && unmatchedTracks.length > 0) { + const trackList = unmatchedTracks.map(t => `${t.artist} - ${t.name}`).join(', '); + showToast(`Sync complete for "${playlistName}". ${wishlistCount} not found in library: ${trackList}`, 'warning'); + } else 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');