mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-03-11 21:27:05 +00:00
Merge branch 'development' of https://github.com/Stremio/stremio-web into feat/addons-configure
This commit is contained in:
commit
50a1955afd
8 changed files with 159 additions and 78 deletions
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -91,6 +91,10 @@
|
|||
padding: 0 0.5rem;
|
||||
overflow: visible;
|
||||
|
||||
.volume-slider {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.control-bar-buttons-menu-button {
|
||||
display: flex;
|
||||
}
|
||||
|
|
|
|||
32
src/routes/Player/OptionsMenu/Option/Option.js
Normal file
32
src/routes/Player/OptionsMenu/Option/Option.js
Normal file
|
|
@ -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, deviceId, disabled, onClick }) => {
|
||||
const onButtonClick = React.useCallback(() => {
|
||||
if (typeof onClick === 'function') {
|
||||
onClick(deviceId);
|
||||
}
|
||||
}, [onClick, deviceId]);
|
||||
return (
|
||||
<Button className={classnames(styles['option-container'], { 'disabled': disabled })} disabled={disabled} onClick={onButtonClick}>
|
||||
<Icon className={styles['icon']} icon={icon} />
|
||||
<div className={styles['label']}>{ label }</div>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
Option.propTypes = {
|
||||
icon: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
deviceId: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
|
||||
module.exports = Option;
|
||||
5
src/routes/Player/OptionsMenu/Option/index.js
Normal file
5
src/routes/Player/OptionsMenu/Option/index.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// Copyright (C) 2017-2022 Smart code 203358507
|
||||
|
||||
const Option = require('./Option');
|
||||
|
||||
module.exports = Option;
|
||||
33
src/routes/Player/OptionsMenu/Option/styles.less
Normal file
33
src/routes/Player/OptionsMenu/Option/styles.less
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,28 +3,28 @@
|
|||
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 { useServices } = require('stremio/services');
|
||||
const { useToast } = require('stremio/common');
|
||||
const { useServices } = require('stremio/services');
|
||||
const Option = require('./Option');
|
||||
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(() => {
|
||||
const [streamingUrl, downloadUrl] = React.useMemo(() => {
|
||||
return stream !== null ?
|
||||
stream.deepLinks &&
|
||||
stream.deepLinks.externalPlayer &&
|
||||
typeof stream.deepLinks.externalPlayer.download === 'string' ?
|
||||
stream.deepLinks.externalPlayer.download
|
||||
:
|
||||
null
|
||||
[stream.deepLinks.externalPlayer.streaming, stream.deepLinks.externalPlayer.download]
|
||||
:
|
||||
null;
|
||||
[null, null];
|
||||
}, [stream]);
|
||||
const externalDevices = React.useMemo(() => {
|
||||
return playbackDevices.filter(({ type }) => type === 'external');
|
||||
}, [playbackDevices]);
|
||||
const onCopyStreamButtonClick = React.useCallback(() => {
|
||||
if (streamUrl !== null) {
|
||||
navigator.clipboard.writeText(streamUrl)
|
||||
if (streamingUrl || downloadUrl) {
|
||||
navigator.clipboard.writeText(streamingUrl || downloadUrl)
|
||||
.then(() => {
|
||||
toast.show({
|
||||
type: 'success',
|
||||
|
|
@ -38,55 +38,78 @@ const OptionsMenu = ({ className, stream }) => {
|
|||
toast.show({
|
||||
type: 'error',
|
||||
title: 'Error',
|
||||
message: `Failed to copy stream link: ${streamUrl}`,
|
||||
message: `Failed to copy stream link: ${streamingUrl || downloadUrl}`,
|
||||
timeout: 3000
|
||||
});
|
||||
});
|
||||
}
|
||||
}, [streamUrl]);
|
||||
}, [streamingUrl, downloadUrl]);
|
||||
const onDownloadVideoButtonClick = React.useCallback(() => {
|
||||
if (streamUrl !== null) {
|
||||
window.open(streamUrl);
|
||||
if (streamingUrl || downloadUrl) {
|
||||
window.open(streamingUrl || downloadUrl);
|
||||
}
|
||||
}, [streamUrl]);
|
||||
// const onExternalPlayerButtonClick = React.useCallback(() => {
|
||||
// if (streamUrl !== null) {
|
||||
// core.transport.dispatch({
|
||||
// action: 'StreamingServer',
|
||||
// args: {
|
||||
// action: 'PlayOnDevice',
|
||||
// args: {
|
||||
// device: 'vlc',
|
||||
// source: streamUrl,
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }, [streamUrl]);
|
||||
}, [streamingUrl, downloadUrl]);
|
||||
const onExternalDeviceRequested = React.useCallback((deviceId) => {
|
||||
if (streamingUrl) {
|
||||
core.transport.dispatch({
|
||||
action: 'StreamingServer',
|
||||
args: {
|
||||
action: 'PlayOnDevice',
|
||||
args: {
|
||||
device: deviceId,
|
||||
source: streamingUrl,
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [streamingUrl]);
|
||||
const onMouseDown = React.useCallback((event) => {
|
||||
event.nativeEvent.optionsMenuClosePrevented = true;
|
||||
}, []);
|
||||
return (
|
||||
<div className={classnames(className, styles['options-menu-container'])} onMouseDown={onMouseDown}>
|
||||
<Button className={classnames(styles['option-container'], { 'disabled': stream === null })} disabled={stream === null} onClick={onCopyStreamButtonClick}>
|
||||
<Icon className={styles['icon']} icon={'ic_link'} />
|
||||
<div className={styles['label']}>Copy Stream Link</div>
|
||||
</Button>
|
||||
<Button className={classnames(styles['option-container'], { 'disabled': stream === null })} disabled={stream === null}onClick={onDownloadVideoButtonClick}>
|
||||
<Icon className={styles['icon']} icon={'ic_downloads'} />
|
||||
<div className={styles['label']}>Download Video</div>
|
||||
</Button>
|
||||
{/* <Button className={classnames(styles['option-container'], { 'disabled': stream === null })} disabled={stream === null} onClick={onExternalPlayerButtonClick}>
|
||||
<Icon className={styles['icon']} icon={'ic_vlc'} />
|
||||
<div className={styles['label']}>Play in External Player</div>
|
||||
</Button> */}
|
||||
{
|
||||
streamingUrl || downloadUrl ?
|
||||
<Option
|
||||
icon={'ic_link'}
|
||||
label={'Copy Stream Link'}
|
||||
disabled={stream === null}
|
||||
onClick={onCopyStreamButtonClick}
|
||||
/>
|
||||
:
|
||||
null
|
||||
}
|
||||
{
|
||||
streamingUrl || downloadUrl ?
|
||||
<Option
|
||||
icon={'ic_downloads'}
|
||||
label={'Download Video'}
|
||||
disabled={stream === null}
|
||||
onClick={onDownloadVideoButtonClick}
|
||||
/>
|
||||
:
|
||||
null
|
||||
}
|
||||
{
|
||||
streamingUrl && externalDevices.map(({ id, name }) => (
|
||||
<Option
|
||||
key={id}
|
||||
icon={'ic_vlc'}
|
||||
label={`Play in ${name}`}
|
||||
deviceId={id}
|
||||
disabled={stream === null}
|
||||
onClick={onExternalDeviceRequested}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
OptionsMenu.propTypes = {
|
||||
className: PropTypes.string,
|
||||
stream: PropTypes.object
|
||||
stream: PropTypes.object,
|
||||
playbackDevices: PropTypes.array
|
||||
};
|
||||
|
||||
module.exports = OptionsMenu;
|
||||
|
|
|
|||
|
|
@ -1,37 +1,5 @@
|
|||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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'),
|
||||
|
|
@ -428,11 +428,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,
|
||||
|
|
@ -716,6 +723,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
<OptionsMenu
|
||||
className={classnames(styles['layer'], styles['menu-layer'])}
|
||||
stream={player.selected.stream}
|
||||
playbackDevices={streamingServer.playbackDevices !== null && streamingServer.playbackDevices.type === 'Ready' ? streamingServer.playbackDevices.content : []}
|
||||
/>
|
||||
:
|
||||
null
|
||||
|
|
|
|||
Loading…
Reference in a new issue