From 5190cb38ef8116e15f1ba275e420d3cafeda568a Mon Sep 17 00:00:00 2001 From: akuma Date: Mon, 18 May 2020 01:02:18 +0200 Subject: [PATCH 01/14] Added rule for no-param-reassign The plugin system as I coded it pass the player instance as a parameter. Therefore, even though I do not change the parameter per se, I enable modifying property of it (source, speed, ...). --- .eslintrc | 57 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/.eslintrc b/.eslintrc index c88c7317a..462a061e6 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,30 +1,31 @@ { - "parser": "babel-eslint", - "extends": ["airbnb-base", "prettier"], - "plugins": ["simple-import-sort", "import"], - "env": { - "browser": true, - "es6": true - }, - "globals": { - "Plyr": false, - "jQuery": false - }, - "rules": { - "import/no-cycle": "warn", - "padding-line-between-statements": [ - "error", - { - "blankLine": "never", - "prev": ["singleline-const", "singleline-let", "singleline-var"], - "next": ["singleline-const", "singleline-let", "singleline-var"] - } - ], - "sort-imports": "off", - "import/order": "off", - "simple-import-sort/sort": "error" - }, - "parserOptions": { - "sourceType": "module" - } + "parser": "babel-eslint", + "extends": ["airbnb-base", "prettier"], + "plugins": ["simple-import-sort", "import"], + "env": { + "browser": true, + "es6": true + }, + "globals": { + "Plyr": false, + "jQuery": false + }, + "rules": { + "import/no-cycle": "warn", + "no-param-reassign": ["error", { "props": false }], + "padding-line-between-statements": [ + "error", + { + "blankLine": "never", + "prev": ["singleline-const", "singleline-let", "singleline-var"], + "next": ["singleline-const", "singleline-let", "singleline-var"] + } + ], + "sort-imports": "off", + "import/order": "off", + "simple-import-sort/sort": "error" + }, + "parserOptions": { + "sourceType": "module" + } } From 4189293ef9707bf9a126e01ad3c84a607218a783 Mon Sep 17 00:00:00 2001 From: akuma Date: Mon, 18 May 2020 01:04:06 +0200 Subject: [PATCH 02/14] Update captions.js Removing isYoutube and isVimeo functions. Since there might be a lot of external platforms supported, internal function for just 2 platforms shouldn't be the norm. --- src/js/captions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/captions.js b/src/js/captions.js index ebb678f88..d46507b15 100644 --- a/src/js/captions.js +++ b/src/js/captions.js @@ -31,7 +31,7 @@ const captions = { } // Only Vimeo and HTML5 video supported at this point - if (!this.isVideo || this.isYouTube || (this.isHTML5 && !support.textTracks)) { + if (!this.isVideo || !this.provider.supportCaptions || (this.isHTML5 && !support.textTracks)) { // Clear menu and hide if ( is.array(this.config.controls) && From 119239e877ab847e0b40af7e8fd66ef65a9f1b7a Mon Sep 17 00:00:00 2001 From: akuma Date: Mon, 18 May 2020 01:07:23 +0200 Subject: [PATCH 03/14] Update types.js Removing the providers constant, it is now a static property on Plyr class. This way it can be augmented through the static "use" method and can be accessed through all Plyr class instanciation. The providers static property is now an array of PlyrProvider class. getProviderByUrl is also moved as a static method inside Plyr and returns a PlyrProvider class instead of a string. --- src/js/config/types.js | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/src/js/config/types.js b/src/js/config/types.js index 31e488eb8..4166f8054 100644 --- a/src/js/config/types.js +++ b/src/js/config/types.js @@ -2,33 +2,9 @@ // Plyr supported types and providers // ========================================================================== -export const providers = { - html5: 'html5', - youtube: 'youtube', - vimeo: 'vimeo', -}; - export const types = { audio: 'audio', video: 'video', }; -/** - * Get provider by URL - * @param {String} url - */ -export function getProviderByUrl(url) { - // YouTube - if (/^(https?:\/\/)?(www\.)?(youtube\.com|youtube-nocookie\.com|youtu\.?be)\/.+$/.test(url)) { - return providers.youtube; - } - - // Vimeo - if (/^https?:\/\/player.vimeo.com\/video\/\d{0,9}(?=\b|\/)/.test(url)) { - return providers.vimeo; - } - - return null; -} - -export default { providers, types }; +export default { types }; From 48c454c9edcb28255482bb836e092273a677890d Mon Sep 17 00:00:00 2001 From: akuma Date: Mon, 18 May 2020 01:12:50 +0200 Subject: [PATCH 04/14] Update defaults.js Config for both vimeo and youtube has been moved to their provider class. Every PyrProvider has a static config property. When adding the PyrProvider to Pyr, the config is set inside config[PyrProvider.name]. The only YouTube event still relevant is statechange (qualitychange support has been removed in a previous release). This event is moved to the static events method of the YouTubeProvider. Each PlyrProvider has a static events method returning an array of additional event specific to the provider. --- src/js/config/defaults.js | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/src/js/config/defaults.js b/src/js/config/defaults.js index 7c6a708e4..3a834dc15 100644 --- a/src/js/config/defaults.js +++ b/src/js/config/defaults.js @@ -197,15 +197,6 @@ const defaults = { // URLs urls: { download: null, - vimeo: { - sdk: 'https://player.vimeo.com/api/player.js', - iframe: 'https://player.vimeo.com/video/{0}?{1}', - api: 'https://vimeo.com/api/v2/video/{0}.json', - }, - youtube: { - sdk: 'https://www.youtube.com/iframe_api', - api: 'https://noembed.com/embed?url=https://www.youtube.com/watch?v={0}', - }, googleIMA: { sdk: 'https://imasdk.googleapis.com/js/sdkloader/ima3.js', }, @@ -268,9 +259,6 @@ const defaults = { 'controlsshown', 'ready', - // YouTube - 'statechange', - // Quality 'qualitychange', @@ -414,29 +402,6 @@ const defaults = { enabled: false, src: '', }, - - // Vimeo plugin - vimeo: { - byline: false, - portrait: false, - title: false, - speed: true, - transparent: false, - // Whether the owner of the video has a Pro or Business account - // (which allows us to properly hide controls without CSS hacks, etc) - premium: false, - // Custom settings from Plyr - referrerPolicy: null, // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/referrerPolicy - }, - - // YouTube plugin - youtube: { - noCookie: true, // Whether to use an alternative version of YouTube without cookies - rel: 0, // No related vids - showinfo: 0, // Hide info - iv_load_policy: 3, // Hide annotations - modestbranding: 1, // Hide logos as much as possible (they still show one in the corner when paused) - }, }; export default defaults; From e3f3099d3e94b7cb4aed3e1cf51226ad5b55bd3d Mon Sep 17 00:00:00 2001 From: akuma Date: Mon, 18 May 2020 01:15:48 +0200 Subject: [PATCH 05/14] Update controls.js Wether to display quality options or not should be the provider decision. Some providers might handle the quality option. Therefore a static getQualityOptions method can be implemented inside the PlyrProvider. Example: HTML5Provider. --- src/js/controls.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/js/controls.js b/src/js/controls.js index ad126de11..c7dde1425 100644 --- a/src/js/controls.js +++ b/src/js/controls.js @@ -6,7 +6,6 @@ import RangeTouch from 'rangetouch'; import captions from './captions'; -import html5 from './html5'; import support from './support'; import { repaint, transitionEndEvent } from './utils/animation'; import { dedupe } from './utils/arrays'; @@ -1275,7 +1274,7 @@ const controls = { const defaultAttributes = { class: 'plyr__controls__item' }; // Loop through controls in order - dedupe(is.array(this.config.controls) ? this.config.controls: []).forEach(control => { + dedupe(is.array(this.config.controls) ? this.config.controls : []).forEach(control => { // Restart button if (control === 'restart') { container.appendChild(createButton.call(this, 'restart', defaultAttributes)); @@ -1596,9 +1595,10 @@ const controls = { } }); + const qualityOptions = this.provider.getQualityOptions(this); // Set available quality levels - if (this.isHTML5) { - setQualityMenu.call(this, html5.getQualityOptions.call(this)); + if (qualityOptions.length > 0) { + setQualityMenu.call(this, qualityOptions); } setSpeedMenu.call(this); From cdd88caa4d3d91bc3051d0e189565e41887c633f Mon Sep 17 00:00:00 2001 From: akuma Date: Mon, 18 May 2020 01:20:17 +0200 Subject: [PATCH 06/14] Update media.js Pointing to the new way to get the name of the provider for the css class. Removing the conditions to better implement new external providers. Since the provider is set before media.setup is called, there is no reason why not just call this.provider.setup. Also moving specific configuration of speed (youtube.js and vimeo.js) here. So the speed availability is always automatically set according to the provider speed available and the options configured. --- src/js/media.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/js/media.js b/src/js/media.js index 4584fea35..822c88515 100644 --- a/src/js/media.js +++ b/src/js/media.js @@ -2,9 +2,6 @@ // Plyr Media // ========================================================================== -import html5 from './html5'; -import vimeo from './plugins/vimeo'; -import youtube from './plugins/youtube'; import { createElement, toggleClass, wrap } from './utils/elements'; const media = { @@ -20,7 +17,7 @@ const media = { toggleClass(this.elements.container, this.config.classNames.type.replace('{0}', this.type), true); // Add provider class - toggleClass(this.elements.container, this.config.classNames.provider.replace('{0}', this.provider), true); + toggleClass(this.elements.container, this.config.classNames.provider.replace('{0}', this.provider.name), true); // Add video class for embeds // This will require changes if audio embeds are added @@ -46,13 +43,16 @@ const media = { this.elements.wrapper.appendChild(this.elements.poster); } - if (this.isHTML5) { - html5.setup.call(this); - } else if (this.isYouTube) { - youtube.setup.call(this); - } else if (this.isVimeo) { - vimeo.setup.call(this); + // Some providers might not have the same speed array + // So we filter out the speeds set in config with the speed from the provider + this.setOptions({ speed: this.provider.filterSpeed(this.config.speed.options) }); + + if (this.provider === undefined) { + this.debug.warn('No provider found!'); + return; } + // Provider should be already set when we call this + this.provider.setup(this); }, }; From aecc4a989e4de956db8f3c4ca77e607374f48ae6 Mon Sep 17 00:00:00 2001 From: akuma Date: Mon, 18 May 2020 01:23:06 +0200 Subject: [PATCH 07/14] Update source.js Fixing the new way to call the methods for HTML5Provider. this.provider is not a string anymore but PlyrProvider class. So we call Plyr.getProvider(provider) to get the class corresponding to the provider string. --- src/js/source.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/js/source.js b/src/js/source.js index b9fc7732b..1e06416f8 100644 --- a/src/js/source.js +++ b/src/js/source.js @@ -2,10 +2,10 @@ // Plyr source update // ========================================================================== -import { providers } from './config/types'; -import html5 from './html5'; +import HTML5Provider from './html5'; import media from './media'; import PreviewThumbnails from './plugins/preview-thumbnails'; +import Plyr from './plyr'; import support from './support'; import ui from './ui'; import { createElement, insertElement, removeElement } from './utils/elements'; @@ -35,7 +35,7 @@ const source = { } // Cancel current network requests - html5.cancelRequests.call(this); + HTML5Provider.cancelRequests(this); // Destroy instance and re-setup this.destroy.call( @@ -55,12 +55,12 @@ const source = { // Set the type and provider const { sources, type } = input; - const [{ provider = providers.html5, src }] = sources; - const tagName = provider === 'html5' ? type : 'div'; - const attributes = provider === 'html5' ? {} : { src }; + const [{ provider = HTML5Provider.name, src }] = sources; + const tagName = provider === HTML5Provider.name ? type : 'div'; + const attributes = provider === HTML5Provider.name ? {} : { src }; Object.assign(this, { - provider, + provider: Plyr.getProvider(provider), type, // Check for support supported: support.check(type, provider, this.config.playsinline), From e7b8bbb3e334d5c2abf65fdd0349eac64a12c671 Mon Sep 17 00:00:00 2001 From: akuma Date: Mon, 18 May 2020 01:25:02 +0200 Subject: [PATCH 08/14] Create providers.js New provider class. Every new plugin should inherit this class and provide the mandatory methods. Other methods have default values and can be omitted. --- src/js/plugins/providers.js | 84 +++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/js/plugins/providers.js diff --git a/src/js/plugins/providers.js b/src/js/plugins/providers.js new file mode 100644 index 000000000..fd09bdc4f --- /dev/null +++ b/src/js/plugins/providers.js @@ -0,0 +1,84 @@ +class PlyrProvider { + // The currently available speeds for the provider + static get availableSpeed() { + return []; + } + + // Specific events for the provider to listent to + static get events() { + return []; + } + + /** Type of provider (e.g. audio or video) + * + * @param {Plyr} player + * + * @returns {string} Either audio or video + */ + // eslint-disable-next-line no-unused-vars + static type(player) { + throw new Error('You need to precise the type of the provider'); + } + + static get supportCaptions() { + return false; + } + + static getQualityOptions(player) { + // Whether we're forcing all options (e.g. for streaming) + if (player.config.quality.forced) { + return player.config.quality.options; + } + return []; + } + + /** + * Function called when setting up the plyr player + * @param {Plyr} player + */ + static setup(player) { + if (!(player instanceof Plyr)) { + throw new Error('Passed object is not an instance of Plyr'); + } + } + + /** + * Name of the provider + */ + static get name() { + throw new Error('You need to name your provider'); + } + + /** + * Test if the provider should be used for this url + * @param {String} url + * + * @return {boolean} Match + */ + // eslint-disable-next-line no-unused-vars + static test(url) { + throw new Error('You need to implement a test for your provider'); + } + + /** + * Check if the array of speed given are supported for the provider + * @param {Array} speedArray + * + * @returns {Array} The filtered out speed array that is available + */ + static filterSpeed(speedArray) { + return this.availableSpeed.filter(speed => speedArray.includes(speed)); + } + + static beforeSetup() {} + + // eslint-disable-next-line no-unused-vars + static async destroy(player) { + throw new Error('You need to implement a destroy function for your provider'); + } +} + +// The specific config for the provider +PlyrProvider.config = {}; + +export default PlyrProvider; From 3e297d5a5dd6a7294f50dfc0e50babe9935c2d91 Mon Sep 17 00:00:00 2001 From: akuma Date: Mon, 18 May 2020 01:25:32 +0200 Subject: [PATCH 09/14] Create providers.d.ts Typescript definition for the new PlyrProvider class --- src/js/plugins/providers.d.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/js/plugins/providers.d.ts diff --git a/src/js/plugins/providers.d.ts b/src/js/plugins/providers.d.ts new file mode 100644 index 000000000..6a41943a4 --- /dev/null +++ b/src/js/plugins/providers.d.ts @@ -0,0 +1,13 @@ +declare abstract class PlyrProvider { + static name: string; + static config: object; + static availableSpeed: Number[]; + static supportCaptions: boolean; + // Specific events for the provider to listent to + static events = []; + static type = 'audio' | 'video'; + static setup(player: Plyr): void; + static test(url: string): boolean; + static beforeSetup?(player: Plyr): void; + static async destroy(player): void; +} From 33bfb5c04cc1f2b9ab88d50de3d9ce89274242f1 Mon Sep 17 00:00:00 2001 From: akuma Date: Mon, 18 May 2020 01:27:49 +0200 Subject: [PATCH 10/14] Update vimeo.js Vimeo is now a class inheriting PlyrProvider. Besides replacing the use of "this" by the "player" parameter, there are the mandatory methods and the vimeo config added here. --- src/js/plugins/vimeo.js | 91 ++++++++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 23 deletions(-) diff --git a/src/js/plugins/vimeo.js b/src/js/plugins/vimeo.js index d098fe969..e0d04293e 100644 --- a/src/js/plugins/vimeo.js +++ b/src/js/plugins/vimeo.js @@ -3,6 +3,7 @@ // ========================================================================== import captions from '../captions'; +import { types } from '../config/types'; import controls from '../controls'; import ui from '../ui'; import { createElement, replaceElement, toggleClass } from '../utils/elements'; @@ -13,6 +14,7 @@ import loadScript from '../utils/load-script'; import { format, stripHTML } from '../utils/strings'; import { setAspectRatio } from '../utils/style'; import { buildUrlParams } from '../utils/urls'; +import PlyrProvider from './providers'; // Parse Vimeo ID from URL function parseId(url) { @@ -39,36 +41,50 @@ function assurePlaybackState(play) { } } -const vimeo = { - setup() { - const player = this; +class VimeoProvider extends PlyrProvider { + // https://github.com/vimeo/player.js/#setplaybackrateplaybackrate-number-promisenumber-rangeerrorerror + static get availableSpeed() { + return [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2]; + } + + static get name() { + return 'vimeo'; + } + + static type() { + return types.video; + } + + static get supportCaptions() { + return true; + } + + static test(url) { + return /^https?:\/\/player.vimeo.com\/video\/\d{0,9}(?=\b|\/)/.test(url); + } + static setup(player) { // Add embed class for responsive toggleClass(player.elements.wrapper, player.config.classNames.embed, true); - // Set speed options from config - player.options.speed = player.config.speed.options; - // Set intial ratio setAspectRatio.call(player); // Load the SDK if not already if (!is.object(window.Vimeo)) { - loadScript(player.config.urls.vimeo.sdk) + loadScript(player.config.vimeo.sdk) .then(() => { - vimeo.ready.call(player); + this.ready(player); }) .catch(error => { player.debug.warn('Vimeo SDK (player.js) failed to load', error); }); } else { - vimeo.ready.call(player); + this.ready(player); } - }, + } - // API Ready - ready() { - const player = this; + static ready(player) { const config = player.config.vimeo; const { premium, referrerPolicy, ...frameParams } = config; @@ -86,7 +102,7 @@ const vimeo = { autoplay: player.autoplay, muted: player.muted, gesture: 'media', - playsinline: !this.config.fullscreen.iosNative, + playsinline: !player.config.fullscreen.iosNative, ...frameParams, }); @@ -101,7 +117,7 @@ const vimeo = { const id = parseId(source); // Build an iframe const iframe = createElement('iframe'); - const src = format(player.config.urls.vimeo.iframe, id, params); + const src = format(player.config.vimeo.iframe, id, params); iframe.setAttribute('src', src); iframe.setAttribute('allowfullscreen', ''); iframe.setAttribute('allow', 'autoplay,fullscreen,picture-in-picture'); @@ -115,15 +131,15 @@ const vimeo = { const { poster } = player; if (premium) { iframe.setAttribute('data-poster', poster); - player.media = replaceElement(iframe, player.media); + player.setMedia(replaceElement(iframe, player.media)); } else { const wrapper = createElement('div', { class: player.config.classNames.embedContainer, 'data-poster': poster }); wrapper.appendChild(iframe); - player.media = replaceElement(wrapper, player.media); + player.setMedia(replaceElement(wrapper, player.media)); } // Get poster image - fetch(format(player.config.urls.vimeo.api, id), 'json').then(response => { + fetch(format(player.config.vimeo.api, id), 'json').then(response => { if (is.empty(response)) { return; } @@ -274,7 +290,7 @@ const vimeo = { controls.setDownloadUrl.call(player); }) .catch(error => { - this.debug.warn(error); + player.debug.warn(error); }); Object.defineProperty(player.media, 'currentSrc', { @@ -294,7 +310,7 @@ const vimeo = { Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then(dimensions => { const [width, height] = dimensions; player.embed.ratio = [width, height]; - setAspectRatio.call(this); + setAspectRatio.call(player); }); // Set autopause @@ -305,7 +321,7 @@ const vimeo = { // Get title player.embed.getVideoTitle().then(title => { player.config.title = title; - ui.setTitle.call(this); + ui.setTitle.call(player); }); // Get current time @@ -408,7 +424,36 @@ const vimeo = { // Rebuild UI setTimeout(() => ui.build.call(player), 0); - }, + } + + static async destroy(player) { + // Destroy Vimeo API + // then clean up (wait, to prevent postmessage errors) + if (player.embed !== null) { + try { + await player.embed.unload(); + } catch (error) { + player.debug.warn(error); + } + } + // This function should always return, no need to timeout + } +} + +VimeoProvider.config = { + sdk: 'https://player.vimeo.com/api/player.js', + iframe: 'https://player.vimeo.com/video/{0}?{1}', + api: 'https://vimeo.com/api/v2/video/{0}.json', + byline: false, + portrait: false, + title: false, + speed: true, + transparent: false, + // Whether the owner of the video has a Pro or Business account + // (which allows us to properly hide controls without CSS hacks, etc) + premium: false, + // Custom settings from Plyr + referrerPolicy: null, // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/referrerPolicy }; -export default vimeo; +export default VimeoProvider; From 7d668a604c2e6dfbfc9aff72ac8ed64c900d9499 Mon Sep 17 00:00:00 2001 From: akuma Date: Mon, 18 May 2020 01:30:20 +0200 Subject: [PATCH 11/14] Update youtube.js Same as vimeo, youtube has now its config set here. It is possible to change the youtube config by modifying the static property. There is a beforeSetup event for youtube due to the playsinline url parameter. --- src/js/plugins/youtube.js | 97 +++++++++++++++++++++++++++++---------- 1 file changed, 74 insertions(+), 23 deletions(-) diff --git a/src/js/plugins/youtube.js b/src/js/plugins/youtube.js index 89a75d895..0d59eede3 100644 --- a/src/js/plugins/youtube.js +++ b/src/js/plugins/youtube.js @@ -2,6 +2,7 @@ // YouTube plugin // ========================================================================== +import { types } from '../config/types'; import ui from '../ui'; import { createElement, replaceElement, toggleClass } from '../utils/elements'; import { triggerEvent } from '../utils/events'; @@ -12,6 +13,8 @@ import loadScript from '../utils/load-script'; import { extend } from '../utils/objects'; import { format, generateId } from '../utils/strings'; import { setAspectRatio } from '../utils/style'; +import { parseUrl } from '../utils/urls'; +import PlyrProvider from './providers'; // Parse YouTube ID from URL function parseId(url) { @@ -47,14 +50,36 @@ function getHost(config) { return undefined; } -const youtube = { - setup() { +class YouTubeProvider extends PlyrProvider { + static get name() { + return 'youtube'; + } + + static type() { + return types.video; + } + + // https://developers.google.com/youtube/iframe_api_reference#setPlaybackRate + static get availableSpeed() { + return [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 4]; + } + + // YouTube specific event + static get events() { + return ['statechange']; + } + + static test(url) { + return /^(https?:\/\/)?(www\.)?(youtube\.com|youtube-nocookie\.com|youtu\.?be)\/.+$/.test(url); + } + + static setup(player) { // Add embed class for responsive - toggleClass(this.elements.wrapper, this.config.classNames.embed, true); + toggleClass(player.elements.wrapper, player.config.classNames.embed, true); // Setup API if (is.object(window.YT) && is.function(window.YT.Player)) { - youtube.ready.call(this); + YouTubeProvider.ready(player); } else { // Reference current global callback const callback = window.onYouTubeIframeAPIReady; @@ -66,19 +91,19 @@ const youtube = { callback(); } - youtube.ready.call(this); + YouTubeProvider.ready(player); }; // Load the SDK - loadScript(this.config.urls.youtube.sdk).catch(error => { - this.debug.warn('YouTube API failed to load', error); + loadScript(player.config.youtube.sdk).catch(error => { + player.debug.warn('YouTube API failed to load', error); }); } - }, + } // Get the media title - getTitle(videoId) { - const url = format(this.config.urls.youtube.api, videoId); + static getTitle(player, videoId) { + const url = format(player.config.youtube.api, videoId); fetch(url) .then(data => { @@ -86,24 +111,22 @@ const youtube = { const { title, height, width } = data; // Set title - this.config.title = title; - ui.setTitle.call(this); + player.config.title = title; + ui.setTitle.call(player); // Set aspect ratio - this.embed.ratio = [width, height]; + player.embed.ratio = [width, height]; } - setAspectRatio.call(this); + setAspectRatio.call(player); }) .catch(() => { // Set aspect ratio - setAspectRatio.call(this); + setAspectRatio.call(player); }); - }, + } - // API ready - ready() { - const player = this; + static ready(player) { // Ignore already setup (race condition) const currentId = player.media && player.media.getAttribute('id'); if (!is.empty(currentId) && currentId.startsWith('youtube-')) { @@ -115,7 +138,7 @@ const youtube = { // Get from
if needed if (is.empty(source)) { - source = player.media.getAttribute(this.config.attributes.embed.id); + source = player.media.getAttribute(player.config.attributes.embed.id); } // Replace the