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 = 'Herkunft wird gelesen…'; 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 = 'Herkunft nicht lesbar.'; }); 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 { treffer.innerHTML = '
'; 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 = `
${esc(v.title)}` + `${esc(v.year ?? '')} · TMDB ${v.tmdbId}
` + `
${esc((v.overview ?? '').slice(0, 160))}
`; 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 = 'Wird zugeordnet…'; 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 = 'Übernommen.'; 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))); } } }