mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-04-20 06:32:11 +00:00
refactor(Player): allow to download a m3u playlist in case of a playback error
This commit is contained in:
parent
2a7183ffab
commit
772cde815e
3 changed files with 62 additions and 5 deletions
|
|
@ -6,7 +6,8 @@ const classnames = require('classnames');
|
|||
const debounce = require('lodash.debounce');
|
||||
const { useRouteFocused } = require('stremio-router');
|
||||
const { useServices } = require('stremio/services');
|
||||
const { HorizontalNavBar, useDeepEqualEffect, useFullscreen, useBinaryState, useToast, useStreamingServer } = require('stremio/common');
|
||||
const { HorizontalNavBar, Button, useDeepEqualEffect, useFullscreen, useBinaryState, useToast, useStreamingServer } = require('stremio/common');
|
||||
const Icon = require('@stremio/stremio-icons/dom');
|
||||
const BufferingLoader = require('./BufferingLoader');
|
||||
const ControlBar = require('./ControlBar');
|
||||
const InfoMenu = require('./InfoMenu');
|
||||
|
|
@ -21,7 +22,7 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
const forceTranscoding = React.useMemo(() => {
|
||||
return queryParams.has('forceTranscoding');
|
||||
}, [queryParams]);
|
||||
const [player, updateLibraryItemState, pushToLibrary] = usePlayer(urlParams);
|
||||
const [player, playlist, updateLibraryItemState, pushToLibrary] = usePlayer(urlParams);
|
||||
const [settings, updateSettings] = useSettings();
|
||||
const streamingServer = useStreamingServer();
|
||||
const routeFocused = useRouteFocused();
|
||||
|
|
@ -418,6 +419,17 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
error !== null ?
|
||||
<div className={classnames(styles['layer'], styles['error-layer'])}>
|
||||
<div className={styles['error-label']}>{error.message}</div>
|
||||
{
|
||||
playlist ?
|
||||
<div className={styles['error-details']}>
|
||||
<Button className={styles['error-details-button']} title={'Download MU3 Playlist'} href={playlist.file} download={playlist.name}>
|
||||
<Icon className={styles['icon']} icon={'ic_downloads'} />
|
||||
<div className={styles['label']}>Download Playlist</div>
|
||||
</Button>
|
||||
</div>
|
||||
:
|
||||
null
|
||||
}
|
||||
</div>
|
||||
:
|
||||
null
|
||||
|
|
|
|||
|
|
@ -36,13 +36,14 @@ html:not(.active-slider-within) {
|
|||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 0;
|
||||
|
||||
&.error-layer {
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1.5rem;
|
||||
background-color: @color-background-dark5;
|
||||
|
||||
.error-label {
|
||||
|
|
@ -53,9 +54,39 @@ html:not(.active-slider-within) {
|
|||
color: @color-surface-light5-90;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-details {
|
||||
gap: 0.8rem;
|
||||
font-size: 1.5rem;
|
||||
color: @color-surface-light5-90;
|
||||
|
||||
.error-details-button {
|
||||
flex: none;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
height: 3.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
font-weight: 500;
|
||||
color: @color-surface-light5-90;
|
||||
background-color: @color-surface-light5-20;
|
||||
|
||||
&:hover, &:focus {
|
||||
background-color: @color-accent3;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
fill: @color-surface-light5-90;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.nav-bar-layer {
|
||||
z-index: 2;
|
||||
bottom: initial;
|
||||
background: transparent;
|
||||
overflow: visible;
|
||||
|
|
@ -86,6 +117,7 @@ html:not(.active-slider-within) {
|
|||
}
|
||||
|
||||
&.control-bar-layer {
|
||||
z-index: 2;
|
||||
top: initial;
|
||||
overflow: visible;
|
||||
|
||||
|
|
@ -101,6 +133,7 @@ html:not(.active-slider-within) {
|
|||
}
|
||||
|
||||
&.menu-layer {
|
||||
z-index: 2;
|
||||
top: initial;
|
||||
left: initial;
|
||||
right: 2rem;
|
||||
|
|
|
|||
|
|
@ -110,7 +110,19 @@ const usePlayer = (urlParams) => {
|
|||
}, 'player');
|
||||
}, []);
|
||||
const player = useModelState({ model: 'player', action, init, map });
|
||||
return [player, updateLibraryItemState, pushToLibrary];
|
||||
const playlist = React.useMemo(() => {
|
||||
if (player.selected === null || typeof player.selected.stream.url !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const playlist = `#EXTM3U\n\n#EXTINF:0,${player.title}\n${player.selected.stream.url}`;
|
||||
const base64File = `data:application/octet-stream;charset=utf-8;base64,${window.btoa(playlist)}`;
|
||||
return {
|
||||
name: `${player.title}.m3u`,
|
||||
file: base64File
|
||||
};
|
||||
}, [player]);
|
||||
return [player, playlist, updateLibraryItemState, pushToLibrary];
|
||||
};
|
||||
|
||||
module.exports = usePlayer;
|
||||
|
|
|
|||
Loading…
Reference in a new issue