From 801c29d3b9c8c65d4a0be2e7fc08de7681ecc828 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Sat, 26 Feb 2022 13:03:41 +0300 Subject: [PATCH 1/3] refactor: Unify handling of play success --- src/components/htmlMediaHelper.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/htmlMediaHelper.js b/src/components/htmlMediaHelper.js index 7f623ee2cc..9a41404355 100644 --- a/src/components/htmlMediaHelper.js +++ b/src/components/htmlMediaHelper.js @@ -201,20 +201,19 @@ import { Events } from 'jellyfin-apiclient'; if (promise && promise.then) { // Chrome now returns a promise return promise - .then(() => { - onSuccessfulPlay(elem, onErrorFn); - return Promise.resolve(); - }) .catch((e) => { const errorName = (e.name || '').toLowerCase(); // safari uses aborterror if (errorName === 'notallowederror' || errorName === 'aborterror') { // swallow this error because the user can still click the play button on the video element - onSuccessfulPlay(elem, onErrorFn); return Promise.resolve(); } return Promise.reject(); + }) + .then(() => { + onSuccessfulPlay(elem, onErrorFn); + return Promise.resolve(); }); } else { onSuccessfulPlay(elem, onErrorFn); From 270430f6a110a97de3f1ea7edc4210ae41394ba7 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Sat, 26 Feb 2022 13:28:16 +0300 Subject: [PATCH 2/3] Polyfill HTMLMediaElement.play Return a `Promise` to match the new standard. --- src/components/htmlMediaHelper.js | 35 +++++++++++++------------------ src/legacy/htmlMediaElement.js | 26 +++++++++++++++++++++++ src/scripts/site.js | 1 + 3 files changed, 41 insertions(+), 21 deletions(-) create mode 100644 src/legacy/htmlMediaElement.js diff --git a/src/components/htmlMediaHelper.js b/src/components/htmlMediaHelper.js index 9a41404355..a7a9961e1c 100644 --- a/src/components/htmlMediaHelper.js +++ b/src/components/htmlMediaHelper.js @@ -197,28 +197,21 @@ import { Events } from 'jellyfin-apiclient'; export function playWithPromise(elem, onErrorFn) { try { - const promise = elem.play(); - if (promise && promise.then) { - // Chrome now returns a promise - return promise - .catch((e) => { - const errorName = (e.name || '').toLowerCase(); - // safari uses aborterror - if (errorName === 'notallowederror' || - errorName === 'aborterror') { - // swallow this error because the user can still click the play button on the video element - return Promise.resolve(); - } - return Promise.reject(); - }) - .then(() => { - onSuccessfulPlay(elem, onErrorFn); + return elem.play() + .catch((e) => { + const errorName = (e.name || '').toLowerCase(); + // safari uses aborterror + if (errorName === 'notallowederror' || + errorName === 'aborterror') { + // swallow this error because the user can still click the play button on the video element return Promise.resolve(); - }); - } else { - onSuccessfulPlay(elem, onErrorFn); - return Promise.resolve(); - } + } + return Promise.reject(); + }) + .then(() => { + onSuccessfulPlay(elem, onErrorFn); + return Promise.resolve(); + }); } catch (err) { console.error('error calling video.play: ' + err); return Promise.reject(); diff --git a/src/legacy/htmlMediaElement.js b/src/legacy/htmlMediaElement.js new file mode 100644 index 0000000000..b7a2c70825 --- /dev/null +++ b/src/legacy/htmlMediaElement.js @@ -0,0 +1,26 @@ +/** + * Polyfill for HTMLMediaElement + * - HTMLMediaElement.play + * Return a `Promise`. + */ + +(function (HTMLMediaElement) { + 'use strict'; + + const HTMLMediaElement_proto = HTMLMediaElement.prototype; + const real_play = HTMLMediaElement_proto.play; + + HTMLMediaElement_proto.play = function () { + try { + const promise = real_play.apply(this, arguments); + + if (typeof promise?.then === 'function') { + return promise; + } + + return Promise.resolve(); + } catch (err) { + return Promise.reject(err); + } + }; +}(HTMLMediaElement)); diff --git a/src/scripts/site.js b/src/scripts/site.js index 9a167ff5d2..7b94f2b204 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -31,6 +31,7 @@ import './serverNotifications'; import '../components/playback/playerSelectionMenu'; import '../legacy/domParserTextHtml'; import '../legacy/focusPreventScroll'; +import '../legacy/htmlMediaElement'; import '../legacy/vendorStyles'; import SyncPlay from '../components/syncPlay/core'; import { playbackManager } from '../components/playback/playbackmanager'; From bde869ecb2e284d1a443625024f01a7621c253f5 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Sat, 26 Feb 2022 13:56:50 +0300 Subject: [PATCH 3/3] refactor: Remove nested Promise --- .../syncPlay/ui/playbackPermissionManager.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/components/syncPlay/ui/playbackPermissionManager.js b/src/components/syncPlay/ui/playbackPermissionManager.js index 41c2dc84d3..2a70d7370a 100644 --- a/src/components/syncPlay/ui/playbackPermissionManager.js +++ b/src/components/syncPlay/ui/playbackPermissionManager.js @@ -39,16 +39,12 @@ class PlaybackPermissionManager { return Promise.resolve(true); } - return new Promise((resolve, reject) => { - const media = createTestMediaElement(); - media.play().then(() => { - resolve(); - }).catch((error) => { - reject(error); - }).finally(() => { + const media = createTestMediaElement(); + + return media.play() + .finally(() => { destroyTestMediaElement(media); }); - }); } }