TMDB_BASE_URL hilft Instanzen hinter einem eigenen Proxy — und erlaubt es, die Suche ohne Schlüssel aus den Beispieldaten zu bedienen. Damit lässt sich die Oberfläche ausprobieren, bevor man einen Schlüssel besorgt, und die Screenshots der Anleitung zeigen eine Suche, die tatsächlich etwas findet.
99 lines
3.2 KiB
JavaScript
99 lines
3.2 KiB
JavaScript
/**
|
|
* Kleiner TMDB-Ersatz für Screenshots und manuelles Ausprobieren.
|
|
*
|
|
* node scripts/tmdb-stub.mjs [port]
|
|
*
|
|
* Beantwortet die Suchendpunkte aus dem lokalen Katalog (den das Seed-Skript
|
|
* angelegt hat) im Format der TMDB-API. Damit lässt sich die Suchseite auch
|
|
* ohne echten TMDB-Schlüssel zeigen — für Screenshots der Anleitung und um
|
|
* die Oberfläche auszuprobieren, bevor man einen Schlüssel besorgt.
|
|
*
|
|
* Verwendung zusammen mit:
|
|
* TMDB_API_KEY=stub TMDB_BASE_URL=http://127.0.0.1:3099/3 npm run dev
|
|
*/
|
|
import { createServer } from "node:http";
|
|
import { PrismaPg } from "@prisma/adapter-pg";
|
|
import { PrismaClient } from "../src/generated/prisma/client.ts";
|
|
|
|
try {
|
|
process.loadEnvFile(".env");
|
|
} catch {
|
|
// Werte kommen aus der Umgebung.
|
|
}
|
|
|
|
const port = Number(process.argv[2] ?? 3099);
|
|
const prisma = new PrismaClient({
|
|
adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
|
|
});
|
|
|
|
function toResult(title, mediaType) {
|
|
const date = title.releaseDate?.toISOString().slice(0, 10) ?? null;
|
|
return {
|
|
id: title.tmdbId,
|
|
media_type: mediaType,
|
|
name: mediaType === "tv" ? title.title : undefined,
|
|
title: mediaType === "movie" ? title.title : undefined,
|
|
original_name: title.originalTitle ?? undefined,
|
|
original_title: title.originalTitle ?? undefined,
|
|
overview: title.overview ?? "",
|
|
poster_path: title.posterPath,
|
|
backdrop_path: title.backdropPath,
|
|
first_air_date: mediaType === "tv" ? date : undefined,
|
|
release_date: mediaType === "movie" ? date : undefined,
|
|
vote_average: title.voteAverage ?? 0,
|
|
};
|
|
}
|
|
|
|
async function search(query, type) {
|
|
const titles = await prisma.title.findMany({
|
|
where: {
|
|
tmdbId: { not: null },
|
|
...(type ? { type } : {}),
|
|
OR: [
|
|
{ title: { contains: query, mode: "insensitive" } },
|
|
{ originalTitle: { contains: query, mode: "insensitive" } },
|
|
{ genres: { has: query } },
|
|
],
|
|
},
|
|
orderBy: { releaseDate: "desc" },
|
|
take: 20,
|
|
});
|
|
|
|
return titles.map((title) => toResult(title, title.type === "SERIES" ? "tv" : "movie"));
|
|
}
|
|
|
|
const server = createServer(async (request, response) => {
|
|
const url = new URL(request.url ?? "/", `http://localhost:${port}`);
|
|
const query = url.searchParams.get("query") ?? "";
|
|
|
|
const send = (body, status = 200) => {
|
|
response.writeHead(status, { "content-type": "application/json; charset=utf-8" });
|
|
response.end(JSON.stringify(body));
|
|
};
|
|
|
|
try {
|
|
if (url.pathname.startsWith("/3/search/")) {
|
|
const kind = url.pathname.split("/").pop();
|
|
const type = kind === "tv" ? "SERIES" : kind === "movie" ? "MOVIE" : null;
|
|
const results = await search(query, type);
|
|
return send({ page: 1, results, total_pages: 1, total_results: results.length });
|
|
}
|
|
|
|
return send({ status_message: `Kein Ersatz für ${url.pathname}` }, 404);
|
|
} catch (error) {
|
|
return send({ status_message: String(error) }, 500);
|
|
}
|
|
});
|
|
|
|
server.listen(port, "127.0.0.1", () => {
|
|
console.log(`TMDB-Ersatz läuft auf http://127.0.0.1:${port}/3`);
|
|
});
|
|
|
|
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
process.on(signal, () => {
|
|
server.close();
|
|
void prisma.$disconnect();
|
|
process.exit(0);
|
|
});
|
|
}
|