From 550bd543e3928f7aba1a368a8be5a1e3ae86bd05 Mon Sep 17 00:00:00 2001 From: John Law Date: Wed, 12 Feb 2020 00:06:28 -0800 Subject: Added missing full screen options for type definition --- src/js/plyr.d.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/js/plyr.d.ts b/src/js/plyr.d.ts index cd204a6f..7c7f28b1 100644 --- a/src/js/plyr.d.ts +++ b/src/js/plyr.d.ts @@ -507,6 +507,7 @@ declare namespace Plyr { enabled?: boolean; fallback?: boolean; allowAudio?: boolean; + iosNative?: boolean; } interface CaptionOptions { -- cgit v1.2.3 From 70470ae8d2a1a9304cec9110043011b9358d20a3 Mon Sep 17 00:00:00 2001 From: CzBiX Date: Mon, 17 Feb 2020 18:02:39 +0800 Subject: Fix issue when controls config is string or element --- src/js/captions.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/js/captions.js b/src/js/captions.js index e33fd81a..724def9e 100644 --- a/src/js/captions.js +++ b/src/js/captions.js @@ -151,7 +151,8 @@ const captions = { toggleClass(this.elements.container, this.config.classNames.captions.enabled, !is.empty(tracks)); // Update available languages in list - if ((this.config.controls || []).includes('settings') && this.config.settings.includes('captions')) { + if ((is.array(this.config.controls) && this.config.controls.includes('settings')) + && this.config.settings.includes('captions')) { controls.setCaptionsMenu.call(this); } }, -- cgit v1.2.3 From 0cf5d25a7f28203553b1fa2db5c600995c284b65 Mon Sep 17 00:00:00 2001 From: Hugues Date: Thu, 20 Feb 2020 12:57:47 +0000 Subject: catch error in setPlaybackRate on Vimeo --- src/js/plugins/vimeo.js | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/js/plugins/vimeo.js b/src/js/plugins/vimeo.js index fa965d8e..010cf5f7 100644 --- a/src/js/plugins/vimeo.js +++ b/src/js/plugins/vimeo.js @@ -204,6 +204,9 @@ const vimeo = { player.embed.setPlaybackRate(input).then(() => { speed = input; triggerEvent.call(player, player.media, 'ratechange'); + }).catch(() => { + // Cannot set Playback Rate, Video is probably not on Pro account + player.options.speed = [1]; }); }, }); -- cgit v1.2.3 From fea5e76b7612f0eb8c0e1b2fa1a4e6d36e85120d Mon Sep 17 00:00:00 2001 From: Morgan Zolob Date: Mon, 24 Feb 2020 11:33:50 -0800 Subject: Use number instead of string in TS quality definitions Using strings for the quality doesn't work, plyr expects numbers, so this fixes the definitions. --- src/js/plyr.d.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/js/plyr.d.ts b/src/js/plyr.d.ts index cd204a6f..50179468 100644 --- a/src/js/plyr.d.ts +++ b/src/js/plyr.d.ts @@ -94,9 +94,8 @@ declare class Plyr { /** * Gets or sets the quality for the player. The setter accepts a value from the options specified in your config. - * Remarks: YouTube only. HTML5 will follow. */ - quality: string; + quality: number; /** * Gets or sets the current loop state of the player. @@ -475,8 +474,8 @@ declare namespace Plyr { } interface QualityOptions { - default: string; - options: string[]; + default: number; + options: number[]; } interface LoopOptions { -- cgit v1.2.3 From bc8a25d0da8720878fbb845b08e1f8170c3d24f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20Burgener?= Date: Tue, 25 Feb 2020 10:46:31 +0100 Subject: Completely hide SVG icons to screen readers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SVG icons should be ignored by screen readers since they have complimentary labels (aria-label or plyr__sr-only). The current « presentation » role simply makes the element behave like a « span » which is incorrect, aria-hidden prevents screen readers from taking care of these elements at all. --- src/js/controls.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/js/controls.js b/src/js/controls.js index 66ec7139..37df497f 100644 --- a/src/js/controls.js +++ b/src/js/controls.js @@ -111,7 +111,7 @@ const controls = { setAttributes( icon, extend(attributes, { - role: 'presentation', + 'aria-hidden': 'true', focusable: 'false', }), ); -- cgit v1.2.3 From 6020f95e50f8a0dba028e527dbec378c9fd17aec Mon Sep 17 00:00:00 2001 From: Hugues Date: Tue, 25 Feb 2020 11:10:06 +0000 Subject: Add missing Typescripts types and options --- src/js/plyr.d.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'src') diff --git a/src/js/plyr.d.ts b/src/js/plyr.d.ts index cd204a6f..1ff7d51f 100644 --- a/src/js/plyr.d.ts +++ b/src/js/plyr.d.ts @@ -134,6 +134,21 @@ declare class Plyr { */ pip: boolean; + /** + * Gets or sets the aspect ratio for embedded players. + */ + ratio?: string; + + /** + * Returns the current video Provider + */ + readonly provider: 'html5' | 'vimeo' | 'youtube'; + + /** + * Returns the native API for Vimeo or Youtube players + */ + readonly embed?: any; + readonly fullscreen: Plyr.FullscreenControl; /** @@ -472,6 +487,16 @@ declare namespace Plyr { * enabled: Whether to enable vi.ai ads. publisherId: Your unique vi.ai publisher ID. */ ads?: AdOptions; + + /** + * Vimeo Player Options. + */ + vimeo?: object; + + /** + * Youtube Player Options. + */ + youtube?: object; } interface QualityOptions { -- cgit v1.2.3 From 81b41be750c9eddbabafdbd304614d827cd0ca82 Mon Sep 17 00:00:00 2001 From: max Date: Tue, 25 Feb 2020 17:53:44 +0100 Subject: preview-thumbnails via src:callback() --- src/js/plugins/preview-thumbnails.js | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/js/plugins/preview-thumbnails.js b/src/js/plugins/preview-thumbnails.js index 86eeebc8..e313a01f 100644 --- a/src/js/plugins/preview-thumbnails.js +++ b/src/js/plugins/preview-thumbnails.js @@ -137,19 +137,31 @@ class PreviewThumbnails { throw new Error('Missing previewThumbnails.src config attribute'); } - // If string, convert into single-element list - const urls = is.string(src) ? [src] : src; - // Loop through each src URL. Download and process the VTT file, storing the resulting data in this.thumbnails - const promises = urls.map(u => this.getThumbnail(u)); - - Promise.all(promises).then(() => { + // Resolve promise + const exec_resolve = () => { // Sort smallest to biggest (e.g., [120p, 480p, 1080p]) this.thumbnails.sort((x, y) => x.height - y.height); this.player.debug.log('Preview thumbnails', this.thumbnails); resolve(); - }); + }; + // Via callback() + if( typeof(src) == 'function' ) { + // Ask + this.thumbnails = src(); + // Resolve + exec_resolve(); + } + // VTT urls + else { + // If string, convert into single-element list + const urls = is.string(src) ? [src] : src; + // Loop through each src URL. Download and process the VTT file, storing the resulting data in this.thumbnails + const promises = urls.map(u => this.getThumbnail(u)); + // Resolve + Promise.all(promises).then(exec_resolve); + } }); } -- cgit v1.2.3 From b212b25a9eca8a440a05d53b744aa56f646b2929 Mon Sep 17 00:00:00 2001 From: max Date: Wed, 26 Feb 2020 10:35:08 +0100 Subject: Fixes --- src/js/plugins/preview-thumbnails.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/js/plugins/preview-thumbnails.js b/src/js/plugins/preview-thumbnails.js index e313a01f..7e9f0dc9 100644 --- a/src/js/plugins/preview-thumbnails.js +++ b/src/js/plugins/preview-thumbnails.js @@ -138,7 +138,7 @@ class PreviewThumbnails { } // Resolve promise - const exec_resolve = () => { + const resolvePromise = () => { // Sort smallest to biggest (e.g., [120p, 480p, 1080p]) this.thumbnails.sort((x, y) => x.height - y.height); @@ -147,11 +147,14 @@ class PreviewThumbnails { resolve(); }; // Via callback() - if( typeof(src) == 'function' ) { + if (typeof(src) == 'function') { // Ask - this.thumbnails = src(); - // Resolve - exec_resolve(); + let that = this; + src(function(thumbnails) { + that.thumbnails = thumbnails; + // Resolve + resolvePromise(); + }); } // VTT urls else { @@ -160,7 +163,7 @@ class PreviewThumbnails { // Loop through each src URL. Download and process the VTT file, storing the resulting data in this.thumbnails const promises = urls.map(u => this.getThumbnail(u)); // Resolve - Promise.all(promises).then(exec_resolve); + Promise.all(promises).then(resolvePromise); } }); } -- cgit v1.2.3 From ace682abbdeba13ea3664e9dde38a903a4a5da5e Mon Sep 17 00:00:00 2001 From: max Date: Wed, 26 Feb 2020 10:41:26 +0100 Subject: Fixes2 --- src/js/plugins/preview-thumbnails.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/js/plugins/preview-thumbnails.js b/src/js/plugins/preview-thumbnails.js index 7e9f0dc9..4c13ab33 100644 --- a/src/js/plugins/preview-thumbnails.js +++ b/src/js/plugins/preview-thumbnails.js @@ -138,7 +138,7 @@ class PreviewThumbnails { } // Resolve promise - const resolvePromise = () => { + const sortAndResolve = () => { // Sort smallest to biggest (e.g., [120p, 480p, 1080p]) this.thumbnails.sort((x, y) => x.height - y.height); @@ -147,13 +147,13 @@ class PreviewThumbnails { resolve(); }; // Via callback() - if (typeof(src) == 'function') { + if (is.function(src)) { // Ask let that = this; src(function(thumbnails) { that.thumbnails = thumbnails; // Resolve - resolvePromise(); + sortAndResolve(); }); } // VTT urls @@ -163,7 +163,7 @@ class PreviewThumbnails { // Loop through each src URL. Download and process the VTT file, storing the resulting data in this.thumbnails const promises = urls.map(u => this.getThumbnail(u)); // Resolve - Promise.all(promises).then(resolvePromise); + Promise.all(promises).then(sortAndResolve); } }); } -- cgit v1.2.3 From fd353225c27fa210a036b87a847336bf10957ed1 Mon Sep 17 00:00:00 2001 From: Steejo Date: Mon, 9 Mar 2020 23:18:19 +0000 Subject: Ads plugin fixes to allow multiple VAST requests --- src/js/plugins/ads.js | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/js/plugins/ads.js b/src/js/plugins/ads.js index 6b4fca10..79e6e5d9 100644 --- a/src/js/plugins/ads.js +++ b/src/js/plugins/ads.js @@ -172,6 +172,17 @@ class Ads { // We assume the adContainer is the video container of the plyr element that will house the ads this.elements.displayContainer = new google.ima.AdDisplayContainer(this.elements.container, this.player.media); + // Create ads loader + this.loader = new google.ima.AdsLoader(this.elements.displayContainer); + + // Listen and respond to ads loaded and error events + this.loader.addEventListener( + google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, + event => this.onAdsManagerLoaded(event), + false, + ); + this.loader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, error => this.onAdError(error), false); + // Request video ads to be pre-loaded this.requestAds(); } @@ -183,17 +194,6 @@ class Ads { const { container } = this.player.elements; try { - // Create ads loader - this.loader = new google.ima.AdsLoader(this.elements.displayContainer); - - // Listen and respond to ads loaded and error events - this.loader.addEventListener( - google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, - event => this.onAdsManagerLoaded(event), - false, - ); - this.loader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, error => this.onAdError(error), false); - // Request video ads const request = new google.ima.AdsRequest(); request.adTagUrl = this.tagUrl; @@ -369,7 +369,14 @@ class Ads { // TODO: So there is still this thing where a video should only be allowed to start // playing when the IMA SDK is ready or has failed - this.loadAds(); + if (player.ended){ + this.loadAds(); + } + else + { + // The SDK won't allow new ads to be called without receiving a contentComplete() + this.loader.contentComplete(); + } break; @@ -563,6 +570,8 @@ class Ads { this.on('loaded', resolve); this.player.debug.log(this.manager); }); + // Now that the manager has been destroyed set it to also be un-initialized + this.initialized = false; // Now request some new advertisements this.requestAds(); -- cgit v1.2.3 From c7bf0c5c03a5c7716a39a0f2f5a11681eedbac7f Mon Sep 17 00:00:00 2001 From: Jesper Date: Tue, 10 Mar 2020 09:19:34 +0100 Subject: Fix prototype used for selector matcher function --- src/js/utils/elements.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/js/utils/elements.js b/src/js/utils/elements.js index b88aad0c..43f46416 100644 --- a/src/js/utils/elements.js +++ b/src/js/utils/elements.js @@ -221,7 +221,7 @@ export function hasClass(element, className) { // Element matches selector export function matches(element, selector) { - const prototype = { Element }; + const prototype = Element.prototype; function match() { return Array.from(document.querySelectorAll(selector)).includes(this); -- cgit v1.2.3 From 99ae4eb3c5aa335926ea76e868d236112404dc22 Mon Sep 17 00:00:00 2001 From: Jesper Date: Tue, 10 Mar 2020 09:30:42 +0100 Subject: Compare fullscreenElement with shadowroot host if player is in shadow DOM --- src/js/fullscreen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/js/fullscreen.js b/src/js/fullscreen.js index c74b3406..6dc069b2 100644 --- a/src/js/fullscreen.js +++ b/src/js/fullscreen.js @@ -118,7 +118,7 @@ class Fullscreen { const element = !this.prefix ? document.fullscreenElement : document[`${this.prefix}${this.property}Element`]; - return element === this.target; + return (element && element.shadowRoot) ? element === this.target.getRootNode().host : element === this.target; } // Get target element -- cgit v1.2.3 From 71928443f317e624ab94ff18e207447f06f745ad Mon Sep 17 00:00:00 2001 From: ydylla Date: Mon, 23 Mar 2020 22:50:19 +0100 Subject: silence all internal play promises --- src/js/fullscreen.js | 3 ++- src/js/html5.js | 3 ++- src/js/listeners.js | 11 ++++++----- src/js/plugins/ads.js | 3 ++- src/js/plyr.js | 5 +++-- src/js/utils/promise.js | 27 +++++++++++++++++++++++++++ 6 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 src/js/utils/promise.js (limited to 'src') diff --git a/src/js/fullscreen.js b/src/js/fullscreen.js index c74b3406..5da89e9c 100644 --- a/src/js/fullscreen.js +++ b/src/js/fullscreen.js @@ -8,6 +8,7 @@ import browser from './utils/browser'; import { getElements, hasClass, toggleClass } from './utils/elements'; import { on, triggerEvent } from './utils/events'; import is from './utils/is'; +import { silencePromise } from './utils/promise'; class Fullscreen { constructor(player) { @@ -268,7 +269,7 @@ class Fullscreen { // iOS native fullscreen if (browser.isIos && this.player.config.fullscreen.iosNative) { this.target.webkitExitFullscreen(); - this.player.play(); + silencePromise(this.player.play()); } else if (!Fullscreen.native || this.forceFallback) { this.toggleFallback(false); } else if (!this.prefix) { diff --git a/src/js/html5.js b/src/js/html5.js index 0591a709..6e8c6483 100644 --- a/src/js/html5.js +++ b/src/js/html5.js @@ -6,6 +6,7 @@ import support from './support'; import { removeElement } from './utils/elements'; import { triggerEvent } from './utils/events'; import is from './utils/is'; +import { silencePromise } from './utils/promise'; import { setAspectRatio } from './utils/style'; const html5 = { @@ -101,7 +102,7 @@ const html5 = { // Resume playing if (!paused) { - player.play(); + silencePromise(player.play()); } }); diff --git a/src/js/listeners.js b/src/js/listeners.js index 6a0046ee..d134a350 100644 --- a/src/js/listeners.js +++ b/src/js/listeners.js @@ -9,6 +9,7 @@ import browser from './utils/browser'; import { getElement, getElements, matches, toggleClass } from './utils/elements'; import { off, on, once, toggleListener, triggerEvent } from './utils/events'; import is from './utils/is'; +import { silencePromise } from './utils/promise'; import { getAspectRatio, setAspectRatio } from './utils/style'; class Listeners { @@ -99,7 +100,7 @@ class Listeners { case 75: // Space and K key if (!repeat) { - player.togglePlay(); + silencePromise(player.togglePlay()); } break; @@ -431,9 +432,9 @@ class Listeners { if (player.ended) { this.proxy(event, player.restart, 'restart'); - this.proxy(event, player.play, 'play'); + this.proxy(event, () => { silencePromise(player.play()) }, 'play'); } else { - this.proxy(event, player.togglePlay, 'play'); + this.proxy(event, () => { silencePromise(player.togglePlay()) }, 'play'); } }); } @@ -539,7 +540,7 @@ class Listeners { // Play/pause toggle if (elements.buttons.play) { Array.from(elements.buttons.play).forEach(button => { - this.bind(button, 'click', player.togglePlay, 'play'); + this.bind(button, 'click', () => { silencePromise(player.togglePlay()) }, 'play'); }); } @@ -681,7 +682,7 @@ class Listeners { // If we're done seeking and it was playing, resume playback if (play && done) { seek.removeAttribute(attribute); - player.play(); + silencePromise(player.play()); } else if (!done && player.playing) { seek.setAttribute(attribute, ''); player.pause(); diff --git a/src/js/plugins/ads.js b/src/js/plugins/ads.js index 6b4fca10..62def372 100644 --- a/src/js/plugins/ads.js +++ b/src/js/plugins/ads.js @@ -11,6 +11,7 @@ import { triggerEvent } from '../utils/events'; import i18n from '../utils/i18n'; import is from '../utils/is'; import loadScript from '../utils/load-script'; +import { silencePromise } from '../utils/promise'; import { formatTime } from '../utils/time'; import { buildUrlParams } from '../utils/urls'; @@ -510,7 +511,7 @@ class Ads { this.playing = false; // Play video - this.player.media.play(); + silencePromise(this.player.media.play()); } /** diff --git a/src/js/plyr.js b/src/js/plyr.js index 00d33463..00b95a5f 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -27,6 +27,7 @@ import is from './utils/is'; import loadSprite from './utils/load-sprite'; import { clamp } from './utils/numbers'; import { cloneDeep, extend } from './utils/objects'; +import { silencePromise } from './utils/promise'; import { getAspectRatio, reduceAspectRatio, setAspectRatio, validateRatio } from './utils/style'; import { parseUrl } from './utils/urls'; @@ -303,7 +304,7 @@ class Plyr { // Autoplay if required if (this.isHTML5 && this.config.autoplay) { - setTimeout(() => this.play(), 10); + setTimeout(() => silencePromise(this.play()), 10); } // Seek time will be recorded (in listeners.js) so we can prevent hiding controls for a few seconds after seek @@ -356,7 +357,7 @@ class Plyr { // Intecept play with ads if (this.ads && this.ads.enabled) { - this.ads.managerPromise.then(() => this.ads.play()).catch(() => this.media.play()); + this.ads.managerPromise.then(() => this.ads.play()).catch(() => silencePromise(this.media.play())); } // Return the promise (for HTML5) diff --git a/src/js/utils/promise.js b/src/js/utils/promise.js new file mode 100644 index 00000000..42fcc2c3 --- /dev/null +++ b/src/js/utils/promise.js @@ -0,0 +1,27 @@ +/** + * Returns whether an object is `Promise`-like (i.e. has a `then` method). + * + * @param {Object} value + * An object that may or may not be `Promise`-like. + * + * @return {boolean} + * Whether or not the object is `Promise`-like. + */ +export function isPromise(value) { + return value !== undefined && value !== null && typeof value.then === 'function'; +} + +/** + * Silence a Promise-like object. + * + * This is useful for avoiding non-harmful, but potentially confusing "uncaught + * play promise" rejection error messages. + * + * @param {Object} value + * An object that may or may not be `Promise`-like. + */ +export function silencePromise(value) { + if (isPromise(value)) { + value.then(null, () => {}); + } +} -- cgit v1.2.3