feat(search): HQ-Thumbnails durch immer aktive og:image-Extraktion
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 54s

Vorher: nur Treffer ohne SearXNG-Thumbnail wurden mit dem Seiten-Bild
angereichert. Treffer mit Thumbnail behielten das kleine 150-200 px-
Bildchen aus dem Such-Engine-Index.

Jetzt: Alle Treffer durchlaufen die Enrichment-Pipeline. Wenn die Seite
ein og:image/JSON-LD/Content-Bild hat (und das hat sie bei Rezept-Seiten
praktisch immer), wird das kleine SearXNG-Thumbnail damit überschrieben.
Wenn die Seite kein Bild liefert, bleibt das SearXNG-Thumbnail als
Fallback erhalten.

Das ist das gleiche Bild, das auch die Vorschau anzeigt — Suchergebnis
und Vorschau sind jetzt visuell konsistent.

Performance: Pro erster Suche bis zu ~6 Sekunden zusätzliche Latenz
(max 6 parallel, je 4 s Timeout). Der 30-min In-Memory-Cache macht
Wiederholsuchen instant.

Tests (98/98):
- Neu: SearXNG-Thumbnail wird durch og:image ersetzt.
- Neu: SearXNG-Thumbnail bleibt erhalten, wenn Seite kein Bild hat.
- Alt ("leaves existing thumbnails untouched") entfernt — Verhalten
  hat sich bewusst umgekehrt.
This commit is contained in:
hsiegeln
2026-04-17 18:31:42 +02:00
parent 53e4815508
commit 1712263fd1
2 changed files with 50 additions and 17 deletions

View File

@@ -198,9 +198,13 @@ async function enrichThumbnail(url: string): Promise<string | null> {
return image;
}
async function enrichMissingThumbnails(hits: WebHit[]): Promise<void> {
const queue = hits.filter((h) => !h.thumbnail);
if (queue.length === 0) return;
async function enrichAllThumbnails(hits: WebHit[]): Promise<void> {
// Always fetch the page image even when SearXNG gave us a thumbnail
// the search engine's thumbnail is typically 150-200px, while og:image
// / JSON-LD image on the page is the full-resolution recipe photo.
// The 30-min URL cache makes repeat searches instant.
if (hits.length === 0) return;
const queue = [...hits];
const LIMIT = 6;
const workers = Array.from({ length: Math.min(LIMIT, queue.length) }, async () => {
while (queue.length > 0) {
@@ -268,7 +272,7 @@ export async function searchWeb(
if (hits.length >= limit) break;
}
if (opts.enrichThumbnails !== false) {
await enrichMissingThumbnails(hits);
await enrichAllThumbnails(hits);
}
return hits;
}