mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-01-11 22:40:31 +00:00
feat: add statistics menu on player
This commit is contained in:
parent
b1f9abb0c9
commit
e542e5d550
6 changed files with 293 additions and 14 deletions
|
|
@ -24,6 +24,8 @@ const ControlBar = ({
|
|||
audioTracks,
|
||||
metaItem,
|
||||
nextVideo,
|
||||
stream,
|
||||
statistics,
|
||||
onPlayRequested,
|
||||
onPauseRequested,
|
||||
onMuteRequested,
|
||||
|
|
@ -35,6 +37,7 @@ const ControlBar = ({
|
|||
onToggleSpeedMenu,
|
||||
onToggleVideosMenu,
|
||||
onToggleOptionsMenu,
|
||||
onToggleStatisticsMenu,
|
||||
...props
|
||||
}) => {
|
||||
const { chromecast } = useServices();
|
||||
|
|
@ -55,6 +58,9 @@ const ControlBar = ({
|
|||
const onOptionsButtonMouseDown = React.useCallback((event) => {
|
||||
event.nativeEvent.optionsMenuClosePrevented = true;
|
||||
}, []);
|
||||
const onStatisticsButtonMouseDown = React.useCallback((event) => {
|
||||
event.nativeEvent.statisticsMenuClosePrevented = true;
|
||||
}, []);
|
||||
const onPlayPauseButtonClick = React.useCallback(() => {
|
||||
if (paused) {
|
||||
if (typeof onPlayRequested === 'function') {
|
||||
|
|
@ -111,6 +117,11 @@ const ControlBar = ({
|
|||
onToggleOptionsMenu();
|
||||
}
|
||||
}, [onToggleOptionsMenu]);
|
||||
const onStatisticsButtonClick = React.useCallback(() => {
|
||||
if (typeof onToggleStatisticsMenu === 'function') {
|
||||
onToggleStatisticsMenu();
|
||||
}
|
||||
}, [onToggleStatisticsMenu]);
|
||||
const onChromecastButtonClick = React.useCallback(() => {
|
||||
chromecast.transport.requestSession();
|
||||
}, []);
|
||||
|
|
@ -165,12 +176,12 @@ 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': statistics === null || statistics.type === 'Err' || stream === null || typeof stream.infoHash !== 'string' || typeof stream.fileIdx !== 'number' })} tabIndex={-1} onMouseDown={onStatisticsButtonMouseDown} onClick={onStatisticsButtonClick}>
|
||||
<Icon className={styles['icon']} icon={'ic_network'} />
|
||||
</Button>
|
||||
<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>
|
||||
<Button className={classnames(styles['control-bar-button'], { 'disabled': metaItem === null || metaItem.type !== 'Ready' })} tabIndex={-1} onMouseDown={onInfoButtonMouseDown} onClick={onInfoButtonClick}>
|
||||
<Icon className={styles['icon']} icon={'ic_info'} />
|
||||
</Button>
|
||||
|
|
@ -209,6 +220,8 @@ ControlBar.propTypes = {
|
|||
audioTracks: PropTypes.array,
|
||||
metaItem: PropTypes.object,
|
||||
nextVideo: PropTypes.object,
|
||||
stream: PropTypes.object,
|
||||
statistics: PropTypes.object,
|
||||
onPlayRequested: PropTypes.func,
|
||||
onPauseRequested: PropTypes.func,
|
||||
onMuteRequested: PropTypes.func,
|
||||
|
|
@ -220,6 +233,7 @@ ControlBar.propTypes = {
|
|||
onToggleSpeedMenu: PropTypes.func,
|
||||
onToggleVideosMenu: PropTypes.func,
|
||||
onToggleOptionsMenu: PropTypes.func,
|
||||
onToggleStatisticsMenu: PropTypes.func,
|
||||
};
|
||||
|
||||
module.exports = ControlBar;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ const Icon = require('@stremio/stremio-icons/dom');
|
|||
const BufferingLoader = require('./BufferingLoader');
|
||||
const ControlBar = require('./ControlBar');
|
||||
const NextVideoPopup = require('./NextVideoPopup');
|
||||
const StatisticsMenu = require('./StatisticsMenu');
|
||||
const InfoMenu = require('./InfoMenu');
|
||||
const OptionsMenu = require('./OptionsMenu');
|
||||
const VideosMenu = require('./VideosMenu');
|
||||
|
|
@ -81,6 +82,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
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);
|
||||
|
|
@ -234,6 +236,9 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
if (!event.nativeEvent.videosMenuClosePrevented) {
|
||||
closeVideosMenu();
|
||||
}
|
||||
if (!event.nativeEvent.statisticsMenuClosePrevented) {
|
||||
closeStatisticsMenu();
|
||||
}
|
||||
}, []);
|
||||
const onContainerMouseMove = React.useCallback((event) => {
|
||||
setImmersed(false);
|
||||
|
|
@ -356,6 +361,26 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
}
|
||||
}
|
||||
}, [player.nextVideo, videoState.time, videoState.duration]);
|
||||
React.useEffect(() => {
|
||||
if (player.selected && player.selected.stream && typeof player.selected.stream.infoHash === 'string' && typeof player.selected.stream.fileIdx === 'number') {
|
||||
const { infoHash, fileIdx } = player.selected.stream;
|
||||
const getStatistics = () => {
|
||||
core.transport.dispatch({
|
||||
action: 'StreamingServer',
|
||||
args: {
|
||||
action: 'GetStatistics',
|
||||
args: {
|
||||
infoHash,
|
||||
fileIdx,
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
getStatistics();
|
||||
const statisticsInterval = setInterval(getStatistics, 5000);
|
||||
return () => clearInterval(statisticsInterval);
|
||||
}
|
||||
}, [player.selected]);
|
||||
React.useEffect(() => {
|
||||
if (!defaultSubtitlesSelected.current) {
|
||||
const findTrackByLang = (tracks, lang) => tracks.find((track) => track.lang === lang || langs.where('1', track.lang)?.[2] === lang);
|
||||
|
|
@ -445,7 +470,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
const onKeyDown = (event) => {
|
||||
switch (event.code) {
|
||||
case 'Space': {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && !speedMenuOpen && !optionsMenuOpen && videoState.paused !== null) {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && !speedMenuOpen && !optionsMenuOpen && !statisticsMenuOpen && videoState.paused !== null) {
|
||||
if (videoState.paused) {
|
||||
onPlayRequested();
|
||||
} else {
|
||||
|
|
@ -456,7 +481,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
break;
|
||||
}
|
||||
case 'ArrowRight': {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && !speedMenuOpen && !optionsMenuOpen && videoState.time !== null) {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && !speedMenuOpen && !optionsMenuOpen && !statisticsMenuOpen && videoState.time !== null) {
|
||||
const seekTimeMultiplier = event.shiftKey ? 3 : 1;
|
||||
onSeekRequested(videoState.time + (settings.seekTimeDuration * seekTimeMultiplier));
|
||||
}
|
||||
|
|
@ -464,7 +489,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
break;
|
||||
}
|
||||
case 'ArrowLeft': {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && !speedMenuOpen && !optionsMenuOpen && videoState.time !== null) {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && !speedMenuOpen && !optionsMenuOpen && !statisticsMenuOpen && videoState.time !== null) {
|
||||
const seekTimeMultiplier = event.shiftKey ? 3 : 1;
|
||||
onSeekRequested(videoState.time - (settings.seekTimeDuration * seekTimeMultiplier));
|
||||
}
|
||||
|
|
@ -472,14 +497,14 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
break;
|
||||
}
|
||||
case 'ArrowUp': {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && !speedMenuOpen && !optionsMenuOpen && videoState.volume !== null) {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && !speedMenuOpen && !optionsMenuOpen && !statisticsMenuOpen && videoState.volume !== null) {
|
||||
onVolumeChangeRequested(videoState.volume + 5);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'ArrowDown': {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && !speedMenuOpen && !optionsMenuOpen && videoState.volume !== null) {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && !speedMenuOpen && !optionsMenuOpen && !statisticsMenuOpen && videoState.volume !== null) {
|
||||
onVolumeChangeRequested(videoState.volume - 5);
|
||||
}
|
||||
|
||||
|
|
@ -490,6 +515,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
closeInfoMenu();
|
||||
closeSpeedMenu();
|
||||
closeVideosMenu();
|
||||
closeStatisticsMenu();
|
||||
if ((Array.isArray(videoState.subtitlesTracks) && videoState.subtitlesTracks.length > 0) ||
|
||||
(Array.isArray(videoState.extraSubtitlesTracks) && videoState.extraSubtitlesTracks.length > 0) ||
|
||||
(Array.isArray(videoState.audioTracks) && videoState.audioTracks.length > 0)) {
|
||||
|
|
@ -503,6 +529,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
closeSubtitlesMenu();
|
||||
closeSpeedMenu();
|
||||
closeVideosMenu();
|
||||
closeStatisticsMenu();
|
||||
if (player.metaItem !== null && player.metaItem.type === 'Ready') {
|
||||
toggleInfoMenu();
|
||||
}
|
||||
|
|
@ -514,6 +541,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
closeInfoMenu();
|
||||
closeSubtitlesMenu();
|
||||
closeVideosMenu();
|
||||
closeStatisticsMenu();
|
||||
if (videoState.playbackSpeed !== null) {
|
||||
toggleSpeedMenu();
|
||||
}
|
||||
|
|
@ -525,18 +553,32 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
closeInfoMenu();
|
||||
closeSubtitlesMenu();
|
||||
closeSpeedMenu();
|
||||
closeStatisticsMenu();
|
||||
if (player.metaItem !== null && player.metaItem.type === 'Ready') {
|
||||
toggleVideosMenu();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'KeyD': {
|
||||
closeOptionsMenu();
|
||||
closeInfoMenu();
|
||||
closeSubtitlesMenu();
|
||||
closeSpeedMenu();
|
||||
closeVideosMenu();
|
||||
if (streamingServer.statistics !== null && streamingServer.statistics.type !== 'Err' && player.selected && typeof player.selected.stream.infoHash === 'string' && typeof player.selected.stream.fileIdx === 'number') {
|
||||
toggleStatisticsMenu();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'Escape': {
|
||||
closeOptionsMenu();
|
||||
closeSubtitlesMenu();
|
||||
closeInfoMenu();
|
||||
closeSpeedMenu();
|
||||
closeVideosMenu();
|
||||
closeStatisticsMenu();
|
||||
onDismissNextVideoPopup();
|
||||
break;
|
||||
}
|
||||
|
|
@ -548,7 +590,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
return () => {
|
||||
window.removeEventListener('keydown', onKeyDown);
|
||||
};
|
||||
}, [player.metaItem, settings.seekTimeDuration, routeFocused, subtitlesMenuOpen, infoMenuOpen, videosMenuOpen, speedMenuOpen, optionsMenuOpen, videoState.paused, videoState.time, videoState.volume, videoState.audioTracks, videoState.subtitlesTracks, videoState.extraSubtitlesTracks, videoState.playbackSpeed, toggleSubtitlesMenu, toggleInfoMenu, toggleVideosMenu]);
|
||||
}, [player.metaItem, player.selected, settings.seekTimeDuration, routeFocused, subtitlesMenuOpen, infoMenuOpen, videosMenuOpen, speedMenuOpen, optionsMenuOpen, statisticsMenuOpen, videoState.paused, videoState.time, videoState.volume, videoState.audioTracks, videoState.subtitlesTracks, videoState.extraSubtitlesTracks, videoState.playbackSpeed, toggleSubtitlesMenu, toggleInfoMenu, toggleVideosMenu, toggleStatisticsMenu]);
|
||||
React.useLayoutEffect(() => {
|
||||
return () => {
|
||||
setImmersedDebounced.cancel();
|
||||
|
|
@ -557,7 +599,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
};
|
||||
}, []);
|
||||
return (
|
||||
<div className={classnames(styles['player-container'], { [styles['immersed']]: immersed && !casting && videoState.paused !== null && !videoState.paused && !subtitlesMenuOpen && !infoMenuOpen && !speedMenuOpen && !videosMenuOpen && !nextVideoPopupOpen && !optionsMenuOpen })}
|
||||
<div className={classnames(styles['player-container'], { [styles['immersed']]: immersed && !casting && videoState.paused !== null && !videoState.paused && !subtitlesMenuOpen && !infoMenuOpen && !speedMenuOpen && !videosMenuOpen && !nextVideoPopupOpen && !optionsMenuOpen && !statisticsMenuOpen })}
|
||||
onMouseDown={onContainerMouseDown}
|
||||
onMouseMove={onContainerMouseMove}
|
||||
onMouseOver={onContainerMouseMove}
|
||||
|
|
@ -608,7 +650,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
null
|
||||
}
|
||||
{
|
||||
subtitlesMenuOpen || infoMenuOpen || videosMenuOpen || speedMenuOpen || optionsMenuOpen ?
|
||||
subtitlesMenuOpen || infoMenuOpen || videosMenuOpen || speedMenuOpen || optionsMenuOpen || statisticsMenuOpen ?
|
||||
<div className={styles['layer']} />
|
||||
:
|
||||
null
|
||||
|
|
@ -633,6 +675,8 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
audioTracks={videoState.audioTracks}
|
||||
metaItem={player.metaItem}
|
||||
nextVideo={player.nextVideo}
|
||||
stream={player.selected !== null ? player.selected.stream : null}
|
||||
statistics={streamingServer.statistics}
|
||||
onPlayRequested={onPlayRequested}
|
||||
onPauseRequested={onPauseRequested}
|
||||
onMuteRequested={onMuteRequested}
|
||||
|
|
@ -644,6 +688,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
onToggleInfoMenu={toggleInfoMenu}
|
||||
onToggleSpeedMenu={toggleSpeedMenu}
|
||||
onToggleVideosMenu={toggleVideosMenu}
|
||||
onToggleStatisticsMenu={toggleStatisticsMenu}
|
||||
onMouseMove={onBarMouseMove}
|
||||
onMouseOver={onBarMouseMove}
|
||||
/>
|
||||
|
|
@ -659,6 +704,16 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
:
|
||||
null
|
||||
}
|
||||
{
|
||||
statisticsMenuOpen ?
|
||||
<StatisticsMenu
|
||||
className={classnames(styles['layer'], styles['menu-layer'])}
|
||||
stream={player.selected !== null ? player.selected.stream : null}
|
||||
statistics={streamingServer.statistics}
|
||||
/>
|
||||
:
|
||||
null
|
||||
}
|
||||
{
|
||||
subtitlesMenuOpen ?
|
||||
<SubtitlesMenu
|
||||
|
|
|
|||
79
src/routes/Player/StatisticsMenu/StatisticsMenu.js
Normal file
79
src/routes/Player/StatisticsMenu/StatisticsMenu.js
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright (C) 2017-2023 Smart code 203358507
|
||||
|
||||
const React = require('react');
|
||||
const classNames = require('classnames');
|
||||
const PropTypes = require('prop-types');
|
||||
const styles = require('./styles.less');
|
||||
|
||||
const StatisticsMenu = ({ className, stream, statistics }) => {
|
||||
const peers = React.useMemo(() => {
|
||||
return statistics.type === 'Ready' && statistics.content?.peers ?
|
||||
statistics.content.peers
|
||||
:
|
||||
0;
|
||||
}, [statistics]);
|
||||
|
||||
const speed = React.useMemo(() => {
|
||||
return statistics.type === 'Ready' && statistics.content?.downloadSpeed ?
|
||||
(statistics.content.downloadSpeed / 1000 / 1000).toFixed(2)
|
||||
:
|
||||
0;
|
||||
}, [statistics]);
|
||||
|
||||
const completed = React.useMemo(() => {
|
||||
return statistics.type === 'Ready' && statistics.content?.streamProgress ?
|
||||
(statistics.content.streamProgress * 100).toFixed(2)
|
||||
:
|
||||
0;
|
||||
}, [statistics]);
|
||||
|
||||
return (
|
||||
<div className={classNames(className, styles['statistics-menu-container'])}>
|
||||
<div className={styles['title']}>
|
||||
Statistics
|
||||
</div>
|
||||
<div className={styles['stats']}>
|
||||
<div className={styles['stat']}>
|
||||
<div className={styles['label']}>
|
||||
Peers
|
||||
</div>
|
||||
<div className={styles['value']}>
|
||||
{ peers }
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles['stat']}>
|
||||
<div className={styles['label']}>
|
||||
Speed
|
||||
</div>
|
||||
<div className={styles['value']}>
|
||||
{ speed } MB/s
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles['stat']}>
|
||||
<div className={styles['label']}>
|
||||
Completed
|
||||
</div>
|
||||
<div className={styles['value']}>
|
||||
{ completed } %
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles['info-hash']}>
|
||||
<div className={styles['label']}>
|
||||
Info Hash
|
||||
</div>
|
||||
<div className={styles['value']}>
|
||||
{ stream.infoHash }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
StatisticsMenu.propTypes = {
|
||||
className: PropTypes.string,
|
||||
stream: PropTypes.object,
|
||||
statistics: PropTypes.object,
|
||||
};
|
||||
|
||||
module.exports = StatisticsMenu;
|
||||
4
src/routes/Player/StatisticsMenu/index.js
Normal file
4
src/routes/Player/StatisticsMenu/index.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// Copyright (C) 2017-2023 Smart code 203358507
|
||||
|
||||
const StatisticsMenu = require('./StatisticsMenu');
|
||||
module.exports = StatisticsMenu;
|
||||
52
src/routes/Player/StatisticsMenu/styles.less
Normal file
52
src/routes/Player/StatisticsMenu/styles.less
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (C) 2017-2023 Smart code 203358507
|
||||
|
||||
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
|
||||
|
||||
.statistics-menu-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
width: 30rem;
|
||||
padding: 1.5rem;
|
||||
|
||||
.title {
|
||||
flex: none;
|
||||
font-weight: 600;
|
||||
color: @color-surface-light5-90;
|
||||
}
|
||||
|
||||
.label {
|
||||
flex: none;
|
||||
font-weight: 500;
|
||||
color: @color-surface-light5-50;
|
||||
}
|
||||
|
||||
.value {
|
||||
flex: none;
|
||||
font-weight: 500;
|
||||
color: @color-surface-light5-90;
|
||||
}
|
||||
|
||||
.stats {
|
||||
flex: auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
|
||||
.stat {
|
||||
flex: auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.info-hash {
|
||||
flex: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
81
src/types/models/StremingServer.d.ts
vendored
81
src/types/models/StremingServer.d.ts
vendored
|
|
@ -25,11 +25,86 @@ type StreamingServerSettings = {
|
|||
serverVersion: string,
|
||||
};
|
||||
|
||||
type SFile = {
|
||||
name: string,
|
||||
path: string,
|
||||
length: number,
|
||||
offset: number,
|
||||
};
|
||||
|
||||
type Source = {
|
||||
last_started: string,
|
||||
numFound: number,
|
||||
numFoundUniq: number,
|
||||
numRequests: number,
|
||||
url: string,
|
||||
}
|
||||
|
||||
type Growler = {
|
||||
flood: number,
|
||||
pulse: number,
|
||||
}
|
||||
|
||||
type PeerSearch = {
|
||||
max: number,
|
||||
min: number,
|
||||
sources: string[],
|
||||
}
|
||||
|
||||
type SwarmCap = {
|
||||
maxSpeed: number,
|
||||
minPeers: number,
|
||||
}
|
||||
|
||||
type Options = {
|
||||
connections: number,
|
||||
dht: boolean,
|
||||
growler: Growler,
|
||||
handshakeTimeout: number,
|
||||
path: string,
|
||||
peerSearch: PeerSearch,
|
||||
swarmCap: SwarmCap,
|
||||
timeout: number,
|
||||
tracker: boolean,
|
||||
virtual: boolean,
|
||||
}
|
||||
|
||||
type Statistics = {
|
||||
name: string,
|
||||
infoHash: string,
|
||||
files: SFile[],
|
||||
sources: Source[],
|
||||
opts: Options,
|
||||
downloadSpeed: number,
|
||||
uploadSpeed: number,
|
||||
downloaded: number,
|
||||
uploaded: number,
|
||||
unchoked: number,
|
||||
peers: number,
|
||||
queued: number,
|
||||
unique: number,
|
||||
connectionTries: number,
|
||||
peerSearchRunning: boolean,
|
||||
streamLen: number,
|
||||
streamName: string,
|
||||
streamProgress: number,
|
||||
swarmConnections: number,
|
||||
swarmPaused: boolean,
|
||||
swarmSize: number,
|
||||
};
|
||||
|
||||
type Selected = {
|
||||
transportUrl: string,
|
||||
statistics: {
|
||||
infoHash: string,
|
||||
fileIdx: number,
|
||||
} | null
|
||||
};
|
||||
|
||||
type StreamingServer = {
|
||||
baseUrl: Loadable<string> | null,
|
||||
selected: {
|
||||
transportUrl: string,
|
||||
} | null,
|
||||
selected: Selected | null,
|
||||
settings: Loadable<StreamingServerSettings> | null,
|
||||
torrent: [string, Loadable<Torrent>] | null,
|
||||
statistics: Loadable<Statistics> | null,
|
||||
};
|
||||
Loading…
Reference in a new issue