jellyfish-web/src/scripts/inputManager.js
Jeff Sharkey afa56c18af
Support for faster playback rates.
The HTML5 video element already has a well-supported "playbackRate" attribute
which can be used to increase playback rate.  This change wires up that control
to be displayed in the Jellyfish web player.

The playback rates offered are between 0.5x and 2x in 0.25x increments, which
matches the YouTube player.  This change also wires up the ">" and "<" key
events to increase and decrease the playback rate, which mirrors the keyboard
shortcuts supported by YouTube.
2020-08-14 18:25:56 -03:00

262 lines
7.5 KiB
JavaScript

import playbackManager from 'playbackManager';
import focusManager from 'focusManager';
import appRouter from 'appRouter';
import dom from 'dom';
import appHost from 'apphost';
/* eslint-disable indent */
let lastInputTime = new Date().getTime();
export function notify() {
lastInputTime = new Date().getTime();
handleCommand('unknown');
}
export function notifyMouseMove() {
lastInputTime = new Date().getTime();
}
export function idleTime() {
return new Date().getTime() - lastInputTime;
}
export function select(sourceElement) {
sourceElement.click();
}
let eventListenerCount = 0;
export function on(scope, fn) {
eventListenerCount++;
dom.addEventListener(scope, 'command', fn, {});
}
export function off(scope, fn) {
if (eventListenerCount) {
eventListenerCount--;
}
dom.removeEventListener(scope, 'command', fn, {});
}
const commandTimes = {};
function checkCommandTime(command) {
const last = commandTimes[command] || 0;
const now = new Date().getTime();
if ((now - last) < 1000) {
return false;
}
commandTimes[command] = now;
return true;
}
export function handleCommand(commandName, options) {
lastInputTime = new Date().getTime();
let sourceElement = (options ? options.sourceElement : null);
if (sourceElement) {
sourceElement = focusManager.focusableParent(sourceElement);
}
if (!sourceElement) {
sourceElement = document.activeElement || window;
const dialogs = document.querySelectorAll('.dialogContainer .dialog.opened');
// Suppose the top open dialog is active
const dlg = dialogs.length ? dialogs[dialogs.length - 1] : null;
if (dlg && !dlg.contains(sourceElement)) {
sourceElement = dlg;
}
}
if (eventListenerCount) {
const customEvent = new CustomEvent('command', {
detail: {
command: commandName
},
bubbles: true,
cancelable: true
});
const eventResult = sourceElement.dispatchEvent(customEvent);
if (!eventResult) {
// event cancelled
return;
}
}
const keyActions = (command) => ({
'up': () => {
focusManager.moveUp(sourceElement);
},
'down': () => {
focusManager.moveDown(sourceElement);
},
'left': () => {
focusManager.moveLeft(sourceElement);
},
'right': () => {
focusManager.moveRight(sourceElement);
},
'home': () => {
appRouter.goHome();
},
'settings': () => {
appRouter.showSettings();
},
'back': () => {
if (appRouter.canGoBack()) {
appRouter.back();
} else if (appHost.supports('exit')) {
appHost.exit();
}
},
'select': () => {
select(sourceElement);
},
'nextchapter': () => {
playbackManager.nextChapter();
},
'next': () => {
playbackManager.nextTrack();
},
'nexttrack': () => {
playbackManager.nextTrack();
},
'previous': () => {
playbackManager.previousTrack();
},
'previoustrack': () => {
playbackManager.previousTrack();
},
'previouschapter': () => {
playbackManager.previousChapter();
},
'guide': () => {
appRouter.showGuide();
},
'recordedtv': () => {
appRouter.showRecordedTV();
},
'livetv': () => {
appRouter.showLiveTV();
},
'mute': () => {
playbackManager.setMute(true);
},
'unmute': () => {
playbackManager.setMute(false);
},
'togglemute': () => {
playbackManager.toggleMute();
},
'channelup': () => {
playbackManager.channelUp();
},
'channeldown': () => {
playbackManager.channelDown();
},
'volumedown': () => {
playbackManager.volumeDown();
},
'volumeup': () => {
playbackManager.volumeUp();
},
'play': () => {
playbackManager.unpause();
},
'pause': () => {
playbackManager.pause();
},
'playpause': () => {
playbackManager.playPause();
},
'stop': () => {
if (checkCommandTime('stop')) {
playbackManager.stop();
}
},
'changezoom': () => {
playbackManager.toggleAspectRatio();
},
'increaseplaybackrate': () => {
playbackManager.increasePlaybackRate();
},
'decreaseplaybackrate': () => {
playbackManager.decreasePlaybackRate();
},
'changeaudiotrack': () => {
playbackManager.changeAudioStream();
},
'changesubtitletrack': () => {
playbackManager.changeSubtitleStream();
},
'search': () => {
appRouter.showSearch();
},
'favorites': () => {
appRouter.showFavorites();
},
'fastforward': () => {
playbackManager.fastForward();
},
'rewind': () => {
playbackManager.rewind();
},
'seek': () => {
playbackManager.seekMs(options);
},
'togglefullscreen': () => {
playbackManager.toggleFullscreen();
},
'disabledisplaymirror': () => {
playbackManager.enableDisplayMirroring(false);
},
'enabledisplaymirror': () => {
playbackManager.enableDisplayMirroring(true);
},
'toggledisplaymirror': () => {
playbackManager.toggleDisplayMirroring();
},
'nowplaying': () => {
appRouter.showNowPlaying();
},
'repeatnone': () => {
playbackManager.setRepeatMode('RepeatNone');
},
'repeatall': () => {
playbackManager.setRepeatMode('RepeatAll');
},
'repeatone': () => {
playbackManager.setRepeatMode('RepeatOne');
}
})[command];
const action = keyActions(commandName);
if (action !== undefined) {
action.call();
} else {
console.debug(`inputManager: tried to process command with no action assigned: ${commandName}`);
}
}
dom.addEventListener(document, 'click', notify, {
passive: true
});
/* eslint-enable indent */
export default {
handleCommand: handleCommand,
notify: notify,
notifyMouseMove: notifyMouseMove,
idleTime: idleTime,
on: on,
off: off
};