fix(home): Discover Songs covers for local-index tracks (#874)

* fix(home): pre-warm and prefetch Discover Songs row covers

The Discover Songs raблин. il came out of the cover pipeline merge with two
gaps that left its cards stuck on the placeholder disc icon on cold
caches.

- `warmHomeMainstageCovers` walked `heroAlbums` / `recent` / `random`
  through `ensureAlbumCoverMisses` + `predecodeWarmAlbums` but skipped
  `discoverSongs`, so songs that were peeked but missed on disk had to
  wait for lazy per-card ensure
- `Home.tsx`'s `coverPrefetchRegister` lumped `songRefs` into a
  `cappedRest` slice already saturated by 48 album refs + 16 artist
  refs at a 24-entry cap, so the song row's background prefetch was
  discarded entirely

Fix: ensure + decode-warm the Discover Songs cells alongside the album
rails, and register the song refs in their own bucket with a sane cap
and `middle` priority. Both follow the same shape as the working album
rails — no behavior change for surfaces that were already painting.

* fix(library): resolve track cover art from albumId for local index songs

Discover Songs uses runLocalRandomSongs; trackToSong only mapped coverArtId,
so rows with empty cover_art_id but a valid album_id showed the disc
placeholder. Mirror Rust COALESCE(cover_art_id, album_id) and Live Search's
coverArt ?? albumId in trackToSong, SongCard, and Home prefetch.

* docs(release): note Discover Songs cover fix in CHANGELOG and credits (PR #874)

* docs(release): credit PR #874 to Psychotoxical and cucadmuh jointly

* chore(credits): drop PR #874 from settingsCredits — minor fix

---------

Co-authored-by: Psychotoxical <171614930+Psychotoxical@users.noreply.github.com>
This commit is contained in:
cucadmuh
2026-05-27 08:49:06 +03:00
committed by GitHub
parent d353482ac5
commit fab6ff19bf
7 changed files with 85 additions and 9 deletions
+3 -2
View File
@@ -4,8 +4,9 @@ export function coverArtIdFromAlbum(album: { coverArt?: string }): CoverArtId |
return album.coverArt ?? null;
}
export function coverArtIdFromSong(song: { coverArt?: string; id?: string }): CoverArtId | null {
return song.coverArt ?? null;
export function coverArtIdFromSong(song: { coverArt?: string; albumId?: string }): CoverArtId | null {
const id = song.coverArt ?? song.albumId;
return id?.trim() || null;
}
export function coverArtIdFromArtist(artist: { coverArt?: string; id: string }): CoverArtId {
+7
View File
@@ -154,16 +154,23 @@ export async function warmHomeMainstageCovers(snapshot: {
// Prepare above-the-fold mainstage covers ahead of return navigation:
// if a refreshed snapshot introduces new albums not yet on disk, ensure them
// now in background so Hero / first rows don't wait on per-cell ensure.
// `discoverSongs` shares the same dense surface as the album rails — the
// pre-merge code only walked album collections here and the song row was
// left to lazy per-card ensure, which produced visible placeholder cards
// on cold caches.
const discoverSongsForEnsure = snapshot.discoverSongs ?? [];
await Promise.allSettled([
ensureAlbumCoverMisses(snapshot.heroAlbums, 220, { surface: 'dense', limit: 8 }),
ensureAlbumCoverMisses(snapshot.recent, 300, { surface: 'dense', limit: 14 }),
ensureAlbumCoverMisses(snapshot.random, 300, { surface: 'dense', limit: 10 }),
ensureAlbumCoverMisses(discoverSongsForEnsure, 200, { surface: 'dense', limit: 12 }),
]);
// Fire-and-forget decode warmup to reduce first-paint "from cache" delay.
void predecodeWarmAlbums(snapshot.heroAlbums, 220, 8);
void predecodeWarmAlbums(snapshot.recent, 300, 10);
void predecodeWarmAlbums(snapshot.random, 300, 8);
void predecodeWarmAlbums(discoverSongsForEnsure, 200, 8);
}
async function predecodeWarmAlbums(