Sync wishlist re-add: carry the cover image through (real parity)
The re-add showed no album/single art. Cause: reconstruct returned the full track from tracks_json AS-IS — and some syncs store tracks_json lean (no album.images), so the re-added wishlist entry had an empty album.images even though the track's cover was sitting right there in the track_result's image_url. Fix: always backfill album.images from the track_result's image_url when the album has none (and copy the dict so tracks_json isn't mutated). Real album art is kept when present; the 250px thumb only fills a gap. Verified against a real sync row in all three cases (full / lean tracks_json / no tracks_json) — album.images now populated in every one. The wishlist card reads album.images, so the cover shows.
This commit is contained in:
parent
e148f859e7
commit
5cad2c02b0
2 changed files with 65 additions and 22 deletions
|
|
@ -35,33 +35,49 @@ def reconstruct_sync_track_data(
|
|||
sid = str(tr.get('source_track_id') or '')
|
||||
tracks = tracks or []
|
||||
|
||||
# Prefer the full original track: same index if its id matches, else search by id.
|
||||
full = None
|
||||
# Base: the FULL original track (best fidelity — full album object, ids, source,
|
||||
# artists) by index if its id matches, else by id search. Copied so we never
|
||||
# mutate the caller's tracks list.
|
||||
base: Optional[Dict[str, Any]] = None
|
||||
if 0 <= track_index < len(tracks):
|
||||
cand = tracks[track_index]
|
||||
if isinstance(cand, dict) and (not sid or str(cand.get('id') or '') == sid):
|
||||
full = cand
|
||||
if full is None and sid:
|
||||
full = next(
|
||||
base = dict(cand)
|
||||
if base is None and sid:
|
||||
match = next(
|
||||
(t for t in tracks if isinstance(t, dict) and str(t.get('id') or '') == sid),
|
||||
None,
|
||||
)
|
||||
if isinstance(full, dict) and full.get('id'):
|
||||
return full
|
||||
if match is not None:
|
||||
base = dict(match)
|
||||
|
||||
# Fallback: rebuild from the track_result fields (id required).
|
||||
if not sid:
|
||||
return None
|
||||
album: Dict[str, Any] = {'name': tr.get('album') or ''}
|
||||
if tr.get('image_url'):
|
||||
album['images'] = [{'url': tr['image_url']}]
|
||||
return {
|
||||
'id': sid,
|
||||
'name': tr.get('name') or '',
|
||||
'artists': [{'name': tr.get('artist') or ''}],
|
||||
'album': album,
|
||||
'duration_ms': tr.get('duration_ms') or 0,
|
||||
}
|
||||
# No full track in the cache — rebuild a minimal dict from the track_result.
|
||||
if not (isinstance(base, dict) and base.get('id')):
|
||||
if not sid:
|
||||
return None
|
||||
base = {
|
||||
'id': sid,
|
||||
'name': tr.get('name') or '',
|
||||
'artists': [{'name': tr.get('artist') or ''}],
|
||||
'album': {'name': tr.get('album') or ''},
|
||||
'duration_ms': tr.get('duration_ms') or 0,
|
||||
}
|
||||
|
||||
# Ensure the cover carries through. The wishlist display reads
|
||||
# spotify_data.album.images; tracks_json is sometimes stored WITHOUT images, so
|
||||
# backfill from the track_result's image_url (the album art the sync extracted)
|
||||
# whenever the album has none. This is what makes the re-add reach full parity
|
||||
# with the original auto-add's appearance.
|
||||
album = base.get('album')
|
||||
if not isinstance(album, dict):
|
||||
album = {'name': base.get('name', '')}
|
||||
else:
|
||||
album = dict(album)
|
||||
img = tr.get('image_url')
|
||||
if img and not album.get('images'):
|
||||
album['images'] = [{'url': img}]
|
||||
base['album'] = album
|
||||
return base
|
||||
|
||||
|
||||
__all__ = ["reconstruct_sync_track_data"]
|
||||
|
|
|
|||
|
|
@ -29,8 +29,35 @@ def test_prefers_full_original_track_by_index():
|
|||
trs = [_tr(0, "sp_a"), _tr(1, "sp_b")]
|
||||
tracks = [_full("sp_a"), _full("sp_b", name="Other")]
|
||||
out = reconstruct_sync_track_data(trs, tracks, 1)
|
||||
assert out is tracks[1] # exact original (full album+images)
|
||||
assert out["album"]["images"][0]["url"] == "http://cdn/cover.jpg"
|
||||
assert out["id"] == "sp_b" # the full original track
|
||||
assert out["album"]["images"][0]["url"] == "http://cdn/cover.jpg" # its own art kept
|
||||
assert out is not tracks[1] # a copy, not the source entry
|
||||
|
||||
|
||||
def test_backfills_album_image_when_full_track_has_none():
|
||||
# THE BUG: tracks_json stored without album.images, but the track_result has the
|
||||
# cover. The re-add must carry the image through (parity with the auto-add).
|
||||
trs = [_tr(0, "sp_a", image_url="http://img/cover250.jpg")]
|
||||
tracks = [_full("sp_a", with_images=False)] # full data but NO images
|
||||
out = reconstruct_sync_track_data(trs, tracks, 0)
|
||||
assert out["id"] == "sp_a"
|
||||
assert out["album"]["images"] == [{"url": "http://img/cover250.jpg"}]
|
||||
|
||||
|
||||
def test_full_track_with_images_is_not_overwritten():
|
||||
# When the cached track already has real album art, keep it (don't downgrade to
|
||||
# the 250px track_result thumb).
|
||||
trs = [_tr(0, "sp_a", image_url="http://img/thumb.jpg")]
|
||||
tracks = [_full("sp_a", with_images=True)]
|
||||
out = reconstruct_sync_track_data(trs, tracks, 0)
|
||||
assert out["album"]["images"][0]["url"] == "http://cdn/cover.jpg" # original kept
|
||||
|
||||
|
||||
def test_does_not_mutate_source_tracks_list():
|
||||
trs = [_tr(0, "sp_a", image_url="http://img/x.jpg")]
|
||||
tracks = [_full("sp_a", with_images=False)]
|
||||
reconstruct_sync_track_data(trs, tracks, 0)
|
||||
assert "images" not in tracks[0]["album"] # source untouched
|
||||
|
||||
|
||||
def test_matches_by_id_when_index_misaligns():
|
||||
|
|
|
|||
Loading…
Reference in a new issue