Skip to content

Commit

Permalink
feat(demo): Add MIME type and extra config to custom assets
Browse files Browse the repository at this point in the history
This allows content like that found in shaka-project#3366 to be configured in the
custom assets tab of the demo app.  For example, to play
src=+WebM+ClearKey, you could set:

1. Manifest URL to a WebM file
2. MIME type to 'video/webm; codecs="vp9"'
3. Extra config to something like:

{
  "drm": {
    "clearKeys": {
      "deadbeefdeadbeefdeadbeefdeadbeef":
          "deadbeefdeadbeefdeadbeefdeadbeef"
    }
  }
}

This would set up Shaka Player to play a single DRM'd WebM file with
ClearKey decryption.

The new "extra config" section of the custom asset dialog is powerful
enough to allow you to configure any arbitrary Shaka Player setting
that can be represented in JSON.  (So no callbacks.)

This was already possible for the assets hard-coded in
demo/common/assets.js, but having that available in custom assets will
make manual testing of private content significantly easier.

Change-Id: I889839ee0d15131a61c215a2f7c09f7715572bd1
  • Loading branch information
joeyparrish committed May 6, 2021
1 parent 6c528c8 commit 24d10ed
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 18 deletions.
11 changes: 11 additions & 0 deletions demo/common/asset.js
Expand Up @@ -75,6 +75,8 @@ const ShakaDemoAssetInfo = class {
this.imaAssetKey = null;
/** @type {?string} */
this.imaContentSrcId = null;
/** @type {?string} */
this.mimeType = null;


// Offline storage values.
Expand Down Expand Up @@ -168,6 +170,15 @@ const ShakaDemoAssetInfo = class {
return this;
}

/**
* @param {string} mimeType
* @return {!ShakaDemoAssetInfo}
*/
setMimeType(mimeType) {
this.mimeType = mimeType;
return this;
}

/**
* @param {!shaka.extern.RequestFilter} requestFilter
* @return {!ShakaDemoAssetInfo}
Expand Down
4 changes: 4 additions & 0 deletions demo/common/message_ids.js
Expand Up @@ -118,6 +118,10 @@ shakaDemo.MessageIds = {
MANIFEST_URL_ERROR: 'DEMO_MANIFEST_URL_ERROR',
NAME: 'DEMO_NAME',
NAME_ERROR: 'DEMO_NAME_ERROR',
EXTRA_SHAKA_PLAYER_CONFIG: 'DEMO_EXTRA_SHAKA_PLAYER_CONFIG',
EXTRA_TAB: 'DEMO_EXTRA_TAB',
INVALID_JSON_CONFIG_ERROR: 'DEMO_INVALID_JSON_CONFIG_ERROR',
MIME_TYPE: 'DEMO_MIME_TYPE',
SAVE_BUTTON: 'DEMO_SAVE_BUTTON',
/* Search panel. */
AD_SEARCH: 'DEMO_AD_SEARCH',
Expand Down
87 changes: 84 additions & 3 deletions demo/custom.js
Expand Up @@ -10,6 +10,7 @@ goog.provide('shakaDemo.Custom');

goog.require('ShakaDemoAssetInfo');
goog.require('shakaDemo.AssetCard');
goog.require('shakaDemo.Input');
goog.require('shakaDemo.InputContainer');
goog.require('shakaDemo.Main');
goog.require('shakaDemo.MessageIds');
Expand Down Expand Up @@ -104,12 +105,14 @@ shakaDemo.Custom = class {
* @param {!shakaDemo.InputContainer} container
* @param {string} name
* @param {function(!HTMLInputElement, !Element)} setup
* @param {function(!Element)} onChange
* @param {function(!Element, !shakaDemo.Input)} onChange
* @param {boolean=} isTextArea
* @private
*/
makeField_(container, name, setup, onChange) {
makeField_(container, name, setup, onChange, isTextArea) {
container.addRow(/* labelString= */ null, /* tooltipString= */ null);
const input = new shakaDemo.TextInput(container, name, onChange);
const input =
new shakaDemo.TextInput(container, name, onChange, isTextArea);
input.extra().textContent = name;
setup(input.input(), input.container());
}
Expand Down Expand Up @@ -423,6 +426,63 @@ shakaDemo.Custom = class {
return drmDiv;
}

/**
* @param {!ShakaDemoAssetInfo} assetInProgress
* @param {!Array.<!HTMLInputElement>} inputsToCheck
* @return {!Element} div
* @private
*/
makeAssetDialogContentsExtra_(assetInProgress, inputsToCheck) {
const extraConfigDiv = document.createElement('div');
const containerStyle = shakaDemo.InputContainer.Style.FLEX;
const container = new shakaDemo.InputContainer(
extraConfigDiv, /* headerText= */ null, containerStyle,
/* docLink= */ null);
container.getClassList().add('wide-input');
container.setDefaultRowClass('wide-input');

const extraSetup = (input, container) => {
input.setAttribute('rows', 10);

if (assetInProgress.extraConfig) {
// Pretty-print the extra config.
input.value = JSON.stringify(
assetInProgress.extraConfig,
/* replacer= */ null, /* spacing= */ 2);
}

inputsToCheck.push(input);

// Make an error that shows up if you did not provide valid JSON.
const error = document.createElement('span');
error.classList.add('mdl-textfield__error');
error.textContent = shakaDemoMain.getLocalizedString(
shakaDemo.MessageIds.INVALID_JSON_CONFIG_ERROR);

container.appendChild(error);
};
const extraOnChange = (inputElement, inputWrapper) => {
try {
if (!inputElement.value) {
assetInProgress.extraConfig = null;
} else {
const config = /** @type {!Object} */(JSON.parse(inputElement.value));
assetInProgress.extraConfig = config;
}
inputWrapper.setValid(true);
} catch (exception) {
inputWrapper.setValid(false);
}
};
const extraConfigLabel = shakaDemoMain.getLocalizedString(
shakaDemo.MessageIds.EXTRA_SHAKA_PLAYER_CONFIG);
this.makeField_(
container, extraConfigLabel, extraSetup, extraOnChange,
/* isTextArea= */ true);

return extraConfigDiv;
}

/**
* @param {!ShakaDemoAssetInfo} assetInProgress
* @param {!Array.<!HTMLInputElement>} inputsToCheck
Expand Down Expand Up @@ -528,6 +588,22 @@ shakaDemo.Custom = class {
this.makeField_(
container, iconURLName, iconSetup, iconOnChange);

// Make the MIME type field.
const mimeTypeSetup = (input, container) => {
if (assetInProgress.mimeType) {
input.value = assetInProgress.mimeType;
}
};

const mimeTypeOnChange = (input) => {
assetInProgress.mimeType = input.value || null;
};

const mimeTypeName = shakaDemoMain.getLocalizedString(
shakaDemo.MessageIds.MIME_TYPE);
this.makeField_(
container, mimeTypeName, mimeTypeSetup, mimeTypeOnChange);

return mainDiv;
}

Expand Down Expand Up @@ -598,6 +674,8 @@ shakaDemo.Custom = class {
assetInProgress, inputsToCheck);
const adsDiv = this.makeAssetDialogContentsAds_(
assetInProgress, inputsToCheck);
const extraConfigDiv = this.makeAssetDialogContentsExtra_(
assetInProgress, inputsToCheck);
const finishDiv = this.makeAssetDialogContentsFinish_(
assetInProgress, inputsToCheck);

Expand Down Expand Up @@ -635,13 +713,16 @@ shakaDemo.Custom = class {
shakaDemo.MessageIds.HEADERS_TAB, headersDiv, /* startOn= */ false);
addTabButton(
shakaDemo.MessageIds.ADS_TAB, adsDiv, /* startOn= */ false);
addTabButton(
shakaDemo.MessageIds.EXTRA_TAB, extraConfigDiv, /* startOn= */ false);

// Append the divs in the desired order.
this.dialog_.appendChild(tabDiv);
this.dialog_.appendChild(mainDiv);
this.dialog_.appendChild(drmDiv);
this.dialog_.appendChild(headersDiv);
this.dialog_.appendChild(adsDiv);
this.dialog_.appendChild(extraConfigDiv);
this.dialog_.appendChild(finishDiv);
this.dialog_.appendChild(iconDiv);

Expand Down
10 changes: 9 additions & 1 deletion demo/demo.less
Expand Up @@ -373,7 +373,7 @@ html, body {

.mdl-dialog {
/* Override MDL dialog width, so that input elements don't overflow. */
width: 320px;
width: 420px;
}

.mdl-dialog img {
Expand Down Expand Up @@ -533,6 +533,8 @@ footer .mdl-mega-footer__link-list {

/* Style the input containers. */
.input-container-row {
/* When a row is preceded by its label, inline-block will ensure the label
* sits before the input row on the same line. */
display: inline-block;
}

Expand Down Expand Up @@ -564,3 +566,9 @@ footer .mdl-mega-footer__link-list {
.input-header {
font-size: 125%;
}

.wide-input,
.wide-input .input-container-row,
.wide-input .mdl-textfield {
width: 100%;
}
43 changes: 31 additions & 12 deletions demo/input.js
Expand Up @@ -25,7 +25,7 @@ shakaDemo.Input = class {
* the input object.
* @param {string} extraType The element type for the "sibling element" to the
* input object. If null, it adds no such element.
* @param {function(!HTMLInputElement)} onChange
* @param {function(!HTMLInputElement, !shakaDemo.Input)} onChange
*/
constructor(parentContainer, inputType, containerType, extraType, onChange) {
/** @private {!Element} */
Expand All @@ -36,8 +36,14 @@ shakaDemo.Input = class {
this.input_ =
/** @type {!HTMLInputElement} */(document.createElement(inputType));
this.input_.onchange = () => {
onChange(this.input_);
onChange(this.input_, this);
};
// <textarea> elements need to also react to 'input' events.
if (inputType == 'textarea') {
this.input_.oninput = () => {
onChange(this.input_, this);
};
}
this.input_.id = shakaDemo.Input.generateNewId_('input');
this.container_.appendChild(this.input_);

Expand Down Expand Up @@ -81,6 +87,17 @@ shakaDemo.Input = class {
return this.extra_;
}

/** @param {boolean} valid */
setValid(valid) {
if (valid) {
this.input_.setCustomValidity(''); // valid
this.container_.classList.remove('is-invalid');
} else {
this.input_.setCustomValidity('invalid'); // any message will do
this.container_.parentElement.classList.add('is-invalid');
}
}

/**
* @param {string} prefix
* @return {string}
Expand All @@ -105,7 +122,7 @@ shakaDemo.SelectInput = class extends shakaDemo.Input {
/**
* @param {!shakaDemo.InputContainer} parentContainer
* @param {?shakaDemo.MessageIds} name
* @param {function(!HTMLInputElement)} onChange
* @param {function(!HTMLInputElement, !shakaDemo.Input)} onChange
* @param {!Object.<string, string>} values
*/
constructor(parentContainer, name, onChange, values) {
Expand Down Expand Up @@ -137,7 +154,7 @@ shakaDemo.BoolInput = class extends shakaDemo.Input {
/**
* @param {!shakaDemo.InputContainer} parentContainer
* @param {string} name
* @param {function(!HTMLInputElement)} onChange
* @param {function(!HTMLInputElement, !shakaDemo.Input)} onChange
*/
constructor(parentContainer, name, onChange) {
super(parentContainer, 'input', 'label', 'span', onChange);
Expand All @@ -159,10 +176,12 @@ shakaDemo.TextInput = class extends shakaDemo.Input {
/**
* @param {!shakaDemo.InputContainer} parentContainer
* @param {string} name
* @param {function(!HTMLInputElement)} onChange
* @param {function(!HTMLInputElement, !shakaDemo.Input)} onChange
* @param {boolean=} isTextArea
*/
constructor(parentContainer, name, onChange) {
super(parentContainer, 'input', 'div', 'label', onChange);
constructor(parentContainer, name, onChange, isTextArea) {
super(parentContainer, isTextArea ? 'textarea' : 'input', 'div', 'label',
onChange);
this.container_.classList.add('mdl-textfield');
this.container_.classList.add('mdl-js-textfield');
this.container_.classList.add('mdl-textfield--floating-label');
Expand All @@ -180,11 +199,11 @@ shakaDemo.DatalistInput = class extends shakaDemo.TextInput {
/**
* @param {!shakaDemo.InputContainer} parentContainer
* @param {string} name
* @param {function(!HTMLInputElement)} onChange
* @param {function(!HTMLInputElement, !shakaDemo.Input)} onChange
* @param {!Array.<string>} values
*/
constructor(parentContainer, name, onChange, values) {
super(parentContainer, name, onChange);
super(parentContainer, name, onChange, /* isTextArea= */ false);
// This element is not literally a datalist, as those are not supported on
// all platforms (and they also have no MDL style support).
// Instead, this is using the third-party "awesomplete" module, which acts
Expand All @@ -199,7 +218,7 @@ shakaDemo.DatalistInput = class extends shakaDemo.TextInput {
awesomplete.evaluate();
});
this.input_.addEventListener('awesomplete-selectcomplete', () => {
onChange(this.input_);
onChange(this.input_, this);
});
}
};
Expand All @@ -212,14 +231,14 @@ shakaDemo.NumberInput = class extends shakaDemo.TextInput {
/**
* @param {!shakaDemo.InputContainer} parentContainer
* @param {string} name
* @param {function(!HTMLInputElement)} onChange
* @param {function(!HTMLInputElement, !shakaDemo.Input)} onChange
* @param {boolean} canBeDecimal
* @param {boolean} canBeZero
* @param {boolean} canBeUnset
*/
constructor(
parentContainer, name, onChange, canBeDecimal, canBeZero, canBeUnset) {
super(parentContainer, name, onChange);
super(parentContainer, name, onChange, /* isTextArea= */ false);
const error = document.createElement('span');
error.classList.add('mdl-textfield__error');
this.container_.appendChild(error);
Expand Down
22 changes: 22 additions & 0 deletions demo/input_container.js
Expand Up @@ -40,6 +40,9 @@ shakaDemo.InputContainer = class {
/** @private {?Element} */
this.latestRow_;

/** @private {?string} */
this.defaultRowClass_ = null;

/** @type {?Element} */
this.latestElementContainer;

Expand Down Expand Up @@ -147,13 +150,32 @@ shakaDemo.InputContainer = class {
parentDiv.appendChild(link);
}

/**
* Set the default row class for future calls to addRow().
*
* @param {?string} rowClass
*/
setDefaultRowClass(rowClass) {
this.defaultRowClass_ = rowClass;
}

/**
* Return the CSS class list for the container.
*
* @return {!DOMTokenList}
*/
getClassList() {
return this.table_.classList;
}

/**
* Makes a row, for storing an input.
* @param {?shakaDemo.MessageIds} labelString
* @param {?shakaDemo.MessageIds} tooltipString
* @param {string=} rowClass
*/
addRow(labelString, tooltipString, rowClass) {
rowClass = rowClass || this.defaultRowClass_ || '';
this.latestRow_ = document.createElement('div');
if (rowClass) {
this.latestRow_.classList.add(rowClass);
Expand Down

0 comments on commit 24d10ed

Please sign in to comment.