mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-03-11 21:27:05 +00:00
basic info menu implemented
This commit is contained in:
parent
19c747f019
commit
1ec803b06d
6 changed files with 83 additions and 50 deletions
|
|
@ -15,7 +15,7 @@ const ControlBar = ({
|
||||||
volume,
|
volume,
|
||||||
muted,
|
muted,
|
||||||
subtitlesTracks,
|
subtitlesTracks,
|
||||||
stream,
|
info,
|
||||||
onPlayRequested,
|
onPlayRequested,
|
||||||
onPauseRequested,
|
onPauseRequested,
|
||||||
onMuteRequested,
|
onMuteRequested,
|
||||||
|
|
@ -97,7 +97,7 @@ const ControlBar = ({
|
||||||
<Button className={classnames(styles['control-bar-button'], 'disabled')} tabIndex={-1}>
|
<Button className={classnames(styles['control-bar-button'], 'disabled')} tabIndex={-1}>
|
||||||
<Icon className={styles['icon']} icon={'ic_network'} />
|
<Icon className={styles['icon']} icon={'ic_network'} />
|
||||||
</Button>
|
</Button>
|
||||||
<Button className={classnames(styles['control-bar-button'], { 'disabled': typeof stream !== 'object' || stream === null })} tabIndex={-1} onMouseDown={onInfoButtonMouseDown} onClick={onInfoButtonClick}>
|
<Button className={classnames(styles['control-bar-button'], { 'disabled': typeof info !== 'object' || info === null })} tabIndex={-1} onMouseDown={onInfoButtonMouseDown} onClick={onInfoButtonClick}>
|
||||||
<Icon className={styles['icon']} icon={'ic_info'} />
|
<Icon className={styles['icon']} icon={'ic_info'} />
|
||||||
</Button>
|
</Button>
|
||||||
<Button className={classnames(styles['control-bar-button'], 'disabled')} tabIndex={-1}>
|
<Button className={classnames(styles['control-bar-button'], 'disabled')} tabIndex={-1}>
|
||||||
|
|
@ -122,7 +122,7 @@ ControlBar.propTypes = {
|
||||||
volume: PropTypes.number,
|
volume: PropTypes.number,
|
||||||
muted: PropTypes.bool,
|
muted: PropTypes.bool,
|
||||||
subtitlesTracks: PropTypes.array,
|
subtitlesTracks: PropTypes.array,
|
||||||
stream: PropTypes.object,
|
info: PropTypes.object,
|
||||||
onPlayRequested: PropTypes.func,
|
onPlayRequested: PropTypes.func,
|
||||||
onPauseRequested: PropTypes.func,
|
onPauseRequested: PropTypes.func,
|
||||||
onMuteRequested: PropTypes.func,
|
onMuteRequested: PropTypes.func,
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,42 @@
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
const classnames = require('classnames');
|
const classnames = require('classnames');
|
||||||
const MetaPreview = require('stremio/common/MetaPreview');
|
const { MetaPreview, CONSTANTS } = require('stremio/common');
|
||||||
const styles = require('./styles');
|
const styles = require('./styles');
|
||||||
|
|
||||||
const InfoMenu = ({ className }) => {
|
const InfoMenu = ({ className, ...props }) => {
|
||||||
|
const metaItem = React.useMemo(() => {
|
||||||
|
return props.metaItem !== null ?
|
||||||
|
{
|
||||||
|
...props.metaItem,
|
||||||
|
links: props.metaItem.links.filter(({ category }) => category === CONSTANTS.SHARE_LINK_CATEGORY)
|
||||||
|
}
|
||||||
|
:
|
||||||
|
null;
|
||||||
|
}, [props.metaItem]);
|
||||||
const onMouseDown = React.useCallback((event) => {
|
const onMouseDown = React.useCallback((event) => {
|
||||||
event.nativeEvent.infoMenuClosePrevented = true;
|
event.nativeEvent.infoMenuClosePrevented = true;
|
||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
<div className={classnames(className, styles['info-menu-container'])} onMouseDown={onMouseDown}>
|
<div className={classnames(className, styles['info-menu-container'])} onMouseDown={onMouseDown}>
|
||||||
<MetaPreview name={'Info menu name'} />
|
<MetaPreview
|
||||||
|
className={styles['meta-preview']}
|
||||||
|
compact={true}
|
||||||
|
name={metaItem.name}
|
||||||
|
logo={metaItem.logo}
|
||||||
|
runtime={metaItem.runtime}
|
||||||
|
releaseInfo={metaItem.releaseInfo}
|
||||||
|
released={metaItem.released}
|
||||||
|
description={metaItem.description}
|
||||||
|
links={metaItem.links}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
InfoMenu.propTypes = {
|
InfoMenu.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
|
metaItem: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = InfoMenu;
|
module.exports = InfoMenu;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
.info-menu-container {
|
.info-menu-container {
|
||||||
|
width: 30rem;
|
||||||
}
|
}
|
||||||
|
|
@ -4,52 +4,23 @@ const classnames = require('classnames');
|
||||||
const debounce = require('lodash.debounce');
|
const debounce = require('lodash.debounce');
|
||||||
const { useRouteFocused } = require('stremio-router');
|
const { useRouteFocused } = require('stremio-router');
|
||||||
const { useServices } = require('stremio/services');
|
const { useServices } = require('stremio/services');
|
||||||
const { HorizontalNavBar, useDeepEqualEffect, useFullscreen, useBinaryState, useToast } = require('stremio/common');
|
const { HorizontalNavBar, useDeepEqualEffect, useFullscreen, useBinaryState, useToast, useProfile } = require('stremio/common');
|
||||||
const BufferingLoader = require('./BufferingLoader');
|
const BufferingLoader = require('./BufferingLoader');
|
||||||
const ControlBar = require('./ControlBar');
|
const ControlBar = require('./ControlBar');
|
||||||
const InfoMenu = require('./InfoMenu');
|
const InfoMenu = require('./InfoMenu');
|
||||||
const SubtitlesMenu = require('./SubtitlesMenu');
|
const SubtitlesMenu = require('./SubtitlesMenu');
|
||||||
const Video = require('./Video');
|
const Video = require('./Video');
|
||||||
|
const useInfo = require('./useInfo');
|
||||||
const usePlayer = require('./usePlayer');
|
const usePlayer = require('./usePlayer');
|
||||||
const useSettings = require('./useSettings');
|
const useSettings = require('./useSettings');
|
||||||
const styles = require('./styles');
|
const styles = require('./styles');
|
||||||
|
|
||||||
const Player = ({ urlParams }) => {
|
const Player = ({ urlParams }) => {
|
||||||
const { core } = useServices();
|
const { core } = useServices();
|
||||||
|
const profile = useProfile();
|
||||||
const [player, updateLibraryItemState, pushToLibrary] = usePlayer(urlParams);
|
const [player, updateLibraryItemState, pushToLibrary] = usePlayer(urlParams);
|
||||||
const [settings, updateSettings] = useSettings();
|
const [settings, updateSettings] = useSettings(profile);
|
||||||
const stream = React.useMemo(() => {
|
const info = useInfo(player, profile);
|
||||||
return player.selected !== null ? player.selected.stream : null;
|
|
||||||
}, [player]);
|
|
||||||
const metaItem = React.useMemo(() => {
|
|
||||||
return player.meta_resource !== null && player.meta_resource.content.type === 'Ready' ? player.meta_resource.content.content : null;
|
|
||||||
}, [player]);
|
|
||||||
const video = React.useMemo(() => {
|
|
||||||
return metaItem !== null && player.selected !== null && typeof player.selected.video_id === 'string' ?
|
|
||||||
metaItem.videos.reduce((result, video) => {
|
|
||||||
if (video.id === player.selected.video_id) {
|
|
||||||
return video;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}, null)
|
|
||||||
:
|
|
||||||
null;
|
|
||||||
}, [player, metaItem]);
|
|
||||||
const title = React.useMemo(() => {
|
|
||||||
const streamTitle = stream !== null && typeof stream.title === 'string' ? stream.title : '';
|
|
||||||
const metaItemTitle = metaItem !== null ? metaItem.name : '';
|
|
||||||
const videoTitle = video !== null && typeof video.title === 'string' && video.title.length > 0 ? video.title : '';
|
|
||||||
const seriesInfo = video !== null && !isNaN(video.season) && !isNaN(video.episode) ? `${video.season}x${video.episode}` : '';
|
|
||||||
if (metaItemTitle.length > 0) {
|
|
||||||
return metaItemTitle
|
|
||||||
.concat(videoTitle.length > 0 || seriesInfo.length > 0 ? ' -' : '')
|
|
||||||
.concat(videoTitle.length > 0 ? ` ${videoTitle}` : '')
|
|
||||||
.concat(seriesInfo.length > 0 ? ` (${seriesInfo})` : '');
|
|
||||||
} else {
|
|
||||||
return streamTitle;
|
|
||||||
}
|
|
||||||
}, [stream, metaItem, video]);
|
|
||||||
const routeFocused = useRouteFocused();
|
const routeFocused = useRouteFocused();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const [, , , toggleFullscreen] = useFullscreen();
|
const [, , , toggleFullscreen] = useFullscreen();
|
||||||
|
|
@ -275,10 +246,10 @@ const Player = ({ urlParams }) => {
|
||||||
}
|
}
|
||||||
}, [videoState.subtitlesTracks]);
|
}, [videoState.subtitlesTracks]);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (stream === null) {
|
if (info === null) {
|
||||||
closeInfoMenu();
|
closeInfoMenu();
|
||||||
}
|
}
|
||||||
}, [stream]);
|
}, [info]);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const intervalId = setInterval(pushToLibrary, 30000);
|
const intervalId = setInterval(pushToLibrary, 30000);
|
||||||
return () => {
|
return () => {
|
||||||
|
|
@ -337,7 +308,7 @@ const Player = ({ urlParams }) => {
|
||||||
}
|
}
|
||||||
case 'KeyM': {
|
case 'KeyM': {
|
||||||
closeSubtitlesMenu();
|
closeSubtitlesMenu();
|
||||||
if (stream !== null) {
|
if (info !== null) {
|
||||||
toggleInfoMenu();
|
toggleInfoMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -356,7 +327,7 @@ const Player = ({ urlParams }) => {
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('keydown', onKeyDown);
|
window.removeEventListener('keydown', onKeyDown);
|
||||||
};
|
};
|
||||||
}, [routeFocused, subtitlesMenuOpen, infoMenuOpen, stream, videoState.paused, videoState.time, videoState.volume, videoState.subtitlesTracks, toggleSubtitlesMenu, toggleInfoMenu]);
|
}, [routeFocused, subtitlesMenuOpen, infoMenuOpen, info, videoState.paused, videoState.time, videoState.volume, videoState.subtitlesTracks, toggleSubtitlesMenu, toggleInfoMenu]);
|
||||||
React.useLayoutEffect(() => {
|
React.useLayoutEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
setImmersedDebounced.cancel();
|
setImmersedDebounced.cancel();
|
||||||
|
|
@ -404,7 +375,7 @@ const Player = ({ urlParams }) => {
|
||||||
}
|
}
|
||||||
<HorizontalNavBar
|
<HorizontalNavBar
|
||||||
className={classnames(styles['layer'], styles['nav-bar-layer'])}
|
className={classnames(styles['layer'], styles['nav-bar-layer'])}
|
||||||
title={title}
|
title={info !== null ? info.title : ''}
|
||||||
backButton={true}
|
backButton={true}
|
||||||
fullscreenButton={true}
|
fullscreenButton={true}
|
||||||
onMouseMove={onBarMouseMove}
|
onMouseMove={onBarMouseMove}
|
||||||
|
|
@ -418,7 +389,7 @@ const Player = ({ urlParams }) => {
|
||||||
volume={videoState.volume}
|
volume={videoState.volume}
|
||||||
muted={videoState.muted}
|
muted={videoState.muted}
|
||||||
subtitlesTracks={videoState.subtitlesTracks}
|
subtitlesTracks={videoState.subtitlesTracks}
|
||||||
stream={stream}
|
info={info}
|
||||||
onPlayRequested={onPlayRequested}
|
onPlayRequested={onPlayRequested}
|
||||||
onPauseRequested={onPauseRequested}
|
onPauseRequested={onPauseRequested}
|
||||||
onMuteRequested={onMuteRequested}
|
onMuteRequested={onMuteRequested}
|
||||||
|
|
@ -454,6 +425,9 @@ const Player = ({ urlParams }) => {
|
||||||
infoMenuOpen ?
|
infoMenuOpen ?
|
||||||
<InfoMenu
|
<InfoMenu
|
||||||
className={classnames(styles['layer'], styles['menu-layer'])}
|
className={classnames(styles['layer'], styles['menu-layer'])}
|
||||||
|
stream={info !== null ? info.stream : null}
|
||||||
|
addon={info !== null ? info.addon : null}
|
||||||
|
metaItem={info !== null ? info.metaItem : null}
|
||||||
/>
|
/>
|
||||||
:
|
:
|
||||||
null
|
null
|
||||||
|
|
|
||||||
41
src/routes/Player/useInfo.js
Normal file
41
src/routes/Player/useInfo.js
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
|
const useInfo = (player, profile) => {
|
||||||
|
const info = React.useMemo(() => {
|
||||||
|
if (player.selected === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stream = player.selected.stream;
|
||||||
|
const addon = stream.addon;
|
||||||
|
const metaItem = player.meta_resource !== null && player.meta_resource.content.type === 'Ready' ?
|
||||||
|
player.meta_resource.content.content
|
||||||
|
:
|
||||||
|
null;
|
||||||
|
const video = metaItem !== null ?
|
||||||
|
metaItem.videos.reduce((result, video) => {
|
||||||
|
if (video.id === player.selected.video_id) {
|
||||||
|
return video;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, null)
|
||||||
|
:
|
||||||
|
null;
|
||||||
|
const streamTitle = typeof stream.title === 'string' ? stream.title : '';
|
||||||
|
const metaItemTitle = metaItem !== null ? metaItem.name : '';
|
||||||
|
const videoTitle = video !== null && typeof video.title === 'string' && video.title.length > 0 ? video.title : '';
|
||||||
|
const seriesInfo = video !== null && !isNaN(video.season) && !isNaN(video.episode) ? `${video.season}x${video.episode}` : '';
|
||||||
|
const title = metaItemTitle.length > 0 ?
|
||||||
|
metaItemTitle
|
||||||
|
.concat(videoTitle.length > 0 || seriesInfo.length > 0 ? ' -' : '')
|
||||||
|
.concat(videoTitle.length > 0 ? ` ${videoTitle}` : '')
|
||||||
|
.concat(seriesInfo.length > 0 ? ` (${seriesInfo})` : '')
|
||||||
|
:
|
||||||
|
streamTitle;
|
||||||
|
return { stream, addon, metaItem, title };
|
||||||
|
}, [player, profile]);
|
||||||
|
return info;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = useInfo;
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { useProfile } = require('stremio/common');
|
|
||||||
const { useServices } = require('stremio/services');
|
const { useServices } = require('stremio/services');
|
||||||
|
|
||||||
const useSettings = () => {
|
const useSettings = (profile) => {
|
||||||
const { core } = useServices();
|
const { core } = useServices();
|
||||||
const profile = useProfile();
|
|
||||||
const updateSettings = React.useCallback((settings) => {
|
const updateSettings = React.useCallback((settings) => {
|
||||||
core.dispatch({
|
core.dispatch({
|
||||||
action: 'Ctx',
|
action: 'Ctx',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue