fix: VideoKonverter v4.0.3 - JSON-Import-Fix, Player D-Pad-Navigation, Overlay-Bugfix
- import json in library_api.py ergänzt (fehlte, Video-Info-API crashte) - Player: D-Pad-Navigation für Samsung TV Fernbedienung eingebaut - Player: Samsung Farbtasten (Rot=Audio, Grün=Subs, Gelb=Qualität, Blau=Speed) - Player: Overlay zeigt nur noch die zum Button passende Sektion - Player: Auto-Fokus beim Öffnen von Overlays Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e8f2d49949
commit
75bb5d796d
2 changed files with 171 additions and 13 deletions
|
|
@ -1,5 +1,6 @@
|
|||
"""REST API Endpoints fuer die Video-Bibliothek"""
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import aiomysql
|
||||
from aiohttp import web
|
||||
|
|
|
|||
|
|
@ -285,6 +285,15 @@ function toggleOverlay() {
|
|||
if (overlayOpen) {
|
||||
renderOverlay();
|
||||
showControls();
|
||||
// Nur Geschwindigkeit (Audio/Subs/Qualitaet haben eigene Buttons)
|
||||
overlay.querySelectorAll(".player-overlay-section").forEach(s => {
|
||||
s.style.display = s.id === "overlay-speed" ? "" : "none";
|
||||
});
|
||||
var el = document.getElementById("overlay-speed");
|
||||
if (el) {
|
||||
var firstBtn = el.querySelector("[data-focusable]");
|
||||
if (firstBtn) firstBtn.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -295,6 +304,9 @@ function openOverlaySection(section) {
|
|||
// Bereits offen -> schliessen
|
||||
overlayOpen = false;
|
||||
overlay.style.display = "none";
|
||||
// Alle Sektionen wieder sichtbar machen
|
||||
overlay.querySelectorAll(".player-overlay-section").forEach(
|
||||
s => s.style.display = "");
|
||||
return;
|
||||
}
|
||||
overlayOpen = true;
|
||||
|
|
@ -302,8 +314,25 @@ function openOverlaySection(section) {
|
|||
renderOverlay();
|
||||
showControls();
|
||||
if (section) {
|
||||
// Nur die gewaehlte Sektion anzeigen, andere verstecken
|
||||
overlay.querySelectorAll(".player-overlay-section").forEach(s => {
|
||||
s.style.display = s.id === "overlay-" + section ? "" : "none";
|
||||
});
|
||||
var el = document.getElementById("overlay-" + section);
|
||||
if (el) el.scrollIntoView({ behavior: "smooth" });
|
||||
if (el) {
|
||||
var firstBtn = el.querySelector("[data-focusable]");
|
||||
if (firstBtn) firstBtn.focus();
|
||||
}
|
||||
} else {
|
||||
// Settings-Button: nur Geschwindigkeit (Audio/Subs/Qualitaet haben eigene Buttons)
|
||||
overlay.querySelectorAll(".player-overlay-section").forEach(s => {
|
||||
s.style.display = s.id === "overlay-speed" ? "" : "none";
|
||||
});
|
||||
var el = document.getElementById("overlay-speed");
|
||||
if (el) {
|
||||
var firstBtn = el.querySelector("[data-focusable]");
|
||||
if (firstBtn) firstBtn.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -435,6 +464,45 @@ function cancelNext() {
|
|||
setTimeout(() => window.history.back(), 500);
|
||||
}
|
||||
|
||||
// === D-Pad Navigation fuer Fernbedienung ===
|
||||
|
||||
/**
|
||||
* Fokussierbare Elemente im aktuellen Kontext finden.
|
||||
* Im Overlay: nur Overlay-Buttons. Sonst: Player-Control-Buttons.
|
||||
*/
|
||||
function _getFocusables() {
|
||||
if (overlayOpen) {
|
||||
const overlay = document.getElementById("player-overlay");
|
||||
return overlay ? Array.from(overlay.querySelectorAll("[data-focusable]")) : [];
|
||||
}
|
||||
// "Naechste Episode" oder "Schaust du noch" Overlay?
|
||||
const nextOv = document.getElementById("next-overlay");
|
||||
if (nextOv && nextOv.style.display !== "none") {
|
||||
return Array.from(nextOv.querySelectorAll("[data-focusable]"));
|
||||
}
|
||||
const stillOv = document.getElementById("still-watching-overlay");
|
||||
if (stillOv && stillOv.style.display !== "none") {
|
||||
return Array.from(stillOv.querySelectorAll("[data-focusable]"));
|
||||
}
|
||||
// Player-Controls
|
||||
const controls = document.getElementById("player-controls");
|
||||
return controls ? Array.from(controls.querySelectorAll("[data-focusable]")) : [];
|
||||
}
|
||||
|
||||
function _focusNext(direction) {
|
||||
const items = _getFocusables();
|
||||
if (!items.length) return false;
|
||||
const cur = items.indexOf(document.activeElement);
|
||||
let next;
|
||||
if (direction === 1) {
|
||||
next = cur < 0 ? 0 : Math.min(cur + 1, items.length - 1);
|
||||
} else {
|
||||
next = cur < 0 ? items.length - 1 : Math.max(cur - 1, 0);
|
||||
}
|
||||
items[next].focus();
|
||||
return true;
|
||||
}
|
||||
|
||||
// === Tastatur-Steuerung ===
|
||||
|
||||
function onKeyDown(e) {
|
||||
|
|
@ -443,29 +511,109 @@ function onKeyDown(e) {
|
|||
10009: "Escape", 10182: "Escape",
|
||||
415: "Play", 19: "Pause", 413: "Stop",
|
||||
417: "FastForward", 412: "Rewind",
|
||||
// Samsung Farbtasten
|
||||
403: "ColorRed", 404: "ColorGreen",
|
||||
405: "ColorYellow", 406: "ColorBlue",
|
||||
};
|
||||
const key = keyMap[e.keyCode] || e.key;
|
||||
|
||||
// Overlay offen? -> Navigation im Overlay
|
||||
if (overlayOpen && (key === "Escape" || key === "Backspace")) {
|
||||
toggleOverlay();
|
||||
e.preventDefault();
|
||||
return;
|
||||
const active = document.activeElement;
|
||||
const buttonFocused = active && active.hasAttribute("data-focusable") &&
|
||||
active.tagName === "BUTTON";
|
||||
|
||||
// --- Overlay offen: D-Pad navigiert im Overlay ---
|
||||
if (overlayOpen) {
|
||||
switch (key) {
|
||||
case "Escape": case "Backspace":
|
||||
toggleOverlay();
|
||||
// Focus zurueck auf Settings-Button
|
||||
const btnSettings = document.getElementById("btn-settings");
|
||||
if (btnSettings) btnSettings.focus();
|
||||
e.preventDefault(); return;
|
||||
case "ArrowUp":
|
||||
_focusNext(-1); e.preventDefault(); return;
|
||||
case "ArrowDown":
|
||||
_focusNext(1); e.preventDefault(); return;
|
||||
case "ArrowLeft":
|
||||
_focusNext(-1); e.preventDefault(); return;
|
||||
case "ArrowRight":
|
||||
_focusNext(1); e.preventDefault(); return;
|
||||
case "Enter":
|
||||
if (buttonFocused) active.click();
|
||||
e.preventDefault(); return;
|
||||
}
|
||||
}
|
||||
|
||||
// --- "Naechste Episode" / "Schaust du noch" Overlay ---
|
||||
const nextOv = document.getElementById("next-overlay");
|
||||
const stillOv = document.getElementById("still-watching-overlay");
|
||||
const modalOpen = (nextOv && nextOv.style.display !== "none") ||
|
||||
(stillOv && stillOv.style.display !== "none");
|
||||
if (modalOpen) {
|
||||
switch (key) {
|
||||
case "ArrowLeft": case "ArrowRight":
|
||||
_focusNext(key === "ArrowRight" ? 1 : -1);
|
||||
e.preventDefault(); return;
|
||||
case "Enter":
|
||||
if (buttonFocused) active.click();
|
||||
e.preventDefault(); return;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Controls sichtbar + Button fokussiert: D-Pad navigiert ---
|
||||
if (controlsVisible && buttonFocused) {
|
||||
switch (key) {
|
||||
case "ArrowLeft":
|
||||
_focusNext(-1); showControls(); e.preventDefault(); return;
|
||||
case "ArrowRight":
|
||||
_focusNext(1); showControls(); e.preventDefault(); return;
|
||||
case "ArrowUp":
|
||||
// Vom Button weg = Controls ausblenden, Video steuern
|
||||
active.blur(); showControls(); e.preventDefault(); return;
|
||||
case "ArrowDown":
|
||||
active.blur(); showControls(); e.preventDefault(); return;
|
||||
case "Enter":
|
||||
active.click(); showControls(); e.preventDefault(); return;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Standard Player-Tasten ---
|
||||
switch (key) {
|
||||
case " ": case "Enter": case "Play": case "Pause":
|
||||
case " ": case "Play": case "Pause":
|
||||
togglePlay(); e.preventDefault(); break;
|
||||
case "Enter":
|
||||
// Kein Button fokussiert: Controls einblenden + Focus auf Play
|
||||
if (!controlsVisible) {
|
||||
showControls();
|
||||
if (playBtn) playBtn.focus();
|
||||
} else {
|
||||
togglePlay();
|
||||
}
|
||||
e.preventDefault(); break;
|
||||
case "ArrowLeft": case "Rewind":
|
||||
seekRelative(-10); e.preventDefault(); break;
|
||||
seekRelative(-10); showControls(); e.preventDefault(); break;
|
||||
case "ArrowRight": case "FastForward":
|
||||
seekRelative(10); e.preventDefault(); break;
|
||||
seekRelative(10); showControls(); e.preventDefault(); break;
|
||||
case "ArrowUp":
|
||||
if (videoEl) videoEl.volume = Math.min(1, videoEl.volume + 0.1);
|
||||
showControls(); e.preventDefault(); break;
|
||||
// Erster Druck: Controls + Focus auf Buttons
|
||||
if (!controlsVisible) {
|
||||
showControls();
|
||||
if (playBtn) playBtn.focus();
|
||||
} else {
|
||||
// Controls sichtbar aber kein Button fokussiert: Focus setzen
|
||||
if (playBtn) playBtn.focus();
|
||||
showControls();
|
||||
}
|
||||
e.preventDefault(); break;
|
||||
case "ArrowDown":
|
||||
if (videoEl) videoEl.volume = Math.max(0, videoEl.volume - 0.1);
|
||||
showControls(); e.preventDefault(); break;
|
||||
if (!controlsVisible) {
|
||||
showControls();
|
||||
if (playBtn) playBtn.focus();
|
||||
} else {
|
||||
if (playBtn) playBtn.focus();
|
||||
showControls();
|
||||
}
|
||||
e.preventDefault(); break;
|
||||
case "Escape": case "Backspace": case "Stop":
|
||||
saveProgress();
|
||||
setTimeout(() => window.history.back(), 100);
|
||||
|
|
@ -477,6 +625,15 @@ function onKeyDown(e) {
|
|||
case "n":
|
||||
if (cfg.nextVideoId) playNextEpisode();
|
||||
e.preventDefault(); break;
|
||||
// Samsung Farbtasten: Direkt-Zugriff auf Overlay-Sektionen
|
||||
case "ColorRed":
|
||||
openOverlaySection("audio"); e.preventDefault(); break;
|
||||
case "ColorGreen":
|
||||
openOverlaySection("subs"); e.preventDefault(); break;
|
||||
case "ColorYellow":
|
||||
openOverlaySection("quality"); e.preventDefault(); break;
|
||||
case "ColorBlue":
|
||||
openOverlaySection("speed"); e.preventDefault(); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue