Add video player hotkeys

This commit is contained in:
MerlinScheurer 2025-01-30 20:17:30 +01:00
parent b836697e2a
commit 702000a493
2 changed files with 140 additions and 1 deletions

View File

@ -1,10 +1,21 @@
import updateVideoProgressById from '../api/actions/updateVideoProgressById';
import updateWatchedState from '../api/actions/updateWatchedState';
import { SponsorBlockSegmentType, SponsorBlockType, VideoResponseType } from '../pages/Video';
import { Dispatch, Fragment, SetStateAction, SyntheticEvent, useState } from 'react';
import {
Dispatch,
Fragment,
SetStateAction,
SyntheticEvent,
useEffect,
useRef,
useState,
} from 'react';
import formatTime from '../functions/formatTime';
import { useSearchParams } from 'react-router-dom';
import getApiUrl from '../configuration/getApiUrl';
import { useKeyPress } from '../functions/useKeypressHook';
const VIDEO_PLAYBACK_SPEEDS = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3];
type VideoTag = SyntheticEvent<HTMLVideoElement, Event>;
@ -109,10 +120,23 @@ const VideoPlayer = ({
onWatchStateChanged,
onVideoEnd,
}: VideoPlayerProps) => {
const videoRef = useRef<HTMLVideoElement | null>(null);
const [searchParams] = useSearchParams();
const searchParamVideoProgress = searchParams.get('t');
const [skippedSegments, setSkippedSegments] = useState<SponsorSegmentsSkippedType>({});
const [isMuted, setIsMuted] = useState(false);
const [playbackSpeedIndex, setPlaybackSpeedIndex] = useState(3);
const [lastSubtitleTack, setLastSubtitleTack] = useState(0);
// const questionmarkPressed = useKeyPress('?');
const mutePressed = useKeyPress('m');
const fullscreenPressed = useKeyPress('f');
const subtitlesPressed = useKeyPress('c');
const increasePlaybackSpeedPressed = useKeyPress('>');
const decreasePlaybackSpeedPressed = useKeyPress('<');
const resetPlaybackSpeedPressed = useKeyPress('=');
const videoId = video.data.youtube_id;
const videoUrl = video.data.media_url;
@ -153,11 +177,95 @@ const VideoPlayer = ({
onVideoEnd?.();
};
useEffect(() => {
if (mutePressed) {
setIsMuted(!isMuted);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mutePressed]);
useEffect(() => {
if (increasePlaybackSpeedPressed) {
const newSpeed = playbackSpeedIndex + 1;
if (videoRef.current && VIDEO_PLAYBACK_SPEEDS[newSpeed]) {
videoRef.current.playbackRate = VIDEO_PLAYBACK_SPEEDS[newSpeed];
setPlaybackSpeedIndex(newSpeed);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [increasePlaybackSpeedPressed]);
useEffect(() => {
if (decreasePlaybackSpeedPressed) {
const newSpeedIndex = playbackSpeedIndex - 1;
if (videoRef.current && VIDEO_PLAYBACK_SPEEDS[newSpeedIndex]) {
videoRef.current.playbackRate = VIDEO_PLAYBACK_SPEEDS[newSpeedIndex];
setPlaybackSpeedIndex(newSpeedIndex);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [decreasePlaybackSpeedPressed]);
useEffect(() => {
if (resetPlaybackSpeedPressed) {
const newSpeedIndex = 3;
if (videoRef.current && VIDEO_PLAYBACK_SPEEDS[newSpeedIndex]) {
videoRef.current.playbackRate = VIDEO_PLAYBACK_SPEEDS[newSpeedIndex];
setPlaybackSpeedIndex(newSpeedIndex);
}
}
}, [resetPlaybackSpeedPressed]);
useEffect(() => {
if (fullscreenPressed) {
if (videoRef.current && videoRef.current.requestFullscreen && !document.fullscreenElement) {
videoRef.current.requestFullscreen();
} else {
document.exitFullscreen().catch(e => {
console.error(e);
});
}
}
}, [fullscreenPressed]);
useEffect(() => {
if (subtitlesPressed) {
if (videoRef.current) {
const tracks = [...videoRef.current.textTracks];
if (tracks.length === 0) {
return;
}
const lastIndex = tracks.findIndex(x => x.mode === 'showing');
const active = tracks[lastIndex];
if (!active && lastSubtitleTack !== 0) {
tracks[lastSubtitleTack - 1].mode = 'showing';
} else {
if (active) {
active.mode = 'hidden';
setLastSubtitleTack(lastIndex + 1);
}
}
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [subtitlesPressed]);
return (
<>
<div id="player" className={embed ? '' : 'player-wrapper'}>
<div className={embed ? '' : 'video-main'}>
<video
ref={videoRef}
key={`${getApiUrl()}${videoUrl}`}
poster={`${getApiUrl()}${videoThumbUrl}`}
onVolumeChange={(videoTag: VideoTag) => {
@ -196,6 +304,7 @@ const VideoPlayer = ({
width="100%"
playsInline
id="video-item"
muted={isMuted}
>
<source
src={`${getApiUrl()}${videoUrl}#t=${videoSrcProgress}`}

View File

@ -0,0 +1,30 @@
import { useEffect, useState } from 'react';
// source: https://thibault.sh/react-hooks/use-key-press
export function useKeyPress(targetKey: string) {
const [isKeyPressed, setIsKeyPressed] = useState(false);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === targetKey) {
setIsKeyPressed(true);
}
};
const handleKeyUp = (event: KeyboardEvent) => {
if (event.key === targetKey) {
setIsKeyPressed(false);
}
};
window.addEventListener('keydown', handleKeyDown);
window.addEventListener('keyup', handleKeyUp);
return () => {
window.removeEventListener('keydown', handleKeyDown);
window.removeEventListener('keyup', handleKeyUp);
};
}, [targetKey]);
return isKeyPressed;
}