From 6d8440ede86274169e8dde8b1ac2f440879fb34b Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Mon, 14 Sep 2026 18:44:41 +0200 Subject: [PATCH] Unten steht jetzt nur noch der Scrubber, wie in der App MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der strukturelle Unterschied, den ich übersehen hatte: in der iPad-App liegt unten nichts als der Regler über die volle Breite, darunter Zeit links und Restzeit rechts. Bei mir standen Zeit, Regler und Restzeit nebeneinander — der Regler wirkte dadurch wie ein Formularfeld zwischen zwei Beschriftungen — und darunter lag noch eine Werkzeugleiste mit Lautstärkeregler und Tempo-Auswahlfeld, die es in der App gar nicht gibt. Alles Einstellbare sitzt jetzt oben rechts beisammen: Tonspur, Untertitel, Qualität, Geschwindigkeit, Ton, Vollbild. Das Tempo als Liste wie die anderen; ein Auswahlfeld mitten in der Bedienleiste war das einzige Bauteil, das nach Formular aussah. Die Lautstärke über Mausrad auf dem Tonknopf und über die Pfeiltasten. Und ein Fehler, der genau das verhinderte, was gewünscht war: Die Bedienfläche lag als Ganzes über dem Bild und schluckte jeden Klick. "Ins Bild tippen hält an" funktionierte damit nur, solange die Bedienung unsichtbar war — also fast nie. Jetzt nehmen nur ihre Knöpfe Klicks entgegen. Nachgemessen: Klick pausiert, Pfeil rechts spult 30 Sekunden. Co-Authored-By: Claude Opus 5 --- web/frontend/index.html | 83 +++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 37 deletions(-) diff --git a/web/frontend/index.html b/web/frontend/index.html index b298a54..7dd11dc 100644 --- a/web/frontend/index.html +++ b/web/frontend/index.html @@ -246,12 +246,19 @@ img { display: block; } } #player video { width: 100%; height: 100%; object-fit: contain; background: #000; cursor: pointer; } +/* **Die Fläche selbst nimmt keine Klicks entgegen, nur ihre Knöpfe.** + Sonst liegt sie über dem Bild und schluckt jeden Klick — und „ins Bild + tippen hält an" funktioniert genau dann nicht, wenn die Bedienung zu + sehen ist, also fast immer. */ .pl-chrome { position: absolute; inset: 0; opacity: 0; pointer-events: none; transition: opacity .25s ease; display: grid; grid-template-rows: auto 1fr auto; } -.pl-chrome.on { opacity: 1; pointer-events: auto; } +.pl-chrome.on { opacity: 1; } +.pl-chrome.on button, +.pl-chrome.on .pl-track, +.pl-chrome.on select { pointer-events: auto; } /* Zwei Verläufe statt einer Fläche: die Mitte bleibt frei, und genau dort ist das Bild. */ @@ -315,15 +322,18 @@ img { display: block; } beides unlesbar, und die Zahl ist die eigentliche Auskunft. */ #pl-back, #pl-fwd { font-size: 15px; font-weight: 600; font-variant-numeric: tabular-nums; } -.pl-scrub { display: flex; align-items: center; gap: var(--s-regular); } -.pl-scrub span { - font-size: 13px; color: var(--text-2); font-variant-numeric: tabular-nums; - min-width: 52px; +/* Wie in der App: der Regler über die **volle Breite**, die Zeiten darunter. + Nebeneinander gestellt nimmt er in der Mitte Platz weg und wirkt wie ein + Formularfeld zwischen zwei Beschriftungen. */ +.pl-times { + display: flex; justify-content: space-between; + margin-top: var(--s-tight); + font-size: 13px; color: rgba(255,255,255,.8); + font-variant-numeric: tabular-nums; } -.pl-scrub span:last-child { text-align: right; } .pl-track { - position: relative; flex: 1; height: 22px; cursor: pointer; + position: relative; width: 100%; height: 22px; cursor: pointer; display: flex; align-items: center; } .pl-track::before { @@ -341,14 +351,7 @@ img { display: block; } } .pl-track:hover i { transform: translateX(-50%) scale(1.25); } -.pl-tools { - display: flex; align-items: center; gap: var(--s-regular); - margin-top: var(--s-snug); -} .pl-spacer { flex: 1; } -.pl-tools input[type=range] { width: 92px; accent-color: var(--accent); } -.pl-rate { font-size: 13px; color: var(--text-2); display: flex; align-items: center; gap: var(--s-snug); } -.pl-rate .pick { width: auto; padding: 6px var(--s-regular); font-size: 13px; } /* Während geladen wird, treten die Transportknöpfe zurück: sie tun ohnehin nichts, und der Kreisel lag vorher genau hinter dem größten von ihnen. */ @@ -1600,6 +1603,9 @@ function openPlayer(item) { + + +
@@ -1609,18 +1615,11 @@ function openPlayer(item) {
-
+
+
0:00 -
–:--
-
- - - - - -
@@ -1715,20 +1714,29 @@ function openPlayer(item) { }; // --- Ton, Tempo, Vollbild --- - const vol = el2('pl-vol'); - vol.oninput = () => { v.volume = +vol.value; v.muted = +vol.value === 0; }; - el2('pl-mute').onclick = () => { - v.muted = !v.muted; - el2('pl-mute').innerHTML = icon(v.muted ? 'muted' : 'sound'); + const paintTon = () => { + el2('pl-mute').innerHTML = icon(v.muted || v.volume === 0 ? 'muted' : 'sound'); + el2('pl-mute').title = `Lautstärke ${Math.round(v.volume * 100)} %`; }; + el2('pl-mute').onclick = () => { v.muted = !v.muted; paintTon(); }; - const rate = el2('pl-rate'); - RATES.forEach((r) => { - const o = document.createElement('option'); - o.value = r; o.textContent = r === 1 ? 'Normal' : r + '×'; o.selected = r === P.rate; - rate.appendChild(o); - }); - rate.onchange = () => { v.playbackRate = +rate.value; }; + // Das Tempo als Liste wie Ton und Untertitel — ein Auswahlfeld mitten in + // der Bedienleiste war das einzige Bauteil, das nach Formular aussah. + el2('pl-speed').onclick = () => zeigeListe(el2('pl-speed'), 'Geschwindigkeit', + RATES.map((r) => ({ + text: r === 1 ? 'Normal' : r + '×', + gewaehlt: Math.abs(v.playbackRate - r) < 0.01, + waehlen: () => { v.playbackRate = r; closeMenu(); show(); }, + }))); + + // Lautstärke über das Mausrad auf dem Tonknopf und über die Pfeiltasten — + // ein Schieberegler in der Leiste steht in der App auch nicht. + el2('pl-mute').addEventListener('wheel', (e) => { + e.preventDefault(); + v.volume = Math.min(1, Math.max(0, v.volume - Math.sign(e.deltaY) * 0.05)); + v.muted = v.volume === 0; + paintTon(); + }, { passive: false }); el2('pl-full').onclick = () => document.fullscreenElement ? document.exitFullscreen() : layer.requestFullscreen?.(); @@ -1740,8 +1748,8 @@ function openPlayer(item) { ' ': () => toggle(), k: () => toggle(), ArrowLeft: () => { v.currentTime -= P.skipBackward; }, ArrowRight: () => { v.currentTime += P.skipForward; }, - ArrowUp: () => { v.volume = Math.min(1, v.volume + 0.1); vol.value = v.volume; }, - ArrowDown: () => { v.volume = Math.max(0, v.volume - 0.1); vol.value = v.volume; }, + ArrowUp: () => { v.volume = Math.min(1, v.volume + 0.1); paintTon(); }, + ArrowDown: () => { v.volume = Math.max(0, v.volume - 0.1); paintTon(); }, f: () => el2('pl-full').click(), Escape: () => close(), }; @@ -2068,6 +2076,7 @@ function streamURL(path, query) { const ICONS = { play: '', waveform: '', + speed: '', captions: '', captionsOn: '', check: '',