// OMDb-Client — eigene Datenbasis, findet oft, was TMDb nicht exakt // trifft. Nur Englisch; der deutsche Datensatz wird danach über // TMDb /find (IMDb-ID) nachgeladen (zuordnung.ts). Ohne Key überspringt // er sich selbst — kein Fehler, keine Behauptung. export interface OmdbTreffer { type: 'movie' | 'tv' id: string title: string year: number | null overview: string poster_path: string runtime: number genres: string[] source: 'omdb' } export class OmdbClient { constructor( private readonly apiKey: string, private readonly holen: typeof fetch = fetch, ) {} get verfuegbar(): boolean { return this.apiKey.length > 0 } async lookup(titel: string): Promise { if (!this.verfuegbar) return null try { const url = new URL('https://www.omdbapi.com/') url.searchParams.set('apikey', this.apiKey) url.searchParams.set('t', titel) url.searchParams.set('r', 'json') const antwort = await this.holen(url.toString(), { signal: AbortSignal.timeout(15_000) }) if (antwort.status >= 300) return null const daten = (await antwort.json()) as Record if (daten['Response'] !== 'True') return null const jahr = /(\d{4})/.exec(daten['Year'] ?? '') const laufzeit = /(\d+)/.exec(daten['Runtime'] ?? '') return { type: daten['Type'] === 'series' ? 'tv' : 'movie', id: daten['imdbID'] ?? '', title: daten['Title'] ?? titel, year: jahr === null ? null : Number(jahr[1]), overview: daten['Plot'] ?? '', poster_path: daten['Poster'] !== 'N/A' ? (daten['Poster'] ?? '') : '', runtime: laufzeit === null ? 0 : Number(laufzeit[1]), genres: (daten['Genre'] ?? '') .split(',') .map((g) => g.trim()) .filter((g) => g.length > 0), source: 'omdb', } } catch { return null } } }