diff --git a/video-konverter/app/routes/library_api.py b/video-konverter/app/routes/library_api.py index 9a6ff4b..1a595f4 100644 --- a/video-konverter/app/routes/library_api.py +++ b/video-konverter/app/routes/library_api.py @@ -1,5 +1,6 @@ """REST API Endpoints fuer die Video-Bibliothek""" import asyncio +import json import logging import aiomysql from aiohttp import web diff --git a/video-konverter/app/static/tv/js/player.js b/video-konverter/app/static/tv/js/player.js index c824c48..22eda4f 100644 --- a/video-konverter/app/static/tv/js/player.js +++ b/video-konverter/app/static/tv/js/player.js @@ -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; } }