Merge branch 'development' of https://github.com/Stremio/stremio-web into feat/addons-configure

This commit is contained in:
Tim 2023-05-23 22:20:08 +02:00
commit 50a1955afd
8 changed files with 159 additions and 78 deletions

View file

@ -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) => {

View file

@ -91,6 +91,10 @@
padding: 0 0.5rem;
overflow: visible;
.volume-slider {
display: none;
}
.control-bar-buttons-menu-button {
display: flex;
}

View 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;

View file

@ -0,0 +1,5 @@
// Copyright (C) 2017-2022 Smart code 203358507
const Option = require('./Option');
module.exports = Option;

View 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;
}
}

View file

@ -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;

View file

@ -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;
}
}
}

View file

@ -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