Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(UI): Add ad statistics button #6827

Merged
merged 6 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build/types/ui
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
+../../third_party/language-mapping-list/language-mapping-list.js
+../../ui/ad_counter.js
+../../ui/ad_position.js
+../../ui/ad_statistics_button.js
+../../ui/audio_language_selection.js
+../../ui/externs/ui.js
+../../ui/play_button.js
Expand Down
1 change: 1 addition & 0 deletions shaka-player.uncompiled.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ goog.require('shaka.ui.OverflowMenu');
goog.require('shaka.ui.AudioLanguageSelection');
goog.require('shaka.ui.AdCounter');
goog.require('shaka.ui.AdPosition');
goog.require('shaka.ui.AdStatisticsButton');
goog.require('shaka.ui.AirPlayButton');
goog.require('shaka.ui.BigPlayButton');
goog.require('shaka.ui.CastButton');
Expand Down
235 changes: 235 additions & 0 deletions ui/ad_statistics_button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
/*! @license
* Shaka Player
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/


goog.provide('shaka.ui.AdStatisticsButton');

goog.require('shaka.log');
goog.require('shaka.ads.AdManager');
goog.require('shaka.ui.ContextMenu');
goog.require('shaka.ui.Controls');
goog.require('shaka.ui.Element');
goog.require('shaka.ui.Enums');
goog.require('shaka.ui.Locales');
goog.require('shaka.ui.Localization');
goog.require('shaka.ui.OverflowMenu');
goog.require('shaka.ui.Utils');
goog.require('shaka.util.Dom');
goog.require('shaka.util.Timer');
goog.requireType('shaka.ui.Controls');


/**
* @extends {shaka.ui.Element}
* @final
* @export
*/
shaka.ui.AdStatisticsButton = class extends shaka.ui.Element {
/**
* @param {!HTMLElement} parent
* @param {!shaka.ui.Controls} controls
*/
constructor(parent, controls) {
super(parent, controls);

/** @private {!HTMLButtonElement} */
this.button_ = shaka.util.Dom.createButton();
this.button_.classList.add('shaka-ad-statistics-button');

/** @private {!HTMLElement} */
this.icon_ = shaka.util.Dom.createHTMLElement('i');
this.icon_.classList.add('material-icons-round');
this.icon_.textContent =
shaka.ui.Enums.MaterialDesignIcons.STATISTICS_ON;
this.button_.appendChild(this.icon_);

const label = shaka.util.Dom.createHTMLElement('label');
label.classList.add('shaka-overflow-button-label');

/** @private {!HTMLElement} */
this.nameSpan_ = shaka.util.Dom.createHTMLElement('span');
label.appendChild(this.nameSpan_);

/** @private {!HTMLElement} */
this.stateSpan_ = shaka.util.Dom.createHTMLElement('span');
this.stateSpan_.classList.add('shaka-current-selection-span');
label.appendChild(this.stateSpan_);

this.button_.appendChild(label);

this.parent.appendChild(this.button_);

/** @private {!HTMLElement} */
this.container_ = shaka.util.Dom.createHTMLElement('div');
this.container_.classList.add('shaka-no-propagation');
this.container_.classList.add('shaka-show-controls-on-mouse-over');
this.container_.classList.add('shaka-ad-statistics-container');
this.container_.classList.add('shaka-hidden');

const controlsContainer = this.controls.getControlsContainer();
controlsContainer.appendChild(this.container_);

/** @private {!Array} */
this.statisticsList_ = [];

/** @private {!Object.<string, (number|!Array.<number>)>} */
this.currentStats_ = this.adManager.getStats();

/** @private {!Object.<string, HTMLElement>} */
this.displayedElements_ = {};

const parseLoadTimes = (name) => {
let totalTime = 0;
const loadTimes =
/** @type {!Array.<number>} */ (this.currentStats_[name]);
for (const loadTime of loadTimes) {
totalTime += parseFloat(loadTime);
}
return totalTime;
};

const showNumber = (name) => {
return this.currentStats_[name];
};

/** @private {!Object.<string, function(string):string>} */
this.parseFrom_ = {
'loadTimes': parseLoadTimes,
'started': showNumber,
'playedCompletely': showNumber,
'skipped': showNumber,
};

/** @private {shaka.util.Timer} */
this.timer_ = new shaka.util.Timer(() => {
this.onTimerTick_();
});

this.updateLocalizedStrings_();

this.loadContainer_();

this.eventManager.listen(
this.localization, shaka.ui.Localization.LOCALE_UPDATED, () => {
this.updateLocalizedStrings_();
});

this.eventManager.listen(
this.localization, shaka.ui.Localization.LOCALE_CHANGED, () => {
this.updateLocalizedStrings_();
});

this.eventManager.listen(this.button_, 'click', () => {
this.onClick_();
this.updateLocalizedStrings_();
});

this.eventManager.listen(this.player, 'loading', () => {
shaka.ui.Utils.setDisplay(this.button_, false);
});

this.eventManager.listen(
this.adManager, shaka.ads.AdManager.AD_STARTED, () => {
shaka.ui.Utils.setDisplay(this.button_, true);
});
}

/** @private */
onClick_() {
shaka.ui.Utils.setDisplay(this.parent, false);

if (this.container_.classList.contains('shaka-hidden')) {
this.icon_.textContent =
shaka.ui.Enums.MaterialDesignIcons.STATISTICS_OFF;
this.timer_.tickEvery(0.1);
shaka.ui.Utils.setDisplay(this.container_, true);
} else {
this.icon_.textContent =
shaka.ui.Enums.MaterialDesignIcons.STATISTICS_ON;
this.timer_.stop();
shaka.ui.Utils.setDisplay(this.container_, false);
}
}

/** @private */
updateLocalizedStrings_() {
const LocIds = shaka.ui.Locales.Ids;

this.nameSpan_.textContent =
this.localization.resolve(LocIds.AD_STATISTICS);

this.button_.ariaLabel = this.localization.resolve(LocIds.AD_STATISTICS);

const labelText = this.container_.classList.contains('shaka-hidden') ?
LocIds.OFF : LocIds.ON;
this.stateSpan_.textContent = this.localization.resolve(labelText);
}

/** @private */
generateComponent_(name) {
const section = shaka.util.Dom.createHTMLElement('div');

const label = shaka.util.Dom.createHTMLElement('label');
label.textContent = name + ':';
section.appendChild(label);

const value = shaka.util.Dom.createHTMLElement('span');
value.textContent = this.parseFrom_[name](name);
section.appendChild(value);

this.displayedElements_[name] = value;

return section;
}

/** @private */
loadContainer_() {
for (const name of this.controls.getConfig().adStatisticsList) {
if (name in this.currentStats_) {
this.container_.appendChild(this.generateComponent_(name));
this.statisticsList_.push(name);
} else {
shaka.log.alwaysWarn('Unrecognized ad statistic element:', name);
}
}
}

/** @private */
onTimerTick_() {
this.currentStats_ = this.adManager.getStats();

for (const name of this.statisticsList_) {
this.displayedElements_[name].textContent =
this.parseFrom_[name](name);
}
}

/** @override */
release() {
this.timer_.stop();
this.timer_ = null;
super.release();
}
};


