Der Katalog ist bewusst gemeinsam für alle Nutzer und über (Typ, TMDB-ID) dedupliziert: zehn Leute mit derselben Serie brauchen die Episodenliste nur einmal. Persönliches steckt ausschließlich in LibraryEntry und Watch. Poster laufen über /api/image statt direkt zu TMDB — einmal holen, danach vom eigenen Server ausliefern. Größe und Pfad werden streng geprüft, damit daraus kein offener Proxy wird. Staffel 0 (Specials) wird importiert, zählt aber nicht zum Fortschritt; Episoden ohne eigene Laufzeit fallen auf die durchschnittliche Episodenlänge der Serie zurück.
35 lines
1.1 KiB
TypeScript
35 lines
1.1 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { getCachedImage, isValidImagePath, isValidSize } from "@/lib/images";
|
|
|
|
/**
|
|
* Ausliefern zwischengespeicherter TMDB-Bilder.
|
|
*
|
|
* /api/image?size=w342&path=/abc123.jpg
|
|
*
|
|
* Größe und Pfad werden streng geprüft — der Endpunkt soll kein offener Proxy
|
|
* für beliebige Adressen werden.
|
|
*/
|
|
export async function GET(request: Request) {
|
|
const url = new URL(request.url);
|
|
const size = url.searchParams.get("size") ?? "w342";
|
|
const path = url.searchParams.get("path") ?? "";
|
|
|
|
if (!isValidSize(size) || !isValidImagePath(path)) {
|
|
return NextResponse.json({ error: "Ungültige Bildanfrage." }, { status: 400 });
|
|
}
|
|
|
|
try {
|
|
const image = await getCachedImage(size, path);
|
|
return new NextResponse(new Uint8Array(image.data), {
|
|
headers: {
|
|
"content-type": image.contentType,
|
|
// Bilder unter einem TMDB-Pfad ändern sich nicht mehr.
|
|
"cache-control": "public, max-age=31536000, immutable",
|
|
"x-flimmer-cache": image.fromCache ? "hit" : "miss",
|
|
},
|
|
});
|
|
} catch {
|
|
return NextResponse.json({ error: "Bild nicht verfügbar." }, { status: 404 });
|
|
}
|
|
}
|