IMDb, Rotten Tomatoes und Metacritic neben der TMDB-Wertung
IMDbs eigene Schnittstelle kostet Geld, Rotten Tomatoes vergibt Zugänge nur an Partner, Letterboxd hat gar keine offene. OMDb liefert alle drei in einer Antwort, über die IMDb-Kennung, die ohnehin schon an jedem Film steht. **Eine Tabelle und kein Feld je Quelle.** `Movie.Rating` ist ein Skalar ohne Quellenangabe, und das war richtig, solange es eine Quelle gab. Für drei weitere wären es drei Spalten — und bei der vierten wieder eine Migration. Jetzt ist jede Quelle eine Zeile, je Titel und Quelle eine, mit eindeutigem Index darauf: ein zweiter Lauf schreibt um statt anzuhäufen. **Zwei Werte je Bewertung, und beide gebraucht.** Der normierte Wert zum Vergleichen und Sortieren, die Anzeige zum Hinschreiben. Rotten Tomatoes in „8,7 von 10" umzurechnen wäre eine Lüge über die Quelle — dort steht 87 %, und genau so kennt man es. **Wieder keine zweite Metadatenquelle.** `IBewertungsquelle` liefert Bewertungen, sonst nichts. Bei Titel und Beschreibung wären zwei Anbieter zwei Wahrheiten; bei einer Bewertung ist jede Quelle eine eigene Zahl, und da sind mehrere kein Widerspruch, sondern der Sinn der Sache. **Ohne Kennung wird nicht gefragt.** OMDb kennt nur den Weg über die IMDb-Kennung; nach einem Titel zu suchen hiesse, denselben Zuordnungsfehler ein zweites Mal zu riskieren. Und auf Unbekanntes antwortet OMDb mit 200 und `Response: "False"` — das als Treffer durchzulassen legte eine leere Bewertung ab, die danach als „keine Wertung" dastünde. Angezeigt wird mit Kürzel statt Stern: vier gleich aussehende Zahlen nebeneinander sagen nicht, woher sie kommen. Kein Logo — die Marken von IMDb und Rotten Tomatoes mitzuliefern hiesse, fremde Zeichen auszuliefern. Kein mitgelieferter Schlüssel, aus demselben Grund wie bei fanart.tv: OMDb begrenzt auf tausend Abrufe am Tag je Schlüssel. Einzutragen unter Einstellungen → Übersicht → Metadaten. 740 Tests grün, davon acht neue. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
97edb49564
commit
af2b844d65
+12
-1
@@ -54,6 +54,9 @@ export type Mitwirkender = {
|
||||
character: string | null; image: string | null;
|
||||
};
|
||||
|
||||
/** Eine Bewertung von anderswo — Quelle, Zahl zum Vergleichen, Text zum Anzeigen. */
|
||||
export type Fremdwertung = { source: string; value: number; display: string };
|
||||
|
||||
export type Film = {
|
||||
id: string; title: string; year: number | null; overview: string | null;
|
||||
posterImage: string | null; backdropImage: string | null; files: Datei[];
|
||||
@@ -61,6 +64,8 @@ export type Film = {
|
||||
logoImage: string | null;
|
||||
runtimeMinutes: number | null;
|
||||
rating: number | null;
|
||||
/** Leer in Listen, gefüllt beim Einzelabruf — und nur, wenn eine Quelle eingerichtet ist. */
|
||||
externalRatings: Fremdwertung[] | null;
|
||||
tagline: string | null;
|
||||
contentRating: string | null;
|
||||
/** Leer in Listen, gefüllt beim Einzelabruf. */
|
||||
@@ -86,6 +91,8 @@ export type Serie = {
|
||||
logoImage: string | null;
|
||||
runtimeMinutes: number | null;
|
||||
rating: number | null;
|
||||
/** Leer in Listen, gefüllt beim Einzelabruf — und nur, wenn eine Quelle eingerichtet ist. */
|
||||
externalRatings: Fremdwertung[] | null;
|
||||
tagline: string | null;
|
||||
contentRating: string | null;
|
||||
genres: string[] | null;
|
||||
@@ -1264,9 +1271,13 @@ export const metadatenEinstellung = () =>
|
||||
hasApiKey: boolean; language: string; usingProjectKey: boolean;
|
||||
/** Ob eine zweite Bildquelle (fanart.tv) eingerichtet ist. */
|
||||
hasFanartKey: boolean;
|
||||
/** Ob eine Bewertungsquelle (OMDb) eingerichtet ist. */
|
||||
hasOmdbKey: boolean;
|
||||
}>('/settings/metadata');
|
||||
|
||||
export const metadatenSetzen = (werte: { apiKey?: string; language?: string; fanartApiKey?: string }) =>
|
||||
export const metadatenSetzen = (werte: {
|
||||
apiKey?: string; language?: string; fanartApiKey?: string; omdbApiKey?: string;
|
||||
}) =>
|
||||
anfrage<void>('/settings/metadata', { method: 'PUT', body: JSON.stringify(werte) });
|
||||
|
||||
/* ---------- Zuordnung ---------- */
|
||||
|
||||
@@ -647,6 +647,22 @@ svg { width: 20px; height: 20px; display: block; flex: none; }
|
||||
.bewertung { display: flex; align-items: center; gap: 5px; color: var(--text); font-weight: 600; }
|
||||
.bewertung svg { width: 15px; height: 15px; color: var(--accent); }
|
||||
|
||||
/* **Fremde Bewertungen: Kürzel statt Stern.**
|
||||
*
|
||||
* Der Stern gehört der Wertung von TMDB; ihn für drei weitere Quellen zu
|
||||
* wiederholen hiesse, vier gleich aussehende Zahlen nebeneinanderzustellen,
|
||||
* von denen keine sagt, woher sie kommt. Das Kürzel sagt es in zwei Zeichen.
|
||||
*
|
||||
* **Und kein Logo.** Die Marken von IMDb und Rotten Tomatoes mitzuliefern
|
||||
* hiesse, fremde Zeichen auszuliefern — mit allem, was daran hängt. */
|
||||
.bewertung.fremd { font-weight: 500; }
|
||||
.bewertungsquelle {
|
||||
font-size: 11px; font-weight: 700; letter-spacing: .03em;
|
||||
color: var(--text-muted);
|
||||
border: 1px solid var(--line); border-radius: var(--r-control);
|
||||
padding: 1px 5px;
|
||||
}
|
||||
|
||||
.werbezeile { color: var(--text-muted); font-style: italic; margin: 0 0 var(--s-loose); }
|
||||
.beschreibung { max-width: 70ch; margin: 0 0 var(--s-section); color: var(--text-dim); }
|
||||
|
||||
|
||||
@@ -22,6 +22,19 @@ type Art = 'filme' | 'serien';
|
||||
* Schriftzug des Films statt der Systemschrift, darunter Eckdaten,
|
||||
* Bewertung, Knöpfe, Beschreibung, die Medienangaben als Tabelle und zuletzt
|
||||
* die Besetzung mit runden Porträts. */
|
||||
/** Wie die fremden Quellen heissen — kurz an der Kachel, lang im Tooltip.
|
||||
*
|
||||
* **Kein Logo.** Die Marken von IMDb und Rotten Tomatoes mitzuliefern
|
||||
* hiesse, fremde Zeichen auszuliefern; ein Kürzel sagt dasselbe und gehört
|
||||
* uns. */
|
||||
const QUELLENKUERZEL: Record<string, string> = {
|
||||
imdb: 'IMDb', rottenTomatoes: 'RT', metacritic: 'MC',
|
||||
};
|
||||
|
||||
const QUELLENNAMEN: Record<string, string> = {
|
||||
imdb: 'IMDb', rottenTomatoes: 'Rotten Tomatoes', metacritic: 'Metacritic',
|
||||
};
|
||||
|
||||
export function detailseite(art: Art, id: string, zurueck: () => void): HTMLElement {
|
||||
const wurzel = document.createElement('div');
|
||||
wurzel.className = 'detail';
|
||||
@@ -238,6 +251,23 @@ export function detailseite(art: Art, id: string, zurueck: () => void): HTMLElem
|
||||
zeile.appendChild(b);
|
||||
}
|
||||
|
||||
// **Fremde Bewertungen daneben, nicht statt dessen.**
|
||||
//
|
||||
// Jede Quelle in ihrer eigenen Form: IMDb zählt bis zehn, Rotten
|
||||
// Tomatoes in Prozent, Metacritic bis hundert. „87 %" in „8,7 von 10"
|
||||
// umzurechnen wäre eine Lüge über die Quelle — man kennt sie so, wie
|
||||
// sie dort steht.
|
||||
(eintrag.externalRatings ?? []).forEach(w => {
|
||||
const b = document.createElement('span');
|
||||
b.className = 'bewertung fremd';
|
||||
const name = document.createElement('span');
|
||||
name.className = 'bewertungsquelle';
|
||||
name.textContent = QUELLENKUERZEL[w.source] ?? w.source;
|
||||
b.append(name, document.createTextNode(w.display));
|
||||
b.title = `Bewertung bei ${QUELLENNAMEN[w.source] ?? w.source}`;
|
||||
zeile.appendChild(b);
|
||||
});
|
||||
|
||||
return zeile;
|
||||
}
|
||||
|
||||
|
||||
@@ -202,7 +202,8 @@ export function serverbereich(geaendert: () => void): HTMLElement {
|
||||
(stand.usingProjectKey ? 'Mitgelieferter TMDB-Zugang'
|
||||
: stand.hasApiKey ? 'Eigener TMDB-Zugang'
|
||||
: 'Kein Zugang hinterlegt')
|
||||
+ (stand.hasFanartKey ? ' · fanart.tv für Plakate' : ''),
|
||||
+ (stand.hasFanartKey ? ' · fanart.tv für Plakate' : '')
|
||||
+ (stand.hasOmdbKey ? ' · OMDb für Bewertungen' : ''),
|
||||
stand.usingProjectKey
|
||||
? 'Ein eigener Zugang löst den mitgelieferten ab.'
|
||||
: 'Nach einer Sprachänderung die Metadaten auffrischen — '
|
||||
@@ -228,12 +229,24 @@ export function serverbereich(geaendert: () => void): HTMLElement {
|
||||
+ 'für Filme — der Serienweg von fanart.tv braucht eine '
|
||||
+ 'TVDB-Kennung, die Flaro nicht führt.',
|
||||
},
|
||||
{
|
||||
name: 'omdb', beschriftung: 'OMDb (Bewertungen)', art: 'password',
|
||||
platzhalter: stand.hasOmdbKey ? 'Hinterlegt — leer lassen, um ihn zu behalten' : 'Persönlicher API-Schlüssel',
|
||||
// IMDbs eigene Schnittstelle kostet Geld, Rotten Tomatoes
|
||||
// vergibt Zugänge nur an Partner, Letterboxd hat gar keine
|
||||
// offene. OMDb liefert alle drei über die IMDb-Kennung, die
|
||||
// ohnehin schon an jedem Film steht.
|
||||
hinweis: 'Liefert IMDb, Rotten Tomatoes und Metacritic in einem '
|
||||
+ 'Abruf. Kostenlos bis tausend Abrufe am Tag, deshalb kein '
|
||||
+ 'mitgelieferter Schlüssel.',
|
||||
},
|
||||
],
|
||||
bestaetigen: 'Speichern',
|
||||
tun: async wert => {
|
||||
await metadatenSetzen({
|
||||
apiKey: wert('schluessel') || undefined,
|
||||
fanartApiKey: wert('fanart') || undefined,
|
||||
omdbApiKey: wert('omdb') || undefined,
|
||||
});
|
||||
location.reload();
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user