Die Oberfläche nach Plex' Vorbild — Hintergrund, Schriftzug, Besetzung
Nach den Bildern deiner Plex-Installation umgebaut. Übernommen ist der Aufbau, nicht die Farbe: Plex ist bernsteingelb, Flaro bleibt Orchidee. **Kopfleiste oben** mit Marke, Suche und Konto. Die Suche gehört dorthin und nicht in jede Mediathek: wer einen Titel sucht, weiss oft gar nicht, in welcher er liegt. Sie geht quer durch alles. **Seitenleiste mit Icons** — echte SVG, keine Zeichen aus einer Schriftart. Ein ▶ aus Unicode sieht auf jedem System anders aus, lässt sich nicht einfärben und springt in der Grundlinie. Die aktive Zeile bekommt einen schmalen Balken links statt eines leuchtenden Blocks; das liest sich in einer langen Liste ruhiger. **Die Kachel steht jetzt an einer Stelle** statt an fünfen. Sie trägt den Haken für Gesehenes in der Ecke — wie bei Plex, ein Blick über das Raster genügt —, die Zahl offener Folgen an derselben Stelle, den Fortschrittsbalken unten und beim Überfahren einen Abspielknopf. Folgen sind dreizeilig: Serie, Folgentitel, „S14 · F22". **Reihen haben Pfeile.** Zwanzig Kacheln passen nicht in den Bildschirm, und quer zu scrollen geht mit dem Rad nur mit Umschalttaste. Die Überschrift nennt die Mediathek beim Namen: „Zuletzt in Dokumentationen hinzugefügt". **Mediatheken haben drei Reiter** — Empfehlungen, Mediathek, Kategorien. Darunter eine Filterzeile mit Genre, Sortierung und der Anzahl. Die Kategorien sind farbige Kacheln; die Farbe kommt aus dem Namen, nicht aus einer festen Liste, die jedes Genre kennen müsste, das TMDB je erfindet. **Die Detailseite** hat jetzt den unscharfen Hintergrund über die ganze Seite, den **Schriftzug des Films** statt der Systemschrift, eine Zeile mit Jahr, Genres, Laufzeit, Altersfreigabe und Bewertung, runde Knöpfe für gesehen/Favorit/weglegen, die Medienangaben als Tabelle — Datei, Video, Ton, Untertitel — und zuletzt Besetzung & Stab mit runden Porträts. **Dazu am Server:** ein Name. Ohne ihn zeigten die Clients den Hostnamen, und die App schrieb „localhost" über die Anmeldung. Vorgabe ist der Rechnername des Containers. --- **Der Fehler, den das erste Bild zeigte:** die Lupe in der Suchleiste war dreihundert Bildpunkte gross und lag über der halben Seite. Ein rohes `<svg>` ohne Breite füllt seinen Container; die Regel galt nur für `.icon`. Jetzt für jedes. 368 Tests grün. Am Container nachgesehen: „Der Untergang" mit eigenem Schriftzug, FSK 12, Bewertung 7,9, Genres Drama/Historie/Kriegsfilm, beide Tonspuren in der Tabelle, 22 Porträts darunter. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ede4069cd4
commit
8c395ac051
+19
-19
@@ -22,7 +22,7 @@ function bibliotheksbereich(geaendert: () => void): HTMLElement {
|
||||
h.textContent = 'Bibliotheken';
|
||||
|
||||
const liste = document.createElement('div');
|
||||
liste.className = 'settings';
|
||||
liste.className = 'einstellungen';
|
||||
bereich.append(h, liste, anlegeform());
|
||||
void laden();
|
||||
|
||||
@@ -45,10 +45,10 @@ function bibliotheksbereich(geaendert: () => void): HTMLElement {
|
||||
|
||||
function block(b: Bibliothek): HTMLElement {
|
||||
const d = document.createElement('div');
|
||||
d.className = 'setting';
|
||||
d.className = 'einstellung';
|
||||
|
||||
const kopf = document.createElement('div');
|
||||
kopf.className = 'rowline';
|
||||
kopf.className = 'zeile';
|
||||
// „1 Filme" ist der Satz, an dem man sieht, dass niemand hingeschaut hat.
|
||||
const serie = b.kind === 'Shows';
|
||||
const anzahl = b.itemCount == null ? ''
|
||||
@@ -56,11 +56,11 @@ function bibliotheksbereich(geaendert: () => void): HTMLElement {
|
||||
? (serie ? 'Serie' : 'Film')
|
||||
: (serie ? 'Serien' : 'Filme'));
|
||||
kopf.innerHTML = `<strong>${esc(b.name)}</strong>`
|
||||
+ `<span class="tiny">${esc(b.path)}${esc(anzahl)}</span>`;
|
||||
+ `<span class="winzig">${esc(b.path)}${esc(anzahl)}</span>`;
|
||||
d.appendChild(kopf);
|
||||
|
||||
const stand = document.createElement('div');
|
||||
stand.className = 'hint';
|
||||
stand.className = 'hinweis';
|
||||
const los = knopf('Scannen', 'btn');
|
||||
const umbenennen = knopf('Umbenennen', 'btn');
|
||||
const entfernen = knopf('Entfernen', 'btn');
|
||||
@@ -71,7 +71,7 @@ function bibliotheksbereich(geaendert: () => void): HTMLElement {
|
||||
eingabe.value = b.name;
|
||||
const fertig = knopf('Speichern', 'btn btn-primary');
|
||||
const reihe = document.createElement('div');
|
||||
reihe.className = 'btn-row';
|
||||
reihe.className = 'knopfreihe';
|
||||
reihe.append(eingabe, fertig);
|
||||
d.appendChild(reihe);
|
||||
eingabe.focus();
|
||||
@@ -140,7 +140,7 @@ function bibliotheksbereich(geaendert: () => void): HTMLElement {
|
||||
}
|
||||
|
||||
const knoepfe = document.createElement('div');
|
||||
knoepfe.className = 'btn-row';
|
||||
knoepfe.className = 'knopfreihe';
|
||||
knoepfe.append(los, umbenennen, entfernen);
|
||||
d.append(knoepfe, stand);
|
||||
void verfolgen();
|
||||
@@ -149,7 +149,7 @@ function bibliotheksbereich(geaendert: () => void): HTMLElement {
|
||||
|
||||
function anlegeform(): HTMLElement {
|
||||
const form = document.createElement('div');
|
||||
form.className = 'setting';
|
||||
form.className = 'einstellung';
|
||||
|
||||
const name = feld('Name, etwa Filme');
|
||||
const pfad = feld('Pfad im Container, etwa /media/Filme');
|
||||
@@ -159,7 +159,7 @@ function bibliotheksbereich(geaendert: () => void): HTMLElement {
|
||||
const los = knopf('Anlegen', 'btn btn-primary');
|
||||
|
||||
const reihe = document.createElement('div');
|
||||
reihe.className = 'btn-row';
|
||||
reihe.className = 'knopfreihe';
|
||||
reihe.append(name, art, pfad, los);
|
||||
form.append(reihe);
|
||||
|
||||
@@ -187,21 +187,21 @@ function benutzerbereich(): HTMLElement {
|
||||
const h = document.createElement('h2');
|
||||
h.textContent = 'Benutzer';
|
||||
const liste = document.createElement('div');
|
||||
liste.className = 'settings';
|
||||
liste.className = 'einstellungen';
|
||||
|
||||
const name = feld('Benutzername');
|
||||
const passwort = feld('Passwort, mindestens 8 Zeichen', 'password');
|
||||
const admin = document.createElement('label');
|
||||
admin.className = 'hint';
|
||||
admin.className = 'hinweis';
|
||||
const haken = document.createElement('input');
|
||||
haken.type = 'checkbox';
|
||||
admin.append(haken, document.createTextNode(' Administrator'));
|
||||
const los = knopf('Anlegen', 'btn');
|
||||
|
||||
const form = document.createElement('div');
|
||||
form.className = 'setting';
|
||||
form.className = 'einstellung';
|
||||
const reihe = document.createElement('div');
|
||||
reihe.className = 'btn-row';
|
||||
reihe.className = 'knopfreihe';
|
||||
reihe.append(name, passwort, admin, los);
|
||||
form.append(reihe);
|
||||
|
||||
@@ -215,9 +215,9 @@ function benutzerbereich(): HTMLElement {
|
||||
liste.innerHTML = '';
|
||||
alle.forEach(b => {
|
||||
const d = document.createElement('div');
|
||||
d.className = 'setting';
|
||||
d.innerHTML = `<div class="rowline"><strong>${esc(b.userName)}</strong>`
|
||||
+ `<span class="tiny">${b.isAdministrator ? 'Administrator' : 'Benutzer'}</span></div>`;
|
||||
d.className = 'einstellung';
|
||||
d.innerHTML = `<div class="zeile"><strong>${esc(b.userName)}</strong>`
|
||||
+ `<span class="winzig">${b.isAdministrator ? 'Administrator' : 'Benutzer'}</span></div>`;
|
||||
liste.appendChild(d);
|
||||
});
|
||||
} catch (fehler) {
|
||||
@@ -247,7 +247,7 @@ function metadatenbereich(): HTMLElement {
|
||||
const h = document.createElement('h2');
|
||||
h.textContent = 'Metadaten';
|
||||
const block = document.createElement('div');
|
||||
block.className = 'setting';
|
||||
block.className = 'einstellung';
|
||||
bereich.append(h, block);
|
||||
|
||||
void (async () => {
|
||||
@@ -255,7 +255,7 @@ function metadatenbereich(): HTMLElement {
|
||||
const stand = await metadatenEinstellung();
|
||||
|
||||
const auskunft = document.createElement('div');
|
||||
auskunft.className = 'hint';
|
||||
auskunft.className = 'hinweis';
|
||||
auskunft.textContent = stand.usingProjectKey
|
||||
? 'Es wird der mitgelieferte TMDB-Zugang benutzt. Ein eigener löst ihn ab.'
|
||||
: stand.hasApiKey ? 'Ein eigener TMDB-Zugang ist hinterlegt.'
|
||||
@@ -267,7 +267,7 @@ function metadatenbereich(): HTMLElement {
|
||||
const los = knopf('Speichern', 'btn');
|
||||
|
||||
const reihe = document.createElement('div');
|
||||
reihe.className = 'btn-row';
|
||||
reihe.className = 'knopfreihe';
|
||||
reihe.append(schluessel, sprache, los);
|
||||
|
||||
los.onclick = async () => {
|
||||
|
||||
Reference in New Issue
Block a user