diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index c1da1247e..000000000 --- a/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -screenshots/* -screenshots* diff --git a/package-lock.json b/package-lock.json index 50ef9df6f..12ca92c87 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "stremio", - "version": "5.0.0-beta.0", + "version": "5.0.0-beta.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "stremio", - "version": "5.0.0-beta.0", + "version": "5.0.0-beta.4", "license": "gpl-2.0", "dependencies": { "@babel/runtime": "7.16.0", @@ -35,8 +35,8 @@ "react-focus-lock": "2.9.1", "react-i18next": "^12.1.1", "react-is": "18.2.0", - "spatial-navigation-polyfill": "git+ssh://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", - "stremio-translations": "git+ssh://git@github.com/Stremio/stremio-translations.git#8e30d9961402a7389f1a6209d9dab5592f32c9d7", + "spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", + "stremio-translations": "github:Stremio/stremio-translations#13c8241ca262541813ce0e2df4ff3e289fbd391b", "url": "0.11.0", "use-long-press": "^3.1.5" }, @@ -13523,7 +13523,7 @@ }, "node_modules/spatial-navigation-polyfill": { "version": "1.2.0", - "resolved": "git+https://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", + "resolved": "git+ssh://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", "integrity": "sha512-zREQWdiN2VSugD90lq0uBsSBjT/YtytCijN83V6LEJTRmAe/fMYUtjUtYr7UvBrSPxfnSM54BNq9r2kKFTJvlQ==", "license": "MIT" }, @@ -13600,9 +13600,8 @@ } }, "node_modules/stremio-translations": { - "version": "1.44.3", - "resolved": "git+ssh://git@github.com/Stremio/stremio-translations.git#8e30d9961402a7389f1a6209d9dab5592f32c9d7", - "integrity": "sha512-E8UqbRzVQzDFQvKLTztrtA2V8xL8JeYfi+hI9IWx9kwIMQ34no2hxuualXH7A4U0s8xlM6BQW4bcQlVKwflhEg==", + "version": "1.44.4", + "resolved": "git+ssh://git@github.com/Stremio/stremio-translations.git#13c8241ca262541813ce0e2df4ff3e289fbd391b", "license": "MIT" }, "node_modules/string_decoder": { @@ -25940,9 +25939,9 @@ "dev": true }, "spatial-navigation-polyfill": { - "version": "git+https://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", + "version": "git+ssh://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", "integrity": "sha512-zREQWdiN2VSugD90lq0uBsSBjT/YtytCijN83V6LEJTRmAe/fMYUtjUtYr7UvBrSPxfnSM54BNq9r2kKFTJvlQ==", - "from": "spatial-navigation-polyfill@git+ssh://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6" + "from": "spatial-navigation-polyfill@github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6" }, "spdy": { "version": "4.0.2", @@ -26007,9 +26006,8 @@ "dev": true }, "stremio-translations": { - "version": "git+ssh://git@github.com/Stremio/stremio-translations.git#8e30d9961402a7389f1a6209d9dab5592f32c9d7", - "integrity": "sha512-E8UqbRzVQzDFQvKLTztrtA2V8xL8JeYfi+hI9IWx9kwIMQ34no2hxuualXH7A4U0s8xlM6BQW4bcQlVKwflhEg==", - "from": "stremio-translations@git+ssh://git@github.com/Stremio/stremio-translations.git#8e30d9961402a7389f1a6209d9dab5592f32c9d7" + "version": "git+ssh://git@github.com/Stremio/stremio-translations.git#13c8241ca262541813ce0e2df4ff3e289fbd391b", + "from": "stremio-translations@github:Stremio/stremio-translations#13c8241ca262541813ce0e2df4ff3e289fbd391b" }, "string_decoder": { "version": "1.1.1", diff --git a/package.json b/package.json index 2ab99c401..d4b310ada 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "stremio", "displayName": "Stremio", - "version": "5.0.0-beta.0", + "version": "5.0.0-beta.4", "author": "Smart Code OOD", "private": true, "license": "gpl-2.0", @@ -38,8 +38,8 @@ "react-focus-lock": "2.9.1", "react-i18next": "^12.1.1", "react-is": "18.2.0", - "spatial-navigation-polyfill": "git+ssh://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", - "stremio-translations": "git+ssh://git@github.com/Stremio/stremio-translations.git#8e30d9961402a7389f1a6209d9dab5592f32c9d7", + "spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", + "stremio-translations": "github:Stremio/stremio-translations#13c8241ca262541813ce0e2df4ff3e289fbd391b", "url": "0.11.0", "use-long-press": "^3.1.5" }, diff --git a/src/App/App.js b/src/App/App.js index de443d9d4..d6e92fbbb 100644 --- a/src/App/App.js +++ b/src/App/App.js @@ -146,8 +146,10 @@ const App = () => { .catch((e) => console.error(e)); } return () => { - window.removeEventListener('focus', onWindowFocus); - services.core.transport.off('CoreEvent', onCoreEvent); + if (services.core.active) { + window.removeEventListener('focus', onWindowFocus); + services.core.transport.off('CoreEvent', onCoreEvent); + } }; }, [initialized]); return ( diff --git a/src/App/ErrorDialog/ErrorDialog.js b/src/App/ErrorDialog/ErrorDialog.js index eb0335d9f..8429bb340 100644 --- a/src/App/ErrorDialog/ErrorDialog.js +++ b/src/App/ErrorDialog/ErrorDialog.js @@ -1,12 +1,15 @@ // Copyright (C) 2017-2023 Smart code 203358507 const React = require('react'); +const { useTranslation } = require('react-i18next'); const PropTypes = require('prop-types'); const classnames = require('classnames'); const { Button, Image } = require('stremio/common'); const styles = require('./styles'); const ErrorDialog = ({ className }) => { + const { t } = useTranslation(); + const [dataCleared, setDataCleared] = React.useState(false); const reload = React.useCallback(() => { window.location.reload(); @@ -22,13 +25,19 @@ const ErrorDialog = ({ className }) => { src={require('/images/empty.png')} alt={' '} /> -
Something went wrong!
+
+ { t('GENERIC_ERROR_MESSAGE') } +
- -
diff --git a/src/App/ErrorDialog/styles.less b/src/App/ErrorDialog/styles.less index 01f6634b2..c3f7813d4 100644 --- a/src/App/ErrorDialog/styles.less +++ b/src/App/ErrorDialog/styles.less @@ -7,12 +7,12 @@ flex-direction: column; align-items: center; justify-content: center; + gap: 1rem; .error-image { flex: none; width: 12rem; height: 12rem; - margin-bottom: 1rem; object-fit: contain; object-position: center; opacity: 0.9; @@ -24,7 +24,7 @@ font-size: 2rem; max-height: 3.6em; text-align: center; - color: @color-surface-light5-90; + color: var(--primary-foreground-color); } .buttons-container { @@ -36,6 +36,8 @@ flex-wrap: wrap; align-items: center; justify-content: center; + gap: 1.5rem; + margin-top: 1rem; .button-container { flex-grow: 0; @@ -45,18 +47,23 @@ flex-direction: row; align-items: center; justify-content: center; - margin: 2rem 1rem 0; - padding: 0 1rem; + padding: 0 2.5rem; min-width: 8rem; - height: 3rem; - background-color: @color-accent3; + height: 3.5rem; + border-radius: 3.5rem; + background-color: var(--overlay-color); &:hover { - background-color: @color-accent3-light1; + outline: var(--focus-outline-size) solid var(--primary-foreground-color); + background-color: transparent; + } + + &:active { + outline: none; } &:global(.disabled) { - background-color: @color-surface-dark5; + opacity: 0.3; } .label { @@ -67,7 +74,7 @@ font-size: 1.1rem; font-weight: 500; text-align: center; - color: @color-surface-light5-90; + color: var(--primary-foreground-color); } } } diff --git a/src/App/styles.less b/src/App/styles.less index 64177ad65..95566c9a8 100644 --- a/src/App/styles.less +++ b/src/App/styles.less @@ -144,7 +144,6 @@ html { .loader-container, .error-container { width: 100%; height: 100%; - background-color: @color-background-dark2; } } } diff --git a/src/common/CONSTANTS.js b/src/common/CONSTANTS.js index 4cc5a474f..50bc770c9 100644 --- a/src/common/CONSTANTS.js +++ b/src/common/CONSTANTS.js @@ -40,6 +40,49 @@ const ICON_FOR_TYPE = new Map([ ['other', 'movies'], ]); +const EXTERNAL_PLAYERS = [ + { + label: 'EXTERNAL_PLAYER_DISABLED', + value: null, + platforms: ['ios', 'android', 'windows', 'linux', 'macos'], + }, + { + label: 'EXTERNAL_PLAYER_ALLOW_CHOOSING', + value: 'choose', + platforms: ['android'], + }, + { + label: 'VLC', + value: 'vlc', + platforms: ['ios', 'android'], + }, + { + label: 'MPV', + value: 'mpv', + platforms: ['macos'], + }, + { + label: 'IINA', + value: 'iina', + platforms: ['macos'], + }, + { + label: 'MX Player', + value: 'mxplayer', + platforms: ['android'], + }, + { + label: 'Just Player', + value: 'justplayer', + platforms: ['android'], + }, + { + label: 'Outplayer', + value: 'outplayer', + platforms: ['ios'], + }, +]; + module.exports = { CHROMECAST_RECEIVER_APP_ID, SUBTITLES_SIZES, @@ -55,5 +98,6 @@ module.exports = { SHARE_LINK_CATEGORY, WRITERS_LINK_CATEGORY, TYPE_PRIORITIES, - ICON_FOR_TYPE + ICON_FOR_TYPE, + EXTERNAL_PLAYERS, }; diff --git a/src/common/EventModal/EventModal.js b/src/common/EventModal/EventModal.js new file mode 100644 index 000000000..dcd9ca626 --- /dev/null +++ b/src/common/EventModal/EventModal.js @@ -0,0 +1,90 @@ +// Copyright (C) 2017-2023 Smart code 203358507 + +const React = require('react'); +const { useTranslation } = require('react-i18next'); +const Button = require('stremio/common/Button'); +const ModalDialog = require('stremio/common/ModalDialog'); +const useEvents = require('./useEvents'); +const styles = require('./styles'); +const { default: Icon } = require('@stremio/stremio-icons/react'); + +const EventModal = () => { + const { t } = useTranslation(); + + const { events, pullEvents, dismissEvent } = useEvents(); + + const modal = React.useMemo(() => { + return events?.modal?.type === 'Ready' ? + events.modal.content + : + null; + }, [events]); + + const onClose = React.useCallback(() => { + modal?.id && dismissEvent(modal.id); + }, [modal]); + + React.useEffect(() => { + pullEvents(); + }, []); + + return ( + modal !== null ? + + { + modal.imageUrl ? + + : + null + } +
+
+ { + modal.title ? +
{modal.title}
+ : + null + } + { + modal.message ? +
{modal.message}
+ : + null + } +
+ { + modal?.addon?.name ? +
+ +
+ { modal.addon.name } +
+
+ : + null + } + { + modal?.addon?.manifestUrl ? + + : + modal.externalUrl ? + + : + null + } +
+
+ : + null + ); +}; + +module.exports = EventModal; diff --git a/src/common/EventModal/index.js b/src/common/EventModal/index.js new file mode 100644 index 000000000..ac88c0210 --- /dev/null +++ b/src/common/EventModal/index.js @@ -0,0 +1,5 @@ +// Copyright (C) 2017-2023 Smart code 203358507 + +const EventModal = require('./EventModal'); + +module.exports = EventModal; diff --git a/src/common/EventModal/styles.less b/src/common/EventModal/styles.less new file mode 100644 index 000000000..0fa9919b8 --- /dev/null +++ b/src/common/EventModal/styles.less @@ -0,0 +1,119 @@ +// Copyright (C) 2017-2023 Smart code 203358507 + +@import (reference) '~stremio/common/screen-sizes.less'; + +:import('~stremio/common/ModalDialog/styles.less') { + modal-dialog-content: modal-dialog-content; + modal-dialog-container: modal-dialog-container; +} + +.event-modal { + backdrop-filter: blur(10px); + + .modal-dialog-container { + overflow: visible; + max-width: 45rem; + + .modal-dialog-content { + display: flex; + flex-direction: column; + align-items: center; + overflow: visible; + + .image { + width: 100%; + height: 100%; + margin-top: -10rem; + } + + .info-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 2.5rem; + padding: 1rem 4rem; + margin-top: -7rem; + + .title-container { + display: flex; + flex-direction: column; + gap: 1rem; + + .title { + color: var(--primary-foreground-color); + font-size: 1.325rem; + text-align: center; + padding: 0 6rem; + } + + .label { + color: var(--primary-foreground-color); + font-size: 1rem; + text-align: center; + opacity: 0.5; + } + } + + .addon-container { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + gap: 0.5rem; + + .icon { + height: 2rem; + width: 2rem; + color: var(--primary-accent-color); + } + + .name { + color: var(--primary-foreground-color); + } + } + + .action-button { + background-color: var(--primary-foreground-color); + border: 2px solid var(--primary-foreground-color); + padding: 0.8rem 2rem; + border-radius: 2rem; + + .button-label { + color: var(--primary-accent-color); + font-size: 1rem; + font-weight: 700; + } + + &:hover { + background-color: transparent; + } + } + } + } + } + + @media only screen and (max-width: @minimum) { + .modal-dialog-container { + .modal-dialog-content { + .image { + height: 125%; + width: 125%; + } + + .info-container { + .title-container { + .title { + padding: 0rem; + font-size: 1rem; + } + + .label { + font-size: 0.875rem; + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/common/EventModal/useEvents.js b/src/common/EventModal/useEvents.js new file mode 100644 index 000000000..f5efaa79e --- /dev/null +++ b/src/common/EventModal/useEvents.js @@ -0,0 +1,36 @@ +// Copyright (C) 2017-2023 Smart code 203358507 + +const useModelState = require('stremio/common/useModelState'); +const { useServices } = require('stremio/services'); + +const map = (ctx) => ({ + ...ctx.events, +}); + +const useEvents = () => { + const { core } = useServices(); + + const pullEvents = () => { + core.transport.dispatch({ + action: 'Ctx', + args: { + action: 'GetEvents', + }, + }); + }; + + const dismissEvent = (id) => { + core.transport.dispatch({ + action: 'Ctx', + args: { + action: 'DismissEvent', + args: id, + }, + }); + }; + + const events = useModelState({ model: 'ctx', map }); + return { events, pullEvents, dismissEvent }; +}; + +module.exports = useEvents; diff --git a/src/common/Multiselect/Multiselect.js b/src/common/Multiselect/Multiselect.js index a308393e3..73638eaa0 100644 --- a/src/common/Multiselect/Multiselect.js +++ b/src/common/Multiselect/Multiselect.js @@ -14,13 +14,17 @@ const Multiselect = ({ className, mode, direction, title, disabled, dataset, ren const [menuOpen, , closeMenu, toggleMenu] = useBinaryState(false); const options = React.useMemo(() => { return Array.isArray(props.options) ? - props.options + props.options.filter((option) => { + return option && (typeof option.value === 'string' || option.value === null); + }) : []; }, [props.options]); const selected = React.useMemo(() => { return Array.isArray(props.selected) ? - props.selected + props.selected.filter((value) => { + return typeof value === 'string' || value === null; + }) : []; }, [props.selected]); diff --git a/src/common/NavBar/HorizontalNavBar/NavMenu/NavMenu.js b/src/common/NavBar/HorizontalNavBar/NavMenu/NavMenu.js index 7af959d75..8c4477b42 100644 --- a/src/common/NavBar/HorizontalNavBar/NavMenu/NavMenu.js +++ b/src/common/NavBar/HorizontalNavBar/NavMenu/NavMenu.js @@ -7,6 +7,7 @@ const { useRouteFocused } = require('stremio-router'); const Popup = require('stremio/common/Popup'); const useBinaryState = require('stremio/common/useBinaryState'); const NavMenuContent = require('./NavMenuContent'); +const styles = require('./styles.less'); const NavMenu = (props) => { const routeFocused = useRouteFocused(); @@ -42,6 +43,7 @@ const NavMenu = (props) => { onCloseRequest={closeMenu} renderLabel={renderLabel} renderMenu={renderMenu} + className={styles['nav-menu-popup-label']} /> ); }; diff --git a/src/common/NavBar/HorizontalNavBar/NavMenu/styles.less b/src/common/NavBar/HorizontalNavBar/NavMenu/styles.less index 8b58f3796..aa9d175a0 100644 --- a/src/common/NavBar/HorizontalNavBar/NavMenu/styles.less +++ b/src/common/NavBar/HorizontalNavBar/NavMenu/styles.less @@ -3,6 +3,15 @@ @import (reference) '~@stremio/stremio-colors/less/stremio-colors.less'; @import (reference) '~stremio/common/screen-sizes.less'; +:import('~stremio/common/Popup/styles.less') { + popup-menu-container: menu-container; +} + +.nav-menu-popup-label { + .popup-menu-container { + margin-top: 1rem; + } +} .nav-menu-container { width: 22rem; max-height: calc(100vh - var(--horizontal-nav-bar-size)); diff --git a/src/common/NavBar/VerticalNavBar/NavTabButton/styles.less b/src/common/NavBar/VerticalNavBar/NavTabButton/styles.less index 95ed91c35..01fd993e3 100644 --- a/src/common/NavBar/VerticalNavBar/NavTabButton/styles.less +++ b/src/common/NavBar/VerticalNavBar/NavTabButton/styles.less @@ -40,14 +40,19 @@ .label { flex: none; + position: relative; + max-width: 100%; max-height: 2.4em; - padding: 0 0.2rem; + padding: 0 0.5rem; font-size: 0.8rem; font-weight: 500; letter-spacing: 0.01rem; text-align: center; + white-space: nowrap; + text-overflow: ellipsis; color: var(--primary-foreground-color); opacity: 0; + overflow: hidden; } } diff --git a/src/common/Popup/styles.less b/src/common/Popup/styles.less index e81de9033..e74c2cfa0 100644 --- a/src/common/Popup/styles.less +++ b/src/common/Popup/styles.less @@ -17,8 +17,7 @@ visibility: hidden; border-radius: var(--border-radius); background-color: var(--modal-background-color); - box-shadow: 0 1.35rem 2.7rem @color-background-dark5-40, - 0 1.1rem 0.85rem @color-background-dark5-20; + box-shadow: var(--outer-glow); cursor: auto; &.menu-direction-top-left { diff --git a/src/common/externalPlayerOptions.js b/src/common/externalPlayerOptions.js deleted file mode 100644 index 36f530a0b..000000000 --- a/src/common/externalPlayerOptions.js +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2017-2023 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' } - ]); -} 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; diff --git a/src/common/index.js b/src/common/index.js index 8d399d9a0..5d8d65cbe 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -44,7 +44,7 @@ const useProfile = require('./useProfile'); const useStreamingServer = require('./useStreamingServer'); const useTorrent = require('./useTorrent'); const platform = require('./platform'); -const externalPlayerOptions = require('./externalPlayerOptions'); +const EventModal = require('./EventModal'); module.exports = { AddonDetailsModal, @@ -95,5 +95,5 @@ module.exports = { useStreamingServer, useTorrent, platform, - externalPlayerOptions, + EventModal, }; diff --git a/src/routes/Board/Board.js b/src/routes/Board/Board.js index 6e92beea5..4c0702eda 100644 --- a/src/routes/Board/Board.js +++ b/src/routes/Board/Board.js @@ -4,7 +4,7 @@ const React = require('react'); const classnames = require('classnames'); const debounce = require('lodash.debounce'); const { useTranslation } = require('react-i18next'); -const { MainNavBars, MetaRow, ContinueWatchingItem, MetaItem, StreamingServerWarning, useStreamingServer, withCoreSuspender, getVisibleChildrenRange } = require('stremio/common'); +const { MainNavBars, MetaRow, ContinueWatchingItem, MetaItem, StreamingServerWarning, useStreamingServer, withCoreSuspender, getVisibleChildrenRange, EventModal } = require('stremio/common'); const useBoard = require('./useBoard'); const useContinueWatchingPreview = require('./useContinueWatchingPreview'); const styles = require('./styles'); @@ -38,6 +38,7 @@ const Board = () => { }, [board.catalogs, onVisibleRangeChange]); return (
+
{ diff --git a/src/routes/MetaDetails/StreamsList/Stream/Stream.js b/src/routes/MetaDetails/StreamsList/Stream/Stream.js index be46486a4..6587c72d0 100644 --- a/src/routes/MetaDetails/StreamsList/Stream/Stream.js +++ b/src/routes/MetaDetails/StreamsList/Stream/Stream.js @@ -4,31 +4,49 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); const { default: Icon } = require('@stremio/stremio-icons/react'); -const { Button, Image, useProfile, platform, useStreamingServer, useToast } = require('stremio/common'); +const { Button, Image, useProfile, platform, useToast } = require('stremio/common'); const { useServices } = require('stremio/services'); const StreamPlaceholder = require('./StreamPlaceholder'); const styles = require('./styles'); const Stream = ({ className, videoId, videoReleased, addonName, name, description, thumbnail, progress, deepLinks, ...props }) => { const profile = useProfile(); - const streamingServer = useStreamingServer(); - const { core } = useServices(); const toast = useToast(); + const { core } = useServices(); + 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 + deepLinks.externalPlayer ? + deepLinks.externalPlayer.web ? + deepLinks.externalPlayer.web : - null + deepLinks.externalPlayer.openPlayer ? + deepLinks.externalPlayer.openPlayer[platform.name] ? + deepLinks.externalPlayer.openPlayer[platform.name] + : + deepLinks.externalPlayer.playlist + : + deepLinks.player + : + deepLinks.player : null; - }, [deepLinks, profile, streamingServer]); + }, [deepLinks]); + + const download = React.useMemo(() => { + return href === deepLinks?.externalPlayer?.playlist ? + deepLinks.externalPlayer.fileName + : + null; + }, [href, deepLinks]); + + const target = React.useMemo(() => { + return href === deepLinks?.externalPlayer?.web ? + '_blank' + : + null; + }, [href, deepLinks]); + const markVideoAsWatched = React.useCallback(() => { if (typeof videoId === 'string') { core.transport.dispatch({ @@ -40,22 +58,9 @@ const Stream = ({ className, videoId, videoReleased, addonName, name, descriptio }); } }, [videoId, videoReleased]); + const onClick = React.useCallback((event) => { - if (href === null) { - // link does not lead to the player, it is expected to - // open with local video player through the streaming server - markVideoAsWatched(); - core.transport.dispatch({ - action: 'StreamingServer', - args: { - action: 'PlayOnDevice', - args: { - device: 'vlc', - source: deepLinks.externalPlayer.streaming - } - } - }); - } else if (profile.settings.playerType && profile.settings.playerType !== 'internal') { + if (profile.settings.playerType !== null) { markVideoAsWatched(); toast.show({ type: 'success', @@ -63,20 +68,18 @@ const Stream = ({ className, videoId, videoReleased, addonName, name, descriptio timeout: 4000 }); } + if (typeof props.onClick === 'function') { props.onClick(event); } - }, [href, deepLinks, props.onClick, profile, toast, markVideoAsWatched]); - 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]); + }, [props.onClick, profile.settings, markVideoAsWatched]); + const renderThumbnailFallback = React.useCallback(() => ( ), []); + return ( - diff --git a/src/routes/Settings/useProfileSettingsInputs.js b/src/routes/Settings/useProfileSettingsInputs.js index 64b1beb17..939e5e7cf 100644 --- a/src/routes/Settings/useProfileSettingsInputs.js +++ b/src/routes/Settings/useProfileSettingsInputs.js @@ -3,7 +3,7 @@ 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 useProfileSettingsInputs = (profile) => { const { t } = useTranslation(); @@ -211,11 +211,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: CONSTANTS.EXTERNAL_PLAYERS + .filter(({ platforms }) => platforms.includes(platform.name)) + .map(({ label, value }) => ({ + value, + label: t(label), + })), + selected: [profile.settings.playerType], + renderLabelText: () => { + const selectedOption = CONSTANTS.EXTERNAL_PLAYERS.find(({ value }) => value === profile.settings.playerType); + return selectedOption ? t(selectedOption.label, { defaultValue: selectedOption.label }) : profile.settings.playerType; + }, onSelect: (event) => { core.transport.dispatch({ action: 'Ctx',