diff --git a/src/components/alert.js b/src/components/alert.js index 3d967b55a9..f6339489f3 100644 --- a/src/components/alert.js +++ b/src/components/alert.js @@ -1,4 +1,4 @@ - +import { appRouter } from './appRouter'; import browser from '../scripts/browser'; import dialog from './dialog/dialog'; import globalize from '../scripts/globalize'; @@ -10,7 +10,7 @@ import globalize from '../scripts/globalize'; return originalString.replace(reg, strWith); } - export default function (text, title) { + export default async function (text, title) { let options; if (typeof text === 'string') { options = { @@ -22,7 +22,9 @@ import globalize from '../scripts/globalize'; } if (browser.tv && window.alert) { + await appRouter.ready(); alert(replaceAll(options.text || '', '
', '\n')); + return Promise.resolve(); } else { const items = []; @@ -35,8 +37,6 @@ import globalize from '../scripts/globalize'; options.buttons = items; return dialog.show(options); } - - return Promise.resolve(); } /* eslint-enable indent */ diff --git a/src/components/appRouter.js b/src/components/appRouter.js index 5a89deca54..aa869b7cc6 100644 --- a/src/components/appRouter.js +++ b/src/components/appRouter.js @@ -24,6 +24,7 @@ class AppRouter { isDummyBackToHome; msgTimeout; popstateOccurred = false; + promiseShow; resolveOnNextShow; previousRoute = {}; /** @@ -122,11 +123,24 @@ class AppRouter { } } - back() { - page.back(); + ready() { + return this.promiseShow || Promise.resolve(); } - show(path, options) { + async back() { + if (this.promiseShow) await this.promiseShow; + + this.promiseShow = new Promise((resolve) => { + this.resolveOnNextShow = resolve; + page.back(); + }); + + return this.promiseShow; + } + + async show(path, options) { + if (this.promiseShow) await this.promiseShow; + // ensure the path does not start with '#!' since the router adds this if (path.startsWith('#!')) { path = path.substring(2); @@ -146,17 +160,25 @@ class AppRouter { } } - return new Promise((resolve) => { + this.promiseShow = new Promise((resolve) => { this.resolveOnNextShow = resolve; - page.show(path, options); + // Schedule a call to return the promise + setTimeout(() => page.show(path, options), 0); }); + + return this.promiseShow; } - showDirect(path) { - return new Promise(function(resolve) { + async showDirect(path) { + if (this.promiseShow) await this.promiseShow; + + this.promiseShow = new Promise((resolve) => { this.resolveOnNextShow = resolve; - page.show(this.baseUrl() + path); + // Schedule a call to return the promise + setTimeout(() => page.show(this.baseUrl() + path), 0); }); + + return this.promiseShow; } start(options) { @@ -414,6 +436,7 @@ class AppRouter { onViewShow() { const resolve = this.resolveOnNextShow; if (resolve) { + this.promiseShow = null; this.resolveOnNextShow = null; resolve(); } diff --git a/src/components/confirm/confirm.js b/src/components/confirm/confirm.js index 1978324e7c..7fe7fb9832 100644 --- a/src/components/confirm/confirm.js +++ b/src/components/confirm/confirm.js @@ -1,3 +1,4 @@ +import { appRouter } from '../appRouter'; import browser from '../../scripts/browser'; import dialog from '../dialog/dialog'; import globalize from '../../scripts/globalize'; @@ -6,7 +7,7 @@ function replaceAll(str, find, replace) { return str.split(find).join(replace); } -function nativeConfirm(options) { +async function nativeConfirm(options) { if (typeof options === 'string') { options = { title: '', @@ -15,6 +16,7 @@ function nativeConfirm(options) { } const text = replaceAll(options.text || '', '
', '\n'); + await appRouter.ready(); const result = window.confirm(text); if (result) { diff --git a/src/components/dialogHelper/dialogHelper.js b/src/components/dialogHelper/dialogHelper.js index 8a6a22fd15..fbaf86a024 100644 --- a/src/components/dialogHelper/dialogHelper.js +++ b/src/components/dialogHelper/dialogHelper.js @@ -184,7 +184,9 @@ import '../../assets/css/scrollstyles.scss'; return dlg.getAttribute('data-history') === 'true'; } - export function open(dlg) { + export async function open(dlg) { + await appRouter.ready(); + if (globalOnOpenCallback) { globalOnOpenCallback(dlg); } diff --git a/src/components/htmlMediaHelper.js b/src/components/htmlMediaHelper.js index da53d6a063..82d6d790bc 100644 --- a/src/components/htmlMediaHelper.js +++ b/src/components/htmlMediaHelper.js @@ -185,6 +185,12 @@ import { Events } from 'jellyfin-apiclient'; return Promise.resolve(); } + export function resetSrc(elem) { + elem.src = ''; + elem.innerHTML = ''; + elem.removeAttribute('src'); + } + function onSuccessfulPlay(elem, onErrorFn) { elem.addEventListener('error', onErrorFn); } @@ -344,9 +350,7 @@ import { Events } from 'jellyfin-apiclient'; export function onEndedInternal(instance, elem, onErrorFn) { elem.removeEventListener('error', onErrorFn); - elem.src = ''; - elem.innerHTML = ''; - elem.removeAttribute('src'); + resetSrc(elem); destroyHlsPlayer(instance); destroyFlvPlayer(instance); diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index df84b072ae..85da076c5d 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -3064,7 +3064,9 @@ class PlaybackManager { const data = getPlayerData(player); const streamInfo = data.streamInfo; - const nextItem = self._playNextAfterEnded ? self._playQueueManager.getNextItemInfo() : null; + const errorOccurred = displayErrorCode && typeof (displayErrorCode) === 'string'; + + const nextItem = self._playNextAfterEnded && !errorOccurred ? self._playQueueManager.getNextItemInfo() : null; const nextMediaType = (nextItem ? nextItem.item.MediaType : null); @@ -3101,17 +3103,15 @@ class PlaybackManager { const newPlayer = nextItem ? getPlayer(nextItem.item, nextItemPlayOptions) : null; if (newPlayer !== player) { + data.streamInfo = null; destroyPlayer(player); removeCurrentPlayer(player); } - if (displayErrorCode && typeof (displayErrorCode) === 'string') { + if (errorOccurred) { showPlaybackInfoErrorMessage(self, 'PlaybackError' + displayErrorCode); } else if (nextItem) { self.nextTrack(); - } else { - // Nothing more to play - clear data - data.streamInfo = null; } } diff --git a/src/plugins/htmlAudioPlayer/plugin.js b/src/plugins/htmlAudioPlayer/plugin.js index 1e7d47f099..baa396dbb8 100644 --- a/src/plugins/htmlAudioPlayer/plugin.js +++ b/src/plugins/htmlAudioPlayer/plugin.js @@ -105,8 +105,6 @@ class HtmlAudioPlayer { }; function setCurrentSrc(elem, options) { - elem.removeEventListener('error', onError); - unBindEvents(elem); bindEvents(elem); @@ -184,6 +182,7 @@ class HtmlAudioPlayer { elem.removeEventListener('playing', onPlaying); elem.removeEventListener('play', onPlay); elem.removeEventListener('waiting', onWaiting); + elem.removeEventListener('error', onError); // bound in htmlMediaHelper } self.stop = function (destroyPlayer) { @@ -222,6 +221,7 @@ class HtmlAudioPlayer { self.destroy = function () { unBindEvents(self._mediaElement); + htmlMediaHelper.resetSrc(self._mediaElement); }; function createMediaElement() { diff --git a/src/plugins/htmlVideoPlayer/plugin.js b/src/plugins/htmlVideoPlayer/plugin.js index eeef1d5181..e4692e5971 100644 --- a/src/plugins/htmlVideoPlayer/plugin.js +++ b/src/plugins/htmlVideoPlayer/plugin.js @@ -13,6 +13,7 @@ import { getCrossOriginValue, enableHlsJsPlayer, applySrc, + resetSrc, playWithPromise, onEndedInternal, saveVolume, @@ -708,6 +709,9 @@ function tryRemoveElement(elem) { videoElement.removeEventListener('click', this.onClick); videoElement.removeEventListener('dblclick', this.onDblClick); videoElement.removeEventListener('waiting', this.onWaiting); + videoElement.removeEventListener('error', this.onError); // bound in htmlMediaHelper + + resetSrc(videoElement); videoElement.parentNode.removeChild(videoElement); }