forked from Keelrleek/SoulStream
restoreSession rebuilds the waveform and the cache badge from the backend, and a direct album arrival gets its artist portrait
Two bugs Kyle reported, both in what a session restore does NOT bring back. THE LANE AND THE STREAM BADGE. Kyle: after the machine sleeps, "the playbar waveform goes away and the track says streming even though its cashed". The trigger is not sleep — it is the frontend restarting with a track loaded, which WebView2 does on its own under memory pressure (and which a page reload does too). Neither survives it: the waveform is measured out of the audio bytes and was never re-requested, so the lane rested as dots while ALSO flagged complete; and streamReceived/streamTotal reset to 0, which is what the Now Playing header picks between "transferring", "Cached · size" and "Streaming from <peer>" with — both at zero fell through to the last, for a file that finished downloading sessions ago. His read was exactly right: it WAS cached and the label was lying. Both are now rebuilt from the BACKEND's truth rather than the snapshot. The restore fires the same final-quality waveform request the stream-completion path uses (it answers from whatever the cache holds — full, partial or nothing, each rendering honestly) and asks stream_cache_len for the real cached byte count, setting the counters only when the cache is COMPLETE so "Cached" is never claimed for a prefix. Local tracks get their waveform re-requested too; they had the same dotted-lane bug. Note the guard compares path+peer, not object identity: player.queue is a $state proxy, so the item read back is never === the snapshot object the closure holds. THE BREADCRUMB'S ARTIST PORTRAIT. Reaching an album directly (search -> album, skipping the artist page) left a bare glyph where the artist's picture belongs: trail() synthesises that crumb from the record's own artist NAME, and metadata::CatalogAlbum carries only the name — no id, no picture. openCatalogAlbum now resolves the artist seed after the page lands and re-records the crumb in place (same key), so trail() reads artistArt/artistId out of it. The resolver shares the album header's own cache and in-flight dedupe, so this is the lookup the page mounting already pays for. It also heals the crumb's CLICK, which had artistId null on every direct arrival. Verified live: reloading the app in its broken state restored a 2000-peak waveform and flipped the header to "Cached · 14.6 MB"; Daft Punk's portrait shows on a direct-from-search album arrival. 98/98 tests.
This commit is contained in:
1 parent
73a677d35c
commit
15746893a9
2 files changed
+56
-1
No files matched your search
@@ -243,7 +243,11 @@ export function trail() {
|
||||
view: "catalogArtist",
|
||||
label: here.artist,
|
||||
artistId: here.artistId ?? null,
|
||||
art: null,
|
||||
// A direct arrival's entry learns these a beat after the
|
||||
// page lands (openCatalogAlbum resolves the artist seed and
|
||||
// updates the crumb in place) — until then the synthesised
|
||||
// crumb honestly has no portrait.
|
||||
art: here.artistArt ?? null,
|
||||
round: true,
|
||||
key: `artist:${here.artistId ?? here.artist}`,
|
||||
},
|
||||
|
||||
@@ -3029,6 +3029,27 @@ export async function openCatalogAlbum(albumId, opts = {}) {
|
||||
art: page.album.cover ?? null,
|
||||
artist: page.album.artist ?? null,
|
||||
});
|
||||
// The artist crumb the trail synthesises for a DIRECT arrival needs
|
||||
// the portrait and id, and the album payload carries only the name
|
||||
// (metadata::CatalogAlbum) — so a reader who skipped the artist page
|
||||
// got a bare disc glyph in the breadcrumb (Kyle, 2026-08-30). The
|
||||
// seed resolver shares the album header's own cache and in-flight
|
||||
// dedupe, so this is the same lookup the page mounting already pays
|
||||
// for; when it lands, the crumb is updated in place (same key) and
|
||||
// trail() reads artistArt/artistId out of it. Also heals the crumb's
|
||||
// CLICK, which had artistId null on every direct arrival.
|
||||
void resolveArtistSeedByName(page.album.artist).then((seed) => {
|
||||
if (!seed || !active()) return;
|
||||
recordVisit({
|
||||
view: "catalogAlbum",
|
||||
label: page.album.title,
|
||||
albumId: page.album.id,
|
||||
art: page.album.cover ?? null,
|
||||
artist: page.album.artist ?? null,
|
||||
artistId: seed.id ?? null,
|
||||
artistArt: seed.picture ?? null,
|
||||
});
|
||||
});
|
||||
// No catalog trackId (e.g. local playback) — match the song by title.
|
||||
if (catalog.pendingTrackId == null && opts.trackTitle) {
|
||||
const t = page.tracks.find(
|
||||
@@ -7395,6 +7416,36 @@ export async function restoreSession() {
|
||||
// they would had it just been started.
|
||||
if (item.stream) setAccentFromArtUrl(item.artUrl ?? null);
|
||||
else setAccentFromArt(item.art ?? null);
|
||||
// The lane and the stream badge are rebuilt from the BACKEND's
|
||||
// truth, not the snapshot. Neither survives a reload on its own:
|
||||
// the waveform was measured out of the audio bytes, and the byte
|
||||
// counters reset to 0 — which left the lane resting as dots and
|
||||
// the header claiming "Streaming from <peer>" for a track whose
|
||||
// cache file finished downloading sessions ago (Kyle, 2026-08-30,
|
||||
// after an overnight sleep). A final-quality waveform request
|
||||
// answers from whatever the cache holds — full, partial, or
|
||||
// nothing, each rendering honestly — and the counters are set
|
||||
// only when the cache is COMPLETE, so "Cached" is never claimed
|
||||
// for a prefix.
|
||||
if (item.stream) {
|
||||
void api
|
||||
.streamCacheLen(item.stream.username, item.path)
|
||||
.then((len) => {
|
||||
// By value, not identity: player.queue is a $state
|
||||
// proxy, so reading the item back never === the raw
|
||||
// snapshot object this closure holds.
|
||||
const cur = player.queue[player.queueIndex];
|
||||
if (cur?.path !== item.path || cur?.stream?.username !== item.stream.username) return;
|
||||
if ((len ?? 0) >= item.stream.size) {
|
||||
player.streamTotal = item.stream.size;
|
||||
player.streamReceived = len;
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
void loadStreamWaveform(item, true);
|
||||
} else {
|
||||
void loadWaveform(item.path, item.segment ?? null);
|
||||
}
|
||||
}
|
||||
|
||||
// Where you were. The trail and the history arrows are restored first so
|
||||
|
||||
Reference in new issue
Block a user