mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-03-11 21:27:05 +00:00
Merge pull request #306 from Stremio/feat/player-playback-speed
Feat: player playback speed
This commit is contained in:
commit
32af326966
10 changed files with 227 additions and 16 deletions
14
package-lock.json
generated
14
package-lock.json
generated
|
|
@ -13,7 +13,7 @@
|
|||
"@sentry/browser": "6.13.3",
|
||||
"@stremio/stremio-colors": "5.0.1",
|
||||
"@stremio/stremio-core-web": "0.44.6",
|
||||
"@stremio/stremio-icons": "3.0.5",
|
||||
"@stremio/stremio-icons": "4.0.0",
|
||||
"@stremio/stremio-video": "0.0.24",
|
||||
"a-color-picker": "1.2.1",
|
||||
"bowser": "2.11.0",
|
||||
|
|
@ -2706,9 +2706,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@stremio/stremio-icons": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@stremio/stremio-icons/-/stremio-icons-3.0.5.tgz",
|
||||
"integrity": "sha512-knlcBibqJW2mbEgid6YEeQN9FPkIGAEtozYWqzKWeHd2DPY2nl8kYX2pMQpa2Db/RVSqbVstu/gdey5TtSgGYw=="
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@stremio/stremio-icons/-/stremio-icons-4.0.0.tgz",
|
||||
"integrity": "sha512-ku1ye/V6WtzCltLKZvLwa60nlEUR2YYT/upjZDzBOoA2VXZu1ubFeR83Hx10tBZPnjALHkG/1QZ5Eyg9PoBMbQ=="
|
||||
},
|
||||
"node_modules/@stremio/stremio-video": {
|
||||
"version": "0.0.24",
|
||||
|
|
@ -16718,9 +16718,9 @@
|
|||
}
|
||||
},
|
||||
"@stremio/stremio-icons": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@stremio/stremio-icons/-/stremio-icons-3.0.5.tgz",
|
||||
"integrity": "sha512-knlcBibqJW2mbEgid6YEeQN9FPkIGAEtozYWqzKWeHd2DPY2nl8kYX2pMQpa2Db/RVSqbVstu/gdey5TtSgGYw=="
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@stremio/stremio-icons/-/stremio-icons-4.0.0.tgz",
|
||||
"integrity": "sha512-ku1ye/V6WtzCltLKZvLwa60nlEUR2YYT/upjZDzBOoA2VXZu1ubFeR83Hx10tBZPnjALHkG/1QZ5Eyg9PoBMbQ=="
|
||||
},
|
||||
"@stremio/stremio-video": {
|
||||
"version": "0.0.24",
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
"@sentry/browser": "6.13.3",
|
||||
"@stremio/stremio-colors": "5.0.1",
|
||||
"@stremio/stremio-core-web": "0.44.6",
|
||||
"@stremio/stremio-icons": "3.0.5",
|
||||
"@stremio/stremio-icons": "4.0.0",
|
||||
"@stremio/stremio-video": "0.0.24",
|
||||
"a-color-picker": "1.2.1",
|
||||
"bowser": "2.11.0",
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ const ControlBar = ({
|
|||
duration,
|
||||
volume,
|
||||
muted,
|
||||
playbackSpeed,
|
||||
subtitlesTracks,
|
||||
audioTracks,
|
||||
metaItem,
|
||||
|
|
@ -30,6 +31,7 @@ const ControlBar = ({
|
|||
onSeekRequested,
|
||||
onToggleSubtitlesMenu,
|
||||
onToggleInfoMenu,
|
||||
onToggleSpeedMenu,
|
||||
onToggleVideosMenu,
|
||||
...props
|
||||
}) => {
|
||||
|
|
@ -42,6 +44,9 @@ const ControlBar = ({
|
|||
const onInfoButtonMouseDown = React.useCallback((event) => {
|
||||
event.nativeEvent.infoMenuClosePrevented = true;
|
||||
}, []);
|
||||
const onSpeedButtonMouseDown = React.useCallback((event) => {
|
||||
event.nativeEvent.speedMenuClosePrevented = true;
|
||||
}, []);
|
||||
const onVideosButtonMouseDown = React.useCallback((event) => {
|
||||
event.nativeEvent.videosMenuClosePrevented = true;
|
||||
}, []);
|
||||
|
|
@ -86,6 +91,11 @@ const ControlBar = ({
|
|||
onToggleInfoMenu();
|
||||
}
|
||||
}, [onToggleInfoMenu]);
|
||||
const onSpeedButtonClick = React.useCallback(() => {
|
||||
if (typeof onToggleSpeedMenu === 'function') {
|
||||
onToggleSpeedMenu();
|
||||
}
|
||||
}, [onToggleSpeedMenu]);
|
||||
const onVideosButtonClick = React.useCallback(() => {
|
||||
if (typeof onToggleVideosMenu === 'function') {
|
||||
onToggleVideosMenu();
|
||||
|
|
@ -145,6 +155,9 @@ const ControlBar = ({
|
|||
<Icon className={styles['icon']} icon={'ic_more'} />
|
||||
</Button>
|
||||
<div className={classnames(styles['control-bar-buttons-menu-container'], { 'open': buttonsMenuOpen })}>
|
||||
<Button className={classnames(styles['control-bar-button'], { 'disabled': playbackSpeed === null })} tabIndex={-1} onMouseDown={onSpeedButtonMouseDown} onClick={onSpeedButtonClick}>
|
||||
<Icon className={styles['icon']} icon={'ic_speedometer'} />
|
||||
</Button>
|
||||
<Button className={classnames(styles['control-bar-button'], 'disabled')} tabIndex={-1}>
|
||||
<Icon className={styles['icon']} icon={'ic_network'} />
|
||||
</Button>
|
||||
|
|
@ -178,6 +191,7 @@ ControlBar.propTypes = {
|
|||
duration: PropTypes.number,
|
||||
volume: PropTypes.number,
|
||||
muted: PropTypes.bool,
|
||||
playbackSpeed: PropTypes.number,
|
||||
subtitlesTracks: PropTypes.array,
|
||||
audioTracks: PropTypes.array,
|
||||
metaItem: PropTypes.object,
|
||||
|
|
@ -190,6 +204,7 @@ ControlBar.propTypes = {
|
|||
onSeekRequested: PropTypes.func,
|
||||
onToggleSubtitlesMenu: PropTypes.func,
|
||||
onToggleInfoMenu: PropTypes.func,
|
||||
onToggleSpeedMenu: PropTypes.func,
|
||||
onToggleVideosMenu: PropTypes.func
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ const ControlBar = require('./ControlBar');
|
|||
const InfoMenu = require('./InfoMenu');
|
||||
const VideosMenu = require('./VideosMenu');
|
||||
const SubtitlesMenu = require('./SubtitlesMenu');
|
||||
const SpeedMenu = require('./SpeedMenu');
|
||||
const Video = require('./Video');
|
||||
const usePlayer = require('./usePlayer');
|
||||
const useSettings = require('./useSettings');
|
||||
|
|
@ -39,6 +40,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
const setImmersedDebounced = React.useCallback(debounce(setImmersed, 3000), []);
|
||||
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(
|
||||
|
|
@ -52,6 +54,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
buffering: null,
|
||||
volume: null,
|
||||
muted: null,
|
||||
playbackSpeed: null,
|
||||
audioTracks: [],
|
||||
selectedAudioTrackId: null,
|
||||
subtitlesTracks: [],
|
||||
|
|
@ -159,6 +162,9 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
const onSeekRequested = React.useCallback((time) => {
|
||||
dispatch({ type: 'setProp', propName: 'time', propValue: time });
|
||||
}, []);
|
||||
const onPlaybackSpeedChanged = React.useCallback((rate) => {
|
||||
dispatch({ type: 'setProp', propName: 'playbackSpeed', propValue: rate });
|
||||
}, []);
|
||||
const onSubtitlesTrackSelected = React.useCallback((id) => {
|
||||
dispatch({ type: 'setProp', propName: 'selectedSubtitlesTrackId', propValue: id });
|
||||
dispatch({ type: 'setProp', propName: 'selectedExtraSubtitlesTrackId', propValue: null });
|
||||
|
|
@ -200,6 +206,9 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
if (!event.nativeEvent.infoMenuClosePrevented) {
|
||||
closeInfoMenu();
|
||||
}
|
||||
if (!event.nativeEvent.speedMenuClosePrevented) {
|
||||
closeSpeedMenu();
|
||||
}
|
||||
if (!event.nativeEvent.videosMenuClosePrevented) {
|
||||
closeVideosMenu();
|
||||
}
|
||||
|
|
@ -329,6 +338,11 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
closeVideosMenu();
|
||||
}
|
||||
}, [player.metaItem]);
|
||||
React.useEffect(() => {
|
||||
if (videoState.playbackSpeed === null) {
|
||||
closeSpeedMenu();
|
||||
}
|
||||
}, [videoState.playbackSpeed]);
|
||||
React.useEffect(() => {
|
||||
const intervalId = setInterval(() => {
|
||||
pushToLibrary();
|
||||
|
|
@ -369,7 +383,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
const onKeyDown = (event) => {
|
||||
switch (event.code) {
|
||||
case 'Space': {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && videoState.paused !== null) {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && !speedMenuOpen&& videoState.paused !== null) {
|
||||
if (videoState.paused) {
|
||||
onPlayRequested();
|
||||
} else {
|
||||
|
|
@ -380,7 +394,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
break;
|
||||
}
|
||||
case 'ArrowRight': {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && videoState.time !== null) {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && !speedMenuOpen && videoState.time !== null) {
|
||||
const seekTimeMultiplier = event.shiftKey ? 3 : 1;
|
||||
onSeekRequested(videoState.time + (settings.seekTimeDuration * seekTimeMultiplier));
|
||||
}
|
||||
|
|
@ -388,7 +402,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
break;
|
||||
}
|
||||
case 'ArrowLeft': {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && videoState.time !== null) {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && !speedMenuOpen && videoState.time !== null) {
|
||||
const seekTimeMultiplier = event.shiftKey ? 3 : 1;
|
||||
onSeekRequested(videoState.time - (settings.seekTimeDuration * seekTimeMultiplier));
|
||||
}
|
||||
|
|
@ -396,14 +410,14 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
break;
|
||||
}
|
||||
case 'ArrowUp': {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && videoState.volume !== null) {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && !speedMenuOpen && videoState.volume !== null) {
|
||||
onVolumeChangeRequested(videoState.volume + 5);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'ArrowDown': {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && videoState.volume !== null) {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && !speedMenuOpen && videoState.volume !== null) {
|
||||
onVolumeChangeRequested(videoState.volume - 5);
|
||||
}
|
||||
|
||||
|
|
@ -411,6 +425,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) ||
|
||||
|
|
@ -422,6 +437,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
}
|
||||
case 'KeyI': {
|
||||
closeSubtitlesMenu();
|
||||
closeSpeedMenu();
|
||||
closeVideosMenu();
|
||||
if (player.metaItem !== null && player.metaItem.type === 'Ready') {
|
||||
toggleInfoMenu();
|
||||
|
|
@ -429,9 +445,20 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
|
||||
break;
|
||||
}
|
||||
case 'KeyR': {
|
||||
closeInfoMenu();
|
||||
closeSubtitlesMenu();
|
||||
closeVideosMenu();
|
||||
if (videoState.playbackSpeed !== null) {
|
||||
toggleSpeedMenu();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'KeyV': {
|
||||
closeInfoMenu();
|
||||
closeSubtitlesMenu();
|
||||
closeSpeedMenu();
|
||||
if (player.metaItem !== null && player.metaItem.type === 'Ready') {
|
||||
toggleVideosMenu();
|
||||
}
|
||||
|
|
@ -441,6 +468,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
case 'Escape': {
|
||||
closeSubtitlesMenu();
|
||||
closeInfoMenu();
|
||||
closeSpeedMenu();
|
||||
closeVideosMenu();
|
||||
break;
|
||||
}
|
||||
|
|
@ -452,7 +480,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
return () => {
|
||||
window.removeEventListener('keydown', onKeyDown);
|
||||
};
|
||||
}, [player.metaItem, settings.seekTimeDuration, routeFocused, subtitlesMenuOpen, infoMenuOpen, videosMenuOpen, videoState.paused, videoState.time, videoState.volume, videoState.audioTracks, videoState.subtitlesTracks, videoState.extraSubtitlesTracks, toggleSubtitlesMenu, toggleInfoMenu, toggleVideosMenu]);
|
||||
}, [player.metaItem, settings.seekTimeDuration, routeFocused, subtitlesMenuOpen, infoMenuOpen, videosMenuOpen, speedMenuOpen, videoState.paused, videoState.time, videoState.volume, videoState.audioTracks, videoState.subtitlesTracks, videoState.extraSubtitlesTracks, videoState.playbackSpeed, toggleSubtitlesMenu, toggleInfoMenu, toggleVideosMenu]);
|
||||
React.useLayoutEffect(() => {
|
||||
return () => {
|
||||
setImmersedDebounced.cancel();
|
||||
|
|
@ -461,7 +489,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
};
|
||||
}, []);
|
||||
return (
|
||||
<div className={classnames(styles['player-container'], { [styles['immersed']]: immersed && !casting && videoState.paused !== null && !videoState.paused && !subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen })}
|
||||
<div className={classnames(styles['player-container'], { [styles['immersed']]: immersed && !casting && videoState.paused !== null && !videoState.paused && !subtitlesMenuOpen && !infoMenuOpen && !speedMenuOpen && !videosMenuOpen })}
|
||||
onMouseDown={onContainerMouseDown}
|
||||
onMouseMove={onContainerMouseMove}
|
||||
onMouseOver={onContainerMouseMove}
|
||||
|
|
@ -506,7 +534,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
null
|
||||
}
|
||||
{
|
||||
subtitlesMenuOpen || infoMenuOpen || videosMenuOpen ?
|
||||
subtitlesMenuOpen || infoMenuOpen || videosMenuOpen || speedMenuOpen ?
|
||||
<div className={styles['layer']} />
|
||||
:
|
||||
null
|
||||
|
|
@ -526,6 +554,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
duration={videoState.duration}
|
||||
volume={videoState.volume}
|
||||
muted={videoState.muted}
|
||||
playbackSpeed={videoState.playbackSpeed}
|
||||
subtitlesTracks={videoState.subtitlesTracks.concat(videoState.extraSubtitlesTracks)}
|
||||
audioTracks={videoState.audioTracks}
|
||||
metaItem={player.metaItem}
|
||||
|
|
@ -538,6 +567,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
onSeekRequested={onSeekRequested}
|
||||
onToggleSubtitlesMenu={toggleSubtitlesMenu}
|
||||
onToggleInfoMenu={toggleInfoMenu}
|
||||
onToggleSpeedMenu={toggleSpeedMenu}
|
||||
onToggleVideosMenu={toggleVideosMenu}
|
||||
onMouseMove={onBarMouseMove}
|
||||
onMouseOver={onBarMouseMove}
|
||||
|
|
@ -580,6 +610,16 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
:
|
||||
null
|
||||
}
|
||||
{
|
||||
speedMenuOpen ?
|
||||
<SpeedMenu
|
||||
className={classnames(styles['layer'], styles['menu-layer'])}
|
||||
playbackSpeed={videoState.playbackSpeed}
|
||||
onPlaybackSpeedChanged={onPlaybackSpeedChanged}
|
||||
/>
|
||||
:
|
||||
null
|
||||
}
|
||||
{
|
||||
videosMenuOpen ?
|
||||
<VideosMenu
|
||||
|
|
|
|||
33
src/routes/Player/SpeedMenu/Option/Option.js
Normal file
33
src/routes/Player/SpeedMenu/Option/Option.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (C) 2017-2022 Smart code 203358507
|
||||
|
||||
const React = require('react');
|
||||
const PropTypes = require('prop-types');
|
||||
const classnames = require('classnames');
|
||||
const { Button } = require('stremio/common');
|
||||
const styles = require('./styles');
|
||||
|
||||
const OptionButton = ({ className, value, selected, onSelect }) => {
|
||||
const onClick = React.useCallback(() => {
|
||||
if (typeof onSelect === 'function') {
|
||||
onSelect(value);
|
||||
}
|
||||
}, [onSelect, value]);
|
||||
return (
|
||||
<Button
|
||||
className={classnames(className, styles['option'], { 'selected': selected })}
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className={styles['label']}>{ value }x</div>
|
||||
<div className={styles['icon']} />
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
OptionButton.propTypes = {
|
||||
className: PropTypes.string,
|
||||
value: PropTypes.number,
|
||||
selected: PropTypes.bool,
|
||||
onSelect: PropTypes.func,
|
||||
};
|
||||
|
||||
module.exports = OptionButton;
|
||||
5
src/routes/Player/SpeedMenu/Option/index.js
Normal file
5
src/routes/Player/SpeedMenu/Option/index.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// Copyright (C) 2017-2022 Smart code 203358507
|
||||
|
||||
const Option = require('./Option');
|
||||
|
||||
module.exports = Option;
|
||||
38
src/routes/Player/SpeedMenu/Option/styles.less
Normal file
38
src/routes/Player/SpeedMenu/Option/styles.less
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (C) 2017-2022 Smart code 203358507
|
||||
|
||||
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
|
||||
|
||||
.option {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0 1.5em;
|
||||
|
||||
&:global(.selected) {
|
||||
background-color: @color-background;
|
||||
|
||||
.icon {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover, &:focus {
|
||||
background-color: @color-background-light2;
|
||||
}
|
||||
|
||||
.label {
|
||||
flex: 1;
|
||||
font-weight: 400;
|
||||
color: @color-surface-light5-90;
|
||||
}
|
||||
|
||||
.icon {
|
||||
flex: none;
|
||||
display: none;
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
border-radius: 100%;
|
||||
margin-left: 1rem;
|
||||
background-color: @color-accent3-90;
|
||||
}
|
||||
}
|
||||
48
src/routes/Player/SpeedMenu/SpeedMenu.js
Normal file
48
src/routes/Player/SpeedMenu/SpeedMenu.js
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright (C) 2017-2022 Smart code 203358507
|
||||
|
||||
const React = require('react');
|
||||
const PropTypes = require('prop-types');
|
||||
const classnames = require('classnames');
|
||||
const Option = require('./Option');
|
||||
const styles = require('./styles');
|
||||
|
||||
const RATES = Array.from(Array(8).keys(), (n) => n * 0.25 + 0.25).reverse();
|
||||
|
||||
const SpeedMenu = ({ className, playbackSpeed, onPlaybackSpeedChanged }) => {
|
||||
const onMouseDown = React.useCallback((event) => {
|
||||
event.nativeEvent.speedMenuClosePrevented = true;
|
||||
}, []);
|
||||
const onOptionSelect = React.useCallback((value) => {
|
||||
if (typeof onPlaybackSpeedChanged === 'function') {
|
||||
onPlaybackSpeedChanged(value);
|
||||
}
|
||||
}, [onPlaybackSpeedChanged]);
|
||||
return (
|
||||
<div className={classnames(className, styles['speed-menu-container'])} onMouseDown={onMouseDown}>
|
||||
<div className={styles['title']}>
|
||||
Playback Speed
|
||||
</div>
|
||||
<div className={styles['options-container']}>
|
||||
{
|
||||
RATES.map((rate) => (
|
||||
<Option
|
||||
className={styles['option']}
|
||||
key={rate}
|
||||
value={rate}
|
||||
selected={rate === playbackSpeed}
|
||||
onSelect={onOptionSelect}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
SpeedMenu.propTypes = {
|
||||
className: PropTypes.string,
|
||||
playbackSpeed: PropTypes.number,
|
||||
onPlaybackSpeedChanged: PropTypes.func,
|
||||
};
|
||||
|
||||
module.exports = SpeedMenu;
|
||||
5
src/routes/Player/SpeedMenu/index.js
Normal file
5
src/routes/Player/SpeedMenu/index.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// Copyright (C) 2017-2022 Smart code 203358507
|
||||
|
||||
const SpeedMenu = require('./SpeedMenu');
|
||||
|
||||
module.exports = SpeedMenu;
|
||||
27
src/routes/Player/SpeedMenu/styles.less
Normal file
27
src/routes/Player/SpeedMenu/styles.less
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (C) 2017-2022 Smart code 203358507
|
||||
|
||||
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
|
||||
|
||||
.speed-menu-container {
|
||||
width: 12rem;
|
||||
overflow: visible !important;
|
||||
|
||||
.title {
|
||||
flex: none;
|
||||
align-self: stretch;
|
||||
max-height: 2.4em;
|
||||
font-weight: 600;
|
||||
color: @color-surface-light5-90;
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.options-container {
|
||||
flex: 0 1 auto;
|
||||
max-height: calc(3.2rem * 8);
|
||||
overflow-y: auto;
|
||||
|
||||
.option {
|
||||
height: 3.2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue