1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Merge pull request #702 from dmitrylyzo/playback_remote

Add playback control from TV remote
This commit is contained in:
dkanada 2020-01-27 22:34:59 +09:00 committed by GitHub
commit 7af58a03dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 169 additions and 31 deletions

View file

@ -1,36 +1,133 @@
define(['inputManager', 'focusManager'], function(inputManager, focusManager) { define(["inputManager", "layoutManager"], function (inputManager, layoutManager) {
'use strict'; "use strict";
console.log("keyboardnavigation"); console.log("keyboardnavigation");
/**
* Key name mapping.
*/
// Add more to support old browsers
var KeyNames = {
13: "Enter",
19: "Pause",
27: "Escape",
32: "Space",
37: "ArrowLeft",
38: "ArrowUp",
39: "ArrowRight",
40: "ArrowDown",
// MediaRewind (Tizen/WebOS)
412: "MediaRewind",
// MediaStop (Tizen/WebOS)
413: "MediaStop",
// MediaPlay (Tizen/WebOS)
415: "MediaPlay",
// MediaFastForward (Tizen/WebOS)
417: "MediaFastForward",
// Back (WebOS)
461: "Back",
// Back (Tizen)
10009: "Back",
// MediaTrackPrevious (Tizen)
10232: "MediaTrackPrevious",
// MediaTrackNext (Tizen)
10233: "MediaTrackNext",
// MediaPlayPause (Tizen)
10252: "MediaPlayPause"
};
var hasFieldKey = false;
try {
hasFieldKey = "key" in new KeyboardEvent("keydown");
} catch (e) {
console.log("error checking 'key' field");
}
if (!hasFieldKey) {
// Add [a..z]
for (var i = 65; i <= 90; i++) {
KeyNames[i] = String.fromCharCode(i).toLowerCase();
}
}
/**
* Returns key name from event.
*
* @param {KeyboardEvent} keyboard event
* @return {string} key name
*/
function getKeyName(event) {
return KeyNames[event.keyCode] || event.key;
}
function enable() { function enable() {
document.addEventListener('keydown', function(e) { document.addEventListener("keydown", function (e) {
var capture = true; var capture = true;
switch (e.keyCode) { switch (getKeyName(e)) {
case 37: // ArrowLeft case "ArrowLeft":
inputManager.handle('left'); inputManager.handle("left");
break; break;
case 38: // ArrowUp case "ArrowUp":
inputManager.handle('up'); inputManager.handle("up");
break; break;
case 39: // ArrowRight case "ArrowRight":
inputManager.handle('right'); inputManager.handle("right");
break; break;
case 40: // ArrowDown case "ArrowDown":
inputManager.handle('down'); inputManager.handle("down");
break; break;
case "Back":
inputManager.handle("back");
break;
case "Escape":
if (layoutManager.tv) {
inputManager.handle("back");
} else {
capture = false;
}
break;
case "MediaPlay":
inputManager.handle("play");
break;
case "Pause":
inputManager.handle("pause");
break;
case "MediaPlayPause":
inputManager.handle("playpause");
break;
case "MediaRewind":
inputManager.handle("rewind");
break;
case "MediaFastForward":
inputManager.handle("fastforward");
break;
case "MediaStop":
inputManager.handle("stop");
break;
case "MediaTrackPrevious":
inputManager.handle("previoustrack");
break;
case "MediaTrackNext":
inputManager.handle("nexttrack");
break;
default: default:
capture = false; capture = false;
} }
if (capture) { if (capture) {
console.log("Disabling default event handling"); console.log("Disabling default event handling");
e.preventDefault(); e.preventDefault();
} }
}); });
} }
return { return {
enable: enable enable: enable,
getKeyName: getKeyName
}; };
}); });

View file

