Merge branch 'development' into Web-app-post-redesign-fixes

This commit is contained in:
kKaskak 2023-10-17 12:38:47 +03:00
commit 4b5d95b91d
23 changed files with 362 additions and 81 deletions

2
.dockerignore Normal file
View file

@ -0,0 +1,2 @@
screenshots/*
screenshots*

View file

@ -1,24 +1,31 @@
# Stremio Node 14.x
FROM stremio/node-base:fermium
# Meta
LABEL Description="Stremio Web" Vendor="Smart Code OOD" Version="1.0.0"
# Update GitHub remote host key
RUN echo "github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=" >> ~/.ssh/known_hosts
# Create app directory
RUN mkdir -p /var/www/stremio-web
# Install app dependencies
WORKDIR /var/www/stremio-web
COPY . /var/www/stremio-web
RUN npm install
# Bundle app source
WORKDIR /var/www/stremio-web
RUN npm run build
EXPOSE 8080
CMD ["node", "http_server.js"]
# Stremio Node 14.x
# the node version for running Stremio Web
ARG NODE_VERSION=15-alpine
FROM node:$NODE_VERSION AS base
# Meta
LABEL Description="Stremio Web" Vendor="Smart Code OOD" Version="1.0.0"
RUN mkdir -p /var/www/stremio-web
WORKDIR /var/www/stremio-web
# Install app dependencies
FROM base AS prebuild
RUN apk update && apk upgrade && \
apk add --no-cache git
WORKDIR /var/www/stremio-web
COPY . .
RUN npm install
RUN npm run build
# Bundle app source
FROM base AS final
WORKDIR /var/www/stremio-web
COPY . .
COPY --from=prebuild /var/www/stremio-web/node_modules ./node_modules
COPY --from=prebuild /var/www/stremio-web/build ./build
EXPOSE 8080
CMD ["node", "http_server.js"]

View file

@ -1,7 +1,7 @@
# Stremio - Freedom to Stream
![Build](https://github.com/stremio/stremio-web/workflows/Build/badge.svg?branch=development)
[![Github Page](https://img.shields.io/website?down_message=offline&label=Page&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iOTgiIGhlaWdodD0iOTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI%2BPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00OC44NTQgMEMyMS44MzkgMCAwIDIyIDAgNDkuMjE3YzAgMjEuNzU2IDEzLjk5MyA0MC4xNzIgMzMuNDA1IDQ2LjY5IDIuNDI3LjQ5IDMuMzE2LTEuMDU5IDMuMzE2LTIuMzYyIDAtMS4xNDEtLjA4LTUuMDUyLS4wOC05LjEyNy0xMy41OSAyLjkzNC0xNi40Mi01Ljg2Ny0xNi40Mi01Ljg2Ny0yLjE4NC01LjcwNC01LjQyLTcuMTctNS40Mi03LjE3LTQuNDQ4LTMuMDE1LjMyNC0zLjAxNS4zMjQtMy4wMTUgNC45MzQuMzI2IDcuNTIzIDUuMDUyIDcuNTIzIDUuMDUyIDQuMzY3IDcuNDk2IDExLjQwNCA1LjM3OCAxNC4yMzUgNC4wNzQuNDA0LTMuMTc4IDEuNjk5LTUuMzc4IDMuMDc0LTYuNi0xMC44MzktMS4xNDEtMjIuMjQzLTUuMzc4LTIyLjI0My0yNC4yODMgMC01LjM3OCAxLjk0LTkuNzc4IDUuMDE0LTEzLjItLjQ4NS0xLjIyMi0yLjE4NC02LjI3NS40ODYtMTMuMDM4IDAgMCA0LjEyNS0xLjMwNCAxMy40MjYgNS4wNTJhNDYuOTcgNDYuOTcgMCAwIDEgMTIuMjE0LTEuNjNjNC4xMjUgMCA4LjMzLjU3MSAxMi4yMTMgMS42MyA5LjMwMi02LjM1NiAxMy40MjctNS4wNTIgMTMuNDI3LTUuMDUyIDIuNjcgNi43NjMuOTcgMTEuODE2LjQ4NSAxMy4wMzggMy4xNTUgMy40MjIgNS4wMTUgNy44MjIgNS4wMTUgMTMuMiAwIDE4LjkwNS0xMS40MDQgMjMuMDYtMjIuMzI0IDI0LjI4MyAxLjc4IDEuNTQ4IDMuMzE2IDQuNDgxIDMuMzE2IDkuMTI2IDAgNi42LS4wOCAxMS44OTctLjA4IDEzLjUyNiAwIDEuMzA0Ljg5IDIuODUzIDMuMzE2IDIuMzY0IDE5LjQxMi02LjUyIDMzLjQwNS0yNC45MzUgMzMuNDA1LTQ2LjY5MUM5Ny43MDcgMjIgNzUuNzg4IDAgNDguODU0IDB6IiBmaWxsPSIjZmZmIi8%2BPC9zdmc%2B&up_message=online&url=https%3A%2F%2Fstremio.github.io%2Fstremio-web%2F)](https://stremio.github.io/stremio-web/)
[![Github Page](https://img.shields.io/website?down_message=offline&label=Page&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iOTgiIGhlaWdodD0iOTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI%2BPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00OC44NTQgMEMyMS44MzkgMCAwIDIyIDAgNDkuMjE3YzAgMjEuNzU2IDEzLjk5MyA0MC4xNzIgMzMuNDA1IDQ2LjY5IDIuNDI3LjQ5IDMuMzE2LTEuMDU5IDMuMzE2LTIuMzYyIDAtMS4xNDEtLjA4LTUuMDUyLS4wOC05LjEyNy0xMy41OSAyLjkzNC0xNi40Mi01Ljg2Ny0xNi40Mi01Ljg2Ny0yLjE4NC01LjcwNC01LjQyLTcuMTctNS40Mi03LjE3LTQuNDQ4LTMuMDE1LjMyNC0zLjAxNS4zMjQtMy4wMTUgNC45MzQuMzI2IDcuNTIzIDUuMDUyIDcuNTIzIDUuMDUyIDQuMzY3IDcuNDk2IDExLjQwNCA1LjM3OCAxNC4yMzUgNC4wNzQuNDA0LTMuMTc4IDEuNjk5LTUuMzc4IDMuMDc0LTYuNi0xMC44MzktMS4xNDEtMjIuMjQzLTUuMzc4LTIyLjI0My0yNC4yODMgMC01LjM3OCAxLjk0LTkuNzc4IDUuMDE0LTEzLjItLjQ4NS0xLjIyMi0yLjE4NC02LjI3NS40ODYtMTMuMDM4IDAgMCA0LjEyNS0xLjMwNCAxMy40MjYgNS4wNTJhNDYuOTcgNDYuOTcgMCAwIDEgMTIuMjE0LTEuNjNjNC4xMjUgMCA4LjMzLjU3MSAxMi4yMTMgMS42MyA5LjMwMi02LjM1NiAxMy40MjctNS4wNTIgMTMuNDI3LTUuMDUyIDIuNjcgNi43NjMuOTcgMTEuODE2LjQ4NSAxMy4wMzggMy4xNTUgMy40MjIgNS4wMTUgNy44MjIgNS4wMTUgMTMuMiAwIDE4LjkwNS0xMS40MDQgMjMuMDYtMjIuMzI0IDI0LjI4MyAxLjc4IDEuNTQ4IDMuMzE2IDQuNDgxIDMuMzE2IDkuMTI2IDAgNi42LS4wOCAxMS44OTctLjA4IDEzLjUyNiAwIDEuMzA0Ljg5IDIuODUzIDMuMzE2IDIuMzY0IDE5LjQxMi02LjUyIDMzLjQwNS0yNC45MzUgMzMuNDA1LTQ2LjY5MUM5Ny43MDcgMjIgNzUuNzg4IDAgNDguODU0IDB6IiBmaWxsPSIjZmZmIi8%2BPC9zdmc%2B&up_message=online&url=https%3A%2F%2Fstremio.github.io%2Fstremio-web%2F)](https://stremio.github.io/stremio-web/development)
Stremio is a modern media center that's a one-stop solution for your video entertainment. You discover, watch and organize video content from easy to install addons.
@ -46,4 +46,4 @@ npm run build
## License
Stremio is copyright 2017-2022 Smart code and available under GPLv2 license. See the [LICENSE](/LICENSE.md) file in the project for more information.
Stremio is copyright 2017-2023 Smart code and available under GPLv2 license. See the [LICENSE](/LICENSE.md) file in the project for more information.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 19 KiB

28
package-lock.json generated
View file

@ -12,9 +12,9 @@
"@babel/runtime": "7.16.0",
"@sentry/browser": "6.13.3",
"@stremio/stremio-colors": "5.0.1",
"@stremio/stremio-core-web": "0.44.23",
"@stremio/stremio-core-web": "0.44.25",
"@stremio/stremio-icons": "5.0.0-beta.3",
"@stremio/stremio-video": "0.0.24",
"@stremio/stremio-video": "0.0.25-rc.2",
"a-color-picker": "1.2.1",
"bowser": "2.11.0",
"buffer": "6.0.3",
@ -2704,9 +2704,9 @@
"integrity": "sha512-Dt3PYmy1DZ473QNs99KYXVWQPHtpIl37VUY0+gCEvvuCqE1fRrZIJtZ9KbysUKonvO7WwdQDztgcW0iGoc1dEA=="
},
"node_modules/@stremio/stremio-core-web": {
"version": "0.44.23",
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.23.tgz",
"integrity": "sha512-I3t0jxZdyNTLIE0w2O1fJIQ562BJc2iN26xL056KiD5FIuGfTAJ/vdHTiPAbcmEXsO3TPxX9fOGsntNEhnO2cA==",
"version": "0.44.25",
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.25.tgz",
"integrity": "sha512-kW/AAh+c1qnfV5xFbfM+VFFvuRXp4M1pQuUj94O6my2kC39zKkZnFMQRDNbzoodoUQY0fusDM1K7rIw3DmlqEg==",
"dependencies": {
"@babel/runtime": "7.16.0"
}
@ -2717,9 +2717,9 @@
"integrity": "sha512-K+jDsizEgxpBC+b0HExCUg+bnsWPZnx96qUfkdQ9nBDVAN/kzcP24Jq/4KwkiEyqDEcvC5l+xBzOLzkhdLwTMw=="
},
"node_modules/@stremio/stremio-video": {
"version": "0.0.24",
"resolved": "https://registry.npmjs.org/@stremio/stremio-video/-/stremio-video-0.0.24.tgz",
"integrity": "sha512-k9bSyQBbZQnLll62P/enc/gy2eILZrW0xuU/MRxxTm9gtjo+8JLp/77H0a4tQFFHjJ9WPuNj4K6Lo2UnGem3kg==",
"version": "0.0.25-rc.2",
"resolved": "https://registry.npmjs.org/@stremio/stremio-video/-/stremio-video-0.0.25-rc.2.tgz",
"integrity": "sha512-OXNimBgPpkin5gX39Bsx/w6M+5ifP71amZEYlvsZz3CDKxGHkQTPLzl4z6RrbVr8wz8nt6eiWIj8BKxa4/u6nQ==",
"dependencies": {
"buffer": "6.0.3",
"color": "4.2.3",
@ -16834,9 +16834,9 @@
"integrity": "sha512-Dt3PYmy1DZ473QNs99KYXVWQPHtpIl37VUY0+gCEvvuCqE1fRrZIJtZ9KbysUKonvO7WwdQDztgcW0iGoc1dEA=="
},
"@stremio/stremio-core-web": {
"version": "0.44.23",
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.23.tgz",
"integrity": "sha512-I3t0jxZdyNTLIE0w2O1fJIQ562BJc2iN26xL056KiD5FIuGfTAJ/vdHTiPAbcmEXsO3TPxX9fOGsntNEhnO2cA==",
"version": "0.44.25",
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.25.tgz",
"integrity": "sha512-kW/AAh+c1qnfV5xFbfM+VFFvuRXp4M1pQuUj94O6my2kC39zKkZnFMQRDNbzoodoUQY0fusDM1K7rIw3DmlqEg==",
"requires": {
"@babel/runtime": "7.16.0"
}
@ -16847,9 +16847,9 @@
"integrity": "sha512-K+jDsizEgxpBC+b0HExCUg+bnsWPZnx96qUfkdQ9nBDVAN/kzcP24Jq/4KwkiEyqDEcvC5l+xBzOLzkhdLwTMw=="
},
"@stremio/stremio-video": {
"version": "0.0.24",
"resolved": "https://registry.npmjs.org/@stremio/stremio-video/-/stremio-video-0.0.24.tgz",
"integrity": "sha512-k9bSyQBbZQnLll62P/enc/gy2eILZrW0xuU/MRxxTm9gtjo+8JLp/77H0a4tQFFHjJ9WPuNj4K6Lo2UnGem3kg==",
"version": "0.0.25-rc.2",
"resolved": "https://registry.npmjs.org/@stremio/stremio-video/-/stremio-video-0.0.25-rc.2.tgz",
"integrity": "sha512-OXNimBgPpkin5gX39Bsx/w6M+5ifP71amZEYlvsZz3CDKxGHkQTPLzl4z6RrbVr8wz8nt6eiWIj8BKxa4/u6nQ==",
"requires": {
"buffer": "6.0.3",
"color": "4.2.3",

View file

@ -15,9 +15,9 @@
"@babel/runtime": "7.16.0",
"@sentry/browser": "6.13.3",
"@stremio/stremio-colors": "5.0.1",
"@stremio/stremio-core-web": "0.44.23",
"@stremio/stremio-core-web": "0.44.25",
"@stremio/stremio-icons": "5.0.0-beta.3",
"@stremio/stremio-video": "0.0.24",
"@stremio/stremio-video": "0.0.25-rc.2",
"a-color-picker": "1.2.1",
"bowser": "2.11.0",
"buffer": "6.0.3",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 976 KiB

After

Width:  |  Height:  |  Size: 1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 947 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View file

@ -6,7 +6,7 @@ const { useTranslation } = require('react-i18next');
const { Router } = require('stremio-router');
const { Core, Shell, Chromecast, DragAndDrop, KeyboardShortcuts, ServicesProvider } = require('stremio/services');
const { NotFound } = require('stremio/routes');
const { ToastProvider, CONSTANTS, withCoreSuspender } = require('stremio/common');
const { ToastProvider, TooltipProvider, CONSTANTS, withCoreSuspender } = require('stremio/common');
const ServicesToaster = require('./ServicesToaster');
const DeepLinkHandler = require('./DeepLinkHandler');
const ErrorDialog = require('./ErrorDialog');
@ -159,13 +159,15 @@ const App = () => {
<ErrorDialog className={styles['error-container']} />
:
<ToastProvider className={styles['toasts-container']}>
<ServicesToaster />
<DeepLinkHandler />
<RouterWithProtectedRoutes
className={styles['router']}
viewsConfig={routerViewsConfig}
onPathNotMatch={onPathNotMatch}
/>
<TooltipProvider className={styles['tooltip-container']}>
<ServicesToaster />
<DeepLinkHandler />
<RouterWithProtectedRoutes
className={styles['router']}
viewsConfig={routerViewsConfig}
onPathNotMatch={onPathNotMatch}
/>
</TooltipProvider>
</ToastProvider>
:
<div className={styles['loader-container']} />

View file

@ -117,6 +117,19 @@ html {
}
}
.tooltip-container {
height: 2.5rem;
display: flex;
align-items: center;
justify-content: center;
padding: 0 1.5rem;
font-size: 1rem;
color: var(--primary-foreground-color);
border-radius: var(--border-radius);
background-color: var(--modal-background-color);
transition: opacity 0.25s ease-out;
}
.router {
width: 100%;
height: 100%;

View file

@ -90,9 +90,14 @@ const NavMenuContent = ({ onClick }) => {
<Button className={styles['nav-menu-option-container']} title={ t('PRIVACY_POLICY') } href={'https://www.stremio.com/privacy'} target={'_blank'}>
<div className={styles['nav-menu-option-label']}>{ t('PRIVACY_POLICY') }</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={ t('ABOUT_STREMIO') } href={'https://www.stremio.com/'} target={'_blank'}>
<div className={styles['nav-menu-option-label']}>{ t('ABOUT_STREMIO') }</div>
</Button>
{
profile.auth !== null ?
<Button className={styles['nav-menu-option-container']} title={ t('USER_PANEL') } href={'https://www.stremio.com/acc-settings'} target={'_blank'}>
<div className={styles['nav-menu-option-label']}>{ t('USER_PANEL') }</div>
</Button>
:
null
}
</div>
</div>
);

View file

@ -0,0 +1,63 @@
// Copyright (C) 2017-2023 Smart code 203358507
const React = require('react');
const PropTypes = require('prop-types');
const useTooltip = require('./useTooltip');
const styles = require('./styles');
const createId = () => (Math.random() + 1).toString(36).substring(7);
const Tooltip = ({ label, position, margin }) => {
const tooltip = useTooltip();
const id = React.useRef(createId());
const element = React.useRef(null);
const onMouseEnter = () => {
tooltip.toggle(id.current, true);
};
const onMouseLeave = () => {
tooltip.toggle(id.current, false);
};
React.useEffect(() => {
if (element.current && element.current.parentElement) {
const parentElement = element.current.parentElement;
tooltip.add({
id: id.current,
label,
position,
margin,
parent: parentElement,
});
parentElement.addEventListener('mouseenter', onMouseEnter);
parentElement.addEventListener('mouseleave', onMouseLeave);
}
return () => {
if (element.current && element.current.parentElement) {
const parentElement = element.current.parentElement;
parentElement.removeEventListener('mouseenter', onMouseEnter);
parentElement.removeEventListener('mouseleave', onMouseLeave);
tooltip.remove(id.current);
}
};
}, []);
return (
<div ref={element} className={styles['tooltip']} />
);
};
Tooltip.displayName = 'Tooltip';
Tooltip.propTypes = {
label: PropTypes.string.isRequired,
position: PropTypes.string.isRequired,
margin: PropTypes.number,
};
module.exports = Tooltip;

View file

@ -0,0 +1,8 @@
// Copyright (C) 2017-2023 Smart code 203358507
const { createContext } = require('react');
const TooltipContext = createContext(null);
module.exports = TooltipContext;

View file

@ -0,0 +1,106 @@
// Copyright (C) 2017-2023 Smart code 203358507
const React = require('react');
const { useState, createRef } = require('react');
const PropTypes = require('prop-types');
const classNames = require('classnames');
const TooltipContext = require('./TooltipContext');
const styles = require('./styles');
const TooltipProvider = ({ children, className }) => {
const [tooltips, setTooltips] = useState([]);
const add = ({ id, label, position, margin = 15, parent }) => {
const ref = createRef(null);
const tooltip = {
ref,
id,
label,
position,
margin: margin,
active: false,
parent,
};
setTooltips((tooltips) => ([
...tooltips,
tooltip,
]));
};
const remove = (id) => {
setTooltips((tooltips) => (
tooltips.filter((tooltip) => tooltip.id !== id)
));
};
const toggle = (id, state) => {
setTooltips((tooltips) => (
tooltips.map((tooltip) => {
if (tooltip.id === id) {
tooltip.active = state;
}
return tooltip;
})
));
};
const style = (ref, position, margin, active, parent) => {
if (!active) return {};
const tooltipHeight = ref.current?.offsetHeight ?? 0;
const tooltipWidth = ref.current?.offsetWidth ?? 0;
const parentBounds = parent.getBoundingClientRect();
switch (position) {
case 'top':
return {
top: `${parentBounds.top - tooltipHeight - margin}px`,
left: `${(parentBounds.left + (parentBounds.width / 2)) - (tooltipWidth / 2)}px`,
};
case 'bottom':
return {
top: `${parentBounds.top + parentBounds.height + margin}px`,
left: `${(parentBounds.left + (parentBounds.width / 2)) - (tooltipWidth / 2)}px`,
};
case 'left':
return {
top: `${parentBounds.top + (parentBounds.height / 2) - (tooltipHeight / 2)}px`,
left: `${(parentBounds.left - tooltipWidth - margin)}px`,
};
case 'right':
return {
top: `${parentBounds.top + (parentBounds.height / 2) - (tooltipHeight / 2)}px`,
left: `${(parentBounds.left + parentBounds.width + margin)}px`,
};
}
};
return (
<TooltipContext.Provider value={{ add, remove, toggle }}>
{ children }
<div className={'tooltips-container'}>
{
tooltips.map(({ ref, id, label, position, margin, active, parent }) => (
<div
key={id}
ref={ref}
className={classNames(className, styles['tooltip-container'], { 'active': active })}
style={style(ref, position, margin, active, parent)}
>
{ label }
</div>
))
}
</div>
</TooltipContext.Provider>
);
};
TooltipProvider.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
};
module.exports = TooltipProvider;

View file

@ -0,0 +1,9 @@
// Copyright (C) 2017-2023 Smart code 203358507
const TooltipProvider = require('./TooltipProvider');
const Tooltip = require('./Tooltip');
module.exports = {
TooltipProvider,
Tooltip,
};

View file

@ -0,0 +1,22 @@
// Copyright (C) 2017-2023 Smart code 203358507
.tooltip {
z-index: -1;
visibility: hidden;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.tooltip-container {
position: fixed;
visibility: hidden;
opacity: 0;
&:global(.active) {
visibility: visible;
opacity: 1;
}
}

View file

@ -0,0 +1,10 @@
// Copyright (C) 2017-2023 Smart code 203358507
const React = require('react');
const TooltipContext = require('./TooltipContext');
const useTooltip = () => {
return React.useContext(TooltipContext);
};
module.exports = useTooltip;

View file

@ -23,6 +23,7 @@ const SharePrompt = require('./SharePrompt');
const Slider = require('./Slider');
const TextInput = require('./TextInput');
const { ToastProvider, useToast } = require('./Toast');
const { TooltipProvider, Tooltip } = require('./Tooltip');
const comparatorWithPriorities = require('./comparatorWithPriorities');
const CONSTANTS = require('./CONSTANTS');
const { withCoreSuspender, useCoreSuspender } = require('./CoreSuspender');
@ -70,6 +71,8 @@ module.exports = {
TextInput,
ToastProvider,
useToast,
TooltipProvider,
Tooltip,
comparatorWithPriorities,
CONSTANTS,
withCoreSuspender,

View file

@ -33,6 +33,28 @@ const Player = ({ urlParams, queryParams }) => {
queryParams.has('maxAudioChannels') ? parseInt(queryParams.get('maxAudioChannels'), 10) : null
];
}, [queryParams]);
const [player, videoParamsChanged, timeChanged, pausedChanged, ended] = usePlayer(urlParams);
const [settings, updateSettings] = useSettings();
const streamingServer = useStreamingServer();
const routeFocused = useRouteFocused();
const toast = useToast();
const [, , , toggleFullscreen] = useFullscreen();
const [casting, setCasting] = React.useState(() => {
return chromecast.active && chromecast.transport.getCastState() === cast.framework.CastState.CONNECTED;
});
const [immersed, setImmersed] = React.useState(true);
const setImmersedDebounced = React.useCallback(debounce(setImmersed, 3000), []);
const [optionsMenuOpen, , closeOptionsMenu, toggleOptionsMenu] = useBinaryState(false);
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 [nextVideoPopupOpen, openNextVideoPopup, closeNextVideoPopup] = useBinaryState(false);
const [statisticsMenuOpen, , closeStatisticsMenu, toggleStatisticsMenu] = useBinaryState(false);
const nextVideoPopupDismissed = React.useRef(false);
const defaultSubtitlesSelected = React.useRef(false);
const defaultAudioTrackSelected = React.useRef(false);
const [error, setError] = React.useState(null);
const [videoState, setVideoState] = React.useReducer(
(videoState, nextVideoState) => ({ ...videoState, ...nextVideoState }),
{
@ -66,28 +88,6 @@ const Player = ({ urlParams, queryParams }) => {
extraSubtitlesOutlineColor: null
}
);
const [player, timeChanged, pausedChanged, ended] = usePlayer(urlParams, videoState.videoParams);
const [settings, updateSettings] = useSettings();
const streamingServer = useStreamingServer();
const routeFocused = useRouteFocused();
const toast = useToast();
const [, , , toggleFullscreen] = useFullscreen();
const [casting, setCasting] = React.useState(() => {
return chromecast.active && chromecast.transport.getCastState() === cast.framework.CastState.CONNECTED;
});
const [immersed, setImmersed] = React.useState(true);
const setImmersedDebounced = React.useCallback(debounce(setImmersed, 3000), []);
const [optionsMenuOpen, , closeOptionsMenu, toggleOptionsMenu] = useBinaryState(false);
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 [nextVideoPopupOpen, openNextVideoPopup, closeNextVideoPopup] = useBinaryState(false);
const [statisticsMenuOpen, , closeStatisticsMenu, toggleStatisticsMenu] = useBinaryState(false);
const nextVideoPopupDismissed = React.useRef(false);
const defaultSubtitlesSelected = React.useRef(false);
const defaultAudioTrackSelected = React.useRef(false);
const [error, setError] = React.useState(null);
const videoRef = React.useRef(null);
const dispatch = React.useCallback((action, options) => {
if (videoRef.current !== null) {
@ -353,6 +353,9 @@ const Player = ({ urlParams, queryParams }) => {
pausedChanged(videoState.paused);
}
}, [videoState.paused]);
React.useEffect(() => {
videoParamsChanged(videoState.videoParams);
}, [videoState.videoParams]);
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) {

View file

@ -32,7 +32,7 @@ const map = (player) => ({
player.metaItem,
});
const usePlayer = (urlParams, videoParams) => {
const usePlayer = (urlParams) => {
const { core } = useServices();
const { decodeStream } = useCoreSuspender();
const stream = decodeStream(urlParams.stream);
@ -44,7 +44,6 @@ const usePlayer = (urlParams, videoParams) => {
model: 'Player',
args: {
stream,
videoParams,
streamRequest: typeof urlParams.streamTransportUrl === 'string' && typeof urlParams.type === 'string' && typeof urlParams.videoId === 'string' ?
{
base: urlParams.streamTransportUrl,
@ -86,7 +85,16 @@ const usePlayer = (urlParams, videoParams) => {
action: 'Unload'
};
}
}, [urlParams, videoParams]);
}, [urlParams]);
const videoParamsChanged = React.useCallback((videoParams) => {
core.transport.dispatch({
action: 'Player',
args: {
action: 'VideoParamsChanged',
args: { videoParams }
}
}, 'player');
}, []);
const timeChanged = React.useCallback((time, duration, device) => {
core.transport.dispatch({
action: 'Player',
@ -114,7 +122,7 @@ const usePlayer = (urlParams, videoParams) => {
}, 'player');
}, []);
const player = useModelState({ model: 'player', action, map });
return [player, timeChanged, pausedChanged, ended];
return [player, videoParamsChanged, timeChanged, pausedChanged, ended];
};
module.exports = usePlayer;

View file

@ -300,6 +300,26 @@ const Settings = () => {
<div className={styles['label']}>{ t('PRIVACY_POLICY') }</div>
</Button>
</div>
{
profile.auth !== null && profile.auth.user !== null && typeof profile.auth.user.email === 'string' ?
<div className={styles['option-container']}>
<Button className={classnames(styles['option-input-container'], styles['link-container'])} title={t('RESET_PASSWORD')} target={'_blank'} href={`https://www.strem.io/reset-password/${profile.auth.user.email}`}>
<div className={styles['label']}>{ t('RESET_PASSWORD') }</div>
</Button>
</div>
:
null
}
{
profile.auth !== null && profile.auth.user !== null ?
<div className={styles['option-container']}>
<Button className={classnames(styles['option-input-container'], styles['link-container'])} title={t('SETTINGS_ACC_DELETE')} target={'_blank'} href={'https://stremio.zendesk.com/hc/en-us/articles/360021428911-How-to-delete-my-account'}>
<div className={styles['label']}>{ t('SETTINGS_ACC_DELETE') }</div>
</Button>
</div>
:
null
}
</div>
<div ref={playerSectionRef} className={styles['section-container']}>
<div className={styles['section-title']}>{ t('SETTINGS_NAV_PLAYER') }</div>
@ -437,8 +457,8 @@ const Settings = () => {
streamingServer.settings.type === 'Ready' ?
t('SETTINGS_SERVER_STATUS_ONLINE')
:
streamingServer.settings.type === 'Error' ?
`${t('SETTINGS_SERVER_STATUS_ERROR')}: (${streamingServer.settings.content})`
streamingServer.settings.type === 'Err' ?
t('SETTINGS_SERVER_STATUS_ERROR')
:
streamingServer.settings.type
}