From 14d0ce3a82b5f90976332c996aa6f5c86cdacd3e Mon Sep 17 00:00:00 2001 From: Guru Prasad Srinivasa Date: Sat, 30 Jun 2018 18:40:52 -0400 Subject: [PATCH 01/13] Tweaked quality selector to be more accomodating --- src/js/controls.js | 68 ++++++++++++++++++++++++++++----------- src/js/html5.js | 18 ++++++++--- src/js/listeners.js | 7 ++-- src/js/plugins/youtube.js | 17 ++++++++-- src/js/plyr.js | 31 +++++++++++------- 5 files changed, 104 insertions(+), 37 deletions(-) diff --git a/src/js/controls.js b/src/js/controls.js index 2bc8b4de0..6da4bd8b3 100644 --- a/src/js/controls.js +++ b/src/js/controls.js @@ -373,7 +373,7 @@ const controls = { }, // Create a settings menu item - createMenuItem({ value, list, type, title, badge = null, checked = false }) { + createMenuItem({ value, list, type, title, badge = null, checked = false, index = null }) { const item = createElement('li'); const label = createElement('label', { @@ -388,6 +388,7 @@ const controls = { value, checked, class: 'plyr__sr-only', + index, }), ); @@ -671,6 +672,17 @@ const controls = { }, // Set the quality menu + // options is expected to be an array of objects + // Each entry in this array should be of the type: + // { + // label: String, // mandatory + // height: Number, // mandatory + // index: Number, // optional + // badge: String, // optional + // } + // The order of qualities will be based on height. If there are multiple + // entries with the same height, then we use index instead. + // If badge is not specified, it will be looked up by height. setQualityMenu(options) { // Menu required if (!is.element(this.elements.settings.panes.quality)) { @@ -680,9 +692,9 @@ const controls = { const type = 'quality'; const list = this.elements.settings.panes.quality.querySelector('ul'); - // Set options if passed and filter based on uniqueness and config + // Set options if passed if (is.array(options)) { - this.options.quality = dedupe(options).filter(quality => this.config.quality.options.includes(quality)); + this.options.quality = dedupe(options); } // Toggle the pane and tab @@ -702,10 +714,17 @@ const controls = { // Get the badge HTML for HD, 4K etc const getBadge = quality => { - const label = i18n.get(`qualityBadge.${quality}`, this.config); + let label; + if (quality.badge) { + label = quality.badge; + } else { + // The badge is based on the height of the video + // TODO: We need some rounding logic here + label = i18n.get(`qualityBadge.${quality.label}`, this.config); - if (!label.length) { - return null; + if (!label.length) { + return null; + } } return controls.createBadge.call(this, label); @@ -714,16 +733,19 @@ const controls = { // Sort options by the config and then render options this.options.quality .sort((a, b) => { - const sorting = this.config.quality.options; - return sorting.indexOf(a) > sorting.indexOf(b) ? 1 : -1; + if (a.height === b.height) { + return a.index > b.index ? -1 : 1; + } + return a.height > b.height ? -1 : 1; }) .forEach(quality => { controls.createMenuItem.call(this, { - value: quality, + value: quality.label, list, type, - title: controls.getLabel.call(this, 'quality', quality), + title: controls.getLabel.call(this, 'quality', quality.height, toTitleCase(quality.label)), badge: getBadge(quality), + index: quality.index, }); }); @@ -731,7 +753,7 @@ const controls = { }, // Translate a value into a nice label - getLabel(setting, value) { + getLabel(setting, value, defaultLabel) { switch (setting) { case 'speed': return value === 1 ? i18n.get('normal', this.config) : `${value}×`; @@ -740,14 +762,14 @@ const controls = { if (is.number(value)) { const label = i18n.get(`qualityLabel.${value}`, this.config); + // If we don't find a valid label, we return passed in defaultLabel if (!label.length) { - return `${value}p`; + return defaultLabel; } return label; } - - return toTitleCase(value); + return defaultLabel || toTitleCase(value); case 'captions': return captions.getLabel.call(this); @@ -773,16 +795,26 @@ const controls = { value = this.config[setting].default; } + let settingOptions = this.options[setting]; + if (setting === 'quality') { + // Quality is not an array of strings. It's an array of Objects + // Extract out the strings + settingOptions = this.options[setting].map(level => controls.getLabel.call(this, 'quality', level.label)); + } + // Unsupported value - if (!is.empty(this.options[setting]) && !this.options[setting].includes(value)) { + if (!is.empty(settingOptions) && !settingOptions.includes(value)) { this.debug.warn(`Unsupported value of '${value}' for ${setting}`); return; } // Disabled value - if (!this.config[setting].options.includes(value)) { - this.debug.warn(`Disabled value of '${value}' for ${setting}`); - return; + // Don't check for quality which is permissive and accepts anything + if (setting !== 'quality') { + if (!this.config[setting].options.includes(value)) { + this.debug.warn(`Disabled value of '${value}' for ${setting}`); + return; + } } } diff --git a/src/js/html5.js b/src/js/html5.js index 0876211a9..96e867fc6 100644 --- a/src/js/html5.js +++ b/src/js/html5.js @@ -21,10 +21,15 @@ const html5 = { // Get quality levels getQualityOptions() { // Get sizes from elements - return html5.getSources + const sizes = html5.getSources .call(this) .map(source => Number(source.getAttribute('size'))) .filter(Boolean); + return sizes.map((size, index) => ({ + label: `${size}`, + height: size, + index, + })); }, extend() { @@ -35,21 +40,26 @@ const html5 = { const player = this; // Quality + const qualityLevels = html5.getQualityOptions.call(player); Object.defineProperty(player.media, 'quality', { get() { // Get sources const sources = html5.getSources.call(player); const source = sources.find(source => source.getAttribute('src') === player.source); + if (!source) { + return undefined; + } + const sourceIndex = sources.indexOf(source); - // Return size, if match is found - return source && Number(source.getAttribute('size')); + // Return label, if match is found + return qualityLevels[sourceIndex].label; }, set(input) { // Get sources const sources = html5.getSources.call(player); // Get first match for requested size - const source = sources.find(source => Number(source.getAttribute('size')) === input); + const source = sources.find(source => source.getAttribute('size') === input.label); // No matching source found if (!source) { diff --git a/src/js/listeners.js b/src/js/listeners.js index 9583bd71f..919a9b2d5 100644 --- a/src/js/listeners.js +++ b/src/js/listeners.js @@ -376,7 +376,7 @@ class Listeners { // Quality change on.call(this.player, this.player.media, 'qualitychange', event => { // Update UI - controls.updateSetting.call(this.player, 'quality', null, event.detail.quality); + controls.updateSetting.call(this.player, 'quality', null, event.detail.quality.label); }); // Proxy events to container @@ -511,7 +511,10 @@ class Listeners { proxy( event, () => { - this.player.quality = event.target.value; + this.player.quality = { + label: event.target.value, + index: Number(event.target.attributes.index.value), + }; showHomeTab(); }, 'quality', diff --git a/src/js/plugins/youtube.js b/src/js/plugins/youtube.js index b521be3c3..0e009a6a3 100644 --- a/src/js/plugins/youtube.js +++ b/src/js/plugins/youtube.js @@ -51,7 +51,14 @@ function mapQualityUnits(levels) { return levels; } - return dedupe(levels.map(level => mapQualityUnit(level))); + const mappedLevels = dedupe(levels.map(level => mapQualityUnit(level))); + const result = mappedLevels.map((level, index) => ({ + label: levels[index], + index, + height: level, + badge: level >= 720 ? 'HD' : 'SD', + })); + return result; } // Set playback state and trigger change (only on actual change) @@ -300,7 +307,13 @@ const youtube = { return mapQualityUnit(instance.getPlaybackQuality()); }, set(input) { - instance.setPlaybackQuality(mapQualityUnit(input)); + let label; + if (input instanceof String) { + label = input; + } else if (input instanceof Object) { + label = input.label; + } + instance.setPlaybackQuality(mapQualityUnit(label)); }, }); diff --git a/src/js/plyr.js b/src/js/plyr.js index 6ecc328a4..4f0f369e7 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -677,21 +677,30 @@ class Plyr { const config = this.config.quality; const options = this.options.quality; + let quality; if (!options.length) { return; } - let quality = [ - !is.empty(input) && Number(input), - this.storage.get('quality'), - config.selected, - config.default, - ].find(is.number); - - if (!options.includes(quality)) { - const value = closest(options, quality); - this.debug.warn(`Unsupported quality option: ${quality}, using ${value} instead`); - quality = value; + if (input instanceof String) { + // We have only a label + // Convert this into an Object of the expected type + quality = { + value: input, + }; + } else if (input instanceof Object) { + quality = input; + } else { + this.debug.warn(`Quality option of unknown type: ${input} (${typeof input}). Ignoring`); + return; + } + + // Get the corresponding quality listing from options + const entry = options.find((level) => level.label === quality.label && (quality.index ? level.index === quality.index : true)); + + if (!entry) { + this.debug.warn(`Unsupported quality option: ${input}. Ignoring`); + return; } // Trigger request event From 6bd94627ca8bc5cf02b4b786e59db521f3914f47 Mon Sep 17 00:00:00 2001 From: Guru Prasad Srinivasa Date: Sat, 30 Jun 2018 20:45:00 -0400 Subject: [PATCH 02/13] Added some minor changes to code relating to i18n --- src/js/controls.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/js/controls.js b/src/js/controls.js index 6da4bd8b3..79f8ad9c1 100644 --- a/src/js/controls.js +++ b/src/js/controls.js @@ -723,6 +723,7 @@ const controls = { label = i18n.get(`qualityBadge.${quality.label}`, this.config); if (!label.length) { + // TODO: Try to find a badge based on height return null; } } @@ -754,23 +755,19 @@ const controls = { // Translate a value into a nice label getLabel(setting, value, defaultLabel) { + let label; switch (setting) { case 'speed': return value === 1 ? i18n.get('normal', this.config) : `${value}×`; case 'quality': - if (is.number(value)) { - const label = i18n.get(`qualityLabel.${value}`, this.config); - - // If we don't find a valid label, we return passed in defaultLabel - if (!label.length) { - return defaultLabel; - } + label = i18n.get(`qualityLabel.${value}`, this.config); - return label; + // If we don't find a valid label, we return passed in defaultLabel + if (!label.length) { + return defaultLabel || toTitleCase(value); } - return defaultLabel || toTitleCase(value); - + return label; case 'captions': return captions.getLabel.call(this); From fda8424cc6aaae209e1b8b0d11e64ace976a4c63 Mon Sep 17 00:00:00 2001 From: Guru Prasad Srinivasa Date: Sat, 30 Jun 2018 20:56:50 -0400 Subject: [PATCH 03/13] Updated to use inbuilt type checkers --- src/js/plugins/youtube.js | 4 ++-- src/js/plyr.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/js/plugins/youtube.js b/src/js/plugins/youtube.js index 0e009a6a3..44cd31fe8 100644 --- a/src/js/plugins/youtube.js +++ b/src/js/plugins/youtube.js @@ -308,9 +308,9 @@ const youtube = { }, set(input) { let label; - if (input instanceof String) { + if (is.string(input)) { label = input; - } else if (input instanceof Object) { + } else if (is.object(input)) { label = input.label; } instance.setPlaybackQuality(mapQualityUnit(label)); diff --git a/src/js/plyr.js b/src/js/plyr.js index 4f0f369e7..42d2cb7c2 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -682,13 +682,13 @@ class Plyr { return; } - if (input instanceof String) { + if (is.string(input)) { // We have only a label // Convert this into an Object of the expected type quality = { value: input, }; - } else if (input instanceof Object) { + } else if (is.object(input)) { quality = input; } else { this.debug.warn(`Quality option of unknown type: ${input} (${typeof input}). Ignoring`); From 6e41d5e830966a999604b07431e84cfe90bddb30 Mon Sep 17 00:00:00 2001 From: Guru Prasad Srinivasa Date: Sat, 30 Jun 2018 20:57:16 -0400 Subject: [PATCH 04/13] Incorported early-return feedback --- src/js/plugins/youtube.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/js/plugins/youtube.js b/src/js/plugins/youtube.js index 44cd31fe8..a03a46455 100644 --- a/src/js/plugins/youtube.js +++ b/src/js/plugins/youtube.js @@ -52,13 +52,12 @@ function mapQualityUnits(levels) { } const mappedLevels = dedupe(levels.map(level => mapQualityUnit(level))); - const result = mappedLevels.map((level, index) => ({ + return mappedLevels.map((level, index) => ({ label: levels[index], index, height: level, badge: level >= 720 ? 'HD' : 'SD', })); - return result; } // Set playback state and trigger change (only on actual change) From faaeb5d931868d0cd3cfb4d8276981de3ae1c283 Mon Sep 17 00:00:00 2001 From: Guru Prasad Srinivasa Date: Sat, 30 Jun 2018 21:14:30 -0400 Subject: [PATCH 05/13] Linting fixes --- src/js/plugins/youtube.js | 2 +- src/js/plyr.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/js/plugins/youtube.js b/src/js/plugins/youtube.js index a03a46455..280406cc4 100644 --- a/src/js/plugins/youtube.js +++ b/src/js/plugins/youtube.js @@ -310,7 +310,7 @@ const youtube = { if (is.string(input)) { label = input; } else if (is.object(input)) { - label = input.label; + ({label} = input); } instance.setPlaybackQuality(mapQualityUnit(label)); }, diff --git a/src/js/plyr.js b/src/js/plyr.js index 42d2cb7c2..cbf6511f7 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -18,7 +18,6 @@ import source from './source'; import Storage from './storage'; import support from './support'; import ui from './ui'; -import { closest } from './utils/arrays'; import { createElement, hasClass, removeElement, replaceElement, toggleClass, wrap } from './utils/elements'; import { off, on, once, triggerEvent, unbindListeners } from './utils/events'; import is from './utils/is'; From 28add9bb33be8f8427d4d3074563ab6fc415a6b8 Mon Sep 17 00:00:00 2001 From: Guru Prasad Srinivasa Date: Sat, 30 Jun 2018 22:31:04 -0400 Subject: [PATCH 06/13] Fix plyr.quality setter --- src/js/plyr.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/js/plyr.js b/src/js/plyr.js index cbf6511f7..f867bb6b6 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -677,18 +677,28 @@ class Plyr { const options = this.options.quality; let quality; + // Create a local copy of input so we can handle modifications + let qualityInput = input; if (!options.length) { return; } - if (is.string(input)) { + // Support setting quality as a Number + if (is.number(qualityInput)) { + // Convert this to a string since quality labels are expected to be strings + qualityInput = `${qualityInput}`; + } + + // Now, convert qualityInput into a quality object. + // This object is emitted as part of the qualityrequested event + if (is.string(qualityInput)) { // We have only a label // Convert this into an Object of the expected type quality = { - value: input, + label: qualityInput, }; - } else if (is.object(input)) { - quality = input; + } else if (is.object(qualityInput)) { + quality = qualityInput; } else { this.debug.warn(`Quality option of unknown type: ${input} (${typeof input}). Ignoring`); return; From c3103f4eaac016aa2fbc1410a757383fb3864edd Mon Sep 17 00:00:00 2001 From: Guru Prasad Srinivasa Date: Sun, 1 Jul 2018 19:30:10 -0400 Subject: [PATCH 07/13] Restored old ${value}p logic for displaying qualities --- src/js/controls.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/js/controls.js b/src/js/controls.js index 79f8ad9c1..73685a4f6 100644 --- a/src/js/controls.js +++ b/src/js/controls.js @@ -763,11 +763,13 @@ const controls = { case 'quality': label = i18n.get(`qualityLabel.${value}`, this.config); - // If we don't find a valid label, we return passed in defaultLabel if (!label.length) { - return defaultLabel || toTitleCase(value); + // Only return with p if value is a number + if (is.number(value)) { + return `${value}p`; + } } - return label; + return toTitleCase(value); case 'captions': return captions.getLabel.call(this); From 99613ca7322bd468d02d6aeda816846b81811bac Mon Sep 17 00:00:00 2001 From: Guru Prasad Srinivasa Date: Sun, 1 Jul 2018 19:31:51 -0400 Subject: [PATCH 08/13] Removed index as a required property Instead, index is taken as the array index and passed around internally where required --- src/js/controls.js | 71 ++++++++++++++++++++++----------------------- src/js/listeners.js | 5 ++-- src/js/plyr.js | 7 +++-- 3 files changed, 40 insertions(+), 43 deletions(-) diff --git a/src/js/controls.js b/src/js/controls.js index 73685a4f6..28cb8d79a 100644 --- a/src/js/controls.js +++ b/src/js/controls.js @@ -373,7 +373,7 @@ const controls = { }, // Create a settings menu item - createMenuItem({ value, list, type, title, badge = null, checked = false, index = null }) { + createMenuItem({ value, list, type, title, badge = null, checked = false }) { const item = createElement('li'); const label = createElement('label', { @@ -388,7 +388,6 @@ const controls = { value, checked, class: 'plyr__sr-only', - index, }), ); @@ -677,11 +676,10 @@ const controls = { // { // label: String, // mandatory // height: Number, // mandatory - // index: Number, // optional // badge: String, // optional // } // The order of qualities will be based on height. If there are multiple - // entries with the same height, then we use index instead. + // entries with the same height, then we use the entry's array index instead. // If badge is not specified, it will be looked up by height. setQualityMenu(options) { // Menu required @@ -714,47 +712,47 @@ const controls = { // Get the badge HTML for HD, 4K etc const getBadge = quality => { - let label; - if (quality.badge) { - label = quality.badge; - } else { - // The badge is based on the height of the video - // TODO: We need some rounding logic here - label = i18n.get(`qualityBadge.${quality.label}`, this.config); - - if (!label.length) { - // TODO: Try to find a badge based on height - return null; - } - } + const { + height, + badge = i18n.get(`qualityBadge.${height}`, this.config), + } = quality; - return controls.createBadge.call(this, label); + return badge ? controls.createBadge.call(this, quality.label) : null; }; // Sort options by the config and then render options - this.options.quality + const finalOptions = this.options.quality .sort((a, b) => { if (a.height === b.height) { - return a.index > b.index ? -1 : 1; + return this.options.quality.indexOf(a) > this.options.quality.indexOf(b); } - return a.height > b.height ? -1 : 1; - }) - .forEach(quality => { - controls.createMenuItem.call(this, { - value: quality.label, - list, - type, - title: controls.getLabel.call(this, 'quality', quality.height, toTitleCase(quality.label)), - badge: getBadge(quality), - index: quality.index, - }); + return a.height > b.height; + }).map(quality => ({ + value: this.options.quality.indexOf(quality), + list, + type, + title: quality.label || controls.getLabel.call(this, 'quality', quality.height), + badge: getBadge(quality), + })).reverse(); // We reverse it so that the lower height options appear at the bottom of the quality menu + + // The incoming quality option may not have labels + // If this is the case, then the text displayed as part of qualityMenu + // comes from 'title' of finalOptions + // Thus, since finalOptions contains all the relevant data for each quality + // entry, we update options.quality with finalOptions + this.options.quality = options.map((quality, index) => { + const finalOpt = finalOptions.find(opt => opt.value === index); + return Object.assign({}, quality, { + label: finalOpt.title, + badge: finalOpt.badge, }); - - controls.updateSetting.call(this, type, list); + }); + finalOptions.forEach(controls.createMenuItem.bind(this)); + controls.updateSetting.call(this, type, list, this.config[type].default); }, // Translate a value into a nice label - getLabel(setting, value, defaultLabel) { + getLabel(setting, value) { let label; switch (setting) { case 'speed': @@ -796,9 +794,8 @@ const controls = { let settingOptions = this.options[setting]; if (setting === 'quality') { - // Quality is not an array of strings. It's an array of Objects - // Extract out the strings - settingOptions = this.options[setting].map(level => controls.getLabel.call(this, 'quality', level.label)); + value = settingOptions[value].label; + settingOptions = settingOptions.map(quality => quality.label); } // Unsupported value diff --git a/src/js/listeners.js b/src/js/listeners.js index 919a9b2d5..661df865c 100644 --- a/src/js/listeners.js +++ b/src/js/listeners.js @@ -376,7 +376,7 @@ class Listeners { // Quality change on.call(this.player, this.player.media, 'qualitychange', event => { // Update UI - controls.updateSetting.call(this.player, 'quality', null, event.detail.quality.label); + controls.updateSetting.call(this.player, 'quality', null, event.detail.quality.index); }); // Proxy events to container @@ -512,8 +512,7 @@ class Listeners { event, () => { this.player.quality = { - label: event.target.value, - index: Number(event.target.attributes.index.value), + index: Number(event.target.value), }; showHomeTab(); }, diff --git a/src/js/plyr.js b/src/js/plyr.js index f867bb6b6..857085455 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -693,9 +693,10 @@ class Plyr { // This object is emitted as part of the qualityrequested event if (is.string(qualityInput)) { // We have only a label - // Convert this into an Object of the expected type + // Find the index of this label and + // convert this into an Object of the expected type quality = { - label: qualityInput, + index: options.map(quality => quality.label).indexOf(qualityInput), }; } else if (is.object(qualityInput)) { quality = qualityInput; @@ -705,7 +706,7 @@ class Plyr { } // Get the corresponding quality listing from options - const entry = options.find((level) => level.label === quality.label && (quality.index ? level.index === quality.index : true)); + const entry = options[quality.index]; if (!entry) { this.debug.warn(`Unsupported quality option: ${input}. Ignoring`); From 41fe6118c3af239d49d5cb6e2184a74e6cc379b7 Mon Sep 17 00:00:00 2001 From: Guru Prasad Srinivasa Date: Sun, 1 Jul 2018 19:32:43 -0400 Subject: [PATCH 09/13] Fix default quality --- src/js/config/defaults.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/config/defaults.js b/src/js/config/defaults.js index 1e90a4f06..5f5e95134 100644 --- a/src/js/config/defaults.js +++ b/src/js/config/defaults.js @@ -67,7 +67,7 @@ const defaults = { // Quality default quality: { - default: 576, + default: 0, // Represents the index instead of label options: [ 4320, 2880, From 2e20972540d777731e48e491ca95ba19f70a32bf Mon Sep 17 00:00:00 2001 From: Guru Prasad Srinivasa Date: Sun, 1 Jul 2018 19:35:09 -0400 Subject: [PATCH 10/13] Updated plyr.quality setter to continue to be backwards compatible --- src/js/plyr.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/js/plyr.js b/src/js/plyr.js index 857085455..ddc47d0f8 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -686,7 +686,9 @@ class Plyr { // Support setting quality as a Number if (is.number(qualityInput)) { // Convert this to a string since quality labels are expected to be strings - qualityInput = `${qualityInput}`; + // XXX: We really only accept labels, but + // this is left in place so as to be backward compatible + qualityInput = `${qualityInput}p`; } // Now, convert qualityInput into a quality object. From 3643a03e4ee970579f6152f13332c5e781d7a6e3 Mon Sep 17 00:00:00 2001 From: Guru Prasad Srinivasa Date: Sun, 1 Jul 2018 19:29:00 -0400 Subject: [PATCH 11/13] Fixed dedupe --- src/js/controls.js | 3 +-- src/js/html5.js | 28 +++++++++++++++++----------- src/js/plugins/youtube.js | 2 -- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/js/controls.js b/src/js/controls.js index 28cb8d79a..c5be1dcb9 100644 --- a/src/js/controls.js +++ b/src/js/controls.js @@ -7,7 +7,6 @@ import html5 from './html5'; import i18n from './i18n'; import support from './support'; import { repaint, transitionEndEvent } from './utils/animation'; -import { dedupe } from './utils/arrays'; import browser from './utils/browser'; import { createElement, @@ -692,7 +691,7 @@ const controls = { // Set options if passed if (is.array(options)) { - this.options.quality = dedupe(options); + this.options.quality = options; } // Toggle the pane and tab diff --git a/src/js/html5.js b/src/js/html5.js index 96e867fc6..f45ead3c2 100644 --- a/src/js/html5.js +++ b/src/js/html5.js @@ -21,15 +21,22 @@ const html5 = { // Get quality levels getQualityOptions() { // Get sizes from elements - const sizes = html5.getSources + return html5.getSources .call(this) - .map(source => Number(source.getAttribute('size'))) - .filter(Boolean); - return sizes.map((size, index) => ({ - label: `${size}`, - height: size, - index, - })); + .map((source) => { + const { + // For backward-compatibility, fallback on the old size-attribute (which I think we should deprecate) + height = Number(source.getAttribute('size')), + label, + badge, + } = source.dataset; + + return { + badge, + label, + height, + }; + }); }, extend() { @@ -40,7 +47,6 @@ const html5 = { const player = this; // Quality - const qualityLevels = html5.getQualityOptions.call(player); Object.defineProperty(player.media, 'quality', { get() { // Get sources @@ -52,14 +58,14 @@ const html5 = { const sourceIndex = sources.indexOf(source); // Return label, if match is found - return qualityLevels[sourceIndex].label; + return player.options.quality[sourceIndex].label; }, set(input) { // Get sources const sources = html5.getSources.call(player); // Get first match for requested size - const source = sources.find(source => source.getAttribute('size') === input.label); + const source = sources[input.index]; // No matching source found if (!source) { diff --git a/src/js/plugins/youtube.js b/src/js/plugins/youtube.js index 280406cc4..235c57384 100644 --- a/src/js/plugins/youtube.js +++ b/src/js/plugins/youtube.js @@ -54,9 +54,7 @@ function mapQualityUnits(levels) { const mappedLevels = dedupe(levels.map(level => mapQualityUnit(level))); return mappedLevels.map((level, index) => ({ label: levels[index], - index, height: level, - badge: level >= 720 ? 'HD' : 'SD', })); } From b081ca0dd88b46f7c272dbef8807abe01e1c277b Mon Sep 17 00:00:00 2001 From: Guru Prasad Srinivasa Date: Sun, 1 Jul 2018 19:45:14 -0400 Subject: [PATCH 12/13] Quality attribute label is no longer mandatory If unavailable, it is replaced with ${height}p --- src/js/controls.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/controls.js b/src/js/controls.js index c5be1dcb9..21f63cb3a 100644 --- a/src/js/controls.js +++ b/src/js/controls.js @@ -673,8 +673,8 @@ const controls = { // options is expected to be an array of objects // Each entry in this array should be of the type: // { - // label: String, // mandatory // height: Number, // mandatory + // label: String, // optional // badge: String, // optional // } // The order of qualities will be based on height. If there are multiple From d50c65ad97d9cdf1531105e5e246fa726251475f Mon Sep 17 00:00:00 2001 From: Guru Prasad Srinivasa Date: Mon, 2 Jul 2018 10:41:49 -0400 Subject: [PATCH 13/13] Fixed getBadge logic --- src/js/controls.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/controls.js b/src/js/controls.js index 21f63cb3a..31a2fca72 100644 --- a/src/js/controls.js +++ b/src/js/controls.js @@ -716,7 +716,7 @@ const controls = { badge = i18n.get(`qualityBadge.${height}`, this.config), } = quality; - return badge ? controls.createBadge.call(this, quality.label) : null; + return badge ? controls.createBadge.call(this, badge) : null; }; // Sort options by the config and then render options