Merge pull request #392 from Stremio/feat/player-buffered-seekbar

feat: display buffered amount on player seek bar
This commit is contained in:
Alexandru Branza 2023-06-09 19:04:01 +02:00 committed by GitHub
commit 19e1fe4e3e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 34 additions and 11 deletions

View file

@ -8,10 +8,11 @@ const useAnimationFrame = require('stremio/common/useAnimationFrame');
const useLiveRef = require('stremio/common/useLiveRef'); const useLiveRef = require('stremio/common/useLiveRef');
const styles = require('./styles'); const styles = require('./styles');
const Slider = ({ className, value, minimumValue, maximumValue, disabled, onSlide, onComplete }) => { const Slider = ({ className, value, buffered, minimumValue, maximumValue, disabled, onSlide, onComplete }) => {
const minimumValueRef = useLiveRef(minimumValue !== null && !isNaN(minimumValue) ? minimumValue : 0); const minimumValueRef = useLiveRef(minimumValue !== null && !isNaN(minimumValue) ? minimumValue : 0);
const maximumValueRef = useLiveRef(maximumValue !== null && !isNaN(maximumValue) ? maximumValue : 100); const maximumValueRef = useLiveRef(maximumValue !== null && !isNaN(maximumValue) ? maximumValue : 100);
const valueRef = useLiveRef(value !== null && !isNaN(value) ? Math.min(maximumValueRef.current, Math.max(minimumValueRef.current, value)) : 0); const valueRef = useLiveRef(value !== null && !isNaN(value) ? Math.min(maximumValueRef.current, Math.max(minimumValueRef.current, value)) : 0);
const bufferedRef = useLiveRef(buffered !== null && !isNaN(buffered) ? Math.min(maximumValueRef.current, Math.max(minimumValueRef.current, buffered)) : 0);
const onSlideRef = useLiveRef(onSlide); const onSlideRef = useLiveRef(onSlide);
const onCompleteRef = useLiveRef(onComplete); const onCompleteRef = useLiveRef(onComplete);
const sliderContainerRef = React.useRef(null); const sliderContainerRef = React.useRef(null);
@ -95,13 +96,17 @@ const Slider = ({ className, value, minimumValue, maximumValue, disabled, onSlid
}; };
}, []); }, []);
const thumbPosition = Math.max(0, Math.min(1, (valueRef.current - minimumValueRef.current) / (maximumValueRef.current - minimumValueRef.current))); const thumbPosition = Math.max(0, Math.min(1, (valueRef.current - minimumValueRef.current) / (maximumValueRef.current - minimumValueRef.current)));
const bufferedPosition = Math.max(0, Math.min(1, (bufferedRef.current - minimumValueRef.current) / (maximumValueRef.current - minimumValueRef.current)));
return ( return (
<div ref={sliderContainerRef} className={classnames(className, styles['slider-container'], { 'disabled': disabled })} onMouseDown={onMouseDown}> <div ref={sliderContainerRef} className={classnames(className, styles['slider-container'], { 'disabled': disabled })} onMouseDown={onMouseDown}>
<div className={styles['layer']}> <div className={styles['layer']}>
<div className={styles['track']} /> <div className={styles['track']} />
</div> </div>
<div className={styles['layer']}> <div className={styles['layer']}>
<div className={styles['track-before']} style={{ width: `calc(100% * ${thumbPosition})` }} /> <div className={styles['track-before']} style={{ width: `calc(100% * ${bufferedPosition})` }} />
</div>
<div className={styles['layer']}>
<div className={styles['track-after']} style={{ width: `calc(100% * ${thumbPosition})` }} />
</div> </div>
<div className={styles['layer']}> <div className={styles['layer']}>
<svg className={styles['thumb']} style={{ marginLeft: `calc(100% * ${thumbPosition})` }} viewBox={'0 0 10 10'}> <svg className={styles['thumb']} style={{ marginLeft: `calc(100% * ${thumbPosition})` }} viewBox={'0 0 10 10'}>
@ -115,6 +120,7 @@ const Slider = ({ className, value, minimumValue, maximumValue, disabled, onSlid
Slider.propTypes = { Slider.propTypes = {
className: PropTypes.string, className: PropTypes.string,
value: PropTypes.number, value: PropTypes.number,
buffered: PropTypes.number,
minimumValue: PropTypes.number, minimumValue: PropTypes.number,
maximumValue: PropTypes.number, maximumValue: PropTypes.number,
disabled: PropTypes.bool, disabled: PropTypes.bool,

View file

@ -17,7 +17,7 @@ html.active-slider-within {
cursor: pointer; cursor: pointer;
&:hover, &:global(.active) { &:hover, &:global(.active) {
.track-before { .track-after {
background-color: @color-primary-light5; background-color: @color-primary-light5;
} }
} }
@ -29,7 +29,7 @@ html.active-slider-within {
background-color: @color-surface-dark5; background-color: @color-surface-dark5;
} }
.track-before { .track-after {
background-color: @color-surface; background-color: @color-surface;
} }
@ -52,18 +52,28 @@ html.active-slider-within {
} }
.track { .track {
z-index: 0;
flex: 1; flex: 1;
height: var(--track-size); height: var(--track-size);
background-color: @color-primary-dark3; background-color: @color-surface-light5-20;
} }
.track-before { .track-before {
z-index: 1;
flex: none;
height: var(--track-size);
background-color: @color-surface-light5-10;
}
.track-after {
z-index: 2;
flex: none; flex: none;
height: var(--track-size); height: var(--track-size);
background-color: @color-primary-light3; background-color: @color-primary-light3;
} }
.thumb { .thumb {
z-index: 3;
flex: none; flex: none;
width: var(--thumb-size); width: var(--thumb-size);
height: var(--thumb-size); height: var(--thumb-size);

View file

@ -17,6 +17,7 @@ const ControlBar = ({
paused, paused,
time, time,
duration, duration,
buffered,
volume, volume,
muted, muted,
playbackSpeed, playbackSpeed,
@ -140,6 +141,7 @@ const ControlBar = ({
className={styles['seek-bar']} className={styles['seek-bar']}
time={time} time={time}
duration={duration} duration={duration}
buffered={buffered}
onSeekRequested={onSeekRequested} onSeekRequested={onSeekRequested}
/> />
<div className={styles['control-bar-buttons-container']}> <div className={styles['control-bar-buttons-container']}>
@ -213,6 +215,7 @@ ControlBar.propTypes = {
paused: PropTypes.bool, paused: PropTypes.bool,
time: PropTypes.number, time: PropTypes.number,
duration: PropTypes.number, duration: PropTypes.number,
buffered: PropTypes.number,
volume: PropTypes.number, volume: PropTypes.number,
muted: PropTypes.bool, muted: PropTypes.bool,
playbackSpeed: PropTypes.number, playbackSpeed: PropTypes.number,

View file

@ -9,7 +9,7 @@ const { Slider } = require('stremio/common');
const formatTime = require('./formatTime'); const formatTime = require('./formatTime');
const styles = require('./styles'); const styles = require('./styles');
const SeekBar = ({ className, time, duration, onSeekRequested }) => { const SeekBar = ({ className, time, duration, buffered, onSeekRequested }) => {
const disabled = time === null || isNaN(time) || duration === null || isNaN(duration); const disabled = time === null || isNaN(time) || duration === null || isNaN(duration);
const routeFocused = useRouteFocused(); const routeFocused = useRouteFocused();
const [seekTime, setSeekTime] = React.useState(null); const [seekTime, setSeekTime] = React.useState(null);
@ -49,6 +49,7 @@ const SeekBar = ({ className, time, duration, onSeekRequested }) => {
: :
0 0
} }
buffered={buffered}
minimumValue={0} minimumValue={0}
maximumValue={duration} maximumValue={duration}
disabled={disabled} disabled={disabled}
@ -64,6 +65,7 @@ SeekBar.propTypes = {
className: PropTypes.string, className: PropTypes.string,
time: PropTypes.number, time: PropTypes.number,
duration: PropTypes.number, duration: PropTypes.number,
buffered: PropTypes.number,
onSeekRequested: PropTypes.func onSeekRequested: PropTypes.func
}; };

View file

@ -3,7 +3,7 @@
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less'; @import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
:import('~stremio/common/Slider/styles.less') { :import('~stremio/common/Slider/styles.less') {
slider-track-before: track-before; slider-track-after: track-after;
slider-thumb: thumb; slider-thumb: thumb;
} }
@ -22,7 +22,7 @@
&:hover { &:hover {
.slider:not(:global(.disabled)) { .slider:not(:global(.disabled)) {
.slider-track-before { .slider-track-after {
transition: background-color 0s 100ms; transition: background-color 0s 100ms;
} }

View file

@ -4,7 +4,7 @@
:import('~stremio/common/Slider/styles.less') { :import('~stremio/common/Slider/styles.less') {
slider-track: track; slider-track: track;
slider-track-before: track-before; slider-track-after: track-after;
} }
.volume-slider:not(:global(.disabled)) { .volume-slider:not(:global(.disabled)) {
@ -12,12 +12,12 @@
background-color: @color-surface-dark5; background-color: @color-surface-dark5;
} }
.slider-track-before { .slider-track-after {
background-color: @color-surface-light3; background-color: @color-surface-light3;
} }
&:hover, &:global(.active) { &:hover, &:global(.active) {
.slider-track-before { .slider-track-after {
background-color: @color-surface-light5; background-color: @color-surface-light5;
} }
} }

View file

@ -42,6 +42,7 @@ const Player = ({ urlParams, queryParams }) => {
time: null, time: null,
duration: null, duration: null,
buffering: null, buffering: null,
buffered: null,
volume: null, volume: null,
muted: null, muted: null,
playbackSpeed: null, playbackSpeed: null,
@ -668,6 +669,7 @@ const Player = ({ urlParams, queryParams }) => {
paused={videoState.paused} paused={videoState.paused}
time={videoState.time} time={videoState.time}
duration={videoState.duration} duration={videoState.duration}
buffered={videoState.buffered}
volume={videoState.volume} volume={videoState.volume}
muted={videoState.muted} muted={videoState.muted}
playbackSpeed={videoState.playbackSpeed} playbackSpeed={videoState.playbackSpeed}