diff --git a/VERSION b/VERSION
index f3e3a7d9..dc0bfeb5 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.69.1
\ No newline at end of file
+0.69.2
\ No newline at end of file
diff --git a/frontend/src/components/DownloadCenter.tsx b/frontend/src/components/DownloadCenter.tsx
index 9facb718..8f3c6b14 100644
--- a/frontend/src/components/DownloadCenter.tsx
+++ b/frontend/src/components/DownloadCenter.tsx
@@ -89,7 +89,12 @@ function Thumb({ job }: { job: DownloadJob }) {
// rule on the source domain can't break it. The source's remote thumbnail_url is only a fallback for
// the rare poster-less asset (a thumbnail-less source whose ffmpeg frame-cut also failed).
const remote = job.asset?.thumbnail_url ?? null;
- const primary = job.poster_url || remote;
+ // `job.poster_url` only SIGNALS a poster exists; the actual
src must carry ?account= (see
+ // api.posterImageUrl). A plain
can't send the X-Siftlode-Account header, so without it the
+ // poster resolves to the session-default account and 404s for a per-tab account's download — which,
+ // now that the poster is the primary image, reads as a blank Library thumbnail.
+ const poster = job.poster_url ? api.posterImageUrl(job.id) : null;
+ const primary = poster || remote;
// On load failure (a genuinely missing poster file) fall back to the remote source thumbnail.
const [src, setSrc] = useState(primary);
useEffect(() => setSrc(primary), [primary]);
diff --git a/frontend/src/lib/api/downloads.ts b/frontend/src/lib/api/downloads.ts
index 296f6ca3..25dbf7c9 100644
--- a/frontend/src/lib/api/downloads.ts
+++ b/frontend/src/lib/api/downloads.ts
@@ -225,6 +225,15 @@ export const downloadsApi = {
? `/api/downloads/${id}/storyboard.jpg?account=${a}`
: `/api/downloads/${id}/storyboard.jpg`;
},
+ // The locally-generated poster (an
src → direct nav, so same ?account= wallet gate). Without
+ // it the poster resolves to the session-default account and 404s for a per-tab account's download —
+ // which reads as a blank Library thumbnail once the poster is the card's primary image.
+ posterImageUrl: (id: number): string => {
+ const a = getActiveAccount();
+ return a != null
+ ? `/api/downloads/${id}/poster.jpg?account=${a}`
+ : `/api/downloads/${id}/poster.jpg`;
+ },
// admin
adminDownloads: (): Promise => req("/api/admin/downloads"),
diff --git a/frontend/src/lib/releaseNotes.ts b/frontend/src/lib/releaseNotes.ts
index d86426f2..76a3d057 100644
--- a/frontend/src/lib/releaseNotes.ts
+++ b/frontend/src/lib/releaseNotes.ts
@@ -14,6 +14,14 @@ export interface ReleaseEntry {
}
export const RELEASE_NOTES: ReleaseEntry[] = [
+ {
+ version: "0.69.2",
+ date: "2026-08-19",
+ summary: "Download thumbnails now load for everyone, including multi-account sessions.",
+ fixes: [
+ "Fixed Library thumbnails staying blank when you're signed into more than one account. The poster image is loaded directly by the browser, which can't send the active-account header, so it resolved to your default account and returned “not found” for downloads owned by another of your accounts. The poster URL now carries the active account (the same way the file download and filmstrip already do), so the local poster loads regardless of which of your accounts is active.",
+ ],
+ },
{
version: "0.69.1",
date: "2026-08-19",