mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-04-28 23:04:08 +00:00
Merge branch 'development' of https://github.com/Stremio/stremio-web into feat/player-options-menu
This commit is contained in:
commit
8a0b0a93e3
11 changed files with 338 additions and 32 deletions
14
package-lock.json
generated
14
package-lock.json
generated
|
|
@ -12,7 +12,7 @@
|
||||||
"@babel/runtime": "7.16.0",
|
"@babel/runtime": "7.16.0",
|
||||||
"@sentry/browser": "6.13.3",
|
"@sentry/browser": "6.13.3",
|
||||||
"@stremio/stremio-colors": "5.0.1",
|
"@stremio/stremio-colors": "5.0.1",
|
||||||
"@stremio/stremio-core-web": "0.44.6",
|
"@stremio/stremio-core-web": "0.44.7",
|
||||||
"@stremio/stremio-icons": "4.0.0",
|
"@stremio/stremio-icons": "4.0.0",
|
||||||
"@stremio/stremio-video": "0.0.24",
|
"@stremio/stremio-video": "0.0.24",
|
||||||
"a-color-picker": "1.2.1",
|
"a-color-picker": "1.2.1",
|
||||||
|
|
@ -2699,9 +2699,9 @@
|
||||||
"integrity": "sha512-Dt3PYmy1DZ473QNs99KYXVWQPHtpIl37VUY0+gCEvvuCqE1fRrZIJtZ9KbysUKonvO7WwdQDztgcW0iGoc1dEA=="
|
"integrity": "sha512-Dt3PYmy1DZ473QNs99KYXVWQPHtpIl37VUY0+gCEvvuCqE1fRrZIJtZ9KbysUKonvO7WwdQDztgcW0iGoc1dEA=="
|
||||||
},
|
},
|
||||||
"node_modules/@stremio/stremio-core-web": {
|
"node_modules/@stremio/stremio-core-web": {
|
||||||
"version": "0.44.6",
|
"version": "0.44.7",
|
||||||
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.6.tgz",
|
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.7.tgz",
|
||||||
"integrity": "sha512-Mxc6oRKgTuXU80JEacJIe4TphccZUJkyHTMUZnUx9sotVetGX+EJsyvr+HLKNMDGJHx5xcwGT/BUikdyQR/Lpw==",
|
"integrity": "sha512-hkeYLfL1On4TMBHn87Onrp93aeRuTh4YXMKdDR1Vz5YikPOiPEq/JRoLLmmSSsFEdifs6Egu+A0qiggTttepOA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.16.0"
|
"@babel/runtime": "7.16.0"
|
||||||
}
|
}
|
||||||
|
|
@ -16716,9 +16716,9 @@
|
||||||
"integrity": "sha512-Dt3PYmy1DZ473QNs99KYXVWQPHtpIl37VUY0+gCEvvuCqE1fRrZIJtZ9KbysUKonvO7WwdQDztgcW0iGoc1dEA=="
|
"integrity": "sha512-Dt3PYmy1DZ473QNs99KYXVWQPHtpIl37VUY0+gCEvvuCqE1fRrZIJtZ9KbysUKonvO7WwdQDztgcW0iGoc1dEA=="
|
||||||
},
|
},
|
||||||
"@stremio/stremio-core-web": {
|
"@stremio/stremio-core-web": {
|
||||||
"version": "0.44.6",
|
"version": "0.44.7",
|
||||||
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.6.tgz",
|
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.7.tgz",
|
||||||
"integrity": "sha512-Mxc6oRKgTuXU80JEacJIe4TphccZUJkyHTMUZnUx9sotVetGX+EJsyvr+HLKNMDGJHx5xcwGT/BUikdyQR/Lpw==",
|
"integrity": "sha512-hkeYLfL1On4TMBHn87Onrp93aeRuTh4YXMKdDR1Vz5YikPOiPEq/JRoLLmmSSsFEdifs6Egu+A0qiggTttepOA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "7.16.0"
|
"@babel/runtime": "7.16.0"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
"@babel/runtime": "7.16.0",
|
"@babel/runtime": "7.16.0",
|
||||||
"@sentry/browser": "6.13.3",
|
"@sentry/browser": "6.13.3",
|
||||||
"@stremio/stremio-colors": "5.0.1",
|
"@stremio/stremio-colors": "5.0.1",
|
||||||
"@stremio/stremio-core-web": "0.44.6",
|
"@stremio/stremio-core-web": "0.44.7",
|
||||||
"@stremio/stremio-icons": "4.0.0",
|
"@stremio/stremio-icons": "4.0.0",
|
||||||
"@stremio/stremio-video": "0.0.24",
|
"@stremio/stremio-video": "0.0.24",
|
||||||
"a-color-picker": "1.2.1",
|
"a-color-picker": "1.2.1",
|
||||||
|
|
@ -24,7 +24,6 @@
|
||||||
"classnames": "2.3.1",
|
"classnames": "2.3.1",
|
||||||
"eventemitter3": "4.0.7",
|
"eventemitter3": "4.0.7",
|
||||||
"filter-invalid-dom-props": "2.1.0",
|
"filter-invalid-dom-props": "2.1.0",
|
||||||
"langs": "^2.0.0",
|
|
||||||
"hat": "0.0.3",
|
"hat": "0.0.3",
|
||||||
"langs": "^2.0.0",
|
"langs": "^2.0.0",
|
||||||
"lodash.debounce": "4.0.8",
|
"lodash.debounce": "4.0.8",
|
||||||
|
|
|
||||||
|
|
@ -14,5 +14,6 @@
|
||||||
|
|
||||||
&:global(.disabled) {
|
&:global(.disabled) {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
opacity: 0.25;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ const CHROMECAST_RECEIVER_APP_ID = '1634F54B';
|
||||||
const SUBTITLES_SIZES = [75, 100, 125, 150, 175, 200, 250];
|
const SUBTITLES_SIZES = [75, 100, 125, 150, 175, 200, 250];
|
||||||
const SUBTITLES_FONTS = ['Roboto', 'Arial', 'Halvetica', 'Times New Roman', 'Verdana', 'Courier', 'Lucida Console', 'sans-serif', 'serif', 'monospace'];
|
const SUBTITLES_FONTS = ['Roboto', 'Arial', 'Halvetica', 'Times New Roman', 'Verdana', 'Courier', 'Lucida Console', 'sans-serif', 'serif', 'monospace'];
|
||||||
const SEEK_TIME_DURATIONS = [5000, 10000, 15000, 20000, 25000, 30000];
|
const SEEK_TIME_DURATIONS = [5000, 10000, 15000, 20000, 25000, 30000];
|
||||||
|
const NEXT_VIDEO_POPUP_DURATIONS = [0, 5000, 10000, 15000, 20000, 25000, 30000, 35000, 40000, 45000, 50000];
|
||||||
const CATALOG_PREVIEW_SIZE = 10;
|
const CATALOG_PREVIEW_SIZE = 10;
|
||||||
const CATALOG_PAGE_SIZE = 100;
|
const CATALOG_PAGE_SIZE = 100;
|
||||||
const NONE_EXTRA_VALUE = 'None';
|
const NONE_EXTRA_VALUE = 'None';
|
||||||
|
|
@ -25,12 +26,26 @@ const TYPE_PRIORITIES = {
|
||||||
adult: 1,
|
adult: 1,
|
||||||
other: -Infinity
|
other: -Infinity
|
||||||
};
|
};
|
||||||
|
const ICON_FOR_TYPE = new Map([
|
||||||
|
['movie', 'ic_movies'],
|
||||||
|
['series', 'ic_series'],
|
||||||
|
['channel', 'ic_channels'],
|
||||||
|
['tv', 'ic_tv'],
|
||||||
|
['book', 'ic_book'],
|
||||||
|
['game', 'ic_games'],
|
||||||
|
['music', 'ic_music'],
|
||||||
|
['adult', 'ic_adult'],
|
||||||
|
['radio', 'ic_radio'],
|
||||||
|
['podcast', 'ic_podcast'],
|
||||||
|
['other', 'ic_movies'],
|
||||||
|
]);
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
CHROMECAST_RECEIVER_APP_ID,
|
CHROMECAST_RECEIVER_APP_ID,
|
||||||
SUBTITLES_SIZES,
|
SUBTITLES_SIZES,
|
||||||
SUBTITLES_FONTS,
|
SUBTITLES_FONTS,
|
||||||
SEEK_TIME_DURATIONS,
|
SEEK_TIME_DURATIONS,
|
||||||
|
NEXT_VIDEO_POPUP_DURATIONS,
|
||||||
CATALOG_PREVIEW_SIZE,
|
CATALOG_PREVIEW_SIZE,
|
||||||
CATALOG_PAGE_SIZE,
|
CATALOG_PAGE_SIZE,
|
||||||
NONE_EXTRA_VALUE,
|
NONE_EXTRA_VALUE,
|
||||||
|
|
@ -39,5 +54,6 @@ module.exports = {
|
||||||
IMDB_LINK_CATEGORY,
|
IMDB_LINK_CATEGORY,
|
||||||
SHARE_LINK_CATEGORY,
|
SHARE_LINK_CATEGORY,
|
||||||
WRITERS_LINK_CATEGORY,
|
WRITERS_LINK_CATEGORY,
|
||||||
TYPE_PRIORITIES
|
TYPE_PRIORITIES,
|
||||||
|
ICON_FOR_TYPE
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,22 +10,9 @@ const Image = require('stremio/common/Image');
|
||||||
const Multiselect = require('stremio/common/Multiselect');
|
const Multiselect = require('stremio/common/Multiselect');
|
||||||
const PlayIconCircleCentered = require('stremio/common/PlayIconCircleCentered');
|
const PlayIconCircleCentered = require('stremio/common/PlayIconCircleCentered');
|
||||||
const useBinaryState = require('stremio/common/useBinaryState');
|
const useBinaryState = require('stremio/common/useBinaryState');
|
||||||
|
const { ICON_FOR_TYPE } = require('stremio/common/CONSTANTS');
|
||||||
const styles = require('./styles');
|
const styles = require('./styles');
|
||||||
|
|
||||||
const ICON_FOR_TYPE = new Map([
|
|
||||||
['movie', 'ic_movies'],
|
|
||||||
['series', 'ic_series'],
|
|
||||||
['channel', 'ic_channels'],
|
|
||||||
['tv', 'ic_tv'],
|
|
||||||
['book', 'ic_book'],
|
|
||||||
['game', 'ic_games'],
|
|
||||||
['music', 'ic_music'],
|
|
||||||
['adult', 'ic_adult'],
|
|
||||||
['radio', 'ic_radio'],
|
|
||||||
['podcast', 'ic_podcast'],
|
|
||||||
['other', 'ic_movies'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
const MetaItem = React.memo(({ className, type, name, poster, posterShape, playIcon, progress, options, deepLinks, dataset, optionOnSelect, ...props }) => {
|
const MetaItem = React.memo(({ className, type, name, poster, posterShape, playIcon, progress, options, deepLinks, dataset, optionOnSelect, ...props }) => {
|
||||||
const [menuOpen, onMenuOpen, onMenuClose] = useBinaryState(false);
|
const [menuOpen, onMenuOpen, onMenuClose] = useBinaryState(false);
|
||||||
const href = React.useMemo(() => {
|
const href = React.useMemo(() => {
|
||||||
|
|
|
||||||
102
src/routes/Player/NextVideoPopup/NextVideoPopup.js
Normal file
102
src/routes/Player/NextVideoPopup/NextVideoPopup.js
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
// Copyright (C) 2017-2022 Smart code 203358507
|
||||||
|
|
||||||
|
const React = require('react');
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const classnames = require('classnames');
|
||||||
|
const Icon = require('@stremio/stremio-icons/dom');
|
||||||
|
const { Image, Button, CONSTANTS } = require('stremio/common');
|
||||||
|
const styles = require('./styles');
|
||||||
|
|
||||||
|
const NextVideoPopup = ({ className, metaItem, nextVideo, onDismiss, onPlayNextVideoRequested }) => {
|
||||||
|
const watchNowButtonRef = React.useRef(null);
|
||||||
|
const [animationEnded, setAnimationEnded] = React.useState(false);
|
||||||
|
const videoName = React.useMemo(() => {
|
||||||
|
const title = nextVideo && nextVideo.title || metaItem && metaItem.title;
|
||||||
|
return nextVideo !== null &&
|
||||||
|
typeof nextVideo.season === 'number' &&
|
||||||
|
typeof nextVideo.episode === 'number' ?
|
||||||
|
`${title} (S${nextVideo.season}E${nextVideo.episode})`
|
||||||
|
:
|
||||||
|
title;
|
||||||
|
}, [metaItem, nextVideo]);
|
||||||
|
const onAnimationEnd = React.useCallback(() => {
|
||||||
|
setAnimationEnded(true);
|
||||||
|
}, []);
|
||||||
|
const renderPosterFallback = React.useCallback(() => {
|
||||||
|
return metaItem !== null && typeof metaItem.type === 'string' ?
|
||||||
|
<Icon
|
||||||
|
className={styles['placeholder-icon']}
|
||||||
|
icon={CONSTANTS.ICON_FOR_TYPE.has(metaItem.type) ? CONSTANTS.ICON_FOR_TYPE.get(metaItem.type) : CONSTANTS.ICON_FOR_TYPE.get('other')}
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
null;
|
||||||
|
}, [metaItem]);
|
||||||
|
const onDismissButtonClick = React.useCallback(() => {
|
||||||
|
if (typeof onDismiss === 'function') {
|
||||||
|
onDismiss();
|
||||||
|
}
|
||||||
|
}, [onDismiss]);
|
||||||
|
const onWatchNowButtonClick = React.useCallback(() => {
|
||||||
|
if (typeof onPlayNextVideoRequested === 'function') {
|
||||||
|
onPlayNextVideoRequested();
|
||||||
|
}
|
||||||
|
}, [onPlayNextVideoRequested]);
|
||||||
|
React.useLayoutEffect(() => {
|
||||||
|
if (animationEnded === true && watchNowButtonRef.current !== null) {
|
||||||
|
watchNowButtonRef.current.focus();
|
||||||
|
}
|
||||||
|
}, [animationEnded]);
|
||||||
|
return (
|
||||||
|
<div className={classnames(className, styles['next-video-popup-container'])} onAnimationEnd={onAnimationEnd}>
|
||||||
|
<div className={styles['poster-container']}>
|
||||||
|
<Image
|
||||||
|
className={styles['poster-image']}
|
||||||
|
src={nextVideo?.thumbnail}
|
||||||
|
alt={' '}
|
||||||
|
fallbackSrc={metaItem?.poster}
|
||||||
|
renderFallback={renderPosterFallback}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles['info-container']}>
|
||||||
|
<div className={styles['details-container']}>
|
||||||
|
{
|
||||||
|
typeof videoName === 'string' ?
|
||||||
|
<div className={styles['name']}>
|
||||||
|
{ videoName }
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
null
|
||||||
|
}
|
||||||
|
{
|
||||||
|
nextVideo !== null && typeof nextVideo.overview === 'string' ?
|
||||||
|
<div className={styles['description']}>
|
||||||
|
{ nextVideo.overview }
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
null
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className={styles['buttons-container']}>
|
||||||
|
<Button className={styles['button-container']} onClick={onDismissButtonClick}>
|
||||||
|
<Icon className={styles['icon']} icon={'ic_x'} />
|
||||||
|
<div className={styles['label']}>Dismiss</div>
|
||||||
|
</Button>
|
||||||
|
<Button ref={watchNowButtonRef} className={classnames(styles['button-container'], styles['play-button'])} onClick={onWatchNowButtonClick}>
|
||||||
|
<Icon className={styles['icon']} icon={'ic_play'} />
|
||||||
|
<div className={styles['label']}>Watch Now</div>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
NextVideoPopup.propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
metaItem: PropTypes.object,
|
||||||
|
nextVideo: PropTypes.object,
|
||||||
|
onDismiss: PropTypes.func,
|
||||||
|
onPlayNextVideoRequested: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = NextVideoPopup;
|
||||||
5
src/routes/Player/NextVideoPopup/index.js
Normal file
5
src/routes/Player/NextVideoPopup/index.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
// Copyright (C) 2017-2022 Smart code 203358507
|
||||||
|
|
||||||
|
const NextVideoPopup = require('./NextVideoPopup');
|
||||||
|
|
||||||
|
module.exports = NextVideoPopup;
|
||||||
124
src/routes/Player/NextVideoPopup/styles.less
Normal file
124
src/routes/Player/NextVideoPopup/styles.less
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
// Copyright (C) 2017-2022 Smart code 203358507
|
||||||
|
|
||||||
|
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
|
||||||
|
|
||||||
|
.next-video-popup-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 16rem;
|
||||||
|
width: 40rem;
|
||||||
|
animation: slide-fade-in 0.5s ease-in;
|
||||||
|
|
||||||
|
@keyframes slide-fade-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(calc(40rem + 2rem));
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.poster-container {
|
||||||
|
flex: 1 1 40%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-color: @color-background;
|
||||||
|
|
||||||
|
.poster-image {
|
||||||
|
flex: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-position: center;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-icon {
|
||||||
|
flex: none;
|
||||||
|
width: 80%;
|
||||||
|
height: 50%;
|
||||||
|
fill: @color-background-light3-90;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-container {
|
||||||
|
flex: 1 1 70%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.details-container {
|
||||||
|
flex: auto;
|
||||||
|
padding: 1.5rem 1.5rem;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
flex: none;
|
||||||
|
align-self: stretch;
|
||||||
|
max-height: 2.4em;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
color: @color-surface-light5-90;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
color: @color-surface-light5-50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.spacing {
|
||||||
|
flex: 0 0 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-container {
|
||||||
|
flex: 0 0 50%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 3.5rem;
|
||||||
|
|
||||||
|
&.play-button {
|
||||||
|
background-color: @color-accent3;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
fill: @color-surface-light5-90;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
color: @color-surface-light5-90;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
background-color: @color-accent3-light1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
flex: none;
|
||||||
|
width: 1.4rem;
|
||||||
|
height: 1.4rem;
|
||||||
|
margin-right: 1rem;
|
||||||
|
fill: @color-secondaryvariant1-90;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
flex: none;
|
||||||
|
max-height: 2.4em;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: @color-secondaryvariant1-90;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
background-color: @color-background-light2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ const { HorizontalNavBar, Button, useFullscreen, useBinaryState, useToast, useSt
|
||||||
const Icon = require('@stremio/stremio-icons/dom');
|
const Icon = require('@stremio/stremio-icons/dom');
|
||||||
const BufferingLoader = require('./BufferingLoader');
|
const BufferingLoader = require('./BufferingLoader');
|
||||||
const ControlBar = require('./ControlBar');
|
const ControlBar = require('./ControlBar');
|
||||||
|
const NextVideoPopup = require('./NextVideoPopup');
|
||||||
const InfoMenu = require('./InfoMenu');
|
const InfoMenu = require('./InfoMenu');
|
||||||
const OptionsMenu = require('./OptionsMenu');
|
const OptionsMenu = require('./OptionsMenu');
|
||||||
const VideosMenu = require('./VideosMenu');
|
const VideosMenu = require('./VideosMenu');
|
||||||
|
|
@ -45,6 +46,8 @@ const Player = ({ urlParams, queryParams }) => {
|
||||||
const [infoMenuOpen, , closeInfoMenu, toggleInfoMenu] = useBinaryState(false);
|
const [infoMenuOpen, , closeInfoMenu, toggleInfoMenu] = useBinaryState(false);
|
||||||
const [speedMenuOpen, , closeSpeedMenu, toggleSpeedMenu] = useBinaryState(false);
|
const [speedMenuOpen, , closeSpeedMenu, toggleSpeedMenu] = useBinaryState(false);
|
||||||
const [videosMenuOpen, , closeVideosMenu, toggleVideosMenu] = useBinaryState(false);
|
const [videosMenuOpen, , closeVideosMenu, toggleVideosMenu] = useBinaryState(false);
|
||||||
|
const [nextVideoPopupOpen, openNextVideoPopup, closeNextVideoPopup] = useBinaryState(false);
|
||||||
|
const nextVideoPopupDismissed = React.useRef(false);
|
||||||
const defaultSubtitlesSelected = React.useRef(false);
|
const defaultSubtitlesSelected = React.useRef(false);
|
||||||
const defaultAudioTrackSelected = React.useRef(false);
|
const defaultAudioTrackSelected = React.useRef(false);
|
||||||
const [error, setError] = React.useState(null);
|
const [error, setError] = React.useState(null);
|
||||||
|
|
@ -108,16 +111,11 @@ const Player = ({ urlParams, queryParams }) => {
|
||||||
ended();
|
ended();
|
||||||
pushToLibrary();
|
pushToLibrary();
|
||||||
if (player.nextVideo !== null) {
|
if (player.nextVideo !== null) {
|
||||||
window.location.replace(
|
onPlayNextVideoRequested();
|
||||||
typeof player.nextVideo.deepLinks.player === 'string' ?
|
|
||||||
player.nextVideo.deepLinks.player
|
|
||||||
:
|
|
||||||
player.nextVideo.deepLinks.metaDetailsStreams
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
window.history.back();
|
window.history.back();
|
||||||
}
|
}
|
||||||
}, [player.libraryItem, player.nextVideo]);
|
}, [player.nextVideo, onPlayNextVideoRequested]);
|
||||||
const onError = React.useCallback((error) => {
|
const onError = React.useCallback((error) => {
|
||||||
console.error('Player', error);
|
console.error('Player', error);
|
||||||
if (error.critical) {
|
if (error.critical) {
|
||||||
|
|
@ -190,6 +188,20 @@ const Player = ({ urlParams, queryParams }) => {
|
||||||
const onSubtitlesOffsetChanged = React.useCallback((offset) => {
|
const onSubtitlesOffsetChanged = React.useCallback((offset) => {
|
||||||
updateSettings({ subtitlesOffset: offset });
|
updateSettings({ subtitlesOffset: offset });
|
||||||
}, [updateSettings]);
|
}, [updateSettings]);
|
||||||
|
const onDismissNextVideoPopup = React.useCallback(() => {
|
||||||
|
closeNextVideoPopup();
|
||||||
|
nextVideoPopupDismissed.current = true;
|
||||||
|
}, []);
|
||||||
|
const onPlayNextVideoRequested = React.useCallback(() => {
|
||||||
|
if (player.nextVideo !== null) {
|
||||||
|
window.location.replace(
|
||||||
|
typeof player.nextVideo.deepLinks.player === 'string' ?
|
||||||
|
player.nextVideo.deepLinks.player
|
||||||
|
:
|
||||||
|
player.nextVideo.deepLinks.metaDetailsStreams
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [player.nextVideo]);
|
||||||
const onVideoClick = React.useCallback(() => {
|
const onVideoClick = React.useCallback(() => {
|
||||||
if (videoState.paused !== null) {
|
if (videoState.paused !== null) {
|
||||||
if (videoState.paused) {
|
if (videoState.paused) {
|
||||||
|
|
@ -333,6 +345,15 @@ const Player = ({ urlParams, queryParams }) => {
|
||||||
pausedChanged(videoState.paused);
|
pausedChanged(videoState.paused);
|
||||||
}
|
}
|
||||||
}, [videoState.paused]);
|
}, [videoState.paused]);
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!!settings.bingeWatching && player.nextVideo !== null && !nextVideoPopupDismissed.current) {
|
||||||
|
if (videoState.time !== null && videoState.duration !== null && videoState.time < videoState.duration && (videoState.duration - videoState.time) <= settings.nextVideoNotificationDuration) {
|
||||||
|
openNextVideoPopup();
|
||||||
|
} else {
|
||||||
|
closeNextVideoPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [player.nextVideo, videoState.time, videoState.duration]);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!defaultSubtitlesSelected.current) {
|
if (!defaultSubtitlesSelected.current) {
|
||||||
const findTrackByLang = (tracks, lang) => tracks.find((track) => track.lang === lang || langs.where('1', track.lang)?.[2] === lang);
|
const findTrackByLang = (tracks, lang) => tracks.find((track) => track.lang === lang || langs.where('1', track.lang)?.[2] === lang);
|
||||||
|
|
@ -363,6 +384,7 @@ const Player = ({ urlParams, queryParams }) => {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
defaultSubtitlesSelected.current = false;
|
defaultSubtitlesSelected.current = false;
|
||||||
defaultAudioTrackSelected.current = false;
|
defaultAudioTrackSelected.current = false;
|
||||||
|
nextVideoPopupDismissed.current = false;
|
||||||
}, [videoState.stream]);
|
}, [videoState.stream]);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if ((!Array.isArray(videoState.subtitlesTracks) || videoState.subtitlesTracks.length === 0) &&
|
if ((!Array.isArray(videoState.subtitlesTracks) || videoState.subtitlesTracks.length === 0) &&
|
||||||
|
|
@ -514,6 +536,7 @@ const Player = ({ urlParams, queryParams }) => {
|
||||||
closeInfoMenu();
|
closeInfoMenu();
|
||||||
closeSpeedMenu();
|
closeSpeedMenu();
|
||||||
closeVideosMenu();
|
closeVideosMenu();
|
||||||
|
onDismissNextVideoPopup();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -533,7 +556,7 @@ const Player = ({ urlParams, queryParams }) => {
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
<div className={classnames(styles['player-container'], { [styles['immersed']]: immersed && !casting && videoState.paused !== null && !videoState.paused && !subtitlesMenuOpen && !infoMenuOpen && !speedMenuOpen && !videosMenuOpen && !optionsMenuOpen })}
|
<div className={classnames(styles['player-container'], { [styles['immersed']]: immersed && !casting && videoState.paused !== null && !videoState.paused && !subtitlesMenuOpen && !infoMenuOpen && !speedMenuOpen && !videosMenuOpen && !nextVideoPopupOpen && !optionsMenuOpen })}
|
||||||
onMouseDown={onContainerMouseDown}
|
onMouseDown={onContainerMouseDown}
|
||||||
onMouseMove={onContainerMouseMove}
|
onMouseMove={onContainerMouseMove}
|
||||||
onMouseOver={onContainerMouseMove}
|
onMouseOver={onContainerMouseMove}
|
||||||
|
|
@ -617,6 +640,18 @@ const Player = ({ urlParams, queryParams }) => {
|
||||||
onMouseMove={onBarMouseMove}
|
onMouseMove={onBarMouseMove}
|
||||||
onMouseOver={onBarMouseMove}
|
onMouseOver={onBarMouseMove}
|
||||||
/>
|
/>
|
||||||
|
{
|
||||||
|
nextVideoPopupOpen ?
|
||||||
|
<NextVideoPopup
|
||||||
|
className={classnames(styles['layer'], styles['menu-layer'])}
|
||||||
|
metaItem={player.metaItem !== null && player.metaItem.type === 'Ready' ? player.metaItem.content : null}
|
||||||
|
nextVideo={player.nextVideo}
|
||||||
|
onDismiss={onDismissNextVideoPopup}
|
||||||
|
onPlayNextVideoRequested={onPlayNextVideoRequested}
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
null
|
||||||
|
}
|
||||||
{
|
{
|
||||||
subtitlesMenuOpen ?
|
subtitlesMenuOpen ?
|
||||||
<SubtitlesMenu
|
<SubtitlesMenu
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ const Settings = () => {
|
||||||
subtitlesOutlineColorInput,
|
subtitlesOutlineColorInput,
|
||||||
audioLanguageSelect,
|
audioLanguageSelect,
|
||||||
seekTimeDurationSelect,
|
seekTimeDurationSelect,
|
||||||
|
nextVideoPopupDurationSelect,
|
||||||
bingeWatchingCheckbox,
|
bingeWatchingCheckbox,
|
||||||
playInBackgroundCheckbox,
|
playInBackgroundCheckbox,
|
||||||
playInExternalPlayerCheckbox,
|
playInExternalPlayerCheckbox,
|
||||||
|
|
@ -338,6 +339,16 @@ const Settings = () => {
|
||||||
{...bingeWatchingCheckbox}
|
{...bingeWatchingCheckbox}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className={styles['option-container']}>
|
||||||
|
<div className={styles['option-name-container']}>
|
||||||
|
<div className={styles['label']}>Next video popup duration</div>
|
||||||
|
</div>
|
||||||
|
<Multiselect
|
||||||
|
className={classnames(styles['option-input-container'], styles['multiselect-container'])}
|
||||||
|
disabled={!profile.settings.bingeWatching}
|
||||||
|
{...nextVideoPopupDurationSelect}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className={styles['option-container']}>
|
<div className={styles['option-container']}>
|
||||||
<div className={styles['option-name-container']}>
|
<div className={styles['option-name-container']}>
|
||||||
<div className={styles['label']}>Play in background</div>
|
<div className={styles['label']}>Play in background</div>
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,31 @@ const useProfileSettingsInputs = (profile) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}), [profile.settings]);
|
}), [profile.settings]);
|
||||||
|
const nextVideoPopupDurationSelect = React.useMemo(() => ({
|
||||||
|
options: CONSTANTS.NEXT_VIDEO_POPUP_DURATIONS.map((duration) => ({
|
||||||
|
value: `${duration}`,
|
||||||
|
label: duration === 0 ? 'Disabled' : `${duration / 1000} seconds`
|
||||||
|
})),
|
||||||
|
selected: [`${profile.settings.nextVideoNotificationDuration}`],
|
||||||
|
renderLabelText: () => {
|
||||||
|
return profile.settings.nextVideoNotificationDuration === 0 ?
|
||||||
|
'Disabled'
|
||||||
|
:
|
||||||
|
`${profile.settings.nextVideoNotificationDuration / 1000} seconds`;
|
||||||
|
},
|
||||||
|
onSelect: (event) => {
|
||||||
|
core.transport.dispatch({
|
||||||
|
action: 'Ctx',
|
||||||
|
args: {
|
||||||
|
action: 'UpdateSettings',
|
||||||
|
args: {
|
||||||
|
...profile.settings,
|
||||||
|
nextVideoNotificationDuration: parseInt(event.value, 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}), [profile.settings]);
|
||||||
const bingeWatchingCheckbox = React.useMemo(() => ({
|
const bingeWatchingCheckbox = React.useMemo(() => ({
|
||||||
checked: profile.settings.bingeWatching,
|
checked: profile.settings.bingeWatching,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
|
|
@ -237,6 +262,7 @@ const useProfileSettingsInputs = (profile) => {
|
||||||
subtitlesOutlineColorInput,
|
subtitlesOutlineColorInput,
|
||||||
audioLanguageSelect,
|
audioLanguageSelect,
|
||||||
seekTimeDurationSelect,
|
seekTimeDurationSelect,
|
||||||
|
nextVideoPopupDurationSelect,
|
||||||
bingeWatchingCheckbox,
|
bingeWatchingCheckbox,
|
||||||
playInBackgroundCheckbox,
|
playInBackgroundCheckbox,
|
||||||
playInExternalPlayerCheckbox,
|
playInExternalPlayerCheckbox,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue