From 253322f690dadd3f44484f34767d0f72c73caaf2 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 12 Jan 2023 01:15:01 +0100 Subject: [PATCH 01/10] feat(Player): add option to open stream in external player --- src/App/ServicesToaster.js | 8 +++ src/routes/Player/OptionsMenu/OptionsMenu.js | 76 ++++++++++++++------ src/routes/Player/Player.js | 1 + 3 files changed, 63 insertions(+), 22 deletions(-) diff --git a/src/App/ServicesToaster.js b/src/App/ServicesToaster.js index d3fdd3461..9ceff4bb7 100644 --- a/src/App/ServicesToaster.js +++ b/src/App/ServicesToaster.js @@ -50,6 +50,14 @@ const ServicesToaster = () => { }); break; } + case 'PlayingOnDevice': { + toast.show({ + type: 'success', + title: `Stream opened in ${args.device}`, + timeout: 4000 + }); + break; + } } }; const onDragAndDropError = (error) => { diff --git a/src/routes/Player/OptionsMenu/OptionsMenu.js b/src/routes/Player/OptionsMenu/OptionsMenu.js index a310dc66c..0f4110f54 100644 --- a/src/routes/Player/OptionsMenu/OptionsMenu.js +++ b/src/routes/Player/OptionsMenu/OptionsMenu.js @@ -5,11 +5,11 @@ const PropTypes = require('prop-types'); const classnames = require('classnames'); const Icon = require('@stremio/stremio-icons/dom'); const { Button, useToast } = require('stremio/common'); -// const { useServices } = require('stremio/services'); +const { useServices } = require('stremio/services'); const styles = require('./styles'); -const OptionsMenu = ({ className, stream }) => { - // const { core } = useServices(); +const OptionsMenu = ({ className, stream, playbackDevices }) => { + const { core } = useServices(); const toast = useToast(); const streamUrl = React.useMemo(() => { return stream !== null ? @@ -22,6 +22,9 @@ const OptionsMenu = ({ className, stream }) => { : null; }, [stream]); + const externalPlayers = React.useMemo(() => { + return playbackDevices.filter(({ type }) => type === 'external'); + }, [playbackDevices]); const onCopyStreamButtonClick = React.useCallback(() => { if (streamUrl !== null) { navigator.clipboard.writeText(streamUrl) @@ -49,20 +52,20 @@ const OptionsMenu = ({ className, stream }) => { window.open(streamUrl); } }, [streamUrl]); - // const onExternalPlayerButtonClick = React.useCallback(() => { - // if (streamUrl !== null) { - // core.transport.dispatch({ - // action: 'StreamingServer', - // args: { - // action: 'PlayOnDevice', - // args: { - // device: 'vlc', - // source: streamUrl, - // } - // } - // }); - // } - // }, [streamUrl]); + const onExternalPlayRequested = React.useCallback((deviceId) => { + if (streamUrl !== null) { + core.transport.dispatch({ + action: 'StreamingServer', + args: { + action: 'PlayOnDevice', + args: { + device: deviceId, + source: streamUrl, + } + } + }); + } + }, [streamUrl]); const onMouseDown = React.useCallback((event) => { event.nativeEvent.optionsMenuClosePrevented = true; }, []); @@ -76,17 +79,46 @@ const OptionsMenu = ({ className, stream }) => {
Download Video
- {/* */} + { + externalPlayers.map(({ id, name }) => ( + + )) + } ); }; OptionsMenu.propTypes = { className: PropTypes.string, - stream: PropTypes.object + stream: PropTypes.object, + playbackDevices: PropTypes.array +}; + +const ExternalPlayerButton = ({ id, name, disabled, onExternalPlayRequested }) => { + const onClick = React.useCallback(() => { + if (typeof onExternalPlayRequested === 'function') { + onExternalPlayRequested(id); + } + }, [onExternalPlayRequested, id]); + return ( + + ); +}; + +ExternalPlayerButton.propTypes = { + id: PropTypes.string, + name: PropTypes.string, + disabled: PropTypes.bool, + onExternalPlayRequested: PropTypes.func, }; module.exports = OptionsMenu; diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index dacf654b7..fee6e2a02 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -715,6 +715,7 @@ const Player = ({ urlParams, queryParams }) => { : null From 392c1f594e1811ec3caa83a707a0dad9a6fa54bc Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 12 Jan 2023 21:04:17 +0100 Subject: [PATCH 02/10] feat(Player): stop video when opening in external player --- src/routes/Player/Player.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index fee6e2a02..bdc8e6e12 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -23,7 +23,7 @@ const useSettings = require('./useSettings'); const styles = require('./styles'); const Player = ({ urlParams, queryParams }) => { - const { chromecast, shell } = useServices(); + const { chromecast, shell, core } = useServices(); const [forceTranscoding, maxAudioChannels] = React.useMemo(() => { return [ queryParams.has('forceTranscoding'), @@ -427,11 +427,18 @@ const Player = ({ urlParams, queryParams }) => { ); } }; + const onCoreEvent = ({ event }) => { + if (event === 'PlayingOnDevice') { + onPauseRequested(); + } + }; chromecast.on('stateChanged', onChromecastServiceStateChange); + core.transport.on('CoreEvent', onCoreEvent); onChromecastServiceStateChange(); return () => { toast.removeFilter(toastFilter); chromecast.off('stateChanged', onChromecastServiceStateChange); + core.transport.off('CoreEvent', onCoreEvent); if (chromecast.active) { chromecast.transport.off( cast.framework.CastContextEventType.CAST_STATE_CHANGED, From 2d3d55c0bb0aa67703813cb9316bb6cb3af7dcd8 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 12 Jan 2023 21:09:15 +0100 Subject: [PATCH 03/10] fix(OptionsMenu): disable external player buttons if stream is torrent --- src/routes/Player/OptionsMenu/OptionsMenu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Player/OptionsMenu/OptionsMenu.js b/src/routes/Player/OptionsMenu/OptionsMenu.js index 0f4110f54..16b51a1e2 100644 --- a/src/routes/Player/OptionsMenu/OptionsMenu.js +++ b/src/routes/Player/OptionsMenu/OptionsMenu.js @@ -80,7 +80,7 @@ const OptionsMenu = ({ className, stream, playbackDevices }) => {
Download Video
{ - externalPlayers.map(({ id, name }) => ( + !stream.infoHash && externalPlayers.map(({ id, name }) => ( Date: Thu, 12 Jan 2023 21:37:30 +0100 Subject: [PATCH 04/10] refactor(OptionMenu): improve code structure --- .../Player/OptionsMenu/Option/Option.js | 32 +++++++++++ src/routes/Player/OptionsMenu/Option/index.js | 5 ++ .../Player/OptionsMenu/Option/styles.less | 33 ++++++++++++ src/routes/Player/OptionsMenu/OptionsMenu.js | 54 +++++++------------ src/routes/Player/OptionsMenu/styles.less | 34 ------------ 5 files changed, 89 insertions(+), 69 deletions(-) create mode 100644 src/routes/Player/OptionsMenu/Option/Option.js create mode 100644 src/routes/Player/OptionsMenu/Option/index.js create mode 100644 src/routes/Player/OptionsMenu/Option/styles.less diff --git a/src/routes/Player/OptionsMenu/Option/Option.js b/src/routes/Player/OptionsMenu/Option/Option.js new file mode 100644 index 000000000..f0e6b04a8 --- /dev/null +++ b/src/routes/Player/OptionsMenu/Option/Option.js @@ -0,0 +1,32 @@ +// Copyright (C) 2017-2022 Smart code 203358507 + +const React = require('react'); +const PropTypes = require('prop-types'); +const classnames = require('classnames'); +const Icon = require('@stremio/stremio-icons/dom'); +const { Button } = require('stremio/common'); +const styles = require('./styles'); + +const Option = ({ icon, label, playerId, disabled, onClick }) => { + const onButtonClick = React.useCallback(() => { + if (typeof onClick === 'function') { + onClick(playerId); + } + }, [onClick, playerId]); + return ( + + ); +}; + +Option.propTypes = { + icon: PropTypes.string, + label: PropTypes.string, + playerId: PropTypes.string, + disabled: PropTypes.bool, + onClick: PropTypes.func, +}; + +module.exports = Option; diff --git a/src/routes/Player/OptionsMenu/Option/index.js b/src/routes/Player/OptionsMenu/Option/index.js new file mode 100644 index 000000000..2bf2d108d --- /dev/null +++ b/src/routes/Player/OptionsMenu/Option/index.js @@ -0,0 +1,5 @@ +// Copyright (C) 2017-2022 Smart code 203358507 + +const Option = require('./Option'); + +module.exports = Option; diff --git a/src/routes/Player/OptionsMenu/Option/styles.less b/src/routes/Player/OptionsMenu/Option/styles.less new file mode 100644 index 000000000..a3f54650d --- /dev/null +++ b/src/routes/Player/OptionsMenu/Option/styles.less @@ -0,0 +1,33 @@ +// Copyright (C) 2017-2022 Smart code 203358507 + +@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less'; + +.option-container { + display: flex; + flex-direction: row; + align-items: center; + height: 4rem; + + .icon { + flex: none; + width: 1.4rem; + height: 1.4rem; + margin: 1.3rem; + fill: @color-surface-light5-90; + } + + .label { + flex: 1; + max-height: 2.4em; + font-weight: 400; + color: @color-surface-light5-90; + } + + &:hover { + background-color: @color-background-light2; + } + + &:global(.disabled) { + opacity: 0.5; + } +} \ No newline at end of file diff --git a/src/routes/Player/OptionsMenu/OptionsMenu.js b/src/routes/Player/OptionsMenu/OptionsMenu.js index 16b51a1e2..ba3d1f955 100644 --- a/src/routes/Player/OptionsMenu/OptionsMenu.js +++ b/src/routes/Player/OptionsMenu/OptionsMenu.js @@ -3,9 +3,9 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); -const Icon = require('@stremio/stremio-icons/dom'); -const { Button, useToast } = require('stremio/common'); +const { useToast } = require('stremio/common'); const { useServices } = require('stremio/services'); +const Option = require('./Option'); const styles = require('./styles'); const OptionsMenu = ({ className, stream, playbackDevices }) => { @@ -71,22 +71,27 @@ const OptionsMenu = ({ className, stream, playbackDevices }) => { }, []); return (
- - +