diff --git a/src/components/htmlMediaHelper.js b/src/components/htmlMediaHelper.js
index 7f623ee2cc..a7a9961e1c 100644
--- a/src/components/htmlMediaHelper.js
+++ b/src/components/htmlMediaHelper.js
@@ -197,29 +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
- .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();
- })
- .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();
- });
- } 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/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);
});
- });
}
}
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';