Merge pull request #981 from friday/hls-captions

Improve captions handling for streaming
This commit is contained in:
Sam Potts 2018-05-30 21:44:42 +10:00 committed by GitHub
commit 450958c290
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 55 additions and 78 deletions

View File

@ -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: '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. |

View File

@ -16,28 +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();
}
// 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 +33,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 +61,45 @@ const captions = {
});
}
// Set language
captions.setLanguage.call(this);
// Try to load the value from storage
let active = this.storage.get('captions');
// Enable UI
captions.show.call(this);
// Otherwise fall back to the default config
if (!utils.is.boolean(active)) {
({ active } = this.config.captions);
}
// Set available languages in list
if (utils.is.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('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, 'addtrack removetrack', captions.update.bind(this));
}
// Update available languages in list next tick (the event must not be triggered before the listeners)
setTimeout(captions.update.bind(this), 0);
},
update() {
// 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) {
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
utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(captions.getTracks.call(this)));
// Update available languages in list
if ((this.config.controls || []).includes('settings') && this.config.settings.includes('captions')) {
controls.setCaptionsMenu.call(this);
}
},
@ -247,24 +245,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;

5
src/js/controls.js vendored
View File

@ -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);
},

View File

@ -115,7 +115,10 @@ 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,
},
// Fullscreen settings

View File

@ -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');
}
}
/**

View File

@ -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;