@ -1,4 +1,4 @@
define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "mediaInfo", "focusManager", "imageLoader", "scrollHelper", "events", "connectionManager", "browser", "globalize", "apphost", "layoutManager", "userSettings", "scrollStyles", "emby-slider", "paper-icon-button-light", "css!assets/css/videoosd"], function (playbackManager, dom, inputManager, datetime, itemHelper, mediaInfo, focusManager, imageLoader, scrollHelper, events, connectionManager, browser, globalize, appHost, layoutManager, userSettings) { define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "mediaInfo", "focusManager", "imageLoader", "scrollHelper", "events", "connectionManager", "browser", "globalize", "apphost", "layoutManager", "userSettings", "keyboardnavigation", "scrollStyles", "emby-slider", "paper-icon-button-light", "css!assets/css/videoosd"], function (playbackManager, dom, inputManager, datetime, itemHelper, mediaInfo, focusManager, imageLoader, scrollHelper, events, connectionManager, browser, globalize, appHost, layoutManager, userSettings, keyboardnavigation) {
"use strict"; "use strict";
function seriesImageUrl(item, options) { function seriesImageUrl(item, options) {
@ -437,6 +437,11 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med
}); });
currentVisibleMenu = null; currentVisibleMenu = null;
toggleSubtitleSync("hide"); toggleSubtitleSync("hide");
// Firefox does not blur by itself
if (document.activeElement) {
document.activeElement.blur();
}
} }
} }
@ -1087,64 +1092,79 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med
*/ */
var NavigationKeys = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"]; var NavigationKeys = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"];
/**
* Clicked element.
* To skip 'click' handling on Firefox/Edge.
*/
var clickedElement;
function onWindowKeyDown(e) { function onWindowKeyDown(e) {
clickedElement = e.srcElement;
var key = keyboardnavigation.getKeyName(e);
if (!currentVisibleMenu && 32 === e.keyCode) { if (!currentVisibleMenu && 32 === e.keyCode) {
playbackManager.playPause(currentPlayer); playbackManager.playPause(currentPlayer);
showOsd(); showOsd();
return; return;
} }
if (layoutManager.tv && NavigationKeys.indexOf(e.key) != -1) { if (layoutManager.tv && NavigationKeys.indexOf(key) != -1) {
showOsd(); showOsd();
return; return;
} }
switch (e.key) { switch (key) {
case "Enter":
showOsd();
break;
case "Escape":
case "Back":
// Ignore key when some dialog is opened
if (currentVisibleMenu === "osd" && !document.querySelector(".dialogContainer")) {
hideOsd();
e.stopPropagation();
}
break;
case "k": case "k":
playbackManager.playPause(currentPlayer); playbackManager.playPause(currentPlayer);
showOsd(); showOsd();
break; break;
case "l": case "l":
case "ArrowRight": case "ArrowRight":
case "Right": case "Right":
playbackManager.fastForward(currentPlayer); playbackManager.fastForward(currentPlayer);
showOsd(); showOsd();
break; break;
case "j": case "j":
case "ArrowLeft": case "ArrowLeft":
case "Left": case "Left":
playbackManager.rewind(currentPlayer); playbackManager.rewind(currentPlayer);
showOsd(); showOsd();
break; break;
case "f": case "f":
if (!e.ctrlKey && !e.metaKey) { if (!e.ctrlKey && !e.metaKey) {
playbackManager.toggleFullscreen(currentPlayer); playbackManager.toggleFullscreen(currentPlayer);
showOsd(); showOsd();
} }
break; break;
case "m": case "m":
playbackManager.toggleMute(currentPlayer); playbackManager.toggleMute(currentPlayer);
showOsd(); showOsd();
break; break;
case "NavigationLeft": case "NavigationLeft":
case "GamepadDPadLeft": case "GamepadDPadLeft":
case "GamepadLeftThumbstickLeft": case "GamepadLeftThumbstickLeft":
// Ignores gamepad events that are always triggered, even when not focused. // Ignores gamepad events that are always triggered, even when not focused.
if (document.hasFocus()) { if (document.hasFocus()) {
playbackManager.rewind(currentPlayer); playbackManager.rewind(currentPlayer);
showOsd(); showOsd();
} }
break; break;
case "NavigationRight": case "NavigationRight":
case "GamepadDPadRight": case "GamepadDPadRight":
case "GamepadLeftThumbstickRight": case "GamepadLeftThumbstickRight":
// Ignores gamepad events that are always triggered, even when not focused. // Ignores gamepad events that are always triggered, even when not focused.
if (document.hasFocus()) { if (document.hasFocus()) {
playbackManager.fastForward(currentPlayer); playbackManager.fastForward(currentPlayer);
showOsd(); showOsd();
@ -1152,6 +1172,14 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med
} }
} }
function onWindowMouseDown(e) {
clickedElement = e.srcElement;
}
function onWindowTouchStart(e) {
clickedElement = e.srcElement;
}
function getImgUrl(item, chapter, index, maxWidth, apiClient) { function getImgUrl(item, chapter, index, maxWidth, apiClient) {
if (chapter.ImageTag) { if (chapter.ImageTag) {
return apiClient.getScaledImageUrl(item.Id, { return apiClient.getScaledImageUrl(item.Id, {
@ -1280,6 +1308,12 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med
showOsd(); showOsd();
inputManager.on(window, onInputCommand); inputManager.on(window, onInputCommand);
dom.addEventListener(window, "keydown", onWindowKeyDown, { dom.addEventListener(window, "keydown", onWindowKeyDown, {
capture: true
});
dom.addEventListener(window, window.PointerEvent ? "pointerdown" : "mousedown", onWindowMouseDown, {
passive: true
});
dom.addEventListener(window, "touchstart", onWindowTouchStart, {
passive: true passive: true
}); });
} catch (e) { } catch (e) {
@ -1294,6 +1328,12 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med
} }
dom.removeEventListener(window, "keydown", onWindowKeyDown, { dom.removeEventListener(window, "keydown", onWindowKeyDown, {
capture: true
});
dom.removeEventListener(window, window.PointerEvent ? "pointerdown" : "mousedown", onWindowMouseDown, {
passive: true
});
dom.removeEventListener(window, "touchstart", onWindowTouchStart, {
passive: true passive: true
}); });
stopOsdHideTimer(); stopOsdHideTimer();
@ -1465,7 +1505,10 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med
playbackManager.previousTrack(currentPlayer); playbackManager.previousTrack(currentPlayer);
}); });
view.querySelector(".btnPause").addEventListener("click", function () { view.querySelector(".btnPause").addEventListener("click", function () {
playbackManager.playPause(currentPlayer); // Ignore 'click' if another element was originally clicked (Firefox/Edge issue)
if (this.contains(clickedElement)) {
playbackManager.playPause(currentPlayer);
}
}); });
view.querySelector(".btnNextTrack").addEventListener("click", function () { view.querySelector(".btnNextTrack").addEventListener("click", function () {
playbackManager.nextTrack(currentPlayer); playbackManager.nextTrack(currentPlayer);

View file

@ -1,4 +1,4 @@
define(['browser', 'dom', 'layoutManager', 'css!./emby-slider', 'registerElement', 'emby-input'], function (browser, dom, layoutManager) { define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-slider', 'registerElement', 'emby-input'], function (browser, dom, layoutManager, keyboardnavigation) {
'use strict'; 'use strict';
var EmbySliderPrototype = Object.create(HTMLInputElement.prototype); var EmbySliderPrototype = Object.create(HTMLInputElement.prototype);
@ -250,7 +250,7 @@ define(['browser', 'dom', 'layoutManager', 'css!./emby-slider', 'registerElement
* Handle KeyDown event * Handle KeyDown event
*/ */
function onKeyDown(e) { function onKeyDown(e) {
switch (e.key) { switch (keyboardnavigation.getKeyName(e)) {
case 'ArrowLeft': case 'ArrowLeft':
case 'Left': case 'Left':
stepKeyboard(this, -this.keyboardStepDown || -1); stepKeyboard(this, -this.keyboardStepDown || -1);

View file

@ -22,9 +22,7 @@ define(['playbackManager', 'focusManager', 'appRouter', 'dom'], function (playba
var eventListenerCount = 0; var eventListenerCount = 0;
function on(scope, fn) { function on(scope, fn) {
if (eventListenerCount) { eventListenerCount++;
eventListenerCount++;
}
dom.addEventListener(scope, 'command', fn, {}); dom.addEventListener(scope, 'command', fn, {});
} }