diff --git a/src/js/plugins/vimeo.js b/src/js/plugins/vimeo.js index 21b1dd0a..09339229 100644 --- a/src/js/plugins/vimeo.js +++ b/src/js/plugins/vimeo.js @@ -140,7 +140,7 @@ const vimeo = { url.pathname = `${url.pathname.split('_')[0]}.jpg`; // Set and show poster - ui.setPoster.call(player, url.href); + ui.setPoster.call(player, url.href).catch(() => {}); }); // Setup instance diff --git a/src/js/plugins/youtube.js b/src/js/plugins/youtube.js index 8c4ebc1c..94369ece 100644 --- a/src/js/plugins/youtube.js +++ b/src/js/plugins/youtube.js @@ -166,7 +166,7 @@ const youtube = { const container = createElement('div', { id, poster }); player.media = replaceElement(container, player.media); - // Set poster image + // Id to poster wrapper const posterSrc = format => `https://img.youtube.com/vi/${videoId}/${format}default.jpg`; // Check thumbnail images in order of quality, but reject fallback thumbnails (120px wide) @@ -179,7 +179,8 @@ const youtube = { if (!posterSrc.includes('maxres')) { player.elements.poster.style.backgroundSize = 'cover'; } - }); + }) + .catch(() => {}); // Setup instance // https://developers.google.com/youtube/iframe_api_reference diff --git a/src/js/plyr.js b/src/js/plyr.js index 753db775..98d3a512 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -790,7 +790,7 @@ class Plyr { return; } - ui.setPoster.call(this, input); + ui.setPoster.call(this, input, false).catch(() => {}); } /** diff --git a/src/js/ui.js b/src/js/ui.js index 285739a7..5d7a6ae3 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -8,7 +8,7 @@ import i18n from './i18n'; import support from './support'; import browser from './utils/browser'; import { getElement, toggleClass, toggleState } from './utils/elements'; -import { triggerEvent } from './utils/events'; +import { ready, triggerEvent } from './utils/events'; import is from './utils/is'; import loadImage from './utils/loadImage'; @@ -109,8 +109,8 @@ const ui = { ui.setTitle.call(this); // Assure the poster image is set, if the property was added before the element was created - if (this.poster && this.elements.poster && !this.elements.poster.style.backgroundImage) { - ui.setPoster.call(this, this.poster); + if (this.poster) { + ui.setPoster.call(this, this.poster, false).catch(() => {}); } // Manually set the duration if user has overridden it. @@ -163,32 +163,43 @@ const ui = { }, // Set the poster image (async) - setPoster(poster) { - // Set property regardless of validity - this.media.setAttribute('poster', poster); - - // Bail if element is missing - if (!is.element(this.elements.poster)) { - return Promise.reject(); + // Used internally for the poster setter, with the passive option forced to false + setPoster(poster, passive = true) { + // Don't override if call is passive + if (passive && this.poster) { + return Promise.reject(new Error('Poster already set')); } - // Load the image, and set poster if successful - const loadPromise = loadImage(poster).then(() => { - this.elements.poster.style.backgroundImage = `url('${poster}')`; - Object.assign(this.elements.poster.style, { - backgroundImage: `url('${poster}')`, - // Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube) - backgroundSize: '', + // Set property synchronously to respect the call order + this.media.setAttribute('poster', poster); + + // Wait until ui is ready + return ready.call(this) + // Load image + .then(() => loadImage(poster)) + .catch(err => { + // Hide poster on error unless it's been set by another call + if (poster === this.poster) { + ui.togglePoster.call(this, false); + } + // Rethrow + throw err; + }) + .then(() => { + // Prevent race conditions + if (poster !== this.poster) { + throw new Error('setPoster cancelled by later call to setPoster'); + } + }) + .then(() => { + Object.assign(this.elements.poster.style, { + backgroundImage: `url('${poster}')`, + // Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube) + backgroundSize: '', + }); + ui.togglePoster.call(this, true); + return poster; }); - ui.togglePoster.call(this, true); - return poster; - }); - - // Hide the element if the poster can't be loaded (otherwise it will just be a black element covering the video) - loadPromise.catch(() => ui.togglePoster.call(this, false)); - - // Return the promise so the caller can use it as well - return loadPromise; }, // Check playing state