From 8cc256f27d68076cd64b8857efef08e5a21280f0 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 22 Jul 2026 22:17:44 +0000 Subject: [PATCH] Don't fall back to a favicon for image-less tweets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit resolveHeroImage's favicon fallback exists so regular articles never look entirely bare, but for a tweet it meant an image-less tweet showed the Nitter instance's own favicon slapped on as if it were the tweet's photo. publishDirect now skips that fallback specifically for tweet items — TweetCard.svelte already renders cleanly with no image at all. --- backend/src/pipeline/publish.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/backend/src/pipeline/publish.ts b/backend/src/pipeline/publish.ts index ad0bc98..e053072 100644 --- a/backend/src/pipeline/publish.ts +++ b/backend/src/pipeline/publish.ts @@ -35,11 +35,15 @@ function deriveTitle(body: string): string { /** * Resolves the hero image for an article: try the best candidate from the source * items, download and locally host it; if there isn't one, fall back to the site's - * favicon rather than leaving the article with no art at all. + * favicon rather than leaving the article with no art at all. That favicon fallback + * is skipped for tweets (allowFaviconFallback: false) — a Nitter instance's own + * favicon slapped onto an image-less tweet reads as a mistake, not a placeholder; + * TweetCard.svelte already handles no-image tweets gracefully with no image at all. */ async function resolveHeroImage( items: ContentItem[], - primaryLink: string + primaryLink: string, + allowFaviconFallback = true ): Promise<{ heroImage: MergedArticle['heroImage']; storedMediaId: string | null }> { const selected = selectBestImage(items); @@ -55,6 +59,10 @@ async function resolveHeroImage( return { heroImage: selected, storedMediaId: null }; } + if (!allowFaviconFallback) { + return { heroImage: null, storedMediaId: null }; + } + const favicon = faviconUrlFor(primaryLink); if (favicon) { const stored = await downloadAndStore(favicon, 'published', {}); @@ -81,7 +89,7 @@ async function resolveHeroImage( */ export async function publishDirect(item: ContentItem): Promise { const category = uniqueCategories([item]); - const { heroImage, storedMediaId } = await resolveHeroImage([item], item.link); + const { heroImage, storedMediaId } = await resolveHeroImage([item], item.link, !item.tweet); const video = item.videos[0] ? { url: item.videos[0].url, provider: item.videos[0].provider, embedUrl: item.videos[0].embedHtml, sourceItemId: item.id } : null;