diff --git a/src/routes/Player/ControlBar/ControlBar.js b/src/routes/Player/ControlBar/ControlBar.js index b63c453f4..fa23e8e2f 100644 --- a/src/routes/Player/ControlBar/ControlBar.js +++ b/src/routes/Player/ControlBar/ControlBar.js @@ -29,6 +29,7 @@ const ControlBar = ({ onSeekRequested, onToggleSubtitlesMenu, onToggleInfoMenu, + onToggleVideosMenu, ...props }) => { const { chromecast } = useServices(); @@ -40,6 +41,9 @@ const ControlBar = ({ const onInfoButtonMouseDown = React.useCallback((event) => { event.nativeEvent.infoMenuClosePrevented = true; }, []); + const onVideosButtonMouseDown = React.useCallback((event) => { + event.nativeEvent.videosMenuClosePrevented = true; + }, []); const onPlayPauseButtonClick = React.useCallback(() => { if (paused) { if (typeof onPlayRequested === 'function') { @@ -72,6 +76,11 @@ const ControlBar = ({ onToggleInfoMenu(); } }, [onToggleInfoMenu]); + const onVideosButtonClick = React.useCallback(() => { + if (typeof onToggleVideosMenu === 'function') { + onToggleVideosMenu(); + } + }, [onToggleVideosMenu]); const onChromecastButtonClick = React.useCallback(() => { chromecast.transport.requestSession(); }, []); @@ -130,9 +139,14 @@ const ControlBar = ({ - + { + metaItem?.content?.videos?.length > 0 ? + + : + null + } @@ -156,7 +170,8 @@ ControlBar.propTypes = { onVolumeChangeRequested: PropTypes.func, onSeekRequested: PropTypes.func, onToggleSubtitlesMenu: PropTypes.func, - onToggleInfoMenu: PropTypes.func + onToggleInfoMenu: PropTypes.func, + onToggleVideosMenu: PropTypes.func }; module.exports = ControlBar; diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index 91e89c310..7ad70f6c0 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -11,6 +11,7 @@ const Icon = require('@stremio/stremio-icons/dom'); const BufferingLoader = require('./BufferingLoader'); const ControlBar = require('./ControlBar'); const InfoMenu = require('./InfoMenu'); +const VideosMenu = require('./VideosMenu'); const SubtitlesMenu = require('./SubtitlesMenu'); const Video = require('./Video'); const usePlayer = require('./usePlayer'); @@ -38,6 +39,7 @@ const Player = ({ urlParams, queryParams }) => { const setImmersedDebounced = React.useCallback(debounce(setImmersed, 3000), []); const [subtitlesMenuOpen, , closeSubtitlesMenu, toggleSubtitlesMenu] = useBinaryState(false); const [infoMenuOpen, , closeInfoMenu, toggleInfoMenu] = useBinaryState(false); + const [videosMenuOpen, , closeVideosMenu, toggleVideosMenu] = useBinaryState(false); const [error, setError] = React.useState(null); const [videoState, setVideoState] = React.useReducer( (videoState, nextVideoState) => ({ ...videoState, ...nextVideoState }), @@ -198,6 +200,9 @@ const Player = ({ urlParams, queryParams }) => { if (!event.nativeEvent.infoMenuClosePrevented) { closeInfoMenu(); } + if (!event.nativeEvent.videosMenuClosePrevented) { + closeVideosMenu(); + } }, []); const onContainerMouseMove = React.useCallback((event) => { setImmersed(false); @@ -318,6 +323,7 @@ const Player = ({ urlParams, queryParams }) => { React.useEffect(() => { if (player.metaItem === null || player.metaItem.type !== 'Ready') { closeInfoMenu(); + closeVideosMenu(); } }, [player.metaItem]); React.useEffect(() => { @@ -402,6 +408,7 @@ const Player = ({ urlParams, queryParams }) => { } case 'KeyS': { closeInfoMenu(); + closeVideosMenu(); if ((Array.isArray(videoState.subtitlesTracks) && videoState.subtitlesTracks.length > 0) || (Array.isArray(videoState.extraSubtitlesTracks) && videoState.extraSubtitlesTracks.length > 0) || (Array.isArray(videoState.audioTracks) && videoState.audioTracks.length > 0)) { @@ -412,15 +419,26 @@ const Player = ({ urlParams, queryParams }) => { } case 'KeyI': { closeSubtitlesMenu(); + closeVideosMenu(); if (player.metaItem !== null && player.metaItem.type === 'Ready') { toggleInfoMenu(); } break; } + case 'KeyV': { + closeInfoMenu(); + closeSubtitlesMenu(); + if (player.metaItem !== null && player.metaItem.type === 'Ready') { + toggleVideosMenu(); + } + + break; + } case 'Escape': { closeSubtitlesMenu(); closeInfoMenu(); + closeVideosMenu(); break; } } @@ -431,7 +449,7 @@ const Player = ({ urlParams, queryParams }) => { return () => { window.removeEventListener('keydown', onKeyDown); }; - }, [player.metaItem, settings.seekTimeDuration, routeFocused, subtitlesMenuOpen, infoMenuOpen, videoState.paused, videoState.time, videoState.volume, videoState.audioTracks, videoState.subtitlesTracks, videoState.extraSubtitlesTracks, toggleSubtitlesMenu, toggleInfoMenu]); + }, [player.metaItem, settings.seekTimeDuration, routeFocused, subtitlesMenuOpen, infoMenuOpen, videoState.paused, videoState.time, videoState.volume, videoState.audioTracks, videoState.subtitlesTracks, videoState.extraSubtitlesTracks, toggleSubtitlesMenu, toggleInfoMenu, toggleVideosMenu]); React.useLayoutEffect(() => { return () => { setImmersedDebounced.cancel(); @@ -440,7 +458,7 @@ const Player = ({ urlParams, queryParams }) => { }; }, []); return ( -
{ onSeekRequested={onSeekRequested} onToggleSubtitlesMenu={toggleSubtitlesMenu} onToggleInfoMenu={toggleInfoMenu} + onToggleVideosMenu={toggleVideosMenu} onMouseMove={onBarMouseMove} onMouseOver={onBarMouseMove} /> @@ -557,6 +576,16 @@ const Player = ({ urlParams, queryParams }) => { : null } + { + videosMenuOpen ? + + : + null + }
); }; diff --git a/src/routes/Player/VideosMenu/VideosMenu.js b/src/routes/Player/VideosMenu/VideosMenu.js new file mode 100644 index 000000000..219a329c2 --- /dev/null +++ b/src/routes/Player/VideosMenu/VideosMenu.js @@ -0,0 +1,51 @@ +// Copyright (C) 2017-2022 Smart code 203358507 + +const React = require('react'); +const PropTypes = require('prop-types'); +const classnames = require('classnames'); +const Video = require('../../MetaDetails/VideosList/Video'); +const styles = require('./styles'); + +const VideosMenu = ({ className, metaItem, seriesInfo }) => { + const onMouseDown = React.useCallback((event) => { + event.nativeEvent.videosMenuClosePrevented = true; + }, []); + const videos = React.useMemo(() => { + return seriesInfo && typeof seriesInfo.season === 'number' && Array.isArray(metaItem.videos) ? + metaItem.videos.filter(({ season }) => season === seriesInfo.season) + : + metaItem.videos; + }, [metaItem, seriesInfo]); + return ( +
+ { + videos.map((video, index) => ( +
+ ); +}; + +VideosMenu.propTypes = { + className: PropTypes.string, + metaItem: PropTypes.object, + seriesInfo: PropTypes.shape({ + season: PropTypes.number, + episode: PropTypes.number, + }), +}; + +module.exports = VideosMenu; diff --git a/src/routes/Player/VideosMenu/index.js b/src/routes/Player/VideosMenu/index.js new file mode 100644 index 000000000..e604ab4cc --- /dev/null +++ b/src/routes/Player/VideosMenu/index.js @@ -0,0 +1,5 @@ +// Copyright (C) 2017-2022 Smart code 203358507 + +const VideosMenu = require('./VideosMenu'); + +module.exports = VideosMenu; diff --git a/src/routes/Player/VideosMenu/styles.less b/src/routes/Player/VideosMenu/styles.less new file mode 100644 index 000000000..47444c72a --- /dev/null +++ b/src/routes/Player/VideosMenu/styles.less @@ -0,0 +1,5 @@ +// Copyright (C) 2017-2022 Smart code 203358507 + +.videos-menu-container { + width: 30rem; +} \ No newline at end of file