diff --git a/src/routes/Player/ControlBar/ControlBar.js b/src/routes/Player/ControlBar/ControlBar.js
index f97a19cea..b0631a3f3 100644
--- a/src/routes/Player/ControlBar/ControlBar.js
+++ b/src/routes/Player/ControlBar/ControlBar.js
@@ -90,36 +90,6 @@ const ControlBar = ({
}
}
}, [muted, onMuteRequested, onUnmuteRequested]);
- const onSubtitlesButtonClick = React.useCallback(() => {
- if (typeof onToggleSubtitlesMenu === 'function') {
- onToggleSubtitlesMenu();
- }
- }, [onToggleSubtitlesMenu]);
- const onInfoButtonClick = React.useCallback(() => {
- if (typeof onToggleInfoMenu === 'function') {
- onToggleInfoMenu();
- }
- }, [onToggleInfoMenu]);
- const onSpeedButtonClick = React.useCallback(() => {
- if (typeof onToggleSpeedMenu === 'function') {
- onToggleSpeedMenu();
- }
- }, [onToggleSpeedMenu]);
- const onVideosButtonClick = React.useCallback(() => {
- if (typeof onToggleVideosMenu === 'function') {
- onToggleVideosMenu();
- }
- }, [onToggleVideosMenu]);
- const onOptionsButtonClick = React.useCallback(() => {
- if (typeof onToggleOptionsMenu === 'function') {
- onToggleOptionsMenu();
- }
- }, [onToggleOptionsMenu]);
- const onStatisticsButtonClick = React.useCallback(() => {
- if (typeof onToggleStatisticsMenu === 'function') {
- onToggleStatisticsMenu();
- }
- }, [onToggleStatisticsMenu]);
const onChromecastButtonClick = React.useCallback(() => {
chromecast.transport.requestSession();
}, []);
@@ -175,30 +145,30 @@ const ControlBar = ({
- {
- videoState.buffering ?
-
- :
- null
- }
-
{
- error !== null ?
-
-
{error.message}
- {
- error.code === 2 ?
-
{t('EXTERNAL_PLAYER_HINT')}
- :
- null
- }
- {
- player.selected?.stream?.deepLinks?.externalPlayer?.playlist !== null ?
-
- :
- null
- }
-
+ video.state.buffering ?
+
:
null
}
{
- subtitlesMenuOpen || infoMenuOpen || videosMenuOpen || speedMenuOpen || optionsMenuOpen || statisticsMenuOpen ?
+ error !== null ?
+
+ :
+ null
+ }
+ {
+ menusOpen ?
:
null
@@ -681,19 +640,19 @@ const Player = ({ urlParams, queryParams }) => {
/>
{
statisticsMenuOpen ?
:
null
@@ -736,17 +694,17 @@ const Player = ({ urlParams, queryParams }) => {
subtitlesMenuOpen ?
{
speedMenuOpen ?
:
diff --git a/src/routes/Player/StatisticsMenu/StatisticsMenu.js b/src/routes/Player/StatisticsMenu/StatisticsMenu.js
index 5ea9b5fdf..6bab8ecf5 100644
--- a/src/routes/Player/StatisticsMenu/StatisticsMenu.js
+++ b/src/routes/Player/StatisticsMenu/StatisticsMenu.js
@@ -5,28 +5,7 @@ const classNames = require('classnames');
const PropTypes = require('prop-types');
const styles = require('./styles.less');
-const StatisticsMenu = ({ className, stream, statistics }) => {
- const peers = React.useMemo(() => {
- return statistics.type === 'Ready' && statistics.content?.peers ?
- statistics.content.peers
- :
- 0;
- }, [statistics]);
-
- const speed = React.useMemo(() => {
- return statistics.type === 'Ready' && statistics.content?.downloadSpeed ?
- (statistics.content.downloadSpeed / 1000 / 1000).toFixed(2)
- :
- 0;
- }, [statistics]);
-
- const completed = React.useMemo(() => {
- return statistics.type === 'Ready' && statistics.content?.streamProgress ?
- (statistics.content.streamProgress * 100).toFixed(2)
- :
- 0;
- }, [statistics]);
-
+const StatisticsMenu = ({ className, peers, speed, completed, infoHash }) => {
return (
@@ -63,7 +42,7 @@ const StatisticsMenu = ({ className, stream, statistics }) => {
Info Hash
- { stream.infoHash }
+ { infoHash }
@@ -72,8 +51,10 @@ const StatisticsMenu = ({ className, stream, statistics }) => {
StatisticsMenu.propTypes = {
className: PropTypes.string,
- stream: PropTypes.object,
- statistics: PropTypes.object,
+ peers: PropTypes.number,
+ speed: PropTypes.number,
+ completed: PropTypes.number,
+ infoHash: PropTypes.string,
};
module.exports = StatisticsMenu;
diff --git a/src/routes/Player/Video/Video.js b/src/routes/Player/Video/Video.js
index 6dee8fe33..78cc5c08e 100644
--- a/src/routes/Player/Video/Video.js
+++ b/src/routes/Player/Video/Video.js
@@ -3,79 +3,12 @@
const React = require('react');
const PropTypes = require('prop-types');
const classnames = require('classnames');
-const StremioVideo = require('@stremio/stremio-video');
-const { useLiveRef } = require('stremio/common');
const styles = require('./styles');
-const Video = React.forwardRef(({ className, ...props }, ref) => {
- const onEndedRef = useLiveRef(props.onEnded);
- const onErrorRef = useLiveRef(props.onError);
- const onPropValueRef = useLiveRef(props.onPropValue);
- const onPropChangedRef = useLiveRef(props.onPropChanged);
- const onSubtitlesTrackLoadedRef = useLiveRef(props.onSubtitlesTrackLoaded);
- const onExtraSubtitlesTrackLoadedRef = useLiveRef(props.onExtraSubtitlesTrackLoaded);
- const onImplementationChangedRef = useLiveRef(props.onImplementationChanged);
- const videoElementRef = React.useRef(null);
- const videoRef = React.useRef(null);
- const dispatch = React.useCallback((action, options = {}) => {
- if (videoRef.current !== null) {
- try {
- videoRef.current.dispatch(action, {
- ...options,
- containerElement: videoElementRef.current
- });
- } catch (error) {
- console.error('Video', error);
- }
- }
- }, []);
- React.useImperativeHandle(ref, () => ({ dispatch }), []);
- React.useEffect(() => {
- if (videoElementRef.current !== null) {
- videoRef.current = new StremioVideo();
- videoRef.current.on('ended', () => {
- if (typeof onEndedRef.current === 'function') {
- onEndedRef.current();
- }
- });
- videoRef.current.on('error', (args) => {
- if (typeof onErrorRef.current === 'function') {
- onErrorRef.current(args);
- }
- });
- videoRef.current.on('propValue', (propName, propValue) => {
- if (typeof onPropValueRef.current === 'function') {
- onPropValueRef.current(propName, propValue);
- }
- });
- videoRef.current.on('propChanged', (propName, propValue) => {
- if (typeof onPropChangedRef.current === 'function') {
- onPropChangedRef.current(propName, propValue);
- }
- });
- videoRef.current.on('subtitlesTrackLoaded', (track) => {
- if (typeof onSubtitlesTrackLoadedRef.current === 'function') {
- onSubtitlesTrackLoadedRef.current(track);
- }
- });
- videoRef.current.on('extraSubtitlesTrackLoaded', (track) => {
- if (typeof onExtraSubtitlesTrackLoadedRef.current === 'function') {
- onExtraSubtitlesTrackLoadedRef.current(track);
- }
- });
- videoRef.current.on('implementationChanged', (manifest) => {
- if (typeof onImplementationChangedRef.current === 'function') {
- onImplementationChangedRef.current(manifest);
- }
- });
- }
- return () => {
- videoRef.current.destroy();
- };
- }, []);
+const Video = React.forwardRef(({ className, onClick, onDoubleClick }, ref) => {
return (
-
-
+
);
});
@@ -84,13 +17,8 @@ Video.displayName = 'Video';
Video.propTypes = {
className: PropTypes.string,
- onEnded: PropTypes.func,
- onError: PropTypes.func,
- onPropValue: PropTypes.func,
- onPropChanged: PropTypes.func,
- onSubtitlesTrackLoaded: PropTypes.func,
- onExtraSubtitlesTrackLoaded: PropTypes.func,
- onImplementationChanged: PropTypes.func
+ onClick: PropTypes.func,
+ onDoubleClick: PropTypes.func,
};
module.exports = Video;
diff --git a/src/routes/Player/styles.less b/src/routes/Player/styles.less
index 01b6a05ae..362b9029b 100644
--- a/src/routes/Player/styles.less
+++ b/src/routes/Player/styles.less
@@ -15,7 +15,7 @@
@background-color: rgba(0, 0, 0, 1);
html:not(.active-slider-within) {
- .player-container.immersed {
+ .player-container.overlayHidden {
cursor: none;
.nav-bar-layer, .control-bar-layer, .menu-layer {
@@ -40,67 +40,6 @@ html:not(.active-slider-within) {
bottom: 0;
z-index: 0;
- &.error-layer {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- background-color: @background-color;
-
- .error-label {
- flex: 0 1 auto;
- padding: 0 8rem;
- max-height: 4.8em;
- font-size: 2rem;
- color: var(--primary-foreground-color);
- text-align: center;
- }
-
- .error-sub {
- flex: 0 1 auto;
- padding: 0 2rem;
- max-height: 4.8em;
- font-size: 1.3rem;
- margin-top: 0.8rem;
- color: var(--primary-foreground-color);
- text-align: center;
- }
-
- .playlist-button {
- flex: none;
- display: flex;
- flex-direction: row;
- align-items: center;
- height: 3.5rem;
- border-radius: 3.5rem;
- margin-top: 1.5rem;
- padding: 0 2rem;
- background-color: var(--secondary-accent-color);
-
- &:hover {
- outline: var(--focus-outline-size) solid var(--secondary-accent-color);
- background-color: transparent;
- }
-
- .icon {
- flex: none;
- width: 1.5rem;
- height: 1.5rem;
- margin-right: 1rem;
- color: var(--primary-foreground-color);
- }
-
- .label {
- flex: 1;
- max-height: 2.4em;
- font-size: 1.1rem;
- font-weight: 500;
- color: var(--primary-foreground-color);
- text-align: center;
- }
- }
- }
-
&.nav-bar-layer {
bottom: initial;
background: transparent;
diff --git a/src/routes/Player/useStatistics.js b/src/routes/Player/useStatistics.js
new file mode 100644
index 000000000..d13411327
--- /dev/null
+++ b/src/routes/Player/useStatistics.js
@@ -0,0 +1,83 @@
+// Copyright (C) 2017-2023 Smart code 203358507
+
+const React = require('react');
+const { useServices } = require('stremio/services');
+
+const useStatistics = (player, streamingServer) => {
+ const { core } = useServices();
+
+ const stream = React.useMemo(() => {
+ return player.selected?.stream ?
+ player.selected.stream
+ :
+ null;
+ }, [player.selected]);
+
+ const infoHash = React.useMemo(() => {
+ return stream?.infoHash ?
+ stream?.infoHash
+ :
+ null;
+ }, [stream]);
+
+ const statistics = React.useMemo(() => {
+ return streamingServer.statistics?.type === 'Ready' ?
+ streamingServer.statistics.content
+ :
+ null;
+ }, [streamingServer.statistics]);
+
+ const peers = React.useMemo(() => {
+ return statistics?.peers ?
+ statistics.peers
+ :
+ 0;
+ }, [statistics]);
+
+ const speed = React.useMemo(() => {
+ return statistics?.downloadSpeed ?
+ parseFloat((statistics.downloadSpeed / 1000 / 1000).toFixed(2))
+ :
+ 0;
+ }, [statistics]);
+
+ const completed = React.useMemo(() => {
+ return statistics?.streamProgress ?
+ parseFloat((statistics.streamProgress * 100).toFixed(2))
+ :
+ 0;
+ }, [statistics]);
+
+ const getStatistics = React.useCallback(() => {
+ if (stream) {
+ const { infoHash, fileIdx } = stream;
+ if (typeof infoHash === 'string' && typeof fileIdx === 'number') {
+ core.transport.dispatch({
+ action: 'StreamingServer',
+ args: {
+ action: 'GetStatistics',
+ args: {
+ infoHash,
+ fileIdx,
+ }
+ }
+ });
+ }
+ }
+ }, [stream]);
+
+ React.useEffect(() => {
+ getStatistics();
+ const interval = setInterval(getStatistics, 5000);
+ return () => clearInterval(interval);
+ }, [getStatistics]);
+
+ return {
+ infoHash,
+ peers,
+ speed,
+ completed,
+ };
+};
+
+module.exports = useStatistics;
diff --git a/src/routes/Player/useVideo.js b/src/routes/Player/useVideo.js
new file mode 100644
index 000000000..6a59a12fb
--- /dev/null
+++ b/src/routes/Player/useVideo.js
@@ -0,0 +1,143 @@
+// Copyright (C) 2017-2023 Smart code 203358507
+
+const React = require('react');
+const Video = require('@stremio/stremio-video');
+const EventEmitter = require('eventemitter3');
+
+const events = new EventEmitter();
+
+const useVideo = () => {
+ const video = React.useRef(null);
+ const containerElement = React.useRef(null);
+
+ const [state, setState] = React.useState({
+ manifest: null,
+ stream: null,
+ paused: null,
+ time: null,
+ duration: null,
+ buffering: null,
+ buffered: null,
+ volume: null,
+ muted: null,
+ playbackSpeed: null,
+ videoParams: null,
+ audioTracks: [],
+ selectedAudioTrackId: null,
+ subtitlesTracks: [],
+ selectedSubtitlesTrackId: null,
+ subtitlesOffset: null,
+ subtitlesSize: null,
+ subtitlesTextColor: null,
+ subtitlesBackgroundColor: null,
+ subtitlesOutlineColor: null,
+ extraSubtitlesTracks: [],
+ selectedExtraSubtitlesTrackId: null,
+ extraSubtitlesSize: null,
+ extraSubtitlesDelay: null,
+ extraSubtitlesOffset: null,
+ extraSubtitlesTextColor: null,
+ extraSubtitlesBackgroundColor: null,
+ extraSubtitlesOutlineColor: null,
+ });
+
+ const dispatch = (action, options) => {
+ if (video.current && containerElement.current) {
+ try {
+ video.current.dispatch(action, {
+ ...options,
+ containerElement: containerElement.current,
+ });
+ } catch (error) {
+ console.error('Video:', error);
+ }
+ }
+ };
+
+ const load = (args, options) => {
+ dispatch({
+ type: 'command',
+ commandName: 'load',
+ commandArgs: args
+ }, options);
+ };
+
+ const unload = () => {
+ dispatch({
+ type: 'command',
+ commandName: 'unload',
+ });
+ };
+
+ const addExtraSubtitlesTracks = (tracks) => {
+ dispatch({
+ type: 'command',
+ commandName: 'addExtraSubtitlesTracks',
+ commandArgs: {
+ tracks,
+ },
+ });
+ };
+
+ const setProp = (name, value) => {
+ dispatch({ type: 'setProp', propName: name, propValue: value });
+ };
+
+ const onError = (error) => {
+ events.emit('error', error);
+ };
+
+ const onEnded = () => {
+ events.emit('ended');
+ };
+
+ const onSubtitlesTrackLoaded = (track) => {
+ events.emit('subtitlesTrackLoaded', track);
+ };
+
+ const onExtraSubtitlesTrackLoaded = (track) => {
+ events.emit('extraSubtitlesTrackLoaded', track);
+ };
+
+ const onPropChanged = (name, value) => {
+ setState((state) => ({
+ ...state,
+ [name]: value
+ }));
+ };
+
+ const onImplementationChanged = (manifest) => {
+ manifest.props.forEach((propName) => dispatch(({ type: 'observeProp', propName })));
+ setState((state) => ({
+ ...state,
+ manifest
+ }));
+
+ events.emit('implementationChanged', manifest);
+ };
+
+ React.useEffect(() => {
+ video.current = new Video();
+ video.current.on('error', onError);
+ video.current.on('ended', onEnded);
+ video.current.on('propChanged', onPropChanged);
+ video.current.on('propValue', onPropChanged);
+ video.current.on('implementationChanged', onImplementationChanged);
+ video.current.on('subtitlesTrackLoaded', onSubtitlesTrackLoaded);
+ video.current.on('extraSubtitlesTrackLoaded', onExtraSubtitlesTrackLoaded);
+
+ return () => video.current.destroy();
+ }, []);
+
+ return {
+ events,
+ containerElement,
+ state,
+ load,
+ unload,
+ addExtraSubtitlesTracks,
+ setProp,
+ };
+};
+
+module.exports = useVideo;