From 7aad747c25b07fedf4a4fc75095c560ea3c9899c Mon Sep 17 00:00:00 2001 From: Albin Larsson Date: Sun, 27 May 2018 05:08:18 +0200 Subject: [PATCH 1/8] Optimize captions code reused and ensure captionsenabled/captionsdisabled will be sent on initial setup --- src/js/captions.js | 64 +++++++++++++++------------------------------- src/js/plyr.js | 21 ++++++--------- src/js/ui.js | 6 +++-- 3 files changed, 32 insertions(+), 59 deletions(-) diff --git a/src/js/captions.js b/src/js/captions.js index df717351..296888b2 100644 --- a/src/js/captions.js +++ b/src/js/captions.js @@ -27,17 +27,6 @@ const captions = { this.captions.language = this.config.captions.language.toLowerCase(); } - // Set captions enabled state if not set - if (!utils.is.boolean(this.captions.active)) { - const active = this.storage.get('captions'); - - if (utils.is.boolean(active)) { - this.captions.active = active; - } else { - this.captions.active = this.config.captions.active; - } - } - // Only Vimeo and HTML5 video supported at this point if (!this.isVideo || this.isYouTube || (this.isHTML5 && !support.textTracks)) { // Clear menu and hide @@ -55,17 +44,6 @@ const captions = { utils.insertAfter(this.elements.captions, this.elements.wrapper); } - // Set the class hook - utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(captions.getTracks.call(this))); - - // Get tracks - const tracks = captions.getTracks.call(this); - - // If no caption file exists, hide container for caption text - if (utils.is.empty(tracks)) { - return; - } - // Get browser info const browser = utils.getBrowser(); @@ -94,14 +72,30 @@ const captions = { }); } + // Try to load the value from storage + let active = this.storage.get('captions'); + + // Otherwise fall back to the default config + if (!utils.is.boolean(active)) { + ({ active } = this.config.captions); + } + + // Set toggled state + this.toggleCaptions(active); + + // Update available languages in list + captions.update.call(this); + }, + + update() { // Set language captions.setLanguage.call(this); - // Enable UI - captions.show.call(this); + // Toggle the class hooks + utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(captions.getTracks.call(this))); - // Set available languages in list - if (utils.is.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) { + // Update available languages in list + if ((this.config.controls || []).includes('settings') && this.config.settings.includes('captions')) { controls.setCaptionsMenu.call(this); } }, @@ -247,24 +241,6 @@ const captions = { this.debug.warn('No captions element to render to'); } }, - - // Display captions container and button (for initialization) - show() { - // Try to load the value from storage - let active = this.storage.get('captions'); - - // Otherwise fall back to the default config - if (!utils.is.boolean(active)) { - ({ active } = this.config.captions); - } else { - this.captions.active = active; - } - - if (active) { - utils.toggleClass(this.elements.container, this.config.classNames.captions.active, true); - utils.toggleState(this.elements.buttons.captions, true); - } - }, }; export default captions; diff --git a/src/js/plyr.js b/src/js/plyr.js index 34b618bd..061225f8 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -838,24 +838,19 @@ class Plyr { } // If the method is called without parameter, toggle based on current value - const show = utils.is.boolean(input) ? input : !this.elements.container.classList.contains(this.config.classNames.captions.active); - - // Nothing to change... - if (this.captions.active === show) { - return; - } - - // Set global - this.captions.active = show; + const active = utils.is.boolean(input) ? input : !this.elements.container.classList.contains(this.config.classNames.captions.active); // Toggle state - utils.toggleState(this.elements.buttons.captions, this.captions.active); + utils.toggleState(this.elements.buttons.captions, active); // Add class hook - utils.toggleClass(this.elements.container, this.config.classNames.captions.active, this.captions.active); + utils.toggleClass(this.elements.container, this.config.classNames.captions.active, active); - // Trigger an event - utils.dispatchEvent.call(this, this.media, this.captions.active ? 'captionsenabled' : 'captionsdisabled'); + // Update state and trigger event + if (active !== this.captions.active) { + this.captions.active = active; + utils.dispatchEvent.call(this, this.media, this.captions.active ? 'captionsenabled' : 'captionsdisabled'); + } } /** diff --git a/src/js/ui.js b/src/js/ui.js index 3a8f2d05..0d8e532f 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -55,8 +55,10 @@ const ui = { // Remove native controls ui.toggleNativeControls.call(this); - // Captions - captions.setup.call(this); + // Setup captions for html5 + if (this.isHTML5) { + captions.setup.call(this); + } // Reset volume this.volume = null; From 813f703211230024b99f4de95e433e4d33119f9a Mon Sep 17 00:00:00 2001 From: Albin Larsson Date: Sun, 27 May 2018 08:10:33 +0200 Subject: [PATCH 2/8] Add option to watch caption track changes and update language options --- readme.md | 2 +- src/js/captions.js | 5 +++++ src/js/defaults.js | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 89a459b6..e925152f 100644 --- a/readme.md +++ b/readme.md @@ -303,7 +303,7 @@ Note the single quotes encapsulating the JSON and double quotes on the object ke | `invertTime` | Boolean | `true` | Display the current time as a countdown rather than an incremental counter. | | `toggleInvert` | Boolean | `true` | Allow users to click to toggle the above. | | `listeners` | Object | `null` | Allows binding of event listeners to the controls before the default handlers. See the `defaults.js` for available listeners. If your handler prevents default on the event (`event.preventDefault()`), the default handler will not fire. | -| `captions` | Object | `{ active: false, language: window.navigator.language.split('-')[0] }` | `active`: Toggles if captions should be active by default. `language`: Sets the default language to load (if available). | +| `captions` | Object | `{ active: false, language: browser language, update: false }` | `active`: Toggles if captions should be active by default. `language`: Sets the default language to load (if available). `update`: Listen to changes to tracks and update menu. This is needed for some streaming libraries, but can result in unselectable language options). | | `fullscreen` | Object | `{ enabled: true, fallback: true, iosNative: false }` | `enabled`: Toggles whether fullscreen should be enabled. `fallback`: Allow fallback to a full-window solution. `iosNative`: whether to use native iOS fullscreen when entering fullscreen (no custom controls) | | `ratio` | String | `16:9` | The aspect ratio you want to use for embedded players. | | `storage` | Object | `{ enabled: true, key: 'plyr' }` | `enabled`: Allow use of local storage to store user settings. `key`: The key name to use. | diff --git a/src/js/captions.js b/src/js/captions.js index 296888b2..5941ebda 100644 --- a/src/js/captions.js +++ b/src/js/captions.js @@ -83,6 +83,11 @@ const captions = { // Set toggled state this.toggleCaptions(active); + // Watch changes to textTracks and update captions menu + if (this.config.captions.update) { + utils.on(this.media.textTracks, 'change', captions.update.bind(this)); + } + // Update available languages in list captions.update.call(this); }, diff --git a/src/js/defaults.js b/src/js/defaults.js index 5b1a4dd3..dc8785d5 100644 --- a/src/js/defaults.js +++ b/src/js/defaults.js @@ -116,6 +116,9 @@ const defaults = { captions: { active: false, language: (navigator.language || navigator.userLanguage).split('-')[0], + // Listen to new tracks added after Plyr is initialized. + // This is needed for streaming captions, but may result in unselectable options + update: false, }, // Fullscreen settings From 0109454a34d58d5fc0b2828c5106486de6a334db Mon Sep 17 00:00:00 2001 From: Albin Larsson Date: Mon, 28 May 2018 04:38:08 +0200 Subject: [PATCH 3/8] Ensure language is set in case the track is added after initialization, and trigger languagechange event when language is initially set --- src/js/captions.js | 21 ++++++++------------- src/js/controls.js | 5 +---- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/js/captions.js b/src/js/captions.js index 5941ebda..f62b2d4f 100644 --- a/src/js/captions.js +++ b/src/js/captions.js @@ -16,17 +16,6 @@ const captions = { return; } - // Set default language if not set - const stored = this.storage.get('language'); - - if (!utils.is.empty(stored)) { - this.captions.language = stored; - } - - if (utils.is.empty(this.captions.language)) { - this.captions.language = this.config.captions.language.toLowerCase(); - } - // Only Vimeo and HTML5 video supported at this point if (!this.isVideo || this.isYouTube || (this.isHTML5 && !support.textTracks)) { // Clear menu and hide @@ -93,8 +82,14 @@ const captions = { }, update() { - // Set language - captions.setLanguage.call(this); + // Update tracks + const tracks = captions.getTracks.call(this); + this.options.captions = tracks.map(({language}) => language); + + // Set language if it hasn't been set already + if (!this.language) { + this.language = this.storage.get('language') || (this.config.captions.language || '').toLowerCase(); + } // Toggle the class hooks utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(captions.getTracks.call(this))); diff --git a/src/js/controls.js b/src/js/controls.js index c76bd66b..32e82f78 100644 --- a/src/js/controls.js +++ b/src/js/controls.js @@ -883,13 +883,10 @@ const controls = { 'language', track.label, track.language !== 'enabled' ? controls.createBadge.call(this, track.language.toUpperCase()) : null, - track.language.toLowerCase() === this.captions.language.toLowerCase(), + track.language.toLowerCase() === this.language, ); }); - // Store reference - this.options.captions = tracks.map(track => track.language); - controls.updateSetting.call(this, type, list); }, From c9298fde768d14975a41ce8018fd0f10116943aa Mon Sep 17 00:00:00 2001 From: Albin Larsson Date: Sun, 13 May 2018 19:58:23 +0200 Subject: [PATCH 4/8] Fix typo --- src/js/ui.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/ui.js b/src/js/ui.js index 0d8e532f..9a6692dc 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -55,7 +55,7 @@ const ui = { // Remove native controls ui.toggleNativeControls.call(this); - // Setup captions for html5 + // Setup captions for HTML5 if (this.isHTML5) { captions.setup.call(this); } From 812e07b7347bc9d9a212b0204af1d90c92ee0c13 Mon Sep 17 00:00:00 2001 From: Albin Larsson Date: Mon, 28 May 2018 07:43:37 +0200 Subject: [PATCH 5/8] Replace browser language detection in defaults.js with explicit 'auto' option --- readme.md | 2 +- src/js/captions.js | 6 +++++- src/js/defaults.js | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index e925152f..46747960 100644 --- a/readme.md +++ b/readme.md @@ -303,7 +303,7 @@ Note the single quotes encapsulating the JSON and double quotes on the object ke | `invertTime` | Boolean | `true` | Display the current time as a countdown rather than an incremental counter. | | `toggleInvert` | Boolean | `true` | Allow users to click to toggle the above. | | `listeners` | Object | `null` | Allows binding of event listeners to the controls before the default handlers. See the `defaults.js` for available listeners. If your handler prevents default on the event (`event.preventDefault()`), the default handler will not fire. | -| `captions` | Object | `{ active: false, language: browser language, update: false }` | `active`: Toggles if captions should be active by default. `language`: Sets the default language to load (if available). `update`: Listen to changes to tracks and update menu. This is needed for some streaming libraries, but can result in unselectable language options). | +| `captions` | Object | `{ active: false, language: `'auto'`, update: false }` | `active`: Toggles if captions should be active by default. `language`: Sets the default language to load (if available). 'auto' uses the browser language. `update`: Listen to changes to tracks and update menu. This is needed for some streaming libraries, but can result in unselectable language options). | | `fullscreen` | Object | `{ enabled: true, fallback: true, iosNative: false }` | `enabled`: Toggles whether fullscreen should be enabled. `fallback`: Allow fallback to a full-window solution. `iosNative`: whether to use native iOS fullscreen when entering fullscreen (no custom controls) | | `ratio` | String | `16:9` | The aspect ratio you want to use for embedded players. | | `storage` | Object | `{ enabled: true, key: 'plyr' }` | `enabled`: Allow use of local storage to store user settings. `key`: The key name to use. | diff --git a/src/js/captions.js b/src/js/captions.js index f62b2d4f..f8083b65 100644 --- a/src/js/captions.js +++ b/src/js/captions.js @@ -88,7 +88,11 @@ const captions = { // Set language if it hasn't been set already if (!this.language) { - this.language = this.storage.get('language') || (this.config.captions.language || '').toLowerCase(); + let { language } = this.config.captions; + if (language === 'auto') { + [ language ] = (navigator.language || navigator.userLanguage).split('-'); + } + this.language = this.storage.get('language') || (language || '').toLowerCase(); } // Toggle the class hooks diff --git a/src/js/defaults.js b/src/js/defaults.js index dc8785d5..977a77e7 100644 --- a/src/js/defaults.js +++ b/src/js/defaults.js @@ -115,7 +115,7 @@ const defaults = { // Captions settings captions: { active: false, - language: (navigator.language || navigator.userLanguage).split('-')[0], + language: 'auto', // Listen to new tracks added after Plyr is initialized. // This is needed for streaming captions, but may result in unselectable options update: false, From f58e23b325a3b1d6ac30771d2167f22ea95fe54f Mon Sep 17 00:00:00 2001 From: Albin Larsson Date: Mon, 28 May 2018 16:19:44 +0200 Subject: [PATCH 6/8] Change to using addtrack and removetrack listeners since 'change' didn't trigger in firefox for embedded captions (may also be a hls.js issue) --- 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 f8083b65..52fdd8b3 100644 --- a/src/js/captions.js +++ b/src/js/captions.js @@ -74,7 +74,7 @@ const captions = { // Watch changes to textTracks and update captions menu if (this.config.captions.update) { - utils.on(this.media.textTracks, 'change', captions.update.bind(this)); + utils.on(this.media.textTracks, 'addtrack removetrack', captions.update.bind(this)); } // Update available languages in list From 64399e0717cf39f547594dad06097bb429cb9010 Mon Sep 17 00:00:00 2001 From: Albin Larsson Date: Mon, 28 May 2018 17:54:25 +0200 Subject: [PATCH 7/8] Defer initial captions update to next tick, to avoid event being triggered to early --- src/js/captions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/captions.js b/src/js/captions.js index 52fdd8b3..30c4bc74 100644 --- a/src/js/captions.js +++ b/src/js/captions.js @@ -77,8 +77,8 @@ const captions = { utils.on(this.media.textTracks, 'addtrack removetrack', captions.update.bind(this)); } - // Update available languages in list - captions.update.call(this); + // Update available languages in list next tick (the event must not be triggered before the listeners) + setTimeout(captions.update.bind(this), 0); }, update() { From 963fe11ad66fcbf28d93b0c6d2a20f3cd77b83cf Mon Sep 17 00:00:00 2001 From: Sam Potts Date: Wed, 30 May 2018 00:22:01 +1000 Subject: [PATCH 8/8] Update readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 46747960..bb05d7f7 100644 --- a/readme.md +++ b/readme.md @@ -303,7 +303,7 @@ Note the single quotes encapsulating the JSON and double quotes on the object ke | `invertTime` | Boolean | `true` | Display the current time as a countdown rather than an incremental counter. | | `toggleInvert` | Boolean | `true` | Allow users to click to toggle the above. | | `listeners` | Object | `null` | Allows binding of event listeners to the controls before the default handlers. See the `defaults.js` for available listeners. If your handler prevents default on the event (`event.preventDefault()`), the default handler will not fire. | -| `captions` | Object | `{ active: false, language: `'auto'`, update: false }` | `active`: Toggles if captions should be active by default. `language`: Sets the default language to load (if available). 'auto' uses the browser language. `update`: Listen to changes to tracks and update menu. This is needed for some streaming libraries, but can result in unselectable language options). | +| `captions` | Object | `{ active: false, language: 'auto', update: false }` | `active`: Toggles if captions should be active by default. `language`: Sets the default language to load (if available). 'auto' uses the browser language. `update`: Listen to changes to tracks and update menu. This is needed for some streaming libraries, but can result in unselectable language options). | | `fullscreen` | Object | `{ enabled: true, fallback: true, iosNative: false }` | `enabled`: Toggles whether fullscreen should be enabled. `fallback`: Allow fallback to a full-window solution. `iosNative`: whether to use native iOS fullscreen when entering fullscreen (no custom controls) | | `ratio` | String | `16:9` | The aspect ratio you want to use for embedded players. | | `storage` | Object | `{ enabled: true, key: 'plyr' }` | `enabled`: Allow use of local storage to store user settings. `key`: The key name to use. |