2021-11-12 15:56:46 +01:00
|
|
|
// @flow
|
|
|
|
import * as OVERLAY from './overlays';
|
|
|
|
import * as KEYCODES from 'constants/keycodes';
|
2022-05-02 08:06:37 +02:00
|
|
|
import { VIDEO_PLAYBACK_RATES } from 'constants/player';
|
2021-11-12 15:56:46 +01:00
|
|
|
import isUserTyping from 'util/detect-typing';
|
|
|
|
|
|
|
|
const SEEK_STEP_5 = 5;
|
|
|
|
const SEEK_STEP = 10; // time to seek in seconds
|
2022-06-06 15:37:44 +02:00
|
|
|
const VOLUME_CHANGE_ON_SCROLL = 0.05;
|
|
|
|
const PRECISE_VOLUME_CHANGE_ON_SCROLL = 0.01;
|
2021-11-12 15:56:46 +01:00
|
|
|
|
|
|
|
// check if active (clicked) element is part of video div, used for keyboard shortcuts (volume etc)
|
|
|
|
function activeElementIsPartOfVideoElement() {
|
|
|
|
const videoElementParent = document.getElementsByClassName('video-js-parent')[0];
|
|
|
|
const activeElement = document.activeElement;
|
|
|
|
return videoElementParent.contains(activeElement);
|
|
|
|
}
|
|
|
|
|
2022-06-06 15:37:44 +02:00
|
|
|
function volumeUp(event, playerRef, checkIsActive = true, amount = 0.05) {
|
2021-11-12 15:56:46 +01:00
|
|
|
// dont run if video element is not active element (otherwise runs when scrolling using keypad)
|
|
|
|
const videoElementIsActive = activeElementIsPartOfVideoElement();
|
|
|
|
const player = playerRef.current;
|
2022-06-06 15:37:44 +02:00
|
|
|
if (!player || (checkIsActive && !videoElementIsActive)) return;
|
2021-11-12 15:56:46 +01:00
|
|
|
event.preventDefault();
|
2022-06-06 15:37:44 +02:00
|
|
|
player.volume(player.volume() + amount);
|
2021-11-12 15:56:46 +01:00
|
|
|
OVERLAY.showVolumeverlay(player, Math.round(player.volume() * 100));
|
|
|
|
player.userActive(true);
|
|
|
|
}
|
|
|
|
|
2022-06-06 15:37:44 +02:00
|
|
|
function volumeDown(event, playerRef, checkIsActive = true, amount = 0.05) {
|
2021-11-12 15:56:46 +01:00
|
|
|
// dont run if video element is not active element (otherwise runs when scrolling using keypad)
|
|
|
|
const videoElementIsActive = activeElementIsPartOfVideoElement();
|
|
|
|
const player = playerRef.current;
|
2022-06-06 15:37:44 +02:00
|
|
|
if (!player || (checkIsActive && !videoElementIsActive)) return;
|
2021-11-12 15:56:46 +01:00
|
|
|
event.preventDefault();
|
2022-06-06 15:37:44 +02:00
|
|
|
player.volume(player.volume() - amount);
|
2021-11-12 15:56:46 +01:00
|
|
|
OVERLAY.showVolumeverlay(player, Math.round(player.volume() * 100));
|
|
|
|
player.userActive(true);
|
|
|
|
}
|
|
|
|
|
2022-03-29 22:40:21 +02:00
|
|
|
function seekVideo(stepSize: number, playerRef, containerRef, jumpTo?: boolean) {
|
2021-11-12 15:56:46 +01:00
|
|
|
const player = playerRef.current;
|
|
|
|
const videoNode = containerRef.current && containerRef.current.querySelector('video, audio');
|
2022-03-29 22:40:21 +02:00
|
|
|
|
2021-11-12 15:56:46 +01:00
|
|
|
if (!videoNode || !player) return;
|
2022-03-29 22:40:21 +02:00
|
|
|
|
2021-11-12 15:56:46 +01:00
|
|
|
const duration = videoNode.duration;
|
|
|
|
const currentTime = videoNode.currentTime;
|
2022-03-29 22:40:21 +02:00
|
|
|
const newDuration = jumpTo ? duration * stepSize : currentTime + stepSize;
|
2021-11-12 15:56:46 +01:00
|
|
|
if (newDuration < 0) {
|
|
|
|
videoNode.currentTime = 0;
|
|
|
|
} else if (newDuration > duration) {
|
|
|
|
videoNode.currentTime = duration;
|
|
|
|
} else {
|
|
|
|
videoNode.currentTime = newDuration;
|
|
|
|
}
|
2022-03-29 22:40:21 +02:00
|
|
|
OVERLAY.showSeekedOverlay(player, Math.abs(jumpTo ? stepSize * 100 : stepSize), !jumpTo && stepSize > 0, jumpTo);
|
2021-11-12 15:56:46 +01:00
|
|
|
player.userActive(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
function toggleFullscreen(playerRef) {
|
|
|
|
const player = playerRef.current;
|
|
|
|
if (!player) return;
|
|
|
|
if (!player.isFullscreen()) {
|
|
|
|
player.requestFullscreen();
|
|
|
|
} else {
|
|
|
|
player.exitFullscreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function toggleMute(containerRef) {
|
|
|
|
const videoNode = containerRef.current && containerRef.current.querySelector('video, audio');
|
|
|
|
if (!videoNode) return;
|
|
|
|
videoNode.muted = !videoNode.muted;
|
|
|
|
}
|
|
|
|
|
|
|
|
function togglePlay(containerRef) {
|
|
|
|
const videoNode = containerRef.current && containerRef.current.querySelector('video, audio');
|
|
|
|
if (!videoNode) return;
|
|
|
|
videoNode.paused ? videoNode.play() : videoNode.pause();
|
|
|
|
}
|
|
|
|
|
|
|
|
function changePlaybackSpeed(shouldSpeedUp: boolean, playerRef) {
|
|
|
|
const player = playerRef.current;
|
|
|
|
if (!player) return;
|
|
|
|
const isSpeedUp = shouldSpeedUp;
|
|
|
|
const rate = player.playbackRate();
|
2022-05-02 08:06:37 +02:00
|
|
|
let rateIndex = VIDEO_PLAYBACK_RATES.findIndex((x) => x === rate);
|
2021-11-12 15:56:46 +01:00
|
|
|
if (rateIndex >= 0) {
|
2022-05-02 08:06:37 +02:00
|
|
|
rateIndex = isSpeedUp ? Math.min(rateIndex + 1, VIDEO_PLAYBACK_RATES.length - 1) : Math.max(rateIndex - 1, 0);
|
|
|
|
const nextRate = VIDEO_PLAYBACK_RATES[rateIndex];
|
2021-11-12 15:56:46 +01:00
|
|
|
|
|
|
|
OVERLAY.showPlaybackRateOverlay(player, nextRate, isSpeedUp);
|
|
|
|
player.userActive(true);
|
|
|
|
player.playbackRate(nextRate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-06 15:37:44 +02:00
|
|
|
const VideoJsShorcuts = ({
|
2021-11-12 15:56:46 +01:00
|
|
|
playNext,
|
|
|
|
playPrevious,
|
|
|
|
toggleVideoTheaterMode,
|
2022-03-15 17:18:08 +01:00
|
|
|
isMobile,
|
|
|
|
isLivestreamClaim,
|
2021-11-12 15:56:46 +01:00
|
|
|
}: {
|
|
|
|
playNext: any, // function
|
|
|
|
playPrevious: any, // function
|
|
|
|
toggleVideoTheaterMode: any, // function
|
2022-03-15 17:18:08 +01:00
|
|
|
isMobile: boolean,
|
|
|
|
isLivestreamClaim: boolean,
|
2021-11-12 15:56:46 +01:00
|
|
|
}) => {
|
|
|
|
function toggleTheaterMode(playerRef) {
|
|
|
|
const player = playerRef.current;
|
|
|
|
if (!player) return;
|
|
|
|
// TODO: have to fix this
|
|
|
|
toggleVideoTheaterMode();
|
|
|
|
if (player.isFullscreen()) {
|
|
|
|
player.exitFullscreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleKeyDown(e: KeyboardEvent, playerRef, containerRef) {
|
|
|
|
const player = playerRef.current;
|
|
|
|
const videoNode = containerRef.current && containerRef.current.querySelector('video, audio');
|
|
|
|
if (!videoNode || !player || isUserTyping()) return;
|
|
|
|
handleSingleKeyActions(e, playerRef, containerRef);
|
|
|
|
handleShiftKeyActions(e, playerRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleShiftKeyActions(e: KeyboardEvent, playerRef) {
|
|
|
|
if (e.altKey || e.ctrlKey || e.metaKey || !e.shiftKey) return;
|
|
|
|
if (e.keyCode === KEYCODES.PERIOD) changePlaybackSpeed(true, playerRef);
|
|
|
|
if (e.keyCode === KEYCODES.COMMA) changePlaybackSpeed(false, playerRef);
|
|
|
|
if (e.keyCode === KEYCODES.N) playNext();
|
|
|
|
if (e.keyCode === KEYCODES.P) playPrevious();
|
|
|
|
}
|
|
|
|
|
|
|
|
// eslint-disable-next-line flowtype/no-types-missing-file-annotation
|
|
|
|
function handleSingleKeyActions(e: KeyboardEvent, playerRef, containerRef) {
|
|
|
|
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return;
|
2022-02-01 21:29:00 +01:00
|
|
|
|
|
|
|
if (e.keyCode === KEYCODES.SPACEBAR || e.keyCode === KEYCODES.K) {
|
|
|
|
e.preventDefault();
|
|
|
|
togglePlay(containerRef);
|
|
|
|
}
|
|
|
|
|
2021-11-12 15:56:46 +01:00
|
|
|
if (e.keyCode === KEYCODES.F) toggleFullscreen(playerRef);
|
|
|
|
if (e.keyCode === KEYCODES.M) toggleMute(containerRef);
|
|
|
|
if (e.keyCode === KEYCODES.UP) volumeUp(e, playerRef);
|
|
|
|
if (e.keyCode === KEYCODES.DOWN) volumeDown(e, playerRef);
|
2022-03-15 17:18:08 +01:00
|
|
|
if (e.keyCode === KEYCODES.T && !isMobile && !isLivestreamClaim) toggleTheaterMode(playerRef);
|
2021-11-12 15:56:46 +01:00
|
|
|
if (e.keyCode === KEYCODES.L) seekVideo(SEEK_STEP, playerRef, containerRef);
|
|
|
|
if (e.keyCode === KEYCODES.J) seekVideo(-SEEK_STEP, playerRef, containerRef);
|
|
|
|
if (e.keyCode === KEYCODES.RIGHT) seekVideo(SEEK_STEP_5, playerRef, containerRef);
|
|
|
|
if (e.keyCode === KEYCODES.LEFT) seekVideo(-SEEK_STEP_5, playerRef, containerRef);
|
2022-03-29 22:40:21 +02:00
|
|
|
if (e.keyCode === KEYCODES.ZERO) seekVideo(0, playerRef, containerRef, true);
|
|
|
|
if (e.keyCode === KEYCODES.ONE) seekVideo(10 / 100, playerRef, containerRef, true);
|
|
|
|
if (e.keyCode === KEYCODES.TWO) seekVideo(20 / 100, playerRef, containerRef, true);
|
|
|
|
if (e.keyCode === KEYCODES.THREE) seekVideo(30 / 100, playerRef, containerRef, true);
|
|
|
|
if (e.keyCode === KEYCODES.FOUR) seekVideo(40 / 100, playerRef, containerRef, true);
|
|
|
|
if (e.keyCode === KEYCODES.FIVE) seekVideo(50 / 100, playerRef, containerRef, true);
|
|
|
|
if (e.keyCode === KEYCODES.SIX) seekVideo(60 / 100, playerRef, containerRef, true);
|
|
|
|
if (e.keyCode === KEYCODES.SEVEN) seekVideo(70 / 100, playerRef, containerRef, true);
|
|
|
|
if (e.keyCode === KEYCODES.EIGHT) seekVideo(80 / 100, playerRef, containerRef, true);
|
|
|
|
if (e.keyCode === KEYCODES.NINE) seekVideo(90 / 100, playerRef, containerRef, true);
|
2021-11-12 15:56:46 +01:00
|
|
|
}
|
|
|
|
|
2022-06-06 15:37:44 +02:00
|
|
|
const handleVideoScrollWheel = (event, playerRef, containerRef) => {
|
|
|
|
// Handle precise volume control when scrolling over the video player while holding down the "SHIFT"-key
|
|
|
|
const player = playerRef.current;
|
|
|
|
const videoNode = containerRef.current && containerRef.current.querySelector('video');
|
|
|
|
|
|
|
|
if (!videoNode || !player || isUserTyping() || !event.shiftKey) return;
|
|
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
|
|
const delta = event.deltaY;
|
|
|
|
|
|
|
|
if (delta > 0) {
|
|
|
|
volumeDown(event, playerRef, false, PRECISE_VOLUME_CHANGE_ON_SCROLL);
|
|
|
|
} else if (delta < 0) {
|
|
|
|
volumeUp(event, playerRef, false, PRECISE_VOLUME_CHANGE_ON_SCROLL);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleVolumeBarScrollWheel = (event, volumeElement, playerRef, containerRef) => {
|
|
|
|
// Handle generic and precise volume control when scrolling over the volume bar
|
|
|
|
const player = playerRef.current;
|
|
|
|
const videoNode = containerRef.current && containerRef.current.querySelector('video');
|
|
|
|
|
|
|
|
if (!volumeElement || !player || !videoNode || isUserTyping()) return;
|
|
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
event.stopImmediatePropagation();
|
|
|
|
|
|
|
|
const delta = event.deltaY;
|
|
|
|
const changeAmount = event.shiftKey ? PRECISE_VOLUME_CHANGE_ON_SCROLL : VOLUME_CHANGE_ON_SCROLL;
|
|
|
|
|
|
|
|
if (delta > 0) {
|
|
|
|
volumeDown(event, playerRef, false, changeAmount);
|
|
|
|
} else if (delta < 0) {
|
|
|
|
volumeUp(event, playerRef, false, changeAmount);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const createKeyDownShortcutsHandler = function (playerRef: any, containerRef: any) {
|
2021-11-12 15:56:46 +01:00
|
|
|
return function curried_func(e: any) {
|
|
|
|
handleKeyDown(e, playerRef, containerRef);
|
|
|
|
};
|
|
|
|
};
|
2022-06-06 15:37:44 +02:00
|
|
|
const createVideoScrollShortcutsHandler = function (playerRef: any, containerRef: any) {
|
|
|
|
return function curried_func(e: any) {
|
|
|
|
handleVideoScrollWheel(e, playerRef, containerRef);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
const createVolumePanelScrollShortcutsHandler = function (volumeElement: any, playerRef: any, containerRef: any) {
|
|
|
|
return function curried_func(e: any) {
|
|
|
|
handleVolumeBarScrollWheel(e, volumeElement, playerRef, containerRef);
|
|
|
|
};
|
|
|
|
};
|
2021-11-12 15:56:46 +01:00
|
|
|
|
|
|
|
return {
|
2022-06-06 15:37:44 +02:00
|
|
|
createKeyDownShortcutsHandler,
|
|
|
|
createVideoScrollShortcutsHandler,
|
|
|
|
createVolumePanelScrollShortcutsHandler,
|
2021-11-12 15:56:46 +01:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2022-06-06 15:37:44 +02:00
|
|
|
export default VideoJsShorcuts;
|