diff --git a/frontend/src/components/DownloadCenter.tsx b/frontend/src/components/DownloadCenter.tsx index cf4a61a9..9facb718 100644 --- a/frontend/src/components/DownloadCenter.tsx +++ b/frontend/src/components/DownloadCenter.tsx @@ -84,10 +84,13 @@ function StatusPill({ job }: { job: DownloadJob }) { } function Thumb({ job }: { job: DownloadJob }) { - // Prefer the source thumbnail; fall back to our generated poster for thumbnail-less sources. - const primary = job.asset?.thumbnail_url || job.poster_url; - // A remote i.ytimg.com thumbnail 404s once the source video is deleted/privated, leaving a - // broken image. On load failure fall back to our always-present self-hosted poster. + // Prefer OUR self-hosted poster: it's always present on disk, never expires, and is same-origin, so + // a source-CDN signature expiry (Facebook's fbcdn URLs expire within days) or an ad-blocker/Shields + // 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; + // On load failure (a genuinely missing poster file) fall back to the remote source thumbnail. const [src, setSrc] = useState(primary); useEffect(() => setSrc(primary), [primary]); return ( @@ -99,7 +102,7 @@ function Thumb({ job }: { job: DownloadJob }) { loading="lazy" className="w-full h-full object-cover" onError={() => { - if (job.poster_url && src !== job.poster_url) setSrc(job.poster_url); + if (remote && src !== remote) setSrc(remote); }} /> ) : null}