From b19896ddf3745452d05b7935d619ce32c9e73da2 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 1 Nov 2022 02:44:26 +0100 Subject: [PATCH 1/4] feat(Player): implement videos menu --- src/routes/Player/ControlBar/ControlBar.js | 23 +++++++++--- src/routes/Player/Player.js | 32 +++++++++++++++-- src/routes/Player/VideosMenu/VideosMenu.js | 41 ++++++++++++++++++++++ src/routes/Player/VideosMenu/index.js | 5 +++ src/routes/Player/VideosMenu/styles.less | 5 +++ 5 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 src/routes/Player/VideosMenu/VideosMenu.js create mode 100644 src/routes/Player/VideosMenu/index.js create mode 100644 src/routes/Player/VideosMenu/styles.less 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..fc3210399 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,15 @@ 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..a960dee52 --- /dev/null +++ b/src/routes/Player/VideosMenu/VideosMenu.js @@ -0,0 +1,41 @@ +// 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 }) => { + const onMouseDown = React.useCallback((event) => { + event.nativeEvent.videosMenuClosePrevented = true; + }, []); + return ( +
+ { + metaItem.videos.map((video, index) => ( +
+ ); +}; + +VideosMenu.propTypes = { + className: PropTypes.string, + metaItem: PropTypes.object, +}; + +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 From 87251a649be1ec02f1e1b163c026409bcb9f7eee Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 1 Nov 2022 03:21:44 +0100 Subject: [PATCH 2/4] refactor(Player): only display videos from same season for series --- src/routes/Player/Player.js | 1 + src/routes/Player/VideosMenu/VideosMenu.js | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index fc3210399..7ad70f6c0 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -581,6 +581,7 @@ const Player = ({ urlParams, queryParams }) => { : null diff --git a/src/routes/Player/VideosMenu/VideosMenu.js b/src/routes/Player/VideosMenu/VideosMenu.js index a960dee52..a3a34f168 100644 --- a/src/routes/Player/VideosMenu/VideosMenu.js +++ b/src/routes/Player/VideosMenu/VideosMenu.js @@ -6,14 +6,20 @@ const classnames = require('classnames'); const Video = require('../../MetaDetails/VideosList/Video'); const styles = require('./styles'); -const VideosMenu = ({ className, metaItem }) => { +const VideosMenu = ({ className, metaItem, seriesInfo }) => { const onMouseDown = React.useCallback((event) => { event.nativeEvent.videosMenuClosePrevented = true; }, []); + const videos = React.useMemo(() => { + return seriesInfo && seriesInfo.season && Array.isArray(metaItem.videos) ? + metaItem.videos.filter(({ season }) => season === seriesInfo.season) + : + metaItem.videos; + }, [metaItem, seriesInfo]); return (
{ - metaItem.videos.map((video, index) => ( + videos.map((video, index) => (