diff options
Diffstat (limited to 'src/js/plugins/youtube.js')
-rw-r--r-- | src/js/plugins/youtube.js | 104 |
1 files changed, 60 insertions, 44 deletions
diff --git a/src/js/plugins/youtube.js b/src/js/plugins/youtube.js index 9b067c8a..e486aa43 100644 --- a/src/js/plugins/youtube.js +++ b/src/js/plugins/youtube.js @@ -4,7 +4,24 @@ import controls from './../controls'; import ui from './../ui'; -import utils from './../utils'; +import { dedupe } from './../utils/arrays'; +import { createElement, replaceElement, toggleClass } from './../utils/elements'; +import { trigger } from './../utils/events'; +import fetch from './../utils/fetch'; +import is from './../utils/is'; +import loadImage from './../utils/loadImage'; +import loadScript from './../utils/loadScript'; +import { format, generateId } from './../utils/strings'; + +// Parse YouTube ID from URL +function parseId(url) { + if (is.empty(url)) { + return null; + } + + const regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/; + return url.match(regex) ? RegExp.$2 : url; +} // Standardise YouTube quality unit function mapQualityUnit(input) { @@ -57,11 +74,11 @@ function mapQualityUnit(input) { } function mapQualityUnits(levels) { - if (utils.is.empty(levels)) { + if (is.empty(levels)) { return levels; } - return utils.dedupe(levels.map(level => mapQualityUnit(level))); + return dedupe(levels.map(level => mapQualityUnit(level))); } // Set playback state and trigger change (only on actual change) @@ -71,24 +88,24 @@ function assurePlaybackState(play) { } if (this.media.paused === play) { this.media.paused = !play; - utils.dispatchEvent.call(this, this.media, play ? 'play' : 'pause'); + trigger.call(this, this.media, play ? 'play' : 'pause'); } } const youtube = { setup() { // Add embed class for responsive - utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true); + toggleClass(this.elements.wrapper, this.config.classNames.embed, true); // Set aspect ratio youtube.setAspectRatio.call(this); // Setup API - if (utils.is.object(window.YT) && utils.is.function(window.YT.Player)) { + if (is.object(window.YT) && is.function(window.YT.Player)) { youtube.ready.call(this); } else { // Load the API - utils.loadScript(this.config.urls.youtube.sdk).catch(error => { + loadScript(this.config.urls.youtube.sdk).catch(error => { this.debug.warn('YouTube API failed to load', error); }); @@ -115,10 +132,10 @@ const youtube = { // Try via undocumented API method first // This method disappears now and then though... // https://github.com/sampotts/plyr/issues/709 - if (utils.is.function(this.embed.getVideoData)) { + if (is.function(this.embed.getVideoData)) { const { title } = this.embed.getVideoData(); - if (utils.is.empty(title)) { + if (is.empty(title)) { this.config.title = title; ui.setTitle.call(this); return; @@ -127,13 +144,12 @@ const youtube = { // Or via Google API const key = this.config.keys.google; - if (utils.is.string(key) && !utils.is.empty(key)) { - const url = utils.format(this.config.urls.youtube.api, videoId, key); + if (is.string(key) && !is.empty(key)) { + const url = format(this.config.urls.youtube.api, videoId, key); - utils - .fetch(url) + fetch(url) .then(result => { - if (utils.is.object(result)) { + if (is.object(result)) { this.config.title = result.items[0].snippet.title; ui.setTitle.call(this); } @@ -154,7 +170,7 @@ const youtube = { // Ignore already setup (race condition) const currentId = player.media.getAttribute('id'); - if (!utils.is.empty(currentId) && currentId.startsWith('youtube-')) { + if (!is.empty(currentId) && currentId.startsWith('youtube-')) { return; } @@ -162,23 +178,23 @@ const youtube = { let source = player.media.getAttribute('src'); // Get from <div> if needed - if (utils.is.empty(source)) { + if (is.empty(source)) { source = player.media.getAttribute(this.config.attributes.embed.id); } // Replace the <iframe> with a <div> due to YouTube API issues - const videoId = utils.parseYouTubeId(source); - const id = utils.generateId(player.provider); - const container = utils.createElement('div', { id }); - player.media = utils.replaceElement(container, player.media); + const videoId = parseId(source); + const id = generateId(player.provider); + const container = createElement('div', { id }); + player.media = replaceElement(container, player.media); // Set poster image 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) - utils.loadImage(posterSrc('maxres'), 121) // Higest quality and unpadded - .catch(() => utils.loadImage(posterSrc('sd'), 121)) // 480p padded 4:3 - .catch(() => utils.loadImage(posterSrc('hq'))) // 360p padded 4:3. Always exists + loadImage(posterSrc('maxres'), 121) // Higest quality and unpadded + .catch(() => loadImage(posterSrc('sd'), 121)) // 480p padded 4:3 + .catch(() => loadImage(posterSrc('hq'))) // 360p padded 4:3. Always exists .then(image => ui.setPoster.call(player, image.src)) .then(posterSrc => { // If the image is padded, use background-size "cover" instead (like youtube does too with their posters) @@ -213,7 +229,7 @@ const youtube = { onError(event) { // If we've already fired an error, don't do it again // YouTube fires onError twice - if (utils.is.object(player.media.error)) { + if (is.object(player.media.error)) { return; } @@ -250,10 +266,10 @@ const youtube = { player.media.error = detail; - utils.dispatchEvent.call(player, player.media, 'error'); + trigger.call(player, player.media, 'error'); }, onPlaybackQualityChange() { - utils.dispatchEvent.call(player, player.media, 'qualitychange', false, { + trigger.call(player, player.media, 'qualitychange', false, { quality: player.media.quality, }); }, @@ -264,7 +280,7 @@ const youtube = { // Get current speed player.media.playbackRate = instance.getPlaybackRate(); - utils.dispatchEvent.call(player, player.media, 'ratechange'); + trigger.call(player, player.media, 'ratechange'); }, onReady(event) { // Get the instance @@ -305,7 +321,7 @@ const youtube = { // Set seeking state and trigger event player.media.seeking = true; - utils.dispatchEvent.call(player, player.media, 'seeking'); + trigger.call(player, player.media, 'seeking'); // Seek after events sent instance.seekTo(time); @@ -334,7 +350,7 @@ const youtube = { instance.setPlaybackQuality(mapQualityUnit(quality)); // Trigger request event - utils.dispatchEvent.call(player, player.media, 'qualityrequested', false, { + trigger.call(player, player.media, 'qualityrequested', false, { quality, }); }, @@ -349,7 +365,7 @@ const youtube = { set(input) { volume = input; instance.setVolume(volume * 100); - utils.dispatchEvent.call(player, player.media, 'volumechange'); + trigger.call(player, player.media, 'volumechange'); }, }); @@ -360,10 +376,10 @@ const youtube = { return muted; }, set(input) { - const toggle = utils.is.boolean(input) ? input : muted; + const toggle = is.boolean(input) ? input : muted; muted = toggle; instance[toggle ? 'mute' : 'unMute'](); - utils.dispatchEvent.call(player, player.media, 'volumechange'); + trigger.call(player, player.media, 'volumechange'); }, }); @@ -389,8 +405,8 @@ const youtube = { player.media.setAttribute('tabindex', -1); } - utils.dispatchEvent.call(player, player.media, 'timeupdate'); - utils.dispatchEvent.call(player, player.media, 'durationchange'); + trigger.call(player, player.media, 'timeupdate'); + trigger.call(player, player.media, 'durationchange'); // Reset timer clearInterval(player.timers.buffering); @@ -402,7 +418,7 @@ const youtube = { // Trigger progress only when we actually buffer something if (player.media.lastBuffered === null || player.media.lastBuffered < player.media.buffered) { - utils.dispatchEvent.call(player, player.media, 'progress'); + trigger.call(player, player.media, 'progress'); } // Set last buffer point @@ -413,7 +429,7 @@ const youtube = { clearInterval(player.timers.buffering); // Trigger event - utils.dispatchEvent.call(player, player.media, 'canplaythrough'); + trigger.call(player, player.media, 'canplaythrough'); } }, 200); @@ -435,7 +451,7 @@ const youtube = { if (seeked) { // Unset seeking and fire seeked event player.media.seeking = false; - utils.dispatchEvent.call(player, player.media, 'seeked'); + trigger.call(player, player.media, 'seeked'); } // Handle events @@ -448,11 +464,11 @@ const youtube = { switch (event.data) { case -1: // Update scrubber - utils.dispatchEvent.call(player, player.media, 'timeupdate'); + trigger.call(player, player.media, 'timeupdate'); // Get loaded % from YouTube player.media.buffered = instance.getVideoLoadedFraction(); - utils.dispatchEvent.call(player, player.media, 'progress'); + trigger.call(player, player.media, 'progress'); break; @@ -465,7 +481,7 @@ const youtube = { instance.stopVideo(); instance.playVideo(); } else { - utils.dispatchEvent.call(player, player.media, 'ended'); + trigger.call(player, player.media, 'ended'); } break; @@ -477,11 +493,11 @@ const youtube = { } else { assurePlaybackState.call(player, true); - utils.dispatchEvent.call(player, player.media, 'playing'); + trigger.call(player, player.media, 'playing'); // Poll to get playback progress player.timers.playing = setInterval(() => { - utils.dispatchEvent.call(player, player.media, 'timeupdate'); + trigger.call(player, player.media, 'timeupdate'); }, 50); // Check duration again due to YouTube bug @@ -489,7 +505,7 @@ const youtube = { // https://code.google.com/p/gdata-issues/issues/detail?id=8690 if (player.media.duration !== instance.getDuration()) { player.media.duration = instance.getDuration(); - utils.dispatchEvent.call(player, player.media, 'durationchange'); + trigger.call(player, player.media, 'durationchange'); } // Get quality @@ -511,7 +527,7 @@ const youtube = { break; } - utils.dispatchEvent.call(player, player.elements.container, 'statechange', false, { + trigger.call(player, player.elements.container, 'statechange', false, { code: event.data, }); }, |