Weiterschauen, sichtbar gemacht

Die Oberfläche zeigt jetzt, was der Server seit Stufe 4 weiss.

Auf der Startseite zwei neue Reihen, ganz oben: **Weiterschauen** und
**Nächste Folge**. Sie überschneiden sich nicht — was angefangen ist, steht
links, was ansteht, rechts. Eine leere Reihe wird gar nicht erst gezeigt.

Auf jeder Kachel ein Balken, und zwar **ab der ersten Sekunde**. Bei einem
Zweieinhalbstünder sind zwei Minuten 1,3 % — anderthalb Bildpunkte. Der
Balken hat deshalb eine Mindestbreite, sonst wäre das Versprechen „wir merken
uns jede Sekunde" ausgerechnet dort unsichtbar, wo es zählt. Serien tragen
die Zahl offener Folgen als Plakette in der Ecke.

Auf der Detailseite stehen **beide Knöpfe nebeneinander**: „Weiterschauen ab
0:42" und „Von vorn". Auch bei einem gesehenen Film — der Server wirft die
Stelle nicht weg, wenn das Häkchen kommt, und sie hier zu verschweigen hiesse,
genau den Unterschied wegzuwerfen, um dessentwillen er sie behält. Dazu ein
Schalter für gesehen/ungesehen.

Der Spieler meldet alle zehn Sekunden, beim Pausieren, am Ende und beim
Schliessen. Nicht bei jedem `timeupdate`: das feuert viermal je Sekunde, und
daraus Anfragen zu machen hiesse, den Server mit Meldungen zu bewerfen, die
sich um 250 ms unterscheiden. Eine verlorene Meldung kostet ein paar Sekunden
Fortschritt — dafür mitten im Film einen Fehler einzublenden wäre die
schlechtere Hälfte des Tauschs.

**Nebenbei ein Fehler beseitigt, der nur mit grosser Mediathek aufgefallen
wäre:** die Detailseite holte 200 Titel und suchte darin nach der Kennung.
Bei 201 Filmen hätte sie vom letzten behauptet, es gebe ihn nicht mehr. Jetzt
gibt es den Einzelabruf, und sie benutzt ihn.

Im Container durchgespielt: „Der Untergang" von vorn, die MKV wird umgepackt,
läuft durch — danach steht „Gesehen" unter dem Titel, der Balken ist voll,
und der Knopf bietet beides an.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-03 21:33:56 +02:00
co-authored by Claude Opus 5
parent 67cf0fd1d9
commit 6d5824b889
6 changed files with 284 additions and 53 deletions
+51 -1
View File
@@ -14,18 +14,36 @@ export type Datei = {
id: string; fileName: string; sizeBytes: number;
container: string | null; durationSeconds: number | null; streams: Spur[];
};
/** Was dieser Benutzer mit einem Titel zu tun hatte. */
export type Stand = {
positionSeconds: number; runtimeSeconds: number | null;
watched: boolean; playCount: number;
favourite: boolean; rating: number | null; dismissed: boolean;
lastPlayedAt: 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;
};
export type Serie = {
id: string; title: string; year: number | null; overview: string | null;
posterImage: string | null; backdropImage: string | null;
episodeCount: number; seasons: number[];
episodeCount: number; seasons: number[]; unwatchedCount: number;
};
export type Folge = {
id: string; season: number; episode: number;
title: string | null; overview: string | null; stillImage: string | null; files: Datei[];
state: Stand | null;
};
/** Eine Kachel in „Weiterschauen" oder „Nächste Folge" — Film wie Folge. */
export type Angefangen = {
id: string; kind: 'movie' | 'episode';
title: string; subTitle: string | null;
season: number | null; episode: number | null; year: number | null;
posterImage: string | null; backdropImage: string | null;
state: Stand;
};
export type Bibliothek = { id: string; name: string; kind: string; path: string };
export type Seite<T> = { items: T[]; total: number };
@@ -168,8 +186,40 @@ export const serien = (q: { search?: string; skip?: number; take?: number } = {}
export const folgen = (serie: string, staffel?: number) =>
anfrage<Seite<Folge>>(`/series/${serie}/episodes${abfrage({ season: staffel })}`);
export const film = (id: string) => anfrage<Film>(`/movies/${id}`);
export const serie = (id: string) => anfrage<Serie>(`/series/${id}`);
export const lieferung = (datei: string) => anfrage<Lieferung>(`/files/${datei}/delivery`);
/* ---------- Stand je Benutzer ---------- */
export const weiterschauen = (take = 20) =>
anfrage<Seite<Angefangen>>(`/home/continue${abfrage({ take })}`);
export const naechsteFolgen = (take = 20) =>
anfrage<Seite<Angefangen>>(`/home/next${abfrage({ take })}`);
/** Meldet, wo man steht. Jede Meldung zählt — es gibt keine zu kleine. */
export const fortschritt = (id: string, stelle: number, laenge?: number | null) =>
anfrage<Stand>(`/items/${id}/progress`, {
method: 'POST',
body: JSON.stringify({ positionSeconds: stelle, runtimeSeconds: laenge ?? null }),
});
export const gesehen = (id: string, wert: boolean) =>
anfrage<Stand>(`/items/${id}/watched`, {
method: 'PUT', body: JSON.stringify({ watched: wert }),
});
/** Aus „Weiterschauen" nehmen, ohne zu behaupten, man habe es gesehen. */
export const weglegen = (id: string) =>
anfrage<Stand>(`/items/${id}/dismiss`, { method: 'POST' });
export const favorit = (id: string, wert: boolean) =>
anfrage<Stand>(`/items/${id}/favourite`, {
method: 'PUT', body: JSON.stringify({ favourite: wert }),
});
export const downloadAdresse = (datei: string) => `/api/v1/files/${datei}/download`;
/* ---------- Verwaltung ---------- */