stremio-web/src/routes/Player/OptionsMenu/OptionsMenu.js
Tim 6dfd0fcbde
Some checks failed
Build / build (push) Has been cancelled
refactor(player): momoize menus
2026-04-02 09:56:21 +02:00

180 lines
6.4 KiB
JavaScript

// Copyright (C) 2017-2023 Smart code 203358507
const React = require('react');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const { useTranslation } = require('react-i18next');
const { usePlatform, useToast } = require('stremio/common');
const { useServices } = require('stremio/services');
const Option = require('./Option');
const styles = require('./styles');
const OptionsMenu = React.memo(React.forwardRef(({ className, stream, playbackDevices, extraSubtitlesTracks, selectedExtraSubtitlesTrackId }, ref) => {
const { t } = useTranslation();
const { core } = useServices();
const platform = usePlatform();
const toast = useToast();
const [streamingUrl, downloadUrl, magnetUrl] = React.useMemo(() => {
return stream !== null ?
stream.deepLinks &&
stream.deepLinks.externalPlayer &&
[
stream.deepLinks.externalPlayer.streaming,
stream.deepLinks.externalPlayer.download,
stream.deepLinks.externalPlayer.magnet,
]
:
[null, null, null];
}, [stream]);
const externalDevices = React.useMemo(() => {
return playbackDevices.filter(({ type }) => type === 'external');
}, [playbackDevices]);
const subtitlesTrackUrl = React.useMemo(() => {
const track = extraSubtitlesTracks?.find(({ id }) => id === selectedExtraSubtitlesTrackId);
return track?.fallbackUrl ?? track?.url ?? null;
}, [extraSubtitlesTracks, selectedExtraSubtitlesTrackId]);
const onCopyStreamButtonClick = React.useCallback(() => {
if (streamingUrl || downloadUrl) {
navigator.clipboard.writeText(streamingUrl || downloadUrl)
.then(() => {
toast.show({
type: 'success',
title: 'Copied',
message: t('PLAYER_COPY_STREAM_SUCCESS'),
timeout: 3000
});
})
.catch((e) => {
console.error(e);
toast.show({
type: 'error',
title: t('ERROR'),
message: `${t('PLAYER_COPY_STREAM_ERROR')}: ${streamingUrl || downloadUrl}`,
timeout: 3000
});
});
}
}, [streamingUrl, downloadUrl]);
const onCopyMagnetButtonClick = React.useCallback(() => {
if (magnetUrl) {
navigator.clipboard.writeText(magnetUrl)
.then(() => {
toast.show({
type: 'success',
title: 'Copied',
message: t('PLAYER_COPY_MAGNET_LINK_SUCCESS'),
timeout: 3000
});
})
.catch((e) => {
console.error(e);
toast.show({
type: 'error',
title: t('Error'),
message: `${t('PLAYER_COPY_MAGNET_LINK_ERROR')}: ${magnetUrl}`,
timeout: 3000
});
});
}
}, [magnetUrl]);
const onDownloadVideoButtonClick = React.useCallback(() => {
if (downloadUrl) {
platform.openExternal(downloadUrl);
}
}, [downloadUrl]);
const onDownloadSubtitlesClick = React.useCallback(() => {
subtitlesTrackUrl && platform.openExternal(subtitlesTrackUrl);
}, [subtitlesTrackUrl]);
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 ref={ref} className={classnames(className, styles['options-menu-container'])} onMouseDown={onMouseDown}>
{
streamingUrl || downloadUrl ?
<Option
icon={'link'}
label={t('CTX_COPY_STREAM_LINK')}
disabled={stream === null}
onClick={onCopyStreamButtonClick}
/>
:
null
}
{
magnetUrl ?
<Option
icon={'magnet-link'}
label={t('CTX_COPY_MAGNET_LINK')}
disabled={stream === null}
onClick={onCopyMagnetButtonClick}
/>
:
null
}
{
downloadUrl ?
<Option
icon={'download'}
label={t('CTX_DOWNLOAD_VIDEO')}
disabled={stream === null}
onClick={onDownloadVideoButtonClick}
/>
:
null
}
{
subtitlesTrackUrl ?
<Option
icon={'download'}
label={t('CTX_DOWNLOAD_SUBS')}
disabled={stream === null}
onClick={onDownloadSubtitlesClick}
/>
:
null
}
{
streamingUrl && externalDevices.map(({ id, name }) => (
<Option
key={id}
icon={'vlc'}
label={t('PLAYER_PLAY_IN', { device: name })}
deviceId={id}
disabled={stream === null}
onClick={onExternalDeviceRequested}
/>
))
}
</div>
);
}));
OptionsMenu.propTypes = {
className: PropTypes.string,
stream: PropTypes.object,
playbackDevices: PropTypes.array,
extraSubtitlesTracks: PropTypes.array,
selectedExtraSubtitlesTrackId: PropTypes.string,
};
module.exports = OptionsMenu;