Files
shaka-player/ui/text_selection.js
T
Muhammad Haris d53fbaeeeb fix: Fix disappearing captions with certain input patterns (#2674)
When the subtitles are turned off, unloadTextStream() on streaming engine is called, but currentTextStream is never set to null. When the captions are turned back on, a check in player.js sees that the current text stream is not null, so it assumes it exists, and is ready to go. Thus captions are never loaded.

Clearing (setting to null) the current stream on unloadTextStream ensures the text stream is properly initialized the next time it is called, and in a state so that captions can resume being parsed.

After further investigation, it seems that the unit tests are written in a way that assumes that the text stream is nulled when unloadTextStream is called. So this fix is definitely something that we assume already happens.

Closes #2671
2020-06-24 13:12:09 -07:00

218 lines
6.4 KiB
JavaScript

/*! @license
* Shaka Player
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
goog.provide('shaka.ui.TextSelection');
goog.require('shaka.ui.Enums');
goog.require('shaka.ui.LanguageUtils');
goog.require('shaka.ui.Locales');
goog.require('shaka.ui.Localization');
goog.require('shaka.ui.OverflowMenu');
goog.require('shaka.ui.SettingsMenu');
goog.require('shaka.util.Dom');
/**
* @extends {shaka.ui.SettingsMenu}
* @final
* @export
*/
shaka.ui.TextSelection = class extends shaka.ui.SettingsMenu {
/**
* @param {!HTMLElement} parent
* @param {!shaka.ui.Controls} controls
*/
constructor(parent, controls) {
super(parent,
controls, shaka.ui.Enums.MaterialDesignIcons.CLOSED_CAPTIONS);
this.button.classList.add('shaka-caption-button');
this.menu.classList.add('shaka-text-languages');
if (this.player && this.player.isTextTrackVisible()) {
this.button.setAttribute('aria-pressed', 'true');
} else {
this.button.setAttribute('aria-pressed', 'false');
}
this.addOffOption_();
this.eventManager.listen(
this.localization, shaka.ui.Localization.LOCALE_UPDATED, () => {
this.updateLocalizedStrings_();
// If captions/subtitles are off, this string needs localization.
// TODO: is there a more efficient way of updating just the strings
// we need instead of running the whole language update?
this.updateTextLanguages_();
});
this.eventManager.listen(
this.localization, shaka.ui.Localization.LOCALE_CHANGED, () => {
this.updateLocalizedStrings_();
// If captions/subtitles are off, this string needs localization.
// TODO: is there a more efficient way of updating just the strings
// we need instead of running the whole language update?
this.updateTextLanguages_();
});
this.eventManager.listen(this.player, 'texttrackvisibility', () => {
this.onCaptionStateChange_();
});
this.eventManager.listen(this.player, 'textchanged', () => {
this.updateTextLanguages_();
});
this.eventManager.listen(this.player, 'trackschanged', () => {
this.onTracksChanged_();
});
// Initialize caption state with a fake event.
this.onCaptionStateChange_();
// Set up all the strings in the user's preferred language.
this.updateLocalizedStrings_();
this.updateTextLanguages_();
this.onTracksChanged_();
}
/**
* @private
*/
addOffOption_() {
const off = shaka.util.Dom.createButton();
off.setAttribute('aria-selected', 'true');
this.menu.appendChild(off);
off.appendChild(shaka.ui.Utils.checkmarkIcon());
/** @private {!HTMLElement} */
this.captionsOffSpan_ = shaka.util.Dom.createHTMLElement('span');
this.captionsOffSpan_.classList.add('shaka-auto-span');
off.appendChild(this.captionsOffSpan_);
}
/** @private */
onCaptionStateChange_() {
if (this.player.isTextTrackVisible()) {
this.icon.classList.add('shaka-captions-on');
this.icon.classList.remove('shaka-captions-off');
this.button.setAttribute('aria-pressed', 'true');
} else {
this.icon.classList.add('shaka-captions-off');
this.icon.classList.remove('shaka-captions-on');
this.button.setAttribute('aria-pressed', 'false');
}
this.controls.dispatchEvent(
new shaka.util.FakeEvent('captionselectionupdated'));
}
/** @private */
updateTextLanguages_() {
const tracks = this.player.getTextTracks();
shaka.ui.LanguageUtils.updateTracks(tracks, this.menu,
(track) => this.onTextTrackSelected_(track),
// Don't mark current text language as chosen unless captions are
// enabled
this.player.isTextTrackVisible(),
this.currentSelection,
this.localization,
this.controls.getConfig().trackLabelFormat);
// Add the Off button
const offButton = shaka.util.Dom.createButton();
offButton.classList.add('shaka-turn-captions-off-button');
this.eventManager.listen(offButton, 'click', () => {
this.player.setTextTrackVisibility(false);
this.updateTextLanguages_();
});
offButton.appendChild(this.captionsOffSpan_);
this.menu.appendChild(offButton);
if (!this.player.isTextTrackVisible()) {
offButton.setAttribute('aria-selected', 'true');
offButton.appendChild(shaka.ui.Utils.checkmarkIcon());
this.captionsOffSpan_.classList.add('shaka-chosen-item');
this.currentSelection.textContent =
this.localization.resolve(shaka.ui.Locales.Ids.OFF);
}
shaka.ui.Utils.focusOnTheChosenItem(this.menu);
this.controls.dispatchEvent(
new shaka.util.FakeEvent('captionselectionupdated'));
}
/**
* @param {!shaka.extern.Track} track
* @return {!Promise}
* @private
*/
async onTextTrackSelected_(track) {
// setTextTrackVisibility should be called after selectTextLanguage.
// selectTextLanguage sets a text stream, and setTextTrackVisiblity(true)
// will set a text stream if it isn't already set. Consequently, reversing
// the order of these calls makes two languages display simultaneously
// if captions are turned off -> on in a different language.
this.player.selectTextLanguage(track.language, track.roles[0]);
await this.player.setTextTrackVisibility(true);
}
/**
* @private
*/
updateLocalizedStrings_() {
const LocIds = shaka.ui.Locales.Ids;
this.button.setAttribute(shaka.ui.Constants.ARIA_LABEL,
this.localization.resolve(LocIds.CAPTIONS));
this.backButton.setAttribute(shaka.ui.Constants.ARIA_LABEL,
this.localization.resolve(LocIds.BACK));
this.nameSpan.textContent =
this.localization.resolve(LocIds.CAPTIONS);
this.backSpan.textContent =
this.localization.resolve(LocIds.CAPTIONS);
this.captionsOffSpan_.textContent =
this.localization.resolve(LocIds.OFF);
}
/** @private */
onTracksChanged_() {
const hasText = this.player.getTextTracks().length > 0;
shaka.ui.Utils.setDisplay(this.button, hasText);
this.updateTextLanguages_();
}
};
/**
* @implements {shaka.extern.IUIElement.Factory}
* @final
*/
shaka.ui.TextSelection.Factory = class {
/** @override */
create(rootElement, controls) {
return new shaka.ui.TextSelection(rootElement, controls);
}
};
shaka.ui.OverflowMenu.registerElement(
'captions', new shaka.ui.TextSelection.Factory());