„Doctor Strange" war fälschlich auf ein Making-of von 2022 gelaufen. Beim Korrigieren hängte die Vorschlagssuche `year=2022` an — aus genau der Zuordnung, die weg sollte — und lieferte nur noch Titel von 2022. Der richtige Film von 2016 konnte gar nicht erscheinen, egal wie oft man suchte. An TMDB nachgemessen: query='Doctor Strange' ohne Jahr → 2016 Doctor Strange, 2022 Multiverse … query='Doctor Strange' mit year=2022 → genau die drei Treffer aus der Meldung Gesucht wird jetzt mit dem Jahr aus dem Dateinamen, nie mit dem aus dem Treffer. Die Oberfläche trägt ebenfalls keines mehr vor: das angezeigte stammt aus derselben falschen Zuordnung. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
180 lines
6.6 KiB
TypeScript
180 lines
6.6 KiB
TypeScript
import { vorschlaege, zuordnen, herkunft } from './api';
|
|
import { esc, knopf, feld, fehlerzeile, leer, meldung } from './util';
|
|
import { icons } from './icons';
|
|
|
|
/** Einen Titel einem anderen Eintrag der Metadatenquelle zuordnen.
|
|
*
|
|
* **Als eigener Dialog und nicht nur auf der Detailseite.** Wer in der
|
|
* Mediathek zwanzig falsch erkannte Titel vor sich hat, soll sie dort
|
|
* geradeziehen können — vierzig Seitenwechsel sind keine Bedienung.
|
|
*
|
|
* `fertig` wird gerufen, wenn etwas zugeordnet wurde; der Aufrufer frischt
|
|
* damit auf, was er zeigt.
|
|
*/
|
|
export function zuordnungsdialog(
|
|
art: 'movies' | 'series',
|
|
id: string,
|
|
titel: string,
|
|
jahr: number | null,
|
|
fertig?: () => void,
|
|
): void {
|
|
const d = document.createElement('dialog');
|
|
d.className = 'dlg dlg-breit';
|
|
|
|
const kopf = document.createElement('div');
|
|
kopf.className = 'dlg-kopf';
|
|
const h = document.createElement('h2');
|
|
h.textContent = 'Zuordnung korrigieren';
|
|
const zu = document.createElement('button');
|
|
zu.className = 'dlg-zu';
|
|
zu.innerHTML = icons.schliessen;
|
|
zu.setAttribute('aria-label', 'Schliessen');
|
|
zu.onclick = () => d.close();
|
|
kopf.append(h, zu);
|
|
|
|
const koerper = document.createElement('div');
|
|
koerper.className = 'dlg-koerper';
|
|
|
|
// **Woher der Titel stammt, über allem anderen.**
|
|
//
|
|
// Liegt die Zuordnung völlig daneben, sagt der angezeigte Titel nichts mehr
|
|
// darüber, welche Datei hier eigentlich gemeint ist — „Dark Shadows" von
|
|
// 1966 kann alles sein. Der Ordner sagt es in einer Zeile.
|
|
const quelle = document.createElement('div');
|
|
quelle.className = 'quelle';
|
|
quelle.innerHTML = '<span class="winzig">Herkunft wird gelesen…</span>';
|
|
|
|
void herkunft(art, id).then(h => {
|
|
quelle.innerHTML = '';
|
|
|
|
const ordner = document.createElement('div');
|
|
ordner.className = 'quelle-pfad';
|
|
ordner.textContent = h.ordner ?? 'Kein gemeinsamer Ordner';
|
|
ordner.title = h.ordner ?? '';
|
|
quelle.appendChild(ordner);
|
|
|
|
const dazu = document.createElement('span');
|
|
dazu.className = 'winzig';
|
|
const namen = h.beispiele.join(' · ');
|
|
dazu.textContent = h.dateien === 0
|
|
? 'Keine Datei mehr zugeordnet.'
|
|
: `${h.dateien} ${h.dateien === 1 ? 'Datei' : 'Dateien'}`
|
|
+ (namen ? ` — ${namen}${h.dateien > h.beispiele.length ? ' …' : ''}` : '');
|
|
quelle.appendChild(dazu);
|
|
}).catch(() => {
|
|
quelle.innerHTML = '<span class="winzig">Herkunft nicht lesbar.</span>';
|
|
});
|
|
|
|
const hinweis = document.createElement('p');
|
|
hinweis.className = 'hinweis';
|
|
hinweis.textContent = 'Der gewählte Eintrag ersetzt Titel, Beschreibung, Bilder '
|
|
+ 'und Besetzung. Von Hand gewählte Bilder bleiben.';
|
|
|
|
const suche = feld('Anderer Suchbegriff');
|
|
suche.value = titel;
|
|
// Leer heisst: der Server nimmt das Jahr aus dem Dateinamen. Hier etwas
|
|
// vorzutragen, das aus der zu korrigierenden Zuordnung stammt, hiesse die
|
|
// Suche auf genau den falschen Jahrgang einzugrenzen.
|
|
const jahrfeld = feld('Jahr (aus dem Dateinamen)', 'number');
|
|
if (jahr) jahrfeld.value = String(jahr);
|
|
const suchen = knopf('Suchen', 'btn');
|
|
|
|
const reihe = document.createElement('div');
|
|
reihe.className = 'knopfreihe';
|
|
reihe.append(suche, jahrfeld, suchen);
|
|
|
|
const treffer = document.createElement('div');
|
|
treffer.className = 'einstellungen';
|
|
|
|
koerper.append(quelle, hinweis, reihe, treffer);
|
|
d.append(kopf, koerper);
|
|
|
|
d.addEventListener('close', () => d.remove());
|
|
d.addEventListener('click', e => { if (e.target === d) d.close(); });
|
|
// Enter im Suchfeld sucht, statt den Dialog zu schliessen.
|
|
suche.onkeydown = e => { if (e.key === 'Enter') { e.preventDefault(); void fragen(); } };
|
|
|
|
document.body.appendChild(d);
|
|
d.showModal();
|
|
|
|
suchen.onclick = () => void fragen();
|
|
void fragen();
|
|
|
|
async function fragen(): Promise<void> {
|
|
treffer.innerHTML = '<div class="spinner"></div>';
|
|
try {
|
|
const liste = await vorschlaege(art, id,
|
|
suche.value.trim(), jahrfeld.value ? Number(jahrfeld.value) : undefined);
|
|
|
|
treffer.innerHTML = '';
|
|
if (liste.length === 0) { treffer.appendChild(leer('Nichts gefunden.')); return; }
|
|
|
|
const zeilen: HTMLButtonElement[] = [];
|
|
|
|
liste.forEach(v => {
|
|
const zeile = document.createElement('button');
|
|
zeile.className = 'vorschlag';
|
|
|
|
// **Mit Plakat.** „Dark" von 2017 und „Dark Shadows" von 1966 sind an
|
|
// einer Textzeile kaum zu unterscheiden, am Plakat in einer
|
|
// Zehntelsekunde. Der Server liefert die Adresse längst mit; sie wurde
|
|
// bisher weggeworfen.
|
|
const bild = document.createElement('div');
|
|
bild.className = 'vorschlag-bild';
|
|
if (v.posterUrl) {
|
|
const img = document.createElement('img');
|
|
img.src = v.posterUrl;
|
|
img.alt = '';
|
|
img.loading = 'lazy';
|
|
img.onload = () => bild.classList.add('geladen');
|
|
bild.appendChild(img);
|
|
}
|
|
|
|
const text = document.createElement('div');
|
|
text.className = 'vorschlag-text';
|
|
text.innerHTML =
|
|
`<div class="zeile"><strong>${esc(v.title)}</strong>`
|
|
+ `<span class="winzig">${esc(v.year ?? '')} · TMDB ${v.tmdbId}</span></div>`
|
|
+ `<div class="hinweis">${esc((v.overview ?? '').slice(0, 160))}</div>`;
|
|
|
|
zeile.append(bild, text);
|
|
|
|
zeile.onclick = async () => {
|
|
// **Sichtbar machen, dass etwas passiert.**
|
|
//
|
|
// Ein Klick löste einen Abruf bei TMDB aus, der Bilder nachlädt und
|
|
// mehrere Sekunden braucht — und die Oberfläche sah aus wie vorher.
|
|
// Wer nichts sieht, klickt noch einmal, und dann laufen zwei.
|
|
zeilen.forEach(z => { z.disabled = true; });
|
|
zeile.classList.add('laeuft');
|
|
|
|
const lauf = document.createElement('span');
|
|
lauf.className = 'vorschlag-lauf';
|
|
lauf.innerHTML = '<span class="spinner klein"></span><span>Wird zugeordnet…</span>';
|
|
zeile.appendChild(lauf);
|
|
|
|
try {
|
|
await zuordnen(art, id, v.tmdbId);
|
|
// Erst melden, dann schliessen: sonst blitzt nur der Dialog weg,
|
|
// und es bleibt offen, ob es geklappt hat.
|
|
lauf.innerHTML = '<span>Übernommen.</span>';
|
|
d.close();
|
|
fertig?.();
|
|
} catch (fehler) {
|
|
lauf.remove();
|
|
zeile.classList.remove('laeuft');
|
|
zeilen.forEach(z => { z.disabled = false; });
|
|
treffer.appendChild(fehlerzeile(meldung(fehler)));
|
|
}
|
|
};
|
|
|
|
zeilen.push(zeile);
|
|
treffer.appendChild(zeile);
|
|
});
|
|
} catch (fehler) {
|
|
treffer.innerHTML = '';
|
|
treffer.appendChild(fehlerzeile(meldung(fehler)));
|
|
}
|
|
}
|
|
}
|