mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-15 16:06:41 +03:00
c9bd9a5dc9
Though not part of the library, this corrects/normalizes the license headers in the demo app to be consistent with those in the library. Issue #2638 Change-Id: I4546c4c6970d72ff71a37489fd582623bd6e2ca3
444 lines
13 KiB
JavaScript
444 lines
13 KiB
JavaScript
/*! @license
|
|
* Shaka Player
|
|
* Copyright 2016 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
|
|
goog.provide('shakaDemo.Search');
|
|
|
|
|
|
/** @type {?shakaDemo.Search} */
|
|
let shakaDemoSearch;
|
|
|
|
|
|
/**
|
|
* Shaka Player demo, feature discovery page layout.
|
|
*/
|
|
shakaDemo.Search = class {
|
|
/**
|
|
* Register the page configuration.
|
|
*/
|
|
static init() {
|
|
const elements = shakaDemoMain.addNavButton('search');
|
|
shakaDemoSearch = new shakaDemo.Search(elements.container, elements.button);
|
|
}
|
|
|
|
/**
|
|
* @param {!Element} container
|
|
* @param {!Element} button
|
|
*/
|
|
constructor(container, button) {
|
|
/** @private {!Array.<!shakaAssets.Feature>} */
|
|
this.desiredFeatures_ = [];
|
|
|
|
/** @private {?shakaAssets.Source} */
|
|
this.desiredSource_;
|
|
|
|
/** @private {?shakaAssets.KeySystem} */
|
|
this.desiredDRM_;
|
|
|
|
/** @private {!Element} */
|
|
this.button_ = button;
|
|
|
|
/** @private {!Element} */
|
|
this.resultsDiv_ = document.createElement('div');
|
|
|
|
/** @private {!Array.<!shakaDemo.AssetCard>} */
|
|
this.assetCards_ = [];
|
|
|
|
document.addEventListener('shaka-main-selected-asset-changed', () => {
|
|
this.updateSelected_();
|
|
});
|
|
document.addEventListener('shaka-main-offline-progress', () => {
|
|
this.updateOfflineProgress_();
|
|
});
|
|
document.addEventListener('shaka-main-locale-changed', () => {
|
|
this.remakeSearchDiv_(container);
|
|
this.remakeResultsDiv_();
|
|
// Update the componentHandler, to account for any new MDL elements added.
|
|
componentHandler.upgradeDom();
|
|
});
|
|
document.addEventListener('shaka-main-page-changed', () => {
|
|
if (!this.resultsDiv_.childNodes.length &&
|
|
!container.classList.contains('hidden')) {
|
|
// Now that the page is showing, create the contents that we deferred
|
|
// until now.
|
|
this.remakeResultsDiv_();
|
|
}
|
|
});
|
|
|
|
this.readHashParameters_();
|
|
this.updateHashParameters_();
|
|
this.remakeSearchDiv_(container);
|
|
}
|
|
|
|
/** @private */
|
|
readHashParameters_() {
|
|
const hashValues = this.button_.getAttribute('tab-hash');
|
|
if (hashValues) {
|
|
for (const valueRaw of hashValues.split(',')) {
|
|
if (valueRaw.startsWith('drm:')) {
|
|
const key = valueRaw.split('drm:')[1];
|
|
const value = shakaAssets.KeySystem[key];
|
|
if (value) {
|
|
this.desiredDRM_ = value;
|
|
}
|
|
} else if (valueRaw.startsWith('source:')) {
|
|
const key = valueRaw.split('source:')[1];
|
|
const value = shakaAssets.Source[key];
|
|
if (value) {
|
|
this.desiredSource_ = value;
|
|
}
|
|
} else {
|
|
const value = shakaAssets.Feature[valueRaw];
|
|
if (value) {
|
|
this.desiredFeatures_.push(value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @private */
|
|
updateHashParameters_() {
|
|
const hashValues = [];
|
|
if (this.desiredSource_) {
|
|
for (const key in shakaAssets.Source) {
|
|
if (shakaAssets.Source[key] == this.desiredSource_) {
|
|
hashValues.push('source:' + key);
|
|
}
|
|
}
|
|
}
|
|
if (this.desiredDRM_) {
|
|
for (const key in shakaAssets.KeySystem) {
|
|
if (shakaAssets.KeySystem[key] == this.desiredDRM_) {
|
|
hashValues.push('drm:' + key);
|
|
}
|
|
}
|
|
}
|
|
for (const feature of this.desiredFeatures_) {
|
|
for (const key in shakaAssets.Feature) {
|
|
if (shakaAssets.Feature[key] == feature) {
|
|
hashValues.push(key);
|
|
}
|
|
}
|
|
}
|
|
if (hashValues.length > 0) {
|
|
this.button_.setAttribute('tab-hash', hashValues.join(','));
|
|
} else {
|
|
this.button_.removeAttribute('tab-hash');
|
|
}
|
|
shakaDemoMain.remakeHash();
|
|
}
|
|
|
|
/**
|
|
* @param {!ShakaDemoAssetInfo} asset
|
|
* @return {!shakaDemo.AssetCard}
|
|
* @private
|
|
*/
|
|
createAssetCardFor_(asset) {
|
|
const resultsDiv = this.resultsDiv_;
|
|
const isFeatured = false;
|
|
return new shakaDemo.AssetCard(resultsDiv, asset, isFeatured, (c) => {
|
|
const unsupportedReason = shakaDemoMain.getAssetUnsupportedReason(
|
|
asset, /* needOffline= */ false);
|
|
if (unsupportedReason) {
|
|
c.markAsUnsupported(unsupportedReason);
|
|
} else {
|
|
c.addButton(shakaDemo.MessageIds.PLAY, () => {
|
|
shakaDemoMain.loadAsset(asset);
|
|
this.updateSelected_();
|
|
});
|
|
c.addStoreButton();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Updates progress bars on asset cards.
|
|
* @private
|
|
*/
|
|
updateOfflineProgress_() {
|
|
for (const card of this.assetCards_) {
|
|
card.updateProgress();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates which asset card is selected.
|
|
* @private
|
|
*/
|
|
updateSelected_() {
|
|
for (const card of this.assetCards_) {
|
|
card.selectByAsset(shakaDemoMain.selectedAsset);
|
|
}
|
|
}
|
|
|
|
/** @private */
|
|
remakeResultsDiv_() {
|
|
shaka.util.Dom.removeAllChildren(this.resultsDiv_);
|
|
|
|
const assets = this.searchResults_();
|
|
this.assetCards_ = assets.map((asset) => this.createAssetCardFor_(asset));
|
|
this.updateSelected_();
|
|
}
|
|
|
|
/**
|
|
* @param {!shakaDemo.Search.SearchTerm} term
|
|
* @param {shakaDemo.Search.TermType} type
|
|
* @return {boolean}
|
|
* @private
|
|
*/
|
|
checkDesiredTerm_(term, type) {
|
|
switch (type) {
|
|
case shakaDemo.Search.TermType.DRM:
|
|
return this.desiredDRM_ == term;
|
|
case shakaDemo.Search.TermType.SOURCE:
|
|
return this.desiredSource_ == term;
|
|
case shakaDemo.Search.TermType.FEATURE:
|
|
return this.desiredFeatures_.includes(
|
|
/** @type {!shakaAssets.Feature} */ (term));
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!shakaDemo.Search.SearchTerm} term
|
|
* @param {shakaDemo.Search.TermType} type
|
|
* @param {!Array.<!shakaDemo.Search.SearchTerm>} others
|
|
* @private
|
|
*/
|
|
addDesiredTerm_(term, type, others) {
|
|
switch (type) {
|
|
case shakaDemo.Search.TermType.DRM:
|
|
this.desiredDRM_ = /** @type {shakaAssets.KeySystem} */ (term);
|
|
break;
|
|
case shakaDemo.Search.TermType.SOURCE:
|
|
this.desiredSource_ = /** @type {shakaAssets.Source} */ (term);
|
|
break;
|
|
case shakaDemo.Search.TermType.FEATURE:
|
|
// Only this term should be in the desired features.
|
|
for (const term of others) {
|
|
const index = this.desiredFeatures_.indexOf(
|
|
/** @type {shakaAssets.Feature} */ (term));
|
|
if (index != -1) {
|
|
this.desiredFeatures_.splice(index, 1);
|
|
}
|
|
}
|
|
this.desiredFeatures_.push(/** @type {shakaAssets.Feature} */ (term));
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!shakaDemo.Search.SearchTerm} term
|
|
* @param {shakaDemo.Search.TermType} type
|
|
* @private
|
|
*/
|
|
removeDesiredTerm_(term, type) {
|
|
let index;
|
|
switch (type) {
|
|
case shakaDemo.Search.TermType.DRM:
|
|
this.desiredDRM_ = null;
|
|
break;
|
|
case shakaDemo.Search.TermType.SOURCE:
|
|
this.desiredSource_ = null;
|
|
break;
|
|
case shakaDemo.Search.TermType.FEATURE:
|
|
index = this.desiredFeatures_.indexOf(
|
|
/** @type {shakaAssets.Feature} */ (term));
|
|
if (index != -1) {
|
|
this.desiredFeatures_.splice(index, 1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates an input for a single search term.
|
|
* @param {!shakaDemo.InputContainer} searchContainer
|
|
* @param {!shakaDemo.Search.SearchTerm} choice
|
|
* The term this represents.
|
|
* @param {shakaDemo.Search.TermType} type
|
|
* The type of term that this term is.
|
|
* @param {?shakaDemo.MessageIds} tooltip
|
|
* @private
|
|
*/
|
|
makeBooleanInput_(searchContainer, choice, type, tooltip) {
|
|
// Give the container a significant amount of right padding, to make
|
|
// it clearer which toggle corresponds to which label.
|
|
searchContainer.addRow(choice, tooltip, 'significant-right-padding');
|
|
const onChange = (input) => {
|
|
if (input.checked) {
|
|
this.addDesiredTerm_(choice, type, [choice]);
|
|
} else {
|
|
this.removeDesiredTerm_(choice, type);
|
|
}
|
|
this.remakeResultsDiv_();
|
|
// Update the componentHandler, to account for any new MDL elements
|
|
// added. Notably, tooltips.
|
|
componentHandler.upgradeDom();
|
|
// Update the hash.
|
|
this.updateHashParameters_();
|
|
};
|
|
const input = new shakaDemo.BoolInput(searchContainer, choice, onChange);
|
|
input.input().checked = this.checkDesiredTerm_(choice, type);
|
|
}
|
|
|
|
/**
|
|
* Creates an input for a group of related but mutually-exclusive search
|
|
* terms.
|
|
* @param {!shakaDemo.InputContainer} searchContainer
|
|
* @param {!shakaDemo.MessageIds} name
|
|
* @param {!Array.<!shakaDemo.Search.SearchTerm>} choices
|
|
* An array of the terms in this term group.
|
|
* @param {shakaDemo.Search.TermType} type
|
|
* The type of term that this term group contains. All of the
|
|
* terms in the "choices" array must be of this type.
|
|
* @private
|
|
*/
|
|
makeSelectInput_(searchContainer, name, choices, type) {
|
|
searchContainer.addRow(null, null);
|
|
const nullOption = '---';
|
|
const valuesObject = {};
|
|
for (const term of choices) {
|
|
valuesObject[term] = shakaDemoMain.getLocalizedString(term);
|
|
}
|
|
valuesObject[nullOption] = nullOption;
|
|
let lastValue = nullOption;
|
|
const onChange = (input) => {
|
|
if (input.value != nullOption) {
|
|
this.addDesiredTerm_(input.value, type, choices);
|
|
} else {
|
|
this.removeDesiredTerm_(lastValue, type);
|
|
}
|
|
lastValue = input.value;
|
|
this.remakeResultsDiv_();
|
|
// Update the componentHandler, to account for any new MDL elements added.
|
|
// Notably, tooltips.
|
|
componentHandler.upgradeDom();
|
|
// Update the hash.
|
|
this.updateHashParameters_();
|
|
};
|
|
const input = new shakaDemo.SelectInput(
|
|
searchContainer, name, onChange, valuesObject);
|
|
input.input().value = nullOption;
|
|
for (const choice of choices) {
|
|
if (this.checkDesiredTerm_(choice, type)) {
|
|
input.input().value = choice;
|
|
lastValue = choice;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Element} container
|
|
* @private
|
|
*/
|
|
remakeSearchDiv_(container) {
|
|
shaka.util.Dom.removeAllChildren(container);
|
|
|
|
const Feature = shakaAssets.Feature;
|
|
const FEATURE = shakaDemo.Search.TermType.FEATURE;
|
|
const DRM = shakaDemo.Search.TermType.DRM;
|
|
const SOURCE = shakaDemo.Search.TermType.SOURCE;
|
|
|
|
// Core term inputs.
|
|
const coreContainer = new shakaDemo.InputContainer(
|
|
container, /* headerText= */ null, shakaDemo.InputContainer.Style.FLEX,
|
|
/* docLink= */ null);
|
|
this.makeSelectInput_(coreContainer,
|
|
shakaDemo.MessageIds.MANIFEST_SEARCH,
|
|
[Feature.DASH, Feature.HLS], FEATURE);
|
|
this.makeSelectInput_(coreContainer,
|
|
shakaDemo.MessageIds.CONTAINER_SEARCH,
|
|
[Feature.MP4, Feature.MP2TS, Feature.WEBM], FEATURE);
|
|
this.makeSelectInput_(coreContainer,
|
|
shakaDemo.MessageIds.DRM_SEARCH,
|
|
Object.values(shakaAssets.KeySystem), DRM);
|
|
this.makeSelectInput_(coreContainer,
|
|
shakaDemo.MessageIds.SOURCE_SEARCH,
|
|
Object.values(shakaAssets.Source).filter((term) => {
|
|
return term != shakaAssets.Source.CUSTOM;
|
|
}), SOURCE);
|
|
this.makeSelectInput_(coreContainer,
|
|
shakaDemo.MessageIds.LIVE_SEARCH,
|
|
[Feature.LIVE, Feature.VOD], FEATURE);
|
|
|
|
// Special terms.
|
|
const containerStyle = shakaDemo.InputContainer.Style.FLEX;
|
|
const specialContainer = new shakaDemo.InputContainer(
|
|
container, /* headerText= */ null, containerStyle,
|
|
/* docLink= */ null);
|
|
this.makeBooleanInput_(specialContainer, Feature.HIGH_DEFINITION, FEATURE,
|
|
shakaDemo.MessageIds.HIGH_DEFINITION_SEARCH);
|
|
this.makeBooleanInput_(specialContainer, Feature.XLINK, FEATURE,
|
|
shakaDemo.MessageIds.XLINK_SEARCH);
|
|
this.makeBooleanInput_(specialContainer, Feature.SUBTITLES, FEATURE,
|
|
shakaDemo.MessageIds.SUBTITLES_SEARCH);
|
|
this.makeBooleanInput_(specialContainer, Feature.TRICK_MODE, FEATURE,
|
|
shakaDemo.MessageIds.TRICK_MODE_SEARCH);
|
|
this.makeBooleanInput_(specialContainer, Feature.SURROUND, FEATURE,
|
|
shakaDemo.MessageIds.SURROUND_SEARCH);
|
|
this.makeBooleanInput_(specialContainer, Feature.OFFLINE, FEATURE,
|
|
shakaDemo.MessageIds.OFFLINE_SEARCH);
|
|
this.makeBooleanInput_(specialContainer, Feature.STORED, FEATURE,
|
|
shakaDemo.MessageIds.STORED_SEARCH);
|
|
this.makeBooleanInput_(specialContainer, Feature.ADS, FEATURE,
|
|
shakaDemo.MessageIds.AD_SEARCH);
|
|
this.makeBooleanInput_(specialContainer, Feature.AUDIO_ONLY, FEATURE,
|
|
shakaDemo.MessageIds.AUDIO_ONLY_SEARCH);
|
|
|
|
container.appendChild(this.resultsDiv_);
|
|
}
|
|
|
|
/**
|
|
* @return {!Array.<!ShakaDemoAssetInfo>}
|
|
* @private
|
|
*/
|
|
searchResults_() {
|
|
return shakaAssets.testAssets.filter((asset) => {
|
|
if (asset.disabled) {
|
|
return false;
|
|
}
|
|
if (this.desiredDRM_ && !asset.drm.includes(this.desiredDRM_)) {
|
|
return false;
|
|
}
|
|
if (this.desiredSource_ && asset.source != this.desiredSource_) {
|
|
return false;
|
|
}
|
|
for (const feature of this.desiredFeatures_) {
|
|
if (feature == shakaAssets.Feature.STORED) {
|
|
if (!asset.isStored()) {
|
|
return false;
|
|
}
|
|
} else if (!asset.features.includes(feature)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
};
|
|
|
|
|
|
/** @typedef {shakaAssets.Feature|shakaAssets.Source} */
|
|
shakaDemo.Search.SearchTerm;
|
|
|
|
|
|
/** @enum {string} */
|
|
shakaDemo.Search.TermType = {
|
|
FEATURE: 'Feature',
|
|
DRM: 'DRM',
|
|
SOURCE: 'Source',
|
|
};
|
|
|
|
|
|
document.addEventListener('shaka-main-loaded', shakaDemo.Search.init);
|
|
document.addEventListener('shaka-main-cleanup', () => {
|
|
shakaDemoSearch = null;
|
|
});
|