From 093ce64dfb27870c6875a819f5e98975cc75a8fb Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Tue, 10 Nov 2026 20:30:49 +0000 Subject: [PATCH] fix(chat): show an album cover that lands after the songs The image queue can be slower than the songs (live: cover #7 in line after a 16 min album). After an album finishes without a cover, keep checking /api/v1/album//cover for up to 2 h and show it. Pairs with InfernetProtocol/infernet-protocol#186. Co-Authored-By: Claude Opus 5.5 --- apps/web/src/app/chat/chat-app.tsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/apps/web/src/app/chat/chat-app.tsx b/apps/web/src/app/chat/chat-app.tsx index 561c5e78..9f7a04ab 100644 --- a/apps/web/src/app/chat/chat-app.tsx +++ b/apps/web/src/app/chat/chat-app.tsx @@ -431,6 +431,24 @@ export function ChatApp({ error }: { error: string | null }) { if (poll.status === 200) { const done = album.tracks.filter((t) => t.status === "done").length; setLast({ content: `${head}${album.genre ? ` · ${album.genre}` : ""} · ${done}/${album.tracks.length} tracks (${clock(secs)} on c0mpute workers)`, album }); + // The cover queues on the image workers separately and can land + // after the songs: keep looking (up to 2 h). The zip picks it up + // on the next download. + if (!album.cover) { + const albumId = album.id; + const coverPath = `/api/v1/album/${albumId}/cover`; + void (async () => { + const until = Date.now() + 2 * 60 * 60_000; + while (Date.now() < until) { + await new Promise((r) => setTimeout(r, 30_000)); + const c = await fetch(coverPath, { cache: "no-store" }).catch(() => null); + if (!c?.ok) continue; + await c.body?.cancel().catch(() => {}); + update(id, (cv) => ({ ...cv, messages: cv.messages.map((m) => (m.album?.id === albumId ? { ...m, album: { ...m.album, cover: coverPath } } : m)) })); + return; + } + })(); + } break; } setLast({ content: `// ${head} · ${describeAlbumStage(view)} · ${clock(secs)}${note}`, album });