diff --git a/src/routes/Player/ControlBar/ControlBar.js b/src/routes/Player/ControlBar/ControlBar.js
index b63c453f4..d8fbdb1a8 100644
--- a/src/routes/Player/ControlBar/ControlBar.js
+++ b/src/routes/Player/ControlBar/ControlBar.js
@@ -29,6 +29,7 @@ const ControlBar = ({
onSeekRequested,
onToggleSubtitlesMenu,
onToggleInfoMenu,
+ onToggleOptionsMenu,
...props
}) => {
const { chromecast } = useServices();
@@ -40,6 +41,9 @@ const ControlBar = ({
const onInfoButtonMouseDown = React.useCallback((event) => {
event.nativeEvent.infoMenuClosePrevented = true;
}, []);
+ const onOptionsButtonMouseDown = React.useCallback((event) => {
+ event.nativeEvent.optionsMenuClosePrevented = true;
+ }, []);
const onPlayPauseButtonClick = React.useCallback(() => {
if (paused) {
if (typeof onPlayRequested === 'function') {
@@ -72,6 +76,11 @@ const ControlBar = ({
onToggleInfoMenu();
}
}, [onToggleInfoMenu]);
+ const onOptionsButtonClick = React.useCallback(() => {
+ if (typeof onToggleOptionsMenu === 'function') {
+ onToggleOptionsMenu();
+ }
+ }, [onToggleOptionsMenu]);
const onChromecastButtonClick = React.useCallback(() => {
chromecast.transport.requestSession();
}, []);
@@ -133,6 +142,9 @@ const ControlBar = ({
+
@@ -156,7 +168,8 @@ ControlBar.propTypes = {
onVolumeChangeRequested: PropTypes.func,
onSeekRequested: PropTypes.func,
onToggleSubtitlesMenu: PropTypes.func,
- onToggleInfoMenu: PropTypes.func
+ onToggleInfoMenu: PropTypes.func,
+ onToggleOptionsMenu: PropTypes.func
};
module.exports = ControlBar;
diff --git a/src/routes/Player/OptionsMenu/OptionsMenu.js b/src/routes/Player/OptionsMenu/OptionsMenu.js
new file mode 100644
index 000000000..83ba37e9c
--- /dev/null
+++ b/src/routes/Player/OptionsMenu/OptionsMenu.js
@@ -0,0 +1,86 @@
+// 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, useStreamingServer, useToast } = require('stremio/common');
+const styles = require('./styles');
+
+const OptionsMenu = ({ className, stream }) => {
+ const streamingServer = useStreamingServer();
+ const toast = useToast();
+ const streamUrl = React.useMemo(() => {
+ return stream !== null ?
+ typeof stream.url === 'string' ?
+ stream.url
+ :
+ typeof stream.infoHash === 'string' &&
+ typeof stream.fileIdx === 'number' &&
+ typeof streamingServer.selected === 'object' &&
+ typeof streamingServer.selected.transportUrl === 'string' ?
+ `${streamingServer.selected.transportUrl}${stream.infoHash}/${stream.fileIdx}`
+ :
+ null
+ :
+ null;
+ }, [stream, streamingServer]);
+ const onCopyStreamButtonClick = React.useCallback(() => {
+ if (streamUrl !== null) {
+ navigator.clipboard.writeText(streamUrl)
+ .then(() => {
+ toast.show({
+ type: 'success',
+ title: 'Copied',
+ message: 'Stream link was copied to your clipboard',
+ timeout: 3000
+ });
+ })
+ .catch((e) => {
+ console.error(e);
+ toast.show({
+ type: 'error',
+ title: 'Error',
+ message: 'Failed to copy stream link. Try to enable your browser permission.',
+ timeout: 3000
+ });
+ });
+ }
+ }, [streamUrl]);
+ const onDownloadVideoButtonClick = React.useCallback(() => {
+ if (streamUrl !== null) {
+ window.open(streamUrl);
+ }
+ }, [streamUrl]);
+ const onExternalPlayerButtonClick = React.useCallback(() => {
+ if (streamUrl !== null) {
+ window.open(`vlc://${encodeURIComponent(streamUrl)}`);
+ }
+ }, [streamUrl]);
+ const onMouseDown = React.useCallback((event) => {
+ event.nativeEvent.optionsMenuClosePrevented = true;
+ }, []);
+ return (
+
+
+
+
+
+ );
+};
+
+OptionsMenu.propTypes = {
+ className: PropTypes.string,
+ stream: PropTypes.object
+};
+
+module.exports = OptionsMenu;
diff --git a/src/routes/Player/OptionsMenu/index.js b/src/routes/Player/OptionsMenu/index.js
new file mode 100644
index 000000000..069b002b3
--- /dev/null
+++ b/src/routes/Player/OptionsMenu/index.js
@@ -0,0 +1,5 @@
+// Copyright (C) 2017-2022 Smart code 203358507
+
+const OptionsMenu = require('./OptionsMenu');
+
+module.exports = OptionsMenu;
diff --git a/src/routes/Player/OptionsMenu/styles.less b/src/routes/Player/OptionsMenu/styles.less
new file mode 100644
index 000000000..54ebd0368
--- /dev/null
+++ b/src/routes/Player/OptionsMenu/styles.less
@@ -0,0 +1,37 @@
+// Copyright (C) 2017-2022 Smart code 203358507
+
+@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
+
+.options-menu-container {
+ width: 15rem;
+
+ .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/Player.js b/src/routes/Player/Player.js
index b16334c49..07914b63d 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 OptionsMenu = require('./OptionsMenu');
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 [optionsMenuOpen, , closeOptionsMenu, toggleOptionsMenu] = 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.optionsMenuClosePrevented) {
+ closeOptionsMenu();
+ }
}, []);
const onContainerMouseMove = React.useCallback((event) => {
setImmersed(false);
@@ -399,6 +404,7 @@ const Player = ({ urlParams, queryParams }) => {
}
case 'KeyS': {
closeInfoMenu();
+ closeOptionsMenu();
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)) {
@@ -409,6 +415,7 @@ const Player = ({ urlParams, queryParams }) => {
}
case 'KeyI': {
closeSubtitlesMenu();
+ closeOptionsMenu();
if (player.metaItem !== null && player.metaItem.type === 'Ready') {
toggleInfoMenu();
}
@@ -418,6 +425,7 @@ const Player = ({ urlParams, queryParams }) => {
case 'Escape': {
closeSubtitlesMenu();
closeInfoMenu();
+ closeOptionsMenu();
break;
}
}
@@ -437,7 +445,7 @@ const Player = ({ urlParams, queryParams }) => {
};
}, []);
return (
- {
onSeekRequested={onSeekRequested}
onToggleSubtitlesMenu={toggleSubtitlesMenu}
onToggleInfoMenu={toggleInfoMenu}
+ onToggleOptionsMenu={toggleOptionsMenu}
onMouseMove={onBarMouseMove}
onMouseOver={onBarMouseMove}
/>
@@ -554,6 +563,15 @@ const Player = ({ urlParams, queryParams }) => {
:
null
}
+ {
+ optionsMenuOpen ?
+
+ :
+ null
+ }
);
};