diff --git a/package-lock.json b/package-lock.json
index 73dc115fe..7e0bba17f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,7 +12,7 @@
"@babel/runtime": "7.16.0",
"@sentry/browser": "6.13.3",
"@stremio/stremio-colors": "4.0.1",
- "@stremio/stremio-core-web": "0.44.5",
+ "@stremio/stremio-core-web": "0.44.6",
"@stremio/stremio-icons": "4.0.0",
"@stremio/stremio-video": "0.0.23",
"a-color-picker": "1.2.1",
@@ -2617,9 +2617,9 @@
"integrity": "sha512-yT3No1gIWKLV2BhQIeSgG94EzXxmEqXJLulO+pFpziqWNUbmmEKeE+nRvW5wtoIK4SLy+v0bLd0b6HBH3KFfWw=="
},
"node_modules/@stremio/stremio-core-web": {
- "version": "0.44.5",
- "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.5.tgz",
- "integrity": "sha512-egKYHD7h8Q5CcybT5RcCGpcJDT13TVbW3fyUjcwv4McvOGWRW3iO8RMaZt/ZWbf10tXSaGA2rbmJ52tjwGWqyA==",
+ "version": "0.44.6",
+ "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.6.tgz",
+ "integrity": "sha512-Mxc6oRKgTuXU80JEacJIe4TphccZUJkyHTMUZnUx9sotVetGX+EJsyvr+HLKNMDGJHx5xcwGT/BUikdyQR/Lpw==",
"dependencies": {
"@babel/runtime": "7.16.0"
}
@@ -15757,9 +15757,9 @@
"integrity": "sha512-yT3No1gIWKLV2BhQIeSgG94EzXxmEqXJLulO+pFpziqWNUbmmEKeE+nRvW5wtoIK4SLy+v0bLd0b6HBH3KFfWw=="
},
"@stremio/stremio-core-web": {
- "version": "0.44.5",
- "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.5.tgz",
- "integrity": "sha512-egKYHD7h8Q5CcybT5RcCGpcJDT13TVbW3fyUjcwv4McvOGWRW3iO8RMaZt/ZWbf10tXSaGA2rbmJ52tjwGWqyA==",
+ "version": "0.44.6",
+ "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.6.tgz",
+ "integrity": "sha512-Mxc6oRKgTuXU80JEacJIe4TphccZUJkyHTMUZnUx9sotVetGX+EJsyvr+HLKNMDGJHx5xcwGT/BUikdyQR/Lpw==",
"requires": {
"@babel/runtime": "7.16.0"
}
diff --git a/package.json b/package.json
index 034c65757..371aa3e90 100755
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
"@babel/runtime": "7.16.0",
"@sentry/browser": "6.13.3",
"@stremio/stremio-colors": "4.0.1",
- "@stremio/stremio-core-web": "0.44.5",
+ "@stremio/stremio-core-web": "0.44.6",
"@stremio/stremio-icons": "4.0.0",
"@stremio/stremio-video": "0.0.23",
"a-color-picker": "1.2.1",
diff --git a/src/App/App.js b/src/App/App.js
index e112a291a..c0a79b727 100644
--- a/src/App/App.js
+++ b/src/App/App.js
@@ -103,6 +103,12 @@ const App = () => {
action: 'PullUserFromAPI'
}
});
+ services.core.transport.dispatch({
+ action: 'Ctx',
+ args: {
+ action: 'SyncLibraryWithAPI'
+ }
+ });
}
}, [initialized]);
return (
diff --git a/src/App/ServicesToaster.js b/src/App/ServicesToaster.js
index da1a654af..43b076c9a 100644
--- a/src/App/ServicesToaster.js
+++ b/src/App/ServicesToaster.js
@@ -15,11 +15,18 @@ const ServicesToaster = () => {
break;
}
+ if (args.source.event === 'LibrarySyncWithAPIPlanned' && args.source.args.uid === null) {
+ break;
+ }
+
toast.show({
type: 'error',
title: args.source.event,
message: args.error.message,
- timeout: 4000
+ timeout: 4000,
+ dataset: {
+ type: 'CoreEvent'
+ }
});
break;
}
diff --git a/src/common/NavBar/HorizontalNavBar/NavMenu/NavMenuContent.js b/src/common/NavBar/HorizontalNavBar/NavMenu/NavMenuContent.js
index 7dce73962..efcef4feb 100644
--- a/src/common/NavBar/HorizontalNavBar/NavMenu/NavMenuContent.js
+++ b/src/common/NavBar/HorizontalNavBar/NavMenu/NavMenuContent.js
@@ -8,12 +8,14 @@ const { useServices } = require('stremio/services');
const Button = require('stremio/common/Button');
const useFullscreen = require('stremio/common/useFullscreen');
const useProfile = require('stremio/common/useProfile');
+const useTorrent = require('stremio/common/useTorrent');
const { withCoreSuspender } = require('stremio/common/CoreSuspender');
const styles = require('./styles');
const NavMenuContent = ({ onClick }) => {
const { core } = useServices();
const profile = useProfile();
+ const { createTorrentFromMagnet } = useTorrent();
const [fullscreen, requestFullscreen, exitFullscreen] = useFullscreen();
const logoutButtonOnClick = React.useCallback(() => {
core.transport.dispatch({
@@ -23,6 +25,14 @@ const NavMenuContent = ({ onClick }) => {
}
});
}, []);
+ const onPlayMagnetLinkClick = React.useCallback(async () => {
+ try {
+ const clipboardText = await navigator.clipboard.readText();
+ createTorrentFromMagnet(clipboardText);
+ } catch(e) {
+ console.error(e);
+ }
+ }, []);
return (
@@ -57,11 +67,7 @@ const NavMenuContent = ({ onClick }) => {
Addons
-
-
@@ -163,6 +195,7 @@ ControlBar.propTypes = {
subtitlesTracks: PropTypes.array,
audioTracks: PropTypes.array,
metaItem: PropTypes.object,
+ nextVideo: PropTypes.object,
onPlayRequested: PropTypes.func,
onPauseRequested: PropTypes.func,
onMuteRequested: PropTypes.func,
@@ -171,7 +204,8 @@ ControlBar.propTypes = {
onSeekRequested: PropTypes.func,
onToggleSubtitlesMenu: PropTypes.func,
onToggleInfoMenu: PropTypes.func,
- onToggleSpeedMenu: PropTypes.func
+ onToggleSpeedMenu: PropTypes.func,
+ onToggleVideosMenu: PropTypes.func
};
module.exports = ControlBar;
diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js
index 04cbec555..c2710eb82 100644
--- a/src/routes/Player/Player.js
+++ b/src/routes/Player/Player.js
@@ -11,6 +11,7 @@ const Icon = require('@stremio/stremio-icons/dom');
const BufferingLoader = require('./BufferingLoader');
const ControlBar = require('./ControlBar');
const InfoMenu = require('./InfoMenu');
+const VideosMenu = require('./VideosMenu');
const SubtitlesMenu = require('./SubtitlesMenu');
const SpeedMenu = require('./SpeedMenu');
const Video = require('./Video');
@@ -40,6 +41,7 @@ const Player = ({ urlParams, queryParams }) => {
const [subtitlesMenuOpen, , closeSubtitlesMenu, toggleSubtitlesMenu] = useBinaryState(false);
const [infoMenuOpen, , closeInfoMenu, toggleInfoMenu] = useBinaryState(false);
const [speedMenuOpen, , closeSpeedMenu, toggleSpeedMenu] = useBinaryState(false);
+ const [videosMenuOpen, , closeVideosMenu, toggleVideosMenu] = useBinaryState(false);
const [error, setError] = React.useState(null);
const [videoState, setVideoState] = React.useReducer(
(videoState, nextVideoState) => ({ ...videoState, ...nextVideoState }),
@@ -207,6 +209,9 @@ const Player = ({ urlParams, queryParams }) => {
if (!event.nativeEvent.speedMenuClosePrevented) {
closeSpeedMenu();
}
+ if (!event.nativeEvent.videosMenuClosePrevented) {
+ closeVideosMenu();
+ }
}, []);
const onContainerMouseMove = React.useCallback((event) => {
setImmersed(false);
@@ -327,6 +332,7 @@ const Player = ({ urlParams, queryParams }) => {
React.useEffect(() => {
if (player.metaItem === null || player.metaItem.type !== 'Ready') {
closeInfoMenu();
+ closeVideosMenu();
}
}, [player.metaItem]);
React.useEffect(() => {
@@ -343,6 +349,8 @@ const Player = ({ urlParams, queryParams }) => {
};
}, []);
React.useEffect(() => {
+ const toastFilter = (item) => item?.dataset?.type === 'CoreEvent';
+ toast.addFilter(toastFilter);
const onCastStateChange = () => {
setCasting(chromecast.active && chromecast.transport.getCastState() === cast.framework.CastState.CONNECTED);
};
@@ -358,6 +366,7 @@ const Player = ({ urlParams, queryParams }) => {
chromecast.on('stateChanged', onChromecastServiceStateChange);
onChromecastServiceStateChange();
return () => {
+ toast.removeFilter(toastFilter);
chromecast.off('stateChanged', onChromecastServiceStateChange);
if (chromecast.active) {
chromecast.transport.off(
@@ -414,6 +423,7 @@ const Player = ({ urlParams, queryParams }) => {
case 'KeyS': {
closeInfoMenu();
closeSpeedMenu();
+ closeVideosMenu();
if ((Array.isArray(videoState.subtitlesTracks) && videoState.subtitlesTracks.length > 0) ||
(Array.isArray(videoState.extraSubtitlesTracks) && videoState.extraSubtitlesTracks.length > 0) ||
(Array.isArray(videoState.audioTracks) && videoState.audioTracks.length > 0)) {
@@ -425,6 +435,7 @@ const Player = ({ urlParams, queryParams }) => {
case 'KeyI': {
closeSubtitlesMenu();
closeSpeedMenu();
+ closeVideosMenu();
if (player.metaItem !== null && player.metaItem.type === 'Ready') {
toggleInfoMenu();
}
@@ -440,10 +451,20 @@ const Player = ({ urlParams, queryParams }) => {
break;
}
+ case 'KeyV': {
+ closeInfoMenu();
+ closeSubtitlesMenu();
+ if (player.metaItem !== null && player.metaItem.type === 'Ready') {
+ toggleVideosMenu();
+ }
+
+ break;
+ }
case 'Escape': {
closeSubtitlesMenu();
closeInfoMenu();
closeSpeedMenu();
+ closeVideosMenu();
break;
}
}
@@ -454,7 +475,7 @@ const Player = ({ urlParams, queryParams }) => {
return () => {
window.removeEventListener('keydown', onKeyDown);
};
- }, [player.metaItem, settings.seekTimeDuration, routeFocused, subtitlesMenuOpen, infoMenuOpen, speedMenuOpen, videoState.paused, videoState.time, videoState.volume, videoState.audioTracks, videoState.subtitlesTracks, videoState.extraSubtitlesTracks, toggleSubtitlesMenu, toggleInfoMenu]);
+ }, [player.metaItem, settings.seekTimeDuration, routeFocused, subtitlesMenuOpen, infoMenuOpen, speedMenuOpen, videoState.paused, videoState.time, videoState.volume, videoState.audioTracks, videoState.subtitlesTracks, videoState.extraSubtitlesTracks, toggleSubtitlesMenu, toggleInfoMenu, toggleVideosMenu]);
React.useLayoutEffect(() => {
return () => {
setImmersedDebounced.cancel();
@@ -463,7 +484,7 @@ const Player = ({ urlParams, queryParams }) => {
};
}, []);
return (
- {
subtitlesTracks={videoState.subtitlesTracks.concat(videoState.extraSubtitlesTracks)}
audioTracks={videoState.audioTracks}
metaItem={player.metaItem}
+ nextVideo={player.nextVideo}
onPlayRequested={onPlayRequested}
onPauseRequested={onPauseRequested}
onMuteRequested={onMuteRequested}
@@ -541,6 +563,7 @@ const Player = ({ urlParams, queryParams }) => {
onToggleSubtitlesMenu={toggleSubtitlesMenu}
onToggleInfoMenu={toggleInfoMenu}
onToggleSpeedMenu={toggleSpeedMenu}
+ onToggleVideosMenu={toggleVideosMenu}
onMouseMove={onBarMouseMove}
onMouseOver={onBarMouseMove}
/>
@@ -592,6 +615,16 @@ const Player = ({ urlParams, queryParams }) => {
:
null
}
+ {
+ videosMenuOpen ?
+
+ :
+ null
+ }
);
};
diff --git a/src/routes/Player/VideosMenu/VideosMenu.js b/src/routes/Player/VideosMenu/VideosMenu.js
new file mode 100644
index 000000000..219a329c2
--- /dev/null
+++ b/src/routes/Player/VideosMenu/VideosMenu.js
@@ -0,0 +1,51 @@
+// Copyright (C) 2017-2022 Smart code 203358507
+
+const React = require('react');
+const PropTypes = require('prop-types');
+const classnames = require('classnames');
+const Video = require('../../MetaDetails/VideosList/Video');
+const styles = require('./styles');
+
+const VideosMenu = ({ className, metaItem, seriesInfo }) => {
+ const onMouseDown = React.useCallback((event) => {
+ event.nativeEvent.videosMenuClosePrevented = true;
+ }, []);
+ const videos = React.useMemo(() => {
+ return seriesInfo && typeof seriesInfo.season === 'number' && Array.isArray(metaItem.videos) ?
+ metaItem.videos.filter(({ season }) => season === seriesInfo.season)
+ :
+ metaItem.videos;
+ }, [metaItem, seriesInfo]);
+ return (
+
+ {
+ videos.map((video, index) => (
+
+ ))
+ }
+
+ );
+};
+
+VideosMenu.propTypes = {
+ className: PropTypes.string,
+ metaItem: PropTypes.object,
+ seriesInfo: PropTypes.shape({
+ season: PropTypes.number,
+ episode: PropTypes.number,
+ }),
+};
+
+module.exports = VideosMenu;
diff --git a/src/routes/Player/VideosMenu/index.js b/src/routes/Player/VideosMenu/index.js
new file mode 100644
index 000000000..e604ab4cc
--- /dev/null
+++ b/src/routes/Player/VideosMenu/index.js
@@ -0,0 +1,5 @@
+// Copyright (C) 2017-2022 Smart code 203358507
+
+const VideosMenu = require('./VideosMenu');
+
+module.exports = VideosMenu;
diff --git a/src/routes/Player/VideosMenu/styles.less b/src/routes/Player/VideosMenu/styles.less
new file mode 100644
index 000000000..47444c72a
--- /dev/null
+++ b/src/routes/Player/VideosMenu/styles.less
@@ -0,0 +1,5 @@
+// Copyright (C) 2017-2022 Smart code 203358507
+
+.videos-menu-container {
+ width: 30rem;
+}
\ No newline at end of file
diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js
index 44550794e..9ca68fd52 100644
--- a/src/routes/Settings/Settings.js
+++ b/src/routes/Settings/Settings.js
@@ -14,6 +14,7 @@ const styles = require('./styles');
const GENERAL_SECTION = 'general';
const PLAYER_SECTION = 'player';
const STREAMING_SECTION = 'streaming';
+const SHORTCUTS_SECTION = 'shortcuts';
const Settings = () => {
const { core } = useServices();
@@ -93,10 +94,12 @@ const Settings = () => {
const generalSectionRef = React.useRef(null);
const playerSectionRef = React.useRef(null);
const streamingServerSectionRef = React.useRef(null);
+ const shortcutsSectionRef = React.useRef(null);
const sections = React.useMemo(() => ([
{ ref: generalSectionRef, id: GENERAL_SECTION },
{ ref: playerSectionRef, id: PLAYER_SECTION },
{ ref: streamingServerSectionRef, id: STREAMING_SECTION },
+ { ref: shortcutsSectionRef, id: SHORTCUTS_SECTION },
]), []);
const [selectedSectionId, setSelectedSectionId] = React.useState(GENERAL_SECTION);
const updateSelectedSectionId = React.useCallback(() => {
@@ -142,6 +145,9 @@ const Settings = () => {
Streaming server
+
+ Shortcuts
+
App Version: {process.env.VERSION}
{
@@ -424,6 +430,107 @@ const Settings = () => {
null
}
+
+
Shortcuts
+
+
+
+
+
→
+
or
+
⇧ Shift
+
+
+
→
+
+
+
+
+
+
←
+
or
+
⇧ Shift
+
+
+
←
+
+
+
+
+
+
+
Toggle Subtitles Menu
+
+
+ S
+
+
+
+
+
+
+
Navigate Between Menus
+
+
+
+
+
+
{
diff --git a/src/routes/Settings/styles.less b/src/routes/Settings/styles.less
index be058dd23..7e297080a 100644
--- a/src/routes/Settings/styles.less
+++ b/src/routes/Settings/styles.less
@@ -315,6 +315,30 @@
}
}
}
+
+ &.shortcut-container {
+ justify-content: center;
+ padding: 0;
+ overflow: visible;
+
+ kbd {
+ flex: 0 1 auto;
+ height: 2.5rem;
+ min-width: 2.5rem;
+ line-height: 2.5rem;
+ padding: 0 1rem;
+ font-weight: 500;
+ color: @color-secondaryvariant1-90;
+ border-radius: 0.25em;
+ box-shadow: 0 4px 0 1px @color-background-40;
+ background-color: @color-background;
+ }
+
+ .label {
+ margin: 0 1rem;
+ color: @color-secondaryvariant1-90;
+ }
+ }
}
}
}