Merge pull request #1276 from Stremio/feat/player-torrent-buffering-indicator
Some checks are pending
Build / build (push) Waiting to run

Player: Add buffering indicator for torrents
This commit is contained in:
Tim 2026-05-18 17:12:32 +02:00 committed by GitHub
commit 3b79775245
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 100 additions and 59 deletions

View file

@ -0,0 +1,36 @@
.buffering {
display: flex;
align-items: center;
justify-content: center;
.logo {
position: absolute;
display: block;
max-width: 15rem;
max-height: 15rem;
width: auto;
height: auto;
animation: pulse 2s infinite;
transition: clip-path 0.1s ease-in-out;
&.background {
opacity: 0.2 !important;
}
}
}
@keyframes pulse {
0% {
opacity: 0.4;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(1.05);
}
100% {
opacity: 0.4;
transform: scale(1);
}
}

View file

@ -0,0 +1,38 @@
import React, { forwardRef, useMemo } from 'react';
import classNames from 'classnames';
import { Image } from 'stremio/components';
import styles from './Buffering.less';
type Props = {
className: string,
logo: string,
progress: number,
};
const Buffering = forwardRef<HTMLDivElement, Props>(({ className, logo, progress }, ref) => {
const style = useMemo(() => {
return {
clipPath: `inset(0 ${100 - progress}% 0 0)`,
};
}, [progress]);
return (
<div ref={ref} className={classNames(className, styles['buffering'])}>
<Image
className={styles['logo']}
style={style}
src={logo}
alt={' '}
fallbackSrc={require('/assets/images/stremio_symbol.png')}
/>
<Image
className={classNames(styles['logo'], styles['background'])}
src={logo}
alt={' '}
fallbackSrc={require('/assets/images/stremio_symbol.png')}
/>
</div>
);
});
export default Buffering;

View file

@ -0,0 +1,2 @@
import Buffering from './Buffering';
export default Buffering;

View file

@ -1,27 +0,0 @@
// Copyright (C) 2017-2023 Smart code 203358507
const React = require('react');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const { Image } = require('stremio/components');
const styles = require('./styles');
const BufferingLoader = React.forwardRef(({ className, logo }, ref) => {
return (
<div ref={ref} className={classnames(className, styles['buffering-loader-container'])}>
<Image
className={styles['buffering-loader']}
src={logo}
alt={' '}
fallbackSrc={require('/assets/images/stremio_symbol.png')}
/>
</div>
);
});
BufferingLoader.propTypes = {
className: PropTypes.string,
logo: PropTypes.string,
};
module.exports = BufferingLoader;

View file

@ -1,5 +0,0 @@
// Copyright (C) 2017-2023 Smart code 203358507
const BufferingLoader = require('./BufferingLoader');
module.exports = BufferingLoader;

View file

@ -1,25 +0,0 @@
// Copyright (C) 2017-2023 Smart code 203358507
.buffering-loader-container {
display: flex;
align-items: center;
justify-content: center;
.buffering-loader {
flex: none;
max-width: 15rem;
max-height: 15rem;
animation: fadeInOut 2s infinite;
display: block;
width: auto;
height: auto;
}
}
@keyframes fadeInOut {
0% { opacity: 0.2; }
50% { opacity: 1; }
100% { opacity: 0.2; }
}

View file

@ -12,7 +12,7 @@ const { useServices, useGamepad } = require('stremio/services');
const { useContentGamepadNavigation } = require('stremio/services/GamepadNavigation');
const { useSettings, useProfile, useFullscreen, useBinaryState, useToast, useStreamingServer, withCoreSuspender, usePlatform, onShortcut } = require('stremio/common');
const { HorizontalNavBar, Transition, ContextMenu } = require('stremio/components');
const BufferingLoader = require('./BufferingLoader');
const { default: Buffering } = require('./Buffering');
const VolumeChangeIndicator = require('./VolumeChangeIndicator');
const Error = require('./Error');
const ControlBar = require('./ControlBar');
@ -792,10 +792,11 @@ const Player = ({ urlParams, queryParams }) => {
}
{
(video.state.buffering || !video.state.loaded) && !error ?
<BufferingLoader
<Buffering
ref={bufferingRef}
className={classnames(styles['layer'], styles['buffering-layer'])}
logo={player?.metaItem?.content?.logo}
progress={statistics.progress}
/>
:
null

View file

@ -6,6 +6,8 @@ const { useCore } = require('stremio/core');
const useStatistics = (player, streamingServer) => {
const core = useCore();
const [progress, setProgress] = React.useState(0);
const stream = React.useMemo(() => {
if (player.stream?.type === 'Ready') {
return player.stream.content;
@ -49,6 +51,20 @@ const useStatistics = (player, streamingServer) => {
0;
}, [statistics]);
React.useEffect(() => {
statistics && setProgress(() => {
const MB = 1024 * 1024;
const peerScore = Math.min(1, statistics.peers / 8) * 20;
const minDownload = Math.min(8 * MB, Math.max(2 * MB, statistics.streamLen * 0.008));
const downloadedScore = Math.min(1, statistics.downloaded / minDownload) * 70;
const speedScore = Math.min(1, statistics.downloadSpeed / (1 * MB)) * 10;
return Math.min(99, peerScore + downloadedScore + speedScore);
});
}, [statistics]);
const getStatistics = React.useCallback(() => {
if (stream) {
const { infoHash, fileIdx } = stream;
@ -73,11 +89,16 @@ const useStatistics = (player, streamingServer) => {
return () => clearInterval(interval);
}, [getStatistics]);
React.useEffect(() => {
setProgress(infoHash ? 0 : 100);
}, [infoHash]);
return {
infoHash,
peers,
speed,
completed,
progress,
};
};