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"""
|
"""REST API Endpoints fuer die Video-Bibliothek"""
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import aiomysql
|
import aiomysql
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
|
|
|
||||||
|
|
@ -285,6 +285,15 @@ function toggleOverlay() {
|
||||||
if (overlayOpen) {
|
if (overlayOpen) {
|
||||||
renderOverlay();
|
renderOverlay();
|
||||||
showControls();
|
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
|
// Bereits offen -> schliessen
|
||||||
overlayOpen = false;
|
overlayOpen = false;
|
||||||
overlay.style.display = "none";
|
overlay.style.display = "none";
|
||||||
|
// Alle Sektionen wieder sichtbar machen
|
||||||
|
overlay.querySelectorAll(".player-overlay-section").forEach(
|
||||||
|
s => s.style.display = "");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
overlayOpen = true;
|
overlayOpen = true;
|
||||||
|
|
@ -302,8 +314,25 @@ function openOverlaySection(section) {
|
||||||
renderOverlay();
|
renderOverlay();
|
||||||
showControls();
|
showControls();
|
||||||
if (section) {
|
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);
|
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);
|
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 ===
|
// === Tastatur-Steuerung ===
|
||||||
|
|
||||||
function onKeyDown(e) {
|
function onKeyDown(e) {
|
||||||
|
|
@ -443,29 +511,109 @@ function onKeyDown(e) {
|
||||||
10009: "Escape", 10182: "Escape",
|
10009: "Escape", 10182: "Escape",
|
||||||
415: "Play", 19: "Pause", 413: "Stop",
|
415: "Play", 19: "Pause", 413: "Stop",
|
||||||
417: "FastForward", 412: "Rewind",
|
417: "FastForward", 412: "Rewind",
|
||||||
|
// Samsung Farbtasten
|
||||||
|
403: "ColorRed", 404: "ColorGreen",
|
||||||
|
405: "ColorYellow", 406: "ColorBlue",
|
||||||
};
|
};
|
||||||
const key = keyMap[e.keyCode] || e.key;
|
const key = keyMap[e.keyCode] || e.key;
|
||||||
|
|
||||||
// Overlay offen? -> Navigation im Overlay
|
const active = document.activeElement;
|
||||||
if (overlayOpen && (key === "Escape" || key === "Backspace")) {
|
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();
|
toggleOverlay();
|
||||||
e.preventDefault();
|
// Focus zurueck auf Settings-Button
|
||||||
return;
|
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) {
|
switch (key) {
|
||||||
case " ": case "Enter": case "Play": case "Pause":
|
case "ArrowLeft": case "ArrowRight":
|
||||||
togglePlay(); e.preventDefault(); break;
|
_focusNext(key === "ArrowRight" ? 1 : -1);
|
||||||
case "ArrowLeft": case "Rewind":
|
e.preventDefault(); return;
|
||||||
seekRelative(-10); e.preventDefault(); break;
|
case "Enter":
|
||||||
case "ArrowRight": case "FastForward":
|
if (buttonFocused) active.click();
|
||||||
seekRelative(10); e.preventDefault(); break;
|
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":
|
case "ArrowUp":
|
||||||
if (videoEl) videoEl.volume = Math.min(1, videoEl.volume + 0.1);
|
// Vom Button weg = Controls ausblenden, Video steuern
|
||||||
showControls(); e.preventDefault(); break;
|
active.blur(); showControls(); e.preventDefault(); return;
|
||||||
case "ArrowDown":
|
case "ArrowDown":
|
||||||
if (videoEl) videoEl.volume = Math.max(0, videoEl.volume - 0.1);
|
active.blur(); showControls(); e.preventDefault(); return;
|
||||||
showControls(); e.preventDefault(); break;
|
case "Enter":
|
||||||
|
active.click(); showControls(); e.preventDefault(); return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Standard Player-Tasten ---
|
||||||
|
switch (key) {
|
||||||
|
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); showControls(); e.preventDefault(); break;
|
||||||
|
case "ArrowRight": case "FastForward":
|
||||||
|
seekRelative(10); showControls(); e.preventDefault(); break;
|
||||||
|
case "ArrowUp":
|
||||||
|
// 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 (!controlsVisible) {
|
||||||
|
showControls();
|
||||||
|
if (playBtn) playBtn.focus();
|
||||||
|
} else {
|
||||||
|
if (playBtn) playBtn.focus();
|
||||||
|
showControls();
|
||||||
|
}
|
||||||
|
e.preventDefault(); break;
|
||||||
case "Escape": case "Backspace": case "Stop":
|
case "Escape": case "Backspace": case "Stop":
|
||||||
saveProgress();
|
saveProgress();
|
||||||
setTimeout(() => window.history.back(), 100);
|
setTimeout(() => window.history.back(), 100);
|
||||||
|
|
@ -477,6 +625,15 @@ function onKeyDown(e) {
|
||||||
case "n":
|
case "n":
|
||||||
if (cfg.nextVideoId) playNextEpisode();
|
if (cfg.nextVideoId) playNextEpisode();
|
||||||
e.preventDefault(); break;
|
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