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
+41
-2
@@ -21,15 +21,36 @@ export type Stand = {
|
||||
favourite: boolean; rating: number | null; dismissed: boolean;
|
||||
lastPlayedAt: string | null;
|
||||
};
|
||||
export type Mitwirkender = {
|
||||
id: string; name: string;
|
||||
role: 'Actor' | 'Director' | 'Writer';
|
||||
character: string | null; image: string | null;
|
||||
};
|
||||
|
||||
export type Film = {
|
||||
id: string; title: string; year: number | null; overview: string | null;
|
||||
posterImage: string | null; backdropImage: string | null; files: Datei[];
|
||||
state: Stand | null;
|
||||
logoImage: string | null;
|
||||
runtimeMinutes: number | null;
|
||||
rating: number | null;
|
||||
tagline: string | null;
|
||||
contentRating: string | null;
|
||||
/** Leer in Listen, gefüllt beim Einzelabruf. */
|
||||
genres: string[] | null;
|
||||
credits: Mitwirkender[] | null;
|
||||
};
|
||||
export type Serie = {
|
||||
id: string; title: string; year: number | null; overview: string | null;
|
||||
posterImage: string | null; backdropImage: string | null;
|
||||
episodeCount: number; seasons: number[]; unwatchedCount: number;
|
||||
logoImage: string | null;
|
||||
runtimeMinutes: number | null;
|
||||
rating: number | null;
|
||||
tagline: string | null;
|
||||
contentRating: string | null;
|
||||
genres: string[] | null;
|
||||
credits: Mitwirkender[] | null;
|
||||
};
|
||||
export type Folge = {
|
||||
id: string; season: number; episode: number;
|
||||
@@ -71,6 +92,10 @@ export type Vorschlag = {
|
||||
overview: string | null; posterUrl: string | null;
|
||||
};
|
||||
export type Hinweis = { text: string; logoUrl: string; url: string };
|
||||
export type Systemauskunft = {
|
||||
version: string; serverName: string | null;
|
||||
needsSetup: boolean; attributions: Hinweis[];
|
||||
};
|
||||
|
||||
/* ---------- Sitzung ---------- */
|
||||
|
||||
@@ -139,7 +164,7 @@ export const bildAdresse = (schluessel: string | null): string | null =>
|
||||
/* ---------- System und Anmeldung ---------- */
|
||||
|
||||
export const systemauskunft = () =>
|
||||
anfrage<{ version: string; needsSetup: boolean; attributions: Hinweis[] }>('/system/info');
|
||||
anfrage<Systemauskunft>('/system/info');
|
||||
|
||||
export const einrichten = (benutzer: string, passwort: string) =>
|
||||
anfrage<Rolle>('/system/setup', {
|
||||
@@ -188,7 +213,8 @@ function geraetekennung(): string {
|
||||
export type Ordnung = 'title' | 'added' | 'year';
|
||||
|
||||
type Suchfrage = {
|
||||
library?: string; search?: string; sort?: Ordnung; skip?: number; take?: number;
|
||||
library?: string; search?: string; sort?: Ordnung; genre?: string;
|
||||
skip?: number; take?: number;
|
||||
};
|
||||
|
||||
export const filme = (q: Suchfrage = {}) => anfrage<Seite<Film>>(`/movies${abfrage(q)}`);
|
||||
@@ -247,6 +273,19 @@ export const downloadAdresse = (datei: string) => `/api/v1/files/${datei}/downlo
|
||||
|
||||
export const bibliotheken = () => anfrage<Bibliothek[]>('/libraries');
|
||||
|
||||
/** Nur die Genres, die in dieser Mediathek auch vorkommen. */
|
||||
export const genresVon = (id: string) => anfrage<string[]>(`/libraries/${id}/genres`);
|
||||
|
||||
/** „Weil du Ähnliches gesehen hast" — über die Genres des Gesehenen. */
|
||||
export const empfehlungen = (take = 20) =>
|
||||
anfrage<Seite<Film>>(`/home/suggestions${abfrage({ take })}`);
|
||||
|
||||
export const metadatenAuffrischen = (id: string) =>
|
||||
anfrage<void>(`/libraries/${id}/refresh`, { method: 'POST' });
|
||||
|
||||
export const servernameSetzen = (name: string) =>
|
||||
anfrage<void>('/system/name', { method: 'PUT', body: JSON.stringify({ name }) });
|
||||
|
||||
export const bibliothekUmbenennen = (id: string, name: string) =>
|
||||
anfrage<Bibliothek>(`/libraries/${id}`, {
|
||||
method: 'PUT', body: JSON.stringify({ name }),
|
||||
|
||||
Reference in New Issue
Block a user