From 5b7f99c30bcb01dea0d8e5f737436d4b8a4c01df Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Wed, 24 Jun 2026 16:51:43 -0700 Subject: [PATCH] Sync wishlist re-add: skip wing-it stubs (match the sync), no clickable button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'sami matar' was a wing-it FALLBACK stub — a placeholder the discovery pipeline makes when it can't resolve a track to real metadata (no album, no cover). The live sync explicitly skips wing_it_* ids for the wishlist (no metadata to act on), but my re-add didn't — so it stored a coverless, single-classified placeholder. That's why: sync didn't add it, no images, marked single. Fix (parity): reconstruct refuses ids starting 'wing_it_'. Frontend renders the '-> Wishlist' status as plain, non-clickable text for wing-it rows (with a tooltip) since they were never actually wishlisted. Real tracks keep the working button + the byte-identical-payload re-add from the prior fix. --- core/sync/wishlist_readd.py | 5 +++++ tests/test_sync_wishlist_readd.py | 9 +++++++++ webui/static/pages-extra.js | 17 ++++++++++++----- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/core/sync/wishlist_readd.py b/core/sync/wishlist_readd.py index 6658352e..7414c943 100644 --- a/core/sync/wishlist_readd.py +++ b/core/sync/wishlist_readd.py @@ -71,6 +71,11 @@ def reconstruct_sync_track_data( return None sid = str(tr.get('source_track_id') or '') + # Wing-it fallback stubs have no real metadata (no album/cover) — the live sync + # SKIPS them for the wishlist (services/sync_service.py), so the re-add must too, + # rather than store a coverless, mis-classified placeholder. + if sid.startswith('wing_it_'): + return None # Primary: the exact normalized track the auto-add used. if sid: diff --git a/tests/test_sync_wishlist_readd.py b/tests/test_sync_wishlist_readd.py index b794ed10..227e9784 100644 --- a/tests/test_sync_wishlist_readd.py +++ b/tests/test_sync_wishlist_readd.py @@ -100,6 +100,15 @@ def test_refuses_when_no_id_and_no_full_track(): assert reconstruct_sync_track_data([_tr(0, "")], [], 0) is None +def test_refuses_wing_it_stub(): + # Wing-it fallback stubs have no real metadata; the sync skips them, so must we — + # even if a full (stub) track happens to be in tracks_json. + trs = [_tr(0, "wing_it_abc123", name="Sami Matar", artist="X")] + assert reconstruct_sync_track_data(trs, [], 0) is None + stub = {"id": "wing_it_abc123", "name": "Sami Matar", "album": "Sami Matar", "artists": ["X"]} + assert reconstruct_sync_track_data(trs, [stub], 0) is None + + def test_build_map_skips_idless_and_non_dicts(): m = build_original_tracks_map([_full("a"), {"name": "no id"}, "garbage", None]) assert set(m.keys()) == {"a"} diff --git a/webui/static/pages-extra.js b/webui/static/pages-extra.js index 2a02993a..b82dac7a 100644 --- a/webui/static/pages-extra.js +++ b/webui/static/pages-extra.js @@ -2271,11 +2271,18 @@ async function openSyncDetailModal(entryId) { let dlDisplay = dlIcon; if (!dlDisplay && t.download_status === 'wishlist') { - // Clickable: re-add this exact track to the wishlist with the - // same context the sync originally used. - dlDisplay = ``; + // Wing-it fallback stubs (no real metadata) were never actually + // wishlisted by the sync — show them as plain, non-clickable. + const isWingIt = String(t.source_track_id || '').startsWith('wing_it_'); + if (isWingIt) { + dlDisplay = `→ Wishlist`; + } else { + // Clickable: re-add this exact track to the wishlist with the + // same context the sync originally used. + dlDisplay = ``; + } } return `