From b64497acd5cd364a0c5038c19fdcf85b45cb06de Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Sun, 4 Oct 2026 14:22:47 +0200 Subject: [PATCH] =?UTF-8?q?Nicht=20jede=20erfolgreiche=20Antwort=20hat=20e?= =?UTF-8?q?inen=20K=C3=B6rper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bei jedem Scanstart stand in der Verwaltung "JSON.parse: unexpected end of data" — eine Fehlermeldung für etwas, das tadellos funktioniert hatte. Gemessen: der Scan-Endpunkt antwortet mit 202 Accepted, Content-Length 0 und ohne Content-Type. Er läuft im Hintergrund weiter, da gibt es noch nichts zu melden. Der Abruf-Helfer behandelte aber nur 204 als leer und liess JSON.parse auf eine leere Zeichenkette los. Jetzt entscheidet der Inhalt, nicht der Status. Eine Liste von Status-Codes wäre wieder eine, die beim nächsten Endpunkt unvollständig ist. Dazu: die Zeile "Metadaten" nannte die Sprache ein zweites Mal, obwohl sie seit kurzem eine eigene Zeile hat. Co-Authored-By: Claude Opus 5 --- web/app/src/api.ts | 24 +++++++++++++++++++++++- web/app/src/views/admin.ts | 5 +++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/web/app/src/api.ts b/web/app/src/api.ts index 2acd93f..ffc3a1d 100644 --- a/web/app/src/api.ts +++ b/web/app/src/api.ts @@ -207,7 +207,29 @@ async function anfrage(pfad: string, opts: RequestInit = {}): Promise { throw new ApiFehler(antwort.status, text); } - return antwort.status === 204 ? (undefined as T) : antwort.json(); + // **Nicht jede erfolgreiche Antwort hat einen Körper.** + // + // Vorher galt das nur für 204. Der Scan antwortet aber mit 202 Accepted und + // ohne Inhalt — er läuft im Hintergrund weiter, da gibt es noch nichts zu + // melden. `JSON.parse` lief dann auf eine leere Zeichenkette, und in der + // Verwaltung stand bei jedem Scanstart „unexpected end of data": eine + // Fehlermeldung für etwas, das tadellos funktioniert hatte. + // + // Geprüft wird der Inhalt, nicht der Status. Eine Liste von Status-Codes + // wäre wieder eine, die beim nächsten Endpunkt unvollständig ist. + if (antwort.status === 204 || antwort.status === 205) return undefined as T; + + const laenge = antwort.headers.get('Content-Length'); + const art = antwort.headers.get('Content-Type') ?? ''; + if (laenge === '0' || !art.includes('json')) { + // Ohne `Content-Length` — etwa bei Chunked-Übertragung — entscheidet der + // Text selbst. + const text = await antwort.text(); + if (text.trim().length === 0) return undefined as T; + return JSON.parse(text) as T; + } + + return antwort.json(); } /* ---------- Bilder ---------- */ diff --git a/web/app/src/views/admin.ts b/web/app/src/views/admin.ts index 6f01327..1416f89 100644 --- a/web/app/src/views/admin.ts +++ b/web/app/src/views/admin.ts @@ -872,8 +872,9 @@ function serverbereich(geaendert: () => void): HTMLElement { liste.appendChild(zeile( 'Metadaten', - stand.usingProjectKey ? `Mitgelieferter TMDB-Zugang · ${stand.language}` - : stand.hasApiKey ? `Eigener TMDB-Zugang · ${stand.language}` + // Ohne die Sprache: die steht eine Zeile tiefer und gehört dort hin. + stand.usingProjectKey ? 'Mitgelieferter TMDB-Zugang' + : stand.hasApiKey ? 'Eigener TMDB-Zugang' : 'Kein Zugang hinterlegt', stand.usingProjectKey ? 'Ein eigener Zugang löst den mitgelieferten ab.'