diff --git a/src/routes/Player/ControlBar/ControlBar.js b/src/routes/Player/ControlBar/ControlBar.js index e00f66d00..c9ba5cd7e 100644 --- a/src/routes/Player/ControlBar/ControlBar.js +++ b/src/routes/Player/ControlBar/ControlBar.js @@ -39,6 +39,9 @@ const ControlBar = React.forwardRef(({ onToggleSpeedMenu, onToggleSideDrawer, onToggleOptionsMenu, + videoScale, + videoScaleLabel, + onVideoScaleChanged, onToggleStatisticsMenu, onTouchEnd, ...props @@ -176,6 +179,9 @@ const ControlBar = React.forwardRef(({ : null } + @@ -194,6 +200,9 @@ ControlBar.propTypes = { volume: PropTypes.number, muted: PropTypes.bool, playbackSpeed: PropTypes.number, + videoScale: PropTypes.string, + videoScaleLabel: PropTypes.string, + onVideoScaleChanged: PropTypes.func, subtitlesTracks: PropTypes.array, audioTracks: PropTypes.array, metaItem: PropTypes.object, diff --git a/src/routes/Player/Indicator/Indicator.tsx b/src/routes/Player/Indicator/Indicator.tsx index 7525fe1cd..b9290ad64 100644 --- a/src/routes/Player/Indicator/Indicator.tsx +++ b/src/routes/Player/Indicator/Indicator.tsx @@ -7,7 +7,13 @@ import styles from './Indicator.less'; type Property = { label: string, - format: (value: number) => string, + format: (value: number | string) => string, +}; + +const VIDEO_SCALE_LABELS: Record = { + 'contain': 'Fit', + 'cover': 'Crop', + 'fill': 'Stretch', }; const PROPERTIES: Record = { @@ -15,9 +21,13 @@ const PROPERTIES: Record = { label: 'SUBTITLES_DELAY', format: (value) => `${(value / 1000).toFixed(2)}s`, }, + 'videoScale': { + label: 'VIDEO_SCALE', + format: (value) => VIDEO_SCALE_LABELS[String(value)] || String(value), + }, }; -type VideoState = Record; +type VideoState = Record; type Props = { className: string, diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index 662717b70..add5c0c7c 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -97,6 +97,9 @@ const Player = ({ urlParams, queryParams }) => { const isNavigating = React.useRef(false); + const VIDEO_SCALES = ['contain', 'cover', 'fill']; + const VIDEO_SCALE_LABELS = { contain: 'Fit', cover: 'Crop', fill: 'Stretch' }; + const playbackSpeed = React.useRef(video.state.playbackSpeed || 1); const pressTimer = React.useRef(null); const longPress = React.useRef(false); @@ -235,6 +238,13 @@ const Player = ({ urlParams, queryParams }) => { }, []); + const onVideoScaleChanged = React.useCallback(() => { + const currentScale = video.state.videoScale || 'contain'; + const currentIndex = VIDEO_SCALES.indexOf(currentScale); + const nextScale = VIDEO_SCALES[(currentIndex + 1) % VIDEO_SCALES.length]; + video.setVideoScale(nextScale); + }, [video.state.videoScale]); + const onSubtitlesTrackSelected = React.useCallback((track) => { video.setSubtitlesTrack(track?.id ?? null); streamStateChanged({ @@ -999,6 +1009,9 @@ const Player = ({ urlParams, queryParams }) => { onToggleSubtitlesMenu={toggleSubtitlesMenu} onToggleAudioMenu={toggleAudioMenu} onToggleSpeedMenu={toggleSpeedMenu} + videoScale={video.state.videoScale} + videoScaleLabel={VIDEO_SCALE_LABELS[video.state.videoScale || 'contain']} + onVideoScaleChanged={onVideoScaleChanged} onToggleStatisticsMenu={toggleStatisticsMenu} onToggleSideDrawer={toggleSideDrawer} onMouseMove={onBarMouseMove} diff --git a/src/routes/Player/useVideo.js b/src/routes/Player/useVideo.js index b3a5d2e39..bfc0ec810 100644 --- a/src/routes/Player/useVideo.js +++ b/src/routes/Player/useVideo.js @@ -142,6 +142,10 @@ const useVideo = () => { setProp('extraSubtitlesOffset', offset); }; + const setVideoScale = (scale) => { + setProp('videoScale', scale); + }; + const setSubtitlesTextColor = (color) => { setProp('subtitlesTextColor', color); setProp('extraSubtitlesTextColor', color); @@ -238,6 +242,7 @@ const useVideo = () => { setSubtitlesBackgroundColor, setSubtitlesOutlineColor, setExtraSubtitlesTrack, + setVideoScale, }; };