refactor(Player): allow to download a m3u playlist in case of a playback error

This commit is contained in:
Tim 2021-06-19 11:11:46 +02:00
parent 2a7183ffab
commit 772cde815e
3 changed files with 62 additions and 5 deletions

View file

@ -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

View file

@ -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;

View file

@ -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;