mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-01-11 22:40:31 +00:00
feat: add player subtitles delay shortcuts
This commit is contained in:
parent
d75c9b1d99
commit
f6d4e3f4a6
6 changed files with 172 additions and 1 deletions
|
|
@ -82,6 +82,19 @@
|
|||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
.fade-enter {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.fade-active {
|
||||
opacity: 1;
|
||||
transition: opacity 0.3s cubic-bezier(0.32, 0, 0.67, 0);
|
||||
}
|
||||
|
||||
.fade-exit {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@keyframes fade-in-no-motion {
|
||||
0% {
|
||||
opacity: 0;
|
||||
|
|
|
|||
23
src/routes/Player/Indicator/Indicator.less
Normal file
23
src/routes/Player/Indicator/Indicator.less
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
.indicator-container {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 4rem;
|
||||
user-select: none;
|
||||
|
||||
.indicator {
|
||||
flex: none;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
padding: 0 2rem;
|
||||
border-radius: 4rem;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
color: var(--primary-foreground-color);
|
||||
background-color: var(--modal-background-color);
|
||||
}
|
||||
}
|
||||
73
src/routes/Player/Indicator/Indicator.tsx
Normal file
73
src/routes/Player/Indicator/Indicator.tsx
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { t } from 'i18next';
|
||||
import { Transition } from 'stremio/components';
|
||||
import { useBinaryState } from 'stremio/common';
|
||||
import styles from './Indicator.less';
|
||||
|
||||
type Property = {
|
||||
label: string,
|
||||
format: (value: number) => string,
|
||||
};
|
||||
|
||||
const PROPERTIES: Record<string, Property> = {
|
||||
'extraSubtitlesDelay': {
|
||||
label: 'SUBTITLES_DELAY',
|
||||
format: (value) => `${(value / 1000).toFixed(2)}s`,
|
||||
},
|
||||
};
|
||||
|
||||
type VideoState = Record<string, number>;
|
||||
|
||||
type Props = {
|
||||
className: string,
|
||||
videoState: VideoState,
|
||||
};
|
||||
|
||||
const Indicator = ({ className, videoState }: Props) => {
|
||||
const timeout = useRef<NodeJS.Timeout | null>(null);
|
||||
const prevVideoState = useRef<VideoState>(videoState);
|
||||
|
||||
const [shown, show, hide] = useBinaryState(false);
|
||||
const [current, setCurrent] = useState<string | null>(null);
|
||||
|
||||
const label = useMemo(() => {
|
||||
const property = current && PROPERTIES[current];
|
||||
return property && t(property.label);
|
||||
}, [current]);
|
||||
|
||||
const value = useMemo(() => {
|
||||
const property = current && PROPERTIES[current];
|
||||
const value = current && videoState[current];
|
||||
return property && value && property.format(value);
|
||||
}, [current, videoState]);
|
||||
|
||||
useEffect(() => {
|
||||
for (const property of Object.keys(PROPERTIES)) {
|
||||
const prev = prevVideoState.current[property];
|
||||
const next = videoState[property];
|
||||
|
||||
if (next && next !== prev) {
|
||||
setCurrent(property);
|
||||
show();
|
||||
|
||||
timeout.current && clearTimeout(timeout.current);
|
||||
timeout.current = setTimeout(hide, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
prevVideoState.current = videoState;
|
||||
}, [videoState]);
|
||||
|
||||
return (
|
||||
<Transition when={shown} name={'fade'}>
|
||||
<div className={classNames(className, styles['indicator-container'])}>
|
||||
<div className={styles['indicator']}>
|
||||
<div>{label} {value}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
);
|
||||
};
|
||||
|
||||
export default Indicator;
|
||||
|
|
@ -27,6 +27,7 @@ const useStatistics = require('./useStatistics');
|
|||
const useVideo = require('./useVideo');
|
||||
const styles = require('./styles');
|
||||
const Video = require('./Video');
|
||||
const { default: Indicator } = require('./Indicator/Indicator');
|
||||
|
||||
const Player = ({ urlParams, queryParams }) => {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -216,6 +217,16 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
video.setProp('extraSubtitlesDelay', delay);
|
||||
}, []);
|
||||
|
||||
const onIncreaseSubtitlesDelay = React.useCallback(() => {
|
||||
const delay = video.state.extraSubtitlesDelay + 250;
|
||||
onExtraSubtitlesDelayChanged(delay);
|
||||
}, [video.state.extraSubtitlesDelay, onExtraSubtitlesDelayChanged]);
|
||||
|
||||
const onDecreaseSubtitlesDelay = React.useCallback(() => {
|
||||
const delay = video.state.extraSubtitlesDelay - 250;
|
||||
onExtraSubtitlesDelayChanged(delay);
|
||||
}, [video.state.extraSubtitlesDelay, onExtraSubtitlesDelayChanged]);
|
||||
|
||||
const onSubtitlesSizeChanged = React.useCallback((size) => {
|
||||
updateSettings({ subtitlesSize: size });
|
||||
}, [updateSettings]);
|
||||
|
|
@ -587,6 +598,14 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
|
||||
break;
|
||||
}
|
||||
case 'KeyG': {
|
||||
onDecreaseSubtitlesDelay();
|
||||
break;
|
||||
}
|
||||
case 'KeyH': {
|
||||
onIncreaseSubtitlesDelay();
|
||||
break;
|
||||
}
|
||||
case 'Escape': {
|
||||
closeMenus();
|
||||
!settings.escExitFullscreen && window.history.back();
|
||||
|
|
@ -620,7 +639,29 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
window.removeEventListener('keyup', onKeyUp);
|
||||
window.removeEventListener('wheel', onWheel);
|
||||
};
|
||||
}, [player.metaItem, player.selected, streamingServer.statistics, settings.seekTimeDuration, settings.seekShortTimeDuration, settings.escExitFullscreen, routeFocused, menusOpen, nextVideoPopupOpen, video.state.paused, video.state.time, video.state.volume, video.state.audioTracks, video.state.subtitlesTracks, video.state.extraSubtitlesTracks, video.state.playbackSpeed, toggleSubtitlesMenu, toggleStatisticsMenu, toggleSideDrawer]);
|
||||
}, [
|
||||
player.metaItem,
|
||||
player.selected,
|
||||
streamingServer.statistics,
|
||||
settings.seekTimeDuration,
|
||||
settings.seekShortTimeDuration,
|
||||
settings.escExitFullscreen,
|
||||
routeFocused,
|
||||
menusOpen,
|
||||
nextVideoPopupOpen,
|
||||
video.state.paused,
|
||||
video.state.time,
|
||||
video.state.volume,
|
||||
video.state.audioTracks,
|
||||
video.state.subtitlesTracks,
|
||||
video.state.extraSubtitlesTracks,
|
||||
video.state.playbackSpeed,
|
||||
toggleSubtitlesMenu,
|
||||
toggleStatisticsMenu,
|
||||
toggleSideDrawer,
|
||||
onDecreaseSubtitlesDelay,
|
||||
onIncreaseSubtitlesDelay,
|
||||
]);
|
||||
|
||||
React.useEffect(() => {
|
||||
video.events.on('error', onError);
|
||||
|
|
@ -760,6 +801,10 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
onMouseOver={onBarMouseMove}
|
||||
onTouchEnd={onContainerMouseLeave}
|
||||
/>
|
||||
<Indicator
|
||||
className={classnames(styles['layer'], styles['indicator-layer'])}
|
||||
videoState={video.state}
|
||||
/>
|
||||
{
|
||||
nextVideoPopupOpen ?
|
||||
<NextVideoPopup
|
||||
|
|
|
|||
|
|
@ -107,6 +107,13 @@ html:not(.active-slider-within) {
|
|||
}
|
||||
}
|
||||
|
||||
&.indicator-layer {
|
||||
top: initial;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 10rem;
|
||||
}
|
||||
|
||||
&.menu-layer {
|
||||
top: initial;
|
||||
left: initial;
|
||||
|
|
|
|||
|
|
@ -709,6 +709,16 @@ const Settings = () => {
|
|||
<kbd>F</kbd>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles['option-container']}>
|
||||
<div className={styles['option-name-container']}>
|
||||
<div className={styles['label']}>{ t('SETTINGS_SHORTCUT_SUBTITLES_DELAY') }</div>
|
||||
</div>
|
||||
<div className={classnames(styles['option-input-container'], styles['shortcut-container'])}>
|
||||
<kbd>G</kbd>
|
||||
<div className={styles['label']}>and</div>
|
||||
<kbd>H</kbd>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles['option-container']}>
|
||||
<div className={styles['option-name-container']}>
|
||||
<div className={styles['label']}>{ t('SETTINGS_SHORTCUT_NAVIGATE_MENUS') }</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue