refactor: improve logic related to open player streams

This commit is contained in:
Tim 2023-06-01 00:56:39 +02:00
parent 81de9803da
commit 4ce7ad21d5
6 changed files with 60 additions and 129 deletions

View file

@ -39,6 +39,15 @@ const ICON_FOR_TYPE = new Map([
['podcast', 'ic_podcast'],
['other', 'ic_movies'],
]);
const EXTERNAL_PLAYERS = [
{ id: null, name: 'EXTERNAL_PLAYER_DISABLED' },
{ id: 'choose', name: 'EXTERNAL_PLAYER_ALLOW_CHOOSING', platforms: ['android'] },
{ id: 'vlc', name: 'VLC', platforms: ['android', 'ios', 'windows', 'macos', 'linux'] },
{ id: 'outplayer', name: 'Outplayer', platforms: ['ios'] },
{ id: 'infuse', name: 'Infuse', platforms: ['ios'] },
{ id: 'justplayer', name: 'Just Player', platforms: ['android'] },
{ id: 'mxplayer', name: 'MX Player', platforms: ['android'] },
];
module.exports = {
CHROMECAST_RECEIVER_APP_ID,
@ -55,5 +64,6 @@ module.exports = {
SHARE_LINK_CATEGORY,
WRITERS_LINK_CATEGORY,
TYPE_PRIORITIES,
ICON_FOR_TYPE
ICON_FOR_TYPE,
EXTERNAL_PLAYERS,
};

View file

@ -1,30 +0,0 @@
// Copyright (C) 2017-2022 Smart code 203358507
const platform = require('./platform');
let options = [{ label: 'EXTERNAL_PLAYER_DISABLED', value: 'internal' }];
if (platform.name === 'ios') {
options = options.concat([
{ label: 'VLC', value: 'vlc' },
{ label: 'Outplayer', value: 'outplayer' },
{ label: 'Infuse', value: 'infuse' }
]);
} else if (platform.name === 'android') {
options = options.concat([
{ label: 'EXTERNAL_PLAYER_ALLOW_CHOOSING', value: 'choose' },
{ label: 'VLC', value: 'vlc' },
{ label: 'Just Player', value: 'justplayer' },
{ label: 'MX Player', value: 'mxplayer' }
]);
} else if (['windows', 'macos', 'linux'].includes(platform.name)) {
options = options.concat([
{ label: 'VLC', value: 'vlc' }
]);
} else {
options = options.concat([
{ label: 'M3U Playlist', value: 'm3u' }
]);
}
module.exports = options;

View file

@ -41,7 +41,6 @@ const useProfile = require('./useProfile');
const useStreamingServer = require('./useStreamingServer');
const useTorrent = require('./useTorrent');
const platform = require('./platform');
const externalPlayerOptions = require('./externalPlayerOptions');
module.exports = {
AddonDetailsModal,
@ -88,5 +87,4 @@ module.exports = {
useStreamingServer,
useTorrent,
platform,
externalPlayerOptions,
};

View file

@ -4,64 +4,45 @@ const React = require('react');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const Icon = require('@stremio/stremio-icons/dom');
const { Button, Image, PlayIconCircleCentered, useProfile, platform, useStreamingServer, useToast } = require('stremio/common');
const { useServices } = require('stremio/services');
const { Button, Image, PlayIconCircleCentered, platform, useProfile, useToast } = require('stremio/common');
const StreamPlaceholder = require('./StreamPlaceholder');
const styles = require('./styles');
const Stream = ({ className, addonName, name, description, thumbnail, progress, deepLinks, ...props }) => {
const profile = useProfile();
const streamingServer = useStreamingServer();
const Stream = ({ className, addonName, name, description, thumbnail, progress, deepLinks }) => {
const { core } = useServices();
const profile = useProfile();
const toast = useToast();
const href = React.useMemo(() => {
const haveStreamingServer = streamingServer.settings !== null && streamingServer.settings.type === 'Ready';
return deepLinks ?
profile.settings.playerType && profile.settings.playerType !== 'internal' ?
platform.isMobile() || !haveStreamingServer ?
(deepLinks.externalPlayer.openPlayer || {})[platform.name] || deepLinks.externalPlayer.href
: null
:
typeof deepLinks.player === 'string' ?
deepLinks.player
:
null
:
null;
}, [deepLinks, profile, streamingServer]);
const onClick = React.useCallback((e) => {
if (href === null) {
// link does not lead to the player, it is expected to
// open with local video player through the streaming server
core.transport.dispatch({
action: 'StreamingServer',
args: {
action: 'PlayOnDevice',
const onClick = React.useCallback(() => {
if (deepLinks.externalPlayer.openPlayer) {
if (platform.isMobile() && deepLinks.externalPlayer.openPlayer[platform.name]) {
window.location = deepLinks.externalPlayer.openPlayer[platform.name];
toast.show({
type: 'success',
title: `Stream opened in ${profile.settings.playerType}`,
timeout: 4000
});
} else if (typeof deepLinks.externalPlayer.streaming === 'string') {
core.transport.dispatch({
action: 'StreamingServer',
args: {
device: 'vlc',
source: deepLinks.externalPlayer.streaming
action: 'PlayOnDevice',
args: {
device: profile.settings.playerType,
source: deepLinks.externalPlayer.streaming,
}
}
}
});
} else if (profile.settings.playerType === 'external') {
toast.show({
type: 'success',
title: 'Stream opened in external player',
timeout: 4000
});
});
}
} else if (typeof deepLinks.player === 'string') {
window.location = deepLinks.player;
}
props.onClick(e);
}, [href, deepLinks, props.onClick, profile, toast]);
const forceDownload = React.useMemo(() => {
// we only do this in one case to force the download
// of a M3U playlist generated in the browser
return href === deepLinks.externalPlayer.href ? deepLinks.externalPlayer.fileName : false;
}, [href]);
}, [deepLinks, profile.settings]);
const renderThumbnailFallback = React.useCallback(() => (
<Icon className={styles['placeholder-icon']} icon={'ic_broken_link'} />
), []);
return (
<Button href={href} download={forceDownload} {...props} onClick={onClick} className={classnames(className, styles['stream-container'])} title={addonName}>
<Button className={classnames(className, styles['stream-container'])} title={addonName} onClick={onClick}>
{
typeof thumbnail === 'string' && thumbnail.length > 0 ?
<div className={styles['thumbnail-container']} title={name || addonName}>
@ -107,52 +88,18 @@ Stream.propTypes = {
fileName: PropTypes.string,
streaming: PropTypes.string,
openPlayer: PropTypes.shape({
choose: {
ios: PropTypes.string,
android: PropTypes.string,
windows: PropTypes.string,
macos: PropTypes.string,
linux: PropTypes.string
},
vlc: {
ios: PropTypes.string,
android: PropTypes.string,
windows: PropTypes.string,
macos: PropTypes.string,
linux: PropTypes.string
},
outplayer: {
ios: PropTypes.string,
android: PropTypes.string,
windows: PropTypes.string,
macos: PropTypes.string,
linux: PropTypes.string
},
infuse: {
ios: PropTypes.string,
android: PropTypes.string,
windows: PropTypes.string,
macos: PropTypes.string,
linux: PropTypes.string
},
justplayer: {
ios: PropTypes.string,
android: PropTypes.string,
windows: PropTypes.string,
macos: PropTypes.string,
linux: PropTypes.string
},
mxplayer: {
ios: PropTypes.string,
android: PropTypes.string,
windows: PropTypes.string,
macos: PropTypes.string,
linux: PropTypes.string
},
ios: PropTypes.string,
android: PropTypes.string,
windows: PropTypes.string,
macos: PropTypes.string,
linux: PropTypes.string,
tizen: PropTypes.string,
webos: PropTypes.string,
chromeos: PropTypes.string,
roku: PropTypes.string,
})
})
}),
onClick: PropTypes.func
};
module.exports = Stream;

View file

@ -111,7 +111,6 @@ const StreamsList = ({ className, ...props }) => {
thumbnail={stream.thumbnail}
progress={stream.progress}
deepLinks={stream.deepLinks}
onClick={stream.onClick}
/>
))}
</div>

View file

@ -3,7 +3,8 @@
const React = require('react');
const { useTranslation } = require('react-i18next');
const { useServices } = require('stremio/services');
const { CONSTANTS, interfaceLanguages, languageNames, externalPlayerOptions } = require('stremio/common');
const { CONSTANTS, interfaceLanguages, languageNames, platform } = require('stremio/common');
const { EXTERNAL_PLAYERS } = require('stremio/common/CONSTANTS');
const useProfileSettingsInputs = (profile) => {
const { t } = useTranslation();
@ -158,11 +159,17 @@ const useProfileSettingsInputs = (profile) => {
}
}), [profile.settings]);
const playInExternalPlayerSelect = React.useMemo(() => ({
options: externalPlayerOptions.map((opt) => {
opt.label = t(opt.label);
return opt;
}),
selected: [`${profile.settings.playerType || 'internal'}`],
options: EXTERNAL_PLAYERS
.filter(({ platforms }) => !platforms || platforms.includes(platform.name))
.map(({ id, name }) => ({
value: id === null ? 'none' : id,
label: t(name, { defaultValue: name }),
})),
selected: [profile.settings.playerType],
renderLabelText: () => {
const externalPlayer = EXTERNAL_PLAYERS.find(({ id }) => id === profile.settings.playerType);
return externalPlayer ? t(externalPlayer.name, { defaultValue: externalPlayer.name }) : profile.settings.playerType;
},
onSelect: (event) => {
core.transport.dispatch({
action: 'Ctx',
@ -170,7 +177,7 @@ const useProfileSettingsInputs = (profile) => {
action: 'UpdateSettings',
args: {
...profile.settings,
playerType: event.value
playerType: event.value === 'none' ? null : event.value,
}
}
});