Vorspann und Abspann — erkannt, abgelegt, überspringbar
Beides mit derselben Rechnung, nur in einem anderen Fenster: vorn die ersten fünfzehn Minuten, hinten die letzten zehn. Was `Tonabgleich` findet, hängt allein daran, welchen Ausschnitt man ihm gibt — eine zweite Erkennung für den Abspann wäre dieselbe Suche ein zweites Mal. Je Staffel verglichen, nicht je Serie: Serien wechseln ihren Vorspann zwischen Staffeln, und über alle hinweg fände man nichts oder das Falsche. Gefundenes über fünf Minuten wird verworfen — zwei Folgen, die dieselbe Datei tragen, teilen vierzig, und darüber stünde sonst ein Überspringen-Knopf. Der Lauf geht auf Zuruf, nicht bei jedem Scan: er liest von jeder Folge ein Stück Ton und dauert Minuten. Der Knopf dafür steht auf der Serienseite, bei den Verwalterknöpfen. Die Marken gehen im Folgenvertrag mit — als „intro" und „credits", die Wörter, die die Apps von Jellyfin her ohnehin kennen. In der App zeichnet sie der vorhandene Überspringen-Knopf, der bisher nie etwas zu tun hatte; im Web ist er neu, rechtsbündig über der Zeitleiste. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
acba683c94
commit
8cf2118723
+35
-4
@@ -1,7 +1,7 @@
|
||||
import type HlsTyp from 'hls.js';
|
||||
import {
|
||||
stromStarten, stromBeenden, untertitelHolen, fortschritt, nachbarfolge,
|
||||
type Stromauskunft, type Spurauskunft, type Folge,
|
||||
stromStarten, stromBeenden, untertitelHolen, fortschritt, nachbarfolge, folgeLesen,
|
||||
type Stromauskunft, type Spurauskunft, type Folge, type Marke,
|
||||
} from './api';
|
||||
import { dauer, knopf, meldung, hinweisZeigen } from './util';
|
||||
import { serienton, serientext, spurFuer } from './sprachwahl';
|
||||
@@ -600,6 +600,8 @@ export function spieler(anfrage: Spieleranfrage): void {
|
||||
/** Die Nachbarfolgen, sobald der Server sie genannt hat. */
|
||||
let vorige: Folge | null = null;
|
||||
let naechste: Folge | null = null;
|
||||
/** Vorspann und Abspann dieser Folge — leer heisst: keine erkannt. */
|
||||
let marken: Marke[] = [];
|
||||
|
||||
// **Eine Folge weiter, eine zurück.**
|
||||
//
|
||||
@@ -627,16 +629,28 @@ export function spieler(anfrage: Spieleranfrage): void {
|
||||
unten.className = 'pl-unten';
|
||||
unten.append(links, zeitmitte, rechts);
|
||||
|
||||
bedienung.append(zeitleiste, unten);
|
||||
// **„Intro überspringen" über der Leiste, rechtsbündig.**
|
||||
//
|
||||
// Dort erwartet man ihn — so macht es jeder Spieler —, und dort verdeckt
|
||||
// er weder das Bild noch die Zeitleiste. Er steht im selben Block wie die
|
||||
// Bedienung und blendet mit ihr ein und aus.
|
||||
const ueberspringen = document.createElement('button');
|
||||
ueberspringen.className = 'btn pl-ueberspringen hidden';
|
||||
|
||||
bedienung.append(ueberspringen, zeitleiste, unten);
|
||||
|
||||
// **Beide Nachbarn gleich zu Beginn holen.** Zwei kleine Anfragen, einmal
|
||||
// je Wiedergabe. Erst beim Antippen zu fragen hiesse, einen Knopf zu
|
||||
// zeigen, von dem noch niemand weiss, ob er etwas tut.
|
||||
void (async () => {
|
||||
[vorige, naechste] = await Promise.all([
|
||||
const [davor, danach, diese] = await Promise.all([
|
||||
nachbarfolge(anfrage.titelId, 'previous'),
|
||||
nachbarfolge(anfrage.titelId, 'next'),
|
||||
folgeLesen(anfrage.titelId),
|
||||
]);
|
||||
vorige = davor;
|
||||
naechste = danach;
|
||||
marken = diese?.markers ?? [];
|
||||
if (geschlossen) return;
|
||||
vorigeKnopf.classList.toggle('hidden', vorige?.files[0] == null);
|
||||
naechsteKnopf.classList.toggle('hidden', naechste?.files[0] == null);
|
||||
@@ -741,6 +755,23 @@ export function spieler(anfrage: Spieleranfrage): void {
|
||||
|
||||
kleinStand.textContent = laenge > 0 ? `${dauer(jetzt)} / ${dauer(laenge)}` : dauer(jetzt);
|
||||
kleinGefuellt.style.width = `${Math.min(100, anteil * 100)}%`;
|
||||
|
||||
markenstand(jetzt);
|
||||
}
|
||||
|
||||
/** Zeigt den Überspringen-Knopf, solange man in einer Marke steht.
|
||||
*
|
||||
* **Keine eigene Uhr.** Die Zeit wird ohnehin bei jedem Fortschreiten
|
||||
* nachgeführt; ein zweiter Zeitgeber dafür liefe neben dem ersten her und
|
||||
* wäre eine zweite Stelle, an der etwas hängen bleibt. */
|
||||
function markenstand(jetzt: number): void {
|
||||
const drin = marken.find(m => jetzt >= m.startSeconds && jetzt < m.endSeconds);
|
||||
ueberspringen.classList.toggle('hidden', !drin);
|
||||
if (!drin) return;
|
||||
|
||||
const text = drin.kind === 'intro' ? 'Intro überspringen' : 'Abspann überspringen';
|
||||
if (ueberspringen.textContent !== text) ueberspringen.textContent = text;
|
||||
ueberspringen.onclick = e => { e.stopPropagation(); void springen(drin.endSeconds); };
|
||||
}
|
||||
|
||||
function umschalten(): void {
|
||||
|
||||
Reference in New Issue
Block a user