/**
* @implements {shaka.extern.IUIElement.Factory}
* @final
*/
shaka.ui.AdStatisticsButton.Factory = class {
/** @override */
create(rootElement, controls) {
return new shaka.ui.AdStatisticsButton(rootElement, controls);
}
};


shaka.ui.OverflowMenu.registerElement(
'ad_statistics', new shaka.ui.AdStatisticsButton.Factory());

shaka.ui.ContextMenu.registerElement(
'ad_statistics', new shaka.ui.AdStatisticsButton.Factory());
3 changes: 3 additions & 0 deletions ui/externs/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ shaka.extern.UIVolumeBarColors;
* overflowMenuButtons: !Array.<string>,
* contextMenuElements: !Array.<string>,
* statisticsList: !Array.<string>,
* adStatisticsList: !Array.<string>,
* playbackRates: !Array.<number>,
* fastForwardRates: !Array.<number>,
* rewindRates: !Array.<number>,
Expand Down Expand Up @@ -106,6 +107,8 @@ shaka.extern.UIVolumeBarColors;
* The ordered list of buttons in the context menu.
* @property {!Array.<string>} statisticsList
* The ordered list of statistics present in the statistics container.
* @property {!Array.<string>} adStatisticsList
* The ordered list of ad statistics present in the ad statistics container.
* @property {!Array.<number>} playbackRates
* The ordered list of rates for playback selection.
* @property {!Array.<number>} fastForwardRates
Expand Down
32 changes: 32 additions & 0 deletions ui/less/containers.less
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,38 @@
}
}

.shaka-ad-statistics-container {
overflow-x: hidden;
overflow-y: auto;

min-width: 150px;

color: white;
background-color: rgba(35 35 35 / 90%);

font-size: 14px;

padding: 5px 10px;
border-radius: 2px;

position: absolute;
z-index: 2;
right: 15px;
top: 15px;

/* Fades out with the other controls. */
.show-when-controls-shown();

div {
display: flex;
justify-content: space-between;
}

span {
color: rgb(150 150 150);
}
}

.shaka-context-menu {
background-color: rgba(35 35 35 / 90%);

Expand Down
1 change: 1 addition & 0 deletions ui/locales/en.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"AD_DURATION": "Ad duration",
"AD_PROGRESS": "Ad [AD_ON] of [NUM_ADS]",
"AD_STATISTICS": "Ad statistics",
"AD_TIME": "Ad: [AD_TIME]",
"AIRPLAY": "AirPlay",
"AUTO_QUALITY": "Auto",
Expand Down
2 changes: 2 additions & 0 deletions ui/locales/es-419.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"AD_DURATION": "Duración del anuncio",
"AD_PROGRESS": "Anuncio [AD_ON] de [NUM_ADS]",
"AD_STATISTICS": "Estadísticas de anuncios",
"AD_TIME": "Anuncio: [AD_TIME]",
"AUTO_QUALITY": "Automático",
"BACK": "Atrás",
Expand Down Expand Up @@ -32,6 +33,7 @@
"SEEK": "Buscar",
"SKIP_AD": "Omitir anuncio",
"SKIP_TO_LIVE": "Adelantar hasta la transmisión en vivo",
"STATISTICS": "Estadísticas",
"SUBTITLE_FORCED": "Forzado",
"SURROUND": "Envolvente",
"TOGGLE_STEREOSCOPIC": "Alternar estereoscópico",
Expand Down
2 changes: 2 additions & 0 deletions ui/locales/es.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"AD_DURATION": "Duración del anuncio",
"AD_PROGRESS": "Anuncio [AD_ON] de [NUM_ADS]",
"AD_STATISTICS": "Estadísticas de anuncios",
"AD_TIME": "Anuncio: [AD_TIME]",
"AUTO_QUALITY": "Automático",
"BACK": "Atrás",
Expand Down Expand Up @@ -32,6 +33,7 @@
"SEEK": "Buscar",
"SKIP_AD": "Saltar anuncio",
"SKIP_TO_LIVE": "Ir al vídeo en directo",
"STATISTICS": "Estadísticas",
"SUBTITLE_FORCED": "Forzado",
"SURROUND": "Envolvente",
"TOGGLE_STEREOSCOPIC": "Alternar estereoscópico",
Expand Down
4 changes: 4 additions & 0 deletions ui/locales/source.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
"description": "The contents of the ad countdown element, describing what ad you are on in a sequence of ads.",
"message": "Ad [AD_ON:1] of [NUM_ADS:6]"
},
"AD_STATISTICS": {
"description": "Label for a button that displays ad statistics.",
"message": "Ad statistics"
},
"AD_TIME": {
"description": "The text content of a label that shows how much time is left in the current ad.",
"message": "Ad: [AD_TIME:0:05/0:10]"
Expand Down
7 changes: 7 additions & 0 deletions ui/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,17 @@ shaka.ui.Overlay = class {
'manifestPeriodCount',
'manifestGapCount',
],
adStatisticsList: [
'loadTimes',
'started',
'playedCompletely',
'skipped',
],
contextMenuElements: [
'loop',
'picture_in_picture',
'statistics',
'ad_statistics',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be sure to add this to docs/tutorials/ui-customization.md

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doc also lists elements that can be added to different UI areas. See, for example, where it lists picture_in_picture

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

],
playbackRates: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2],
fastForwardRates: [2, 4, 8, 1],
Expand Down
Loading