aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/js/controls.js89
-rw-r--r--src/js/media.js19
-rw-r--r--src/js/plugins/youtube.js54
-rw-r--r--src/js/plyr.js71
-rw-r--r--src/js/source.js44
-rw-r--r--src/js/support.js100
6 files changed, 128 insertions, 249 deletions
diff --git a/src/js/controls.js b/src/js/controls.js
index f091555f..19c531af 100644
--- a/src/js/controls.js
+++ b/src/js/controls.js
@@ -119,28 +119,17 @@ const controls = {
},
// Create hidden text label
- createLabel(type, attr) {
- let text = i18n.get(type, this.config);
- const attributes = Object.assign({}, attr);
-
- switch (type) {
- case 'pip':
- text = 'PIP';
- break;
-
- case 'airplay':
- text = 'AirPlay';
- break;
-
- default:
- break;
- }
+ createLabel(type, attr = {}) {
+ // Skip i18n for abbreviations and brand names
+ const universals = {
+ pip: 'PIP',
+ airplay: 'AirPlay',
+ };
- if ('class' in attributes) {
- attributes.class += ` ${this.config.classNames.hidden}`;
- } else {
- attributes.class = this.config.classNames.hidden;
- }
+ const text = universals[type] || i18n.get(type, this.config);
+ const attributes = Object.assign({}, attr, {
+ class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' '),
+ });
return createElement('span', attributes, text);
},
@@ -342,19 +331,12 @@ const controls = {
if (type !== 'volume') {
progress.appendChild(createElement('span', null, '0'));
- let suffix = '';
- switch (type) {
- case 'played':
- suffix = i18n.get('played', this.config);
- break;
+ const suffixKey = ({
+ played: 'played',
+ buffer: 'buffered',
+ })[type];
- case 'buffer':
- suffix = i18n.get('buffered', this.config);
- break;
-
- default:
- break;
- }
+ const suffix = suffixKey ? i18n.get(suffixKey, this.config) : '';
progress.innerText = `% ${suffix.toLowerCase()}`;
}
@@ -724,32 +706,27 @@ const controls = {
let value = null;
let list = container;
- switch (setting) {
- case 'captions':
- value = this.currentTrack;
- break;
-
- default:
- value = !is.empty(input) ? input : this[setting];
-
- // Get default
- if (is.empty(value)) {
- value = this.config[setting].default;
- }
+ if (setting === 'captions') {
+ value = this.currentTrack;
+ } else {
+ value = !is.empty(input) ? input : this[setting];
- // Unsupported value
- if (!is.empty(this.options[setting]) && !this.options[setting].includes(value)) {
- this.debug.warn(`Unsupported value of '${value}' for ${setting}`);
- return;
- }
+ // Get default
+ if (is.empty(value)) {
+ value = this.config[setting].default;
+ }
- // Disabled value
- if (!this.config[setting].options.includes(value)) {
- this.debug.warn(`Disabled value of '${value}' for ${setting}`);
- return;
- }
+ // Unsupported value
+ if (!is.empty(this.options[setting]) && !this.options[setting].includes(value)) {
+ this.debug.warn(`Unsupported value of '${value}' for ${setting}`);
+ return;
+ }
- break;
+ // Disabled value
+ if (!this.config[setting].options.includes(value)) {
+ this.debug.warn(`Disabled value of '${value}' for ${setting}`);
+ return;
+ }
}
// Get the list if we need to
diff --git a/src/js/media.js b/src/js/media.js
index 189112a1..eb37d441 100644
--- a/src/js/media.js
+++ b/src/js/media.js
@@ -46,21 +46,12 @@ const media = {
this.elements.wrapper.appendChild(this.elements.poster);
}
- if (this.isEmbed) {
- switch (this.provider) {
- case 'youtube':
- youtube.setup.call(this);
- break;
-
- case 'vimeo':
- vimeo.setup.call(this);
- break;
-
- default:
- break;
- }
- } else if (this.isHTML5) {
+ if (this.isHTML5) {
html5.extend.call(this);
+ } else if (this.isYouTube) {
+ youtube.setup.call(this);
+ } else if (this.isVimeo) {
+ vimeo.setup.call(this);
}
},
};
diff --git a/src/js/plugins/youtube.js b/src/js/plugins/youtube.js
index 94369ece..65b6db75 100644
--- a/src/js/plugins/youtube.js
+++ b/src/js/plugins/youtube.js
@@ -206,46 +206,22 @@ const youtube = {
},
events: {
onError(event) {
- // If we've already fired an error, don't do it again
- // YouTube fires onError twice
- if (is.object(player.media.error)) {
- return;
+ // YouTube may fire onError twice, so only handle it once
+ if (!player.media.error) {
+ const code = event.data;
+ // Messages copied from https://developers.google.com/youtube/iframe_api_reference#onError
+ const message = ({
+ 2: 'The request contains an invalid parameter value. For example, this error occurs if you specify a video ID that does not have 11 characters, or if the video ID contains invalid characters, such as exclamation points or asterisks.',
+ 5: 'The requested content cannot be played in an HTML5 player or another error related to the HTML5 player has occurred.',
+ 100: 'The video requested was not found. This error occurs when a video has been removed (for any reason) or has been marked as private.',
+ 101: 'The owner of the requested video does not allow it to be played in embedded players.',
+ 150: 'The owner of the requested video does not allow it to be played in embedded players.',
+ }[code]) || 'An unknown error occured';
+
+ player.media.error = { code, message };
+
+ triggerEvent.call(player, player.media, 'error');
}
-
- const detail = {
- code: event.data,
- };
-
- // Messages copied from https://developers.google.com/youtube/iframe_api_reference#onError
- switch (event.data) {
- case 2:
- detail.message =
- 'The request contains an invalid parameter value. For example, this error occurs if you specify a video ID that does not have 11 characters, or if the video ID contains invalid characters, such as exclamation points or asterisks.';
- break;
-
- case 5:
- detail.message =
- 'The requested content cannot be played in an HTML5 player or another error related to the HTML5 player has occurred.';
- break;
-
- case 100:
- detail.message =
- 'The video requested was not found. This error occurs when a video has been removed (for any reason) or has been marked as private.';
- break;
-
- case 101:
- case 150:
- detail.message = 'The owner of the requested video does not allow it to be played in embedded players.';
- break;
-
- default:
- detail.message = 'An unknown error occured';
- break;
- }
-
- player.media.error = detail;
-
- triggerEvent.call(player, player.media, 'error');
},
onPlaybackQualityChange() {
triggerEvent.call(player, player.media, 'qualitychange', false, {
diff --git a/src/js/plyr.js b/src/js/plyr.js
index 98d3a512..dcbe384b 100644
--- a/src/js/plyr.js
+++ b/src/js/plyr.js
@@ -1028,50 +1028,37 @@ class Plyr {
// Stop playback
this.stop();
- // Type specific stuff
- switch (`${this.provider}:${this.type}`) {
- case 'html5:video':
- case 'html5:audio':
- // Clear timeout
- clearTimeout(this.timers.loading);
-
- // Restore native video controls
- ui.toggleNativeControls.call(this, true);
-
- // Clean up
- done();
-
- break;
-
- case 'youtube:video':
- // Clear timers
- clearInterval(this.timers.buffering);
- clearInterval(this.timers.playing);
-
- // Destroy YouTube API
- if (this.embed !== null && is.function(this.embed.destroy)) {
- this.embed.destroy();
- }
-
- // Clean up
- done();
-
- break;
-
- case 'vimeo:video':
- // Destroy Vimeo API
- // then clean up (wait, to prevent postmessage errors)
- if (this.embed !== null) {
- this.embed.unload().then(done);
- }
-
- // Vimeo does not always return
- setTimeout(done, 200);
+ // Provider specific stuff
+ if (this.isHTML5) {
+ // Clear timeout
+ clearTimeout(this.timers.loading);
+
+ // Restore native video controls
+ ui.toggleNativeControls.call(this, true);
+
+ // Clean up
+ done();
+ } else if (this.isYouTube) {
+ // Clear timers
+ clearInterval(this.timers.buffering);
+ clearInterval(this.timers.playing);
+
+ // Destroy YouTube API
+ if (this.embed !== null && is.function(this.embed.destroy)) {
+ this.embed.destroy();
+ }
- break;
+ // Clean up
+ done();
+ } else if (this.isVimeo) {
+ // Destroy Vimeo API
+ // then clean up (wait, to prevent postmessage errors)
+ if (this.embed !== null) {
+ this.embed.unload().then(done);
+ }
- default:
- break;
+ // Vimeo does not always return
+ setTimeout(done, 200);
}
}
diff --git a/src/js/source.js b/src/js/source.js
index d4a66963..c62db15a 100644
--- a/src/js/source.js
+++ b/src/js/source.js
@@ -8,6 +8,7 @@ import media from './media';
import support from './support';
import ui from './ui';
import { createElement, insertElement, removeElement } from './utils/elements';
+import { getDeep } from './utils/objects';
import is from './utils/is';
const source = {
@@ -27,7 +28,7 @@ const source = {
// Update source
// Sources are not checked for support so be careful
change(input) {
- if (!is.object(input) || !('sources' in input) || !input.sources.length) {
+ if (!getDeep(input, 'sources.length')) {
this.debug.warn('Invalid source format');
return;
}
@@ -52,32 +53,19 @@ const source = {
}
// Set the type and provider
- this.type = input.type;
- this.provider = !is.empty(input.sources[0].provider) ? input.sources[0].provider : providers.html5;
-
- // Check for support
- this.supported = support.check(this.type, this.provider, this.config.playsinline);
-
- // Create new markup
- switch (`${this.provider}:${this.type}`) {
- case 'html5:video':
- this.media = createElement('video');
- break;
-
- case 'html5:audio':
- this.media = createElement('audio');
- break;
-
- case 'youtube:video':
- case 'vimeo:video':
- this.media = createElement('div', {
- src: input.sources[0].src,
- });
- break;
-
- default:
- break;
- }
+ const { sources, type } = input;
+ const [{ provider = providers.html5, src }] = sources;
+ const tagName = provider === 'html5' ? type : 'div';
+ const attributes = provider === 'html5' ? {} : { src };
+
+ Object.assign(this, {
+ provider,
+ type,
+ // Check for support
+ supported: support.check(type, provider, this.config.playsinline),
+ // Create new element
+ media: createElement(tagName, attributes),
+ });
// Inject the new element
this.elements.container.appendChild(this.media);
@@ -114,7 +102,7 @@ const source = {
// Set new sources for html5
if (this.isHTML5) {
- source.insertElements.call(this, 'source', input.sources);
+ source.insertElements.call(this, 'source', sources);
}
// Set video title
diff --git a/src/js/support.js b/src/js/support.js
index 7eabae3c..6395293f 100644
--- a/src/js/support.js
+++ b/src/js/support.js
@@ -7,6 +7,15 @@ import browser from './utils/browser';
import { createElement } from './utils/elements';
import is from './utils/is';
+// Default codecs for checking mimetype support
+const defaultCodecs = {
+ 'audio/ogg': 'vorbis',
+ 'audio/wav': '1',
+ 'video/webm': 'vp8, vorbis',
+ 'video/mp4': 'avc1.42E01E, mp4a.40.2',
+ 'video/ogg': 'theora',
+};
+
// Check for feature support
const support = {
// Basic support
@@ -16,31 +25,9 @@ const support = {
// Check for support
// Basic functionality vs full UI
check(type, provider, playsinline) {
- let api = false;
- let ui = false;
const canPlayInline = browser.isIPhone && playsinline && support.playsinline;
-
- switch (`${provider}:${type}`) {
- case 'html5:video':
- api = support.video;
- ui = api && support.rangeInput && (!browser.isIPhone || canPlayInline);
- break;
-
- case 'html5:audio':
- api = support.audio;
- ui = api && support.rangeInput;
- break;
-
- case 'youtube:video':
- case 'vimeo:video':
- api = true;
- ui = support.rangeInput && (!browser.isIPhone || canPlayInline);
- break;
-
- default:
- api = support.audio && support.video;
- ui = api && support.rangeInput;
- }
+ const api = support[type] || provider !== 'html5';
+ const ui = api && support.rangeInput && (type !== 'video' || !browser.isIPhone || canPlayInline);
return {
api,
@@ -63,56 +50,29 @@ const support = {
// Check for mime type support against a player instance
// Credits: http://diveintohtml5.info/everything.html
// Related: http://www.leanbackplayer.com/test/h5mt.html
- mime(type) {
- const { media } = this;
+ mime(inputType) {
+ const [mediaType] = inputType.split('/');
+ if (!this.isHTML5 || mediaType !== this.type) {
+ return false;
+ }
+
+ let type;
+ if (inputType && inputType.includes('codecs=')) {
+ // Use input directly
+ type = inputType;
+ } else if (inputType === 'audio/mpeg') {
+ // Skip codec
+ type = 'audio/mpeg;';
+ } else if (inputType in defaultCodecs) {
+ // Use codec
+ type = `${inputType}; codecs="${defaultCodecs[inputType]}"`;
+ }
try {
- // Bail if no checking function
- if (!this.isHTML5 || !is.function(media.canPlayType)) {
- return false;
- }
-
- // Check directly if codecs specified
- if (type.includes('codecs=')) {
- return media.canPlayType(type).replace(/no/, '');
- }
-
- // Type specific checks
- if (this.isVideo) {
- switch (type) {
- case 'video/webm':
- return media.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/no/, '');
-
- case 'video/mp4':
- return media.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"').replace(/no/, '');
-
- case 'video/ogg':
- return media.canPlayType('video/ogg; codecs="theora"').replace(/no/, '');
-
- default:
- return false;
- }
- } else if (this.isAudio) {
- switch (type) {
- case 'audio/mpeg':
- return media.canPlayType('audio/mpeg;').replace(/no/, '');
-
- case 'audio/ogg':
- return media.canPlayType('audio/ogg; codecs="vorbis"').replace(/no/, '');
-
- case 'audio/wav':
- return media.canPlayType('audio/wav; codecs="1"').replace(/no/, '');
-
- default:
- return false;
- }
- }
- } catch (e) {
+ return Boolean(type && this.media.canPlayType(type).replace(/no/, ''));
+ } catch (err) {
return false;
}
-
- // If we got this far, we're stuffed
- return false;
},
// Check for textTracks support