mirror of
https://github.com/DanielnetoDotCom/YouPHPTube
synced 2025-10-03 17:59:55 +02:00
3784 lines
110 KiB
JavaScript
3784 lines
110 KiB
JavaScript
(function (global, factory) {
|
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('video.js')) :
|
|
typeof define === 'function' && define.amd ? define(['exports', 'video.js'], factory) :
|
|
(factory((global.videojsIma = {}),global.videojs));
|
|
}(this, (function (exports,videojs) { 'use strict';
|
|
|
|
videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs;
|
|
|
|
/**
|
|
* Copyright 2017 Google Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* IMA SDK integration plugin for Video.js. For more information see
|
|
* https://www.github.com/googleads/videojs-ima
|
|
*/
|
|
|
|
/**
|
|
* Wraps the video.js player for the plugin.
|
|
*
|
|
* @param {Object} player Video.js player instance.
|
|
* @param {Object} adsPluginSettings Settings for the contrib-ads plugin.
|
|
* @param {Controller} controller Reference to the parent controller.
|
|
*/
|
|
var PlayerWrapper = function PlayerWrapper(player, adsPluginSettings, controller) {
|
|
/**
|
|
* Instance of the video.js player.
|
|
*/
|
|
this.vjsPlayer = player;
|
|
|
|
/**
|
|
* Plugin controller.
|
|
*/
|
|
this.controller = controller;
|
|
|
|
/**
|
|
* Timer used to track content progress.
|
|
*/
|
|
this.contentTrackingTimer = null;
|
|
|
|
/**
|
|
* True if our content video has completed, false otherwise.
|
|
*/
|
|
this.contentComplete = false;
|
|
|
|
/**
|
|
* Handle to interval that repeatedly updates current time.
|
|
*/
|
|
this.updateTimeIntervalHandle = null;
|
|
|
|
/**
|
|
* Interval (ms) to check for player resize for fluid support.
|
|
*/
|
|
this.updateTimeInterval = 1000;
|
|
|
|
/**
|
|
* Handle to interval that repeatedly checks for seeking.
|
|
*/
|
|
this.seekCheckIntervalHandle = null;
|
|
|
|
/**
|
|
* Interval (ms) on which to check if the user is seeking through the
|
|
* content.
|
|
*/
|
|
this.seekCheckInterval = 1000;
|
|
|
|
/**
|
|
* Handle to interval that repeatedly checks for player resize.
|
|
*/
|
|
this.resizeCheckIntervalHandle = null;
|
|
|
|
/**
|
|
* Interval (ms) to check for player resize for fluid support.
|
|
*/
|
|
this.resizeCheckInterval = 250;
|
|
|
|
/**
|
|
* Threshold by which to judge user seeking. We check every 1000 ms to see
|
|
* if the user is seeking. In order for us to decide that they are *not*
|
|
* seeking, the content video playhead must only change by 900-1100 ms
|
|
* between checks. Any greater change and we assume the user is seeking
|
|
* through the video.
|
|
*/
|
|
this.seekThreshold = 100;
|
|
|
|
/**
|
|
* Content ended listeners passed by the publisher to the plugin. Publishers
|
|
* should allow the plugin to handle content ended to ensure proper support
|
|
* of custom ad playback.
|
|
*/
|
|
this.contentEndedListeners = [];
|
|
|
|
/**
|
|
* Stores the content source so we can re-populate it manually after a
|
|
* post-roll on iOS.
|
|
*/
|
|
this.contentSource = '';
|
|
|
|
/**
|
|
* Stores the content source type so we can re-populate it manually after a
|
|
* post-roll.
|
|
*/
|
|
this.contentSourceType = '';
|
|
|
|
/**
|
|
* Stores data for the content playhead tracker.
|
|
*/
|
|
this.contentPlayheadTracker = {
|
|
currentTime: 0,
|
|
previousTime: 0,
|
|
seeking: false,
|
|
duration: 0
|
|
};
|
|
|
|
/**
|
|
* Player dimensions. Used in our resize check.
|
|
*/
|
|
this.vjsPlayerDimensions = {
|
|
width: this.getPlayerWidth(),
|
|
height: this.getPlayerHeight()
|
|
};
|
|
|
|
/**
|
|
* Video.js control bar.
|
|
*/
|
|
this.vjsControls = this.vjsPlayer.getChild('controlBar');
|
|
|
|
/**
|
|
* Vanilla HTML5 video player underneath the video.js player.
|
|
*/
|
|
this.h5Player = null;
|
|
|
|
this.vjsPlayer.one('play', this.setUpPlayerIntervals.bind(this));
|
|
this.boundContentEndedListener = this.localContentEndedListener.bind(this);
|
|
this.vjsPlayer.on('contentended', this.boundContentEndedListener);
|
|
this.vjsPlayer.on('dispose', this.playerDisposedListener.bind(this));
|
|
this.vjsPlayer.on('readyforpreroll', this.onReadyForPreroll.bind(this));
|
|
this.vjsPlayer.on('adtimeout', this.onAdTimeout.bind(this));
|
|
this.vjsPlayer.ready(this.onPlayerReady.bind(this));
|
|
|
|
if (this.controller.getSettings().requestMode === 'onPlay') {
|
|
this.vjsPlayer.one('play', this.controller.requestAds.bind(this.controller));
|
|
}
|
|
|
|
this.vjsPlayer.ads(adsPluginSettings);
|
|
};
|
|
|
|
/**
|
|
* Set up the intervals we use on the player.
|
|
*/
|
|
PlayerWrapper.prototype.setUpPlayerIntervals = function () {
|
|
this.updateTimeIntervalHandle = setInterval(this.updateCurrentTime.bind(this), this.updateTimeInterval);
|
|
this.seekCheckIntervalHandle = setInterval(this.checkForSeeking.bind(this), this.seekCheckInterval);
|
|
this.resizeCheckIntervalHandle = setInterval(this.checkForResize.bind(this), this.resizeCheckInterval);
|
|
};
|
|
|
|
/**
|
|
* Updates the current time of the video
|
|
*/
|
|
PlayerWrapper.prototype.updateCurrentTime = function () {
|
|
if (!this.contentPlayheadTracker.seeking) {
|
|
this.contentPlayheadTracker.currentTime = this.vjsPlayer.currentTime();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Detects when the user is seeking through a video.
|
|
* This is used to prevent mid-rolls from playing while a user is seeking.
|
|
*
|
|
* There *is* a seeking property of the HTML5 video element, but it's not
|
|
* properly implemented on all platforms (e.g. mobile safari), so we have to
|
|
* check ourselves to be sure.
|
|
*/
|
|
PlayerWrapper.prototype.checkForSeeking = function () {
|
|
var tempCurrentTime = this.vjsPlayer.currentTime();
|
|
var diff = (tempCurrentTime - this.contentPlayheadTracker.previousTime) * 1000;
|
|
if (Math.abs(diff) > this.seekCheckInterval + this.seekThreshold) {
|
|
this.contentPlayheadTracker.seeking = true;
|
|
} else {
|
|
this.contentPlayheadTracker.seeking = false;
|
|
}
|
|
this.contentPlayheadTracker.previousTime = this.vjsPlayer.currentTime();
|
|
};
|
|
|
|
/**
|
|
* Detects when the player is resized (for fluid support) and resizes the
|
|
* ads manager to match.
|
|
*/
|
|
PlayerWrapper.prototype.checkForResize = function () {
|
|
var currentWidth = this.getPlayerWidth();
|
|
var currentHeight = this.getPlayerHeight();
|
|
|
|
if (currentWidth != this.vjsPlayerDimensions.width || currentHeight != this.vjsPlayerDimensions.height) {
|
|
this.vjsPlayerDimensions.width = currentWidth;
|
|
this.vjsPlayerDimensions.height = currentHeight;
|
|
this.controller.onPlayerResize(currentWidth, currentHeight);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Local content ended listener for contentComplete.
|
|
*/
|
|
PlayerWrapper.prototype.localContentEndedListener = function () {
|
|
if (!this.contentComplete) {
|
|
this.contentComplete = true;
|
|
this.controller.onContentComplete();
|
|
}
|
|
|
|
for (var index in this.contentEndedListeners) {
|
|
if (typeof this.contentEndedListeners[index] === 'function') {
|
|
this.contentEndedListeners[index]();
|
|
}
|
|
}
|
|
|
|
clearInterval(this.updateTimeIntervalHandle);
|
|
clearInterval(this.seekCheckIntervalHandle);
|
|
clearInterval(this.resizeCheckIntervalHandle);
|
|
if (this.vjsPlayer.el()) {
|
|
this.vjsPlayer.one('play', this.setUpPlayerIntervals.bind(this));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Called when it's time to play a post-roll but we don't have one to play.
|
|
*/
|
|
PlayerWrapper.prototype.onNoPostroll = function () {
|
|
this.vjsPlayer.trigger('nopostroll');
|
|
};
|
|
|
|
/**
|
|
* Detects when the video.js player has been disposed.
|
|
*/
|
|
PlayerWrapper.prototype.playerDisposedListener = function () {
|
|
this.contentEndedListeners = [];
|
|
this.controller.onPlayerDisposed();
|
|
|
|
this.contentComplete = true;
|
|
this.vjsPlayer.off('contentended', this.boundContentEndedListener);
|
|
|
|
// Bug fix: https://github.com/googleads/videojs-ima/issues/306
|
|
if (this.vjsPlayer.ads.adTimeoutTimeout) {
|
|
clearTimeout(this.vjsPlayer.ads.adTimeoutTimeout);
|
|
}
|
|
|
|
var intervalsToClear = [this.updateTimeIntervalHandle, this.seekCheckIntervalHandle, this.resizeCheckIntervalHandle];
|
|
for (var index in intervalsToClear) {
|
|
if (intervalsToClear[index]) {
|
|
clearInterval(intervalsToClear[index]);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Start ad playback, or content video playback in the absence of a
|
|
* pre-roll.
|
|
*/
|
|
PlayerWrapper.prototype.onReadyForPreroll = function () {
|
|
this.controller.onPlayerReadyForPreroll();
|
|
};
|
|
|
|
/**
|
|
* Detects if the ad has timed out.
|
|
*/
|
|
PlayerWrapper.prototype.onAdTimeout = function () {
|
|
this.controller.onAdTimeout();
|
|
};
|
|
|
|
/**
|
|
* Called when the player fires its 'ready' event.
|
|
*/
|
|
PlayerWrapper.prototype.onPlayerReady = function () {
|
|
this.h5Player = document.getElementById(this.getPlayerId()).getElementsByClassName('vjs-tech')[0];
|
|
|
|
// Detect inline options
|
|
if (this.h5Player.hasAttribute('autoplay')) {
|
|
this.controller.setSetting('adWillAutoPlay', true);
|
|
}
|
|
|
|
// Sync ad volume with player volume.
|
|
this.onVolumeChange();
|
|
this.vjsPlayer.on('fullscreenchange', this.onFullscreenChange.bind(this));
|
|
this.vjsPlayer.on('volumechange', this.onVolumeChange.bind(this));
|
|
|
|
this.controller.onPlayerReady();
|
|
};
|
|
|
|
/**
|
|
* Listens for the video.js player to change its fullscreen status. This
|
|
* keeps the fullscreen-ness of the AdContainer in sync with the player.
|
|
*/
|
|
PlayerWrapper.prototype.onFullscreenChange = function () {
|
|
if (this.vjsPlayer.isFullscreen()) {
|
|
this.controller.onPlayerEnterFullscreen();
|
|
} else {
|
|
this.controller.onPlayerExitFullscreen();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Listens for the video.js player to change its volume. This keeps the ad
|
|
* volume in sync with the content volume if the volume of the player is
|
|
* changed while content is playing.
|
|
*/
|
|
PlayerWrapper.prototype.onVolumeChange = function () {
|
|
var newVolume = this.vjsPlayer.muted() ? 0 : this.vjsPlayer.volume();
|
|
this.controller.onPlayerVolumeChanged(newVolume);
|
|
};
|
|
|
|
/**
|
|
* Inject the ad container div into the DOM.
|
|
*
|
|
* @param{HTMLElement} adContainerDiv The ad container div.
|
|
*/
|
|
PlayerWrapper.prototype.injectAdContainerDiv = function (adContainerDiv) {
|
|
this.vjsControls.el().parentNode.appendChild(adContainerDiv);
|
|
};
|
|
|
|
/**
|
|
* @return {Object} The content player.
|
|
*/
|
|
PlayerWrapper.prototype.getContentPlayer = function () {
|
|
return this.h5Player;
|
|
};
|
|
|
|
/**
|
|
* @return {number} The volume, 0-1.
|
|
*/
|
|
PlayerWrapper.prototype.getVolume = function () {
|
|
return this.vjsPlayer.muted() ? 0 : this.vjsPlayer.volume();
|
|
};
|
|
|
|
/**
|
|
* Set the volume of the player. 0-1.
|
|
*
|
|
* @param {number} volume The new volume.
|
|
*/
|
|
PlayerWrapper.prototype.setVolume = function (volume) {
|
|
this.vjsPlayer.volume(volume);
|
|
if (volume == 0) {
|
|
this.vjsPlayer.muted(true);
|
|
} else {
|
|
this.vjsPlayer.muted(false);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Ummute the player.
|
|
*/
|
|
PlayerWrapper.prototype.unmute = function () {
|
|
this.vjsPlayer.muted(false);
|
|
};
|
|
|
|
/**
|
|
* Mute the player.
|
|
*/
|
|
PlayerWrapper.prototype.mute = function () {
|
|
this.vjsPlayer.muted(true);
|
|
};
|
|
|
|
/**
|
|
* Play the video.
|
|
*/
|
|
PlayerWrapper.prototype.play = function () {
|
|
this.vjsPlayer.play();
|
|
};
|
|
|
|
/**
|
|
* Toggles playback of the video.
|
|
*/
|
|
PlayerWrapper.prototype.togglePlayback = function () {
|
|
if (this.vjsPlayer.paused()) {
|
|
this.vjsPlayer.play();
|
|
} else {
|
|
this.vjsPlayer.pause();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get the player width.
|
|
*
|
|
* @return {number} The player's width.
|
|
*/
|
|
PlayerWrapper.prototype.getPlayerWidth = function () {
|
|
var width = (getComputedStyle(this.vjsPlayer.el()) || {}).width;
|
|
|
|
if (!width || parseFloat(width) === 0) {
|
|
width = (this.vjsPlayer.el().getBoundingClientRect() || {}).width;
|
|
}
|
|
|
|
return parseFloat(width) || this.vjsPlayer.width();
|
|
};
|
|
|
|
/**
|
|
* Get the player height.
|
|
*
|
|
* @return {number} The player's height.
|
|
*/
|
|
PlayerWrapper.prototype.getPlayerHeight = function () {
|
|
var height = (getComputedStyle(this.vjsPlayer.el()) || {}).height;
|
|
|
|
if (!height || parseFloat(height) === 0) {
|
|
height = (this.vjsPlayer.el().getBoundingClientRect() || {}).height;
|
|
}
|
|
|
|
return parseFloat(height) || this.vjsPlayer.height();
|
|
};
|
|
|
|
/**
|
|
* @return {Object} The vjs player's options object.
|
|
*/
|
|
PlayerWrapper.prototype.getPlayerOptions = function () {
|
|
return this.vjsPlayer.options_;
|
|
};
|
|
|
|
/**
|
|
* Returns the instance of the player id.
|
|
* @return {string} The player id.
|
|
*/
|
|
PlayerWrapper.prototype.getPlayerId = function () {
|
|
return this.vjsPlayer.id();
|
|
};
|
|
|
|
/**
|
|
* Toggle fullscreen state.
|
|
*/
|
|
PlayerWrapper.prototype.toggleFullscreen = function () {
|
|
if (this.vjsPlayer.isFullscreen()) {
|
|
this.vjsPlayer.exitFullscreen();
|
|
} else {
|
|
this.vjsPlayer.requestFullscreen();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns the content playhead tracker.
|
|
*
|
|
* @return {Object} The content playhead tracker.
|
|
*/
|
|
PlayerWrapper.prototype.getContentPlayheadTracker = function () {
|
|
return this.contentPlayheadTracker;
|
|
};
|
|
|
|
/**
|
|
* Handles ad errors.
|
|
*
|
|
* @param {Object} adErrorEvent The ad error event thrown by the IMA SDK.
|
|
*/
|
|
PlayerWrapper.prototype.onAdError = function (adErrorEvent) {
|
|
this.vjsControls.show();
|
|
var errorMessage = adErrorEvent.getError !== undefined ? adErrorEvent.getError() : adErrorEvent.stack;
|
|
this.vjsPlayer.trigger({ type: 'adserror', data: {
|
|
AdError: errorMessage,
|
|
AdErrorEvent: adErrorEvent
|
|
} });
|
|
};
|
|
|
|
/**
|
|
* Handles ad log messages.
|
|
* @param {google.ima.AdEvent} adEvent The AdEvent thrown by the IMA SDK.
|
|
*/
|
|
PlayerWrapper.prototype.onAdLog = function (adEvent) {
|
|
var adData = adEvent.getAdData();
|
|
var errorMessage = adData['adError'] !== undefined ? adData['adError'].getMessage() : undefined;
|
|
this.vjsPlayer.trigger({ type: 'adslog', data: {
|
|
AdError: errorMessage,
|
|
AdEvent: adEvent
|
|
} });
|
|
};
|
|
|
|
/**
|
|
* Handles ad break starting.
|
|
*/
|
|
PlayerWrapper.prototype.onAdBreakStart = function () {
|
|
this.contentSource = this.vjsPlayer.currentSrc();
|
|
this.contentSourceType = this.vjsPlayer.currentType();
|
|
this.vjsPlayer.off('contentended', this.boundContentEndedListener);
|
|
this.vjsPlayer.ads.startLinearAdMode();
|
|
this.vjsControls.hide();
|
|
this.vjsPlayer.pause();
|
|
};
|
|
|
|
/**
|
|
* Handles ad break ending.
|
|
*/
|
|
PlayerWrapper.prototype.onAdBreakEnd = function () {
|
|
this.vjsPlayer.on('contentended', this.boundContentEndedListener);
|
|
if (this.vjsPlayer.ads.inAdBreak()) {
|
|
this.vjsPlayer.ads.endLinearAdMode();
|
|
}
|
|
this.vjsControls.show();
|
|
};
|
|
|
|
/**
|
|
* Handles an individual ad start.
|
|
*/
|
|
PlayerWrapper.prototype.onAdStart = function () {
|
|
this.vjsPlayer.trigger('ads-ad-started');
|
|
};
|
|
|
|
/**
|
|
* Handles when all ads have finished playing.
|
|
*/
|
|
PlayerWrapper.prototype.onAllAdsCompleted = function () {
|
|
if (this.contentComplete == true) {
|
|
// The null check on this.contentSource was added to fix
|
|
// an error when the post-roll was an empty VAST tag.
|
|
if (this.contentSource && this.vjsPlayer.currentSrc() != this.contentSource) {
|
|
this.vjsPlayer.src({
|
|
src: this.contentSource,
|
|
type: this.contentSourceType
|
|
});
|
|
}
|
|
this.controller.onContentAndAdsCompleted();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Triggers adsready for contrib-ads.
|
|
*/
|
|
PlayerWrapper.prototype.onAdsReady = function () {
|
|
this.vjsPlayer.trigger('adsready');
|
|
};
|
|
|
|
/**
|
|
* Changes the player source.
|
|
* @param {?string} contentSrc The URI for the content to be played. Leave
|
|
* blank to use the existing content.
|
|
*/
|
|
PlayerWrapper.prototype.changeSource = function (contentSrc) {
|
|
// Only try to pause the player when initialised with a source already
|
|
if (this.vjsPlayer.currentSrc()) {
|
|
this.vjsPlayer.currentTime(0);
|
|
this.vjsPlayer.pause();
|
|
}
|
|
if (contentSrc) {
|
|
this.vjsPlayer.src(contentSrc);
|
|
}
|
|
this.vjsPlayer.one('loadedmetadata', this.seekContentToZero.bind(this));
|
|
};
|
|
|
|
/**
|
|
* Seeks content to 00:00:00. This is used as an event handler for the
|
|
* loadedmetadata event, since seeking is not possible until that event has
|
|
* fired.
|
|
*/
|
|
PlayerWrapper.prototype.seekContentToZero = function () {
|
|
this.vjsPlayer.currentTime(0);
|
|
};
|
|
|
|
/**
|
|
* Triggers an event on the VJS player
|
|
* @param {string} name The event name.
|
|
* @param {Object} data The event data.
|
|
*/
|
|
PlayerWrapper.prototype.triggerPlayerEvent = function (name, data) {
|
|
this.vjsPlayer.trigger(name, data);
|
|
};
|
|
|
|
/**
|
|
* Listener JSDoc for ESLint. This listener can be passed to
|
|
* addContentEndedListener.
|
|
* @callback listener
|
|
*/
|
|
|
|
/**
|
|
* Adds a listener for the 'contentended' event of the video player. This should
|
|
* be used instead of setting an 'contentended' listener directly to ensure that
|
|
* the ima can do proper cleanup of the SDK before other event listeners are
|
|
* called.
|
|
* @param {listener} listener The listener to be called when content
|
|
* completes.
|
|
*/
|
|
PlayerWrapper.prototype.addContentEndedListener = function (listener) {
|
|
this.contentEndedListeners.push(listener);
|
|
};
|
|
|
|
/**
|
|
* Reset the player.
|
|
*/
|
|
PlayerWrapper.prototype.reset = function () {
|
|
// Attempts to remove the contentEndedListener before adding it.
|
|
// This is to prevent an error where an erroring video caused multiple
|
|
// contentEndedListeners to be added.
|
|
this.vjsPlayer.off('contentended', this.boundContentEndedListener);
|
|
|
|
this.vjsPlayer.on('contentended', this.boundContentEndedListener);
|
|
this.vjsControls.show();
|
|
if (this.vjsPlayer.ads.inAdBreak()) {
|
|
this.vjsPlayer.ads.endLinearAdMode();
|
|
}
|
|
// Reset the content time we give the SDK. Fixes an issue where requesting
|
|
// VMAP followed by VMAP would play the second mid-rolls as pre-rolls if
|
|
// the first playthrough of the video passed the second response's
|
|
// mid-roll time.
|
|
this.contentPlayheadTracker.currentTime = 0;
|
|
this.contentComplete = false;
|
|
};
|
|
|
|
/**
|
|
* Copyright 2017 Google Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* IMA SDK integration plugin for Video.js. For more information see
|
|
* https://www.github.com/googleads/videojs-ima
|
|
*/
|
|
|
|
/**
|
|
* Ad UI implementation.
|
|
*
|
|
* @param {Controller} controller Plugin controller.
|
|
* @constructor
|
|
* @struct
|
|
* @final
|
|
*/
|
|
var AdUi = function AdUi(controller) {
|
|
/**
|
|
* Plugin controller.
|
|
*/
|
|
this.controller = controller;
|
|
|
|
/**
|
|
* Div used as an ad container.
|
|
*/
|
|
this.adContainerDiv = document.createElement('div');
|
|
|
|
/**
|
|
* Div used to display ad controls.
|
|
*/
|
|
this.controlsDiv = document.createElement('div');
|
|
|
|
/**
|
|
* Div used to display ad countdown timer.
|
|
*/
|
|
this.countdownDiv = document.createElement('div');
|
|
|
|
/**
|
|
* Div used to display add seek bar.
|
|
*/
|
|
this.seekBarDiv = document.createElement('div');
|
|
|
|
/**
|
|
* Div used to display ad progress (in seek bar).
|
|
*/
|
|
this.progressDiv = document.createElement('div');
|
|
|
|
/**
|
|
* Div used to display ad play/pause button.
|
|
*/
|
|
this.playPauseDiv = document.createElement('div');
|
|
|
|
/**
|
|
* Div used to display ad mute button.
|
|
*/
|
|
this.muteDiv = document.createElement('div');
|
|
|
|
/**
|
|
* Div used by the volume slider.
|
|
*/
|
|
this.sliderDiv = document.createElement('div');
|
|
|
|
/**
|
|
* Volume slider level visuals
|
|
*/
|
|
this.sliderLevelDiv = document.createElement('div');
|
|
|
|
/**
|
|
* Div used to display ad fullscreen button.
|
|
*/
|
|
this.fullscreenDiv = document.createElement('div');
|
|
|
|
/**
|
|
* Bound event handler for onMouseUp.
|
|
*/
|
|
this.boundOnMouseUp = this.onMouseUp.bind(this);
|
|
|
|
/**
|
|
* Bound event handler for onMouseMove.
|
|
*/
|
|
this.boundOnMouseMove = this.onMouseMove.bind(this);
|
|
|
|
/**
|
|
* Stores data for the ad playhead tracker.
|
|
*/
|
|
this.adPlayheadTracker = {
|
|
'currentTime': 0,
|
|
'duration': 0,
|
|
'isPod': false,
|
|
'adPosition': 0,
|
|
'totalAds': 0
|
|
};
|
|
|
|
/**
|
|
* Used to prefix videojs ima controls.
|
|
*/
|
|
this.controlPrefix = this.controller.getPlayerId() + '_';
|
|
|
|
/**
|
|
* Boolean flag to show or hide the ad countdown timer.
|
|
*/
|
|
this.showCountdown = true;
|
|
if (this.controller.getSettings().showCountdown === false) {
|
|
this.showCountdown = false;
|
|
}
|
|
|
|
/**
|
|
* Boolean flag if the current ad is nonlinear.
|
|
*/
|
|
this.isAdNonlinear = false;
|
|
|
|
this.createAdContainer();
|
|
};
|
|
|
|
/**
|
|
* Creates the ad container.
|
|
*/
|
|
AdUi.prototype.createAdContainer = function () {
|
|
this.assignControlAttributes(this.adContainerDiv, 'ima-ad-container');
|
|
this.adContainerDiv.style.position = 'absolute';
|
|
this.adContainerDiv.style.zIndex = 1111;
|
|
this.adContainerDiv.addEventListener('mouseenter', this.showAdControls.bind(this), false);
|
|
this.adContainerDiv.addEventListener('mouseleave', this.hideAdControls.bind(this), false);
|
|
this.adContainerDiv.addEventListener('click', this.onAdContainerClick.bind(this), false);
|
|
this.createControls();
|
|
this.controller.injectAdContainerDiv(this.adContainerDiv);
|
|
};
|
|
|
|
/**
|
|
* Create the controls.
|
|
*/
|
|
AdUi.prototype.createControls = function () {
|
|
this.assignControlAttributes(this.controlsDiv, 'ima-controls-div');
|
|
this.controlsDiv.style.width = '100%';
|
|
|
|
if (!this.controller.getIsMobile()) {
|
|
this.assignControlAttributes(this.countdownDiv, 'ima-countdown-div');
|
|
this.countdownDiv.innerHTML = this.controller.getSettings().adLabel;
|
|
this.countdownDiv.style.display = this.showCountdown ? 'block' : 'none';
|
|
} else {
|
|
this.countdownDiv.style.display = 'none';
|
|
}
|
|
|
|
this.assignControlAttributes(this.seekBarDiv, 'ima-seek-bar-div');
|
|
this.seekBarDiv.style.width = '100%';
|
|
|
|
this.assignControlAttributes(this.progressDiv, 'ima-progress-div');
|
|
|
|
this.assignControlAttributes(this.playPauseDiv, 'ima-play-pause-div');
|
|
this.addClass(this.playPauseDiv, 'ima-playing');
|
|
this.playPauseDiv.addEventListener('click', this.onAdPlayPauseClick.bind(this), false);
|
|
|
|
this.assignControlAttributes(this.muteDiv, 'ima-mute-div');
|
|
this.addClass(this.muteDiv, 'ima-non-muted');
|
|
this.muteDiv.addEventListener('click', this.onAdMuteClick.bind(this), false);
|
|
|
|
this.assignControlAttributes(this.sliderDiv, 'ima-slider-div');
|
|
this.sliderDiv.addEventListener('mousedown', this.onAdVolumeSliderMouseDown.bind(this), false);
|
|
|
|
// Hide volume slider controls on iOS as they aren't supported.
|
|
if (this.controller.getIsIos()) {
|
|
this.sliderDiv.style.display = 'none';
|
|
}
|
|
|
|
this.assignControlAttributes(this.sliderLevelDiv, 'ima-slider-level-div');
|
|
|
|
this.assignControlAttributes(this.fullscreenDiv, 'ima-fullscreen-div');
|
|
this.addClass(this.fullscreenDiv, 'ima-non-fullscreen');
|
|
this.fullscreenDiv.addEventListener('click', this.onAdFullscreenClick.bind(this), false);
|
|
|
|
this.adContainerDiv.appendChild(this.controlsDiv);
|
|
this.controlsDiv.appendChild(this.countdownDiv);
|
|
this.controlsDiv.appendChild(this.seekBarDiv);
|
|
this.controlsDiv.appendChild(this.playPauseDiv);
|
|
this.controlsDiv.appendChild(this.muteDiv);
|
|
this.controlsDiv.appendChild(this.sliderDiv);
|
|
this.controlsDiv.appendChild(this.fullscreenDiv);
|
|
this.seekBarDiv.appendChild(this.progressDiv);
|
|
this.sliderDiv.appendChild(this.sliderLevelDiv);
|
|
};
|
|
|
|
/**
|
|
* Listener for clicks on the play/pause button during ad playback.
|
|
*/
|
|
AdUi.prototype.onAdPlayPauseClick = function () {
|
|
this.controller.onAdPlayPauseClick();
|
|
};
|
|
|
|
/**
|
|
* Listener for clicks on the play/pause button during ad playback.
|
|
*/
|
|
AdUi.prototype.onAdMuteClick = function () {
|
|
this.controller.onAdMuteClick();
|
|
};
|
|
|
|
/**
|
|
* Listener for clicks on the fullscreen button during ad playback.
|
|
*/
|
|
AdUi.prototype.onAdFullscreenClick = function () {
|
|
this.controller.toggleFullscreen();
|
|
};
|
|
|
|
/**
|
|
* Show pause and hide play button
|
|
*/
|
|
AdUi.prototype.onAdsPaused = function () {
|
|
this.controller.sdkImpl.adPlaying = false;
|
|
this.addClass(this.playPauseDiv, 'ima-paused');
|
|
this.removeClass(this.playPauseDiv, 'ima-playing');
|
|
this.showAdControls();
|
|
};
|
|
|
|
/**
|
|
* Show pause and hide play button
|
|
*/
|
|
AdUi.prototype.onAdsResumed = function () {
|
|
this.onAdsPlaying();
|
|
this.showAdControls();
|
|
};
|
|
|
|
/**
|
|
* Show play and hide pause button
|
|
*/
|
|
AdUi.prototype.onAdsPlaying = function () {
|
|
this.controller.sdkImpl.adPlaying = true;
|
|
this.addClass(this.playPauseDiv, 'ima-playing');
|
|
this.removeClass(this.playPauseDiv, 'ima-paused');
|
|
};
|
|
|
|
/**
|
|
* Takes data from the controller to update the UI.
|
|
*
|
|
* @param {number} currentTime Current time of the ad.
|
|
* @param {number} remainingTime Remaining time of the ad.
|
|
* @param {number} duration Duration of the ad.
|
|
* @param {number} adPosition Index of the ad in the pod.
|
|
* @param {number} totalAds Total number of ads in the pod.
|
|
*/
|
|
AdUi.prototype.updateAdUi = function (currentTime, remainingTime, duration, adPosition, totalAds) {
|
|
// Update countdown timer data
|
|
var remainingMinutes = Math.floor(remainingTime / 60);
|
|
var remainingSeconds = Math.floor(remainingTime % 60);
|
|
if (remainingSeconds.toString().length < 2) {
|
|
remainingSeconds = '0' + remainingSeconds;
|
|
}
|
|
var podCount = ': ';
|
|
if (totalAds > 1) {
|
|
podCount = ' (' + adPosition + ' ' + this.controller.getSettings().adLabelNofN + ' ' + totalAds + '): ';
|
|
}
|
|
this.countdownDiv.innerHTML = this.controller.getSettings().adLabel + podCount + remainingMinutes + ':' + remainingSeconds;
|
|
|
|
// Update UI
|
|
var playProgressRatio = currentTime / duration;
|
|
var playProgressPercent = playProgressRatio * 100;
|
|
this.progressDiv.style.width = playProgressPercent + '%';
|
|
};
|
|
|
|
/**
|
|
* Handles UI changes when the ad is unmuted.
|
|
*/
|
|
AdUi.prototype.unmute = function () {
|
|
this.addClass(this.muteDiv, 'ima-non-muted');
|
|
this.removeClass(this.muteDiv, 'ima-muted');
|
|
this.sliderLevelDiv.style.width = this.controller.getPlayerVolume() * 100 + '%';
|
|
};
|
|
|
|
/**
|
|
* Handles UI changes when the ad is muted.
|
|
*/
|
|
AdUi.prototype.mute = function () {
|
|
this.addClass(this.muteDiv, 'ima-muted');
|
|
this.removeClass(this.muteDiv, 'ima-non-muted');
|
|
this.sliderLevelDiv.style.width = '0%';
|
|
};
|
|
|
|
/*
|
|
* Listener for mouse down events during ad playback. Used for volume.
|
|
*/
|
|
AdUi.prototype.onAdVolumeSliderMouseDown = function () {
|
|
document.addEventListener('mouseup', this.boundOnMouseUp, false);
|
|
document.addEventListener('mousemove', this.boundOnMouseMove, false);
|
|
};
|
|
|
|
/*
|
|
* Mouse movement listener used for volume slider.
|
|
*/
|
|
AdUi.prototype.onMouseMove = function (event) {
|
|
this.changeVolume(event);
|
|
};
|
|
|
|
/*
|
|
* Mouse release listener used for volume slider.
|
|
*/
|
|
AdUi.prototype.onMouseUp = function (event) {
|
|
this.changeVolume(event);
|
|
document.removeEventListener('mouseup', this.boundOnMouseUp);
|
|
document.removeEventListener('mousemove', this.boundOnMouseMove);
|
|
};
|
|
|
|
/*
|
|
* Utility function to set volume and associated UI
|
|
*/
|
|
AdUi.prototype.changeVolume = function (event) {
|
|
var percent = (event.clientX - this.sliderDiv.getBoundingClientRect().left) / this.sliderDiv.offsetWidth;
|
|
percent *= 100;
|
|
// Bounds value 0-100 if mouse is outside slider region.
|
|
percent = Math.min(Math.max(percent, 0), 100);
|
|
this.sliderLevelDiv.style.width = percent + '%';
|
|
if (this.percent == 0) {
|
|
this.addClass(this.muteDiv, 'ima-muted');
|
|
this.removeClass(this.muteDiv, 'ima-non-muted');
|
|
} else {
|
|
this.addClass(this.muteDiv, 'ima-non-muted');
|
|
this.removeClass(this.muteDiv, 'ima-muted');
|
|
}
|
|
this.controller.setVolume(percent / 100); // 0-1
|
|
};
|
|
|
|
/**
|
|
* Show the ad container.
|
|
*/
|
|
AdUi.prototype.showAdContainer = function () {
|
|
this.adContainerDiv.style.display = 'block';
|
|
};
|
|
|
|
/**
|
|
* Hide the ad container
|
|
*/
|
|
AdUi.prototype.hideAdContainer = function () {
|
|
this.adContainerDiv.style.display = 'none';
|
|
};
|
|
|
|
/**
|
|
* Handles clicks on the ad container
|
|
*/
|
|
AdUi.prototype.onAdContainerClick = function () {
|
|
if (this.isAdNonlinear) {
|
|
this.controller.togglePlayback();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Resets the state of the ad ui.
|
|
*/
|
|
AdUi.prototype.reset = function () {
|
|
this.hideAdContainer();
|
|
};
|
|
|
|
/**
|
|
* Handles ad errors.
|
|
*/
|
|
AdUi.prototype.onAdError = function () {
|
|
this.hideAdContainer();
|
|
};
|
|
|
|
/**
|
|
* Handles ad break starting.
|
|
*
|
|
* @param {Object} adEvent The event fired by the IMA SDK.
|
|
*/
|
|
AdUi.prototype.onAdBreakStart = function (adEvent) {
|
|
this.showAdContainer();
|
|
|
|
var contentType = adEvent.getAd().getContentType();
|
|
if (contentType === 'application/javascript' && !this.controller.getSettings().showControlsForJSAds) {
|
|
this.controlsDiv.style.display = 'none';
|
|
} else {
|
|
this.controlsDiv.style.display = 'block';
|
|
}
|
|
this.onAdsPlaying();
|
|
// Start with the ad controls minimized.
|
|
this.hideAdControls();
|
|
};
|
|
|
|
/**
|
|
* Handles ad break ending.
|
|
*/
|
|
AdUi.prototype.onAdBreakEnd = function () {
|
|
var currentAd = this.controller.getCurrentAd();
|
|
if (currentAd == null || // hide for post-roll only playlist
|
|
currentAd.isLinear()) {
|
|
// don't hide for non-linear ads
|
|
this.hideAdContainer();
|
|
}
|
|
this.controlsDiv.style.display = 'none';
|
|
this.countdownDiv.innerHTML = '';
|
|
};
|
|
|
|
/**
|
|
* Handles when all ads have finished playing.
|
|
*/
|
|
AdUi.prototype.onAllAdsCompleted = function () {
|
|
this.hideAdContainer();
|
|
};
|
|
|
|
/**
|
|
* Handles when a linear ad starts.
|
|
*/
|
|
AdUi.prototype.onLinearAdStart = function () {
|
|
// Don't bump container when controls are shown
|
|
this.removeClass(this.adContainerDiv, 'bumpable-ima-ad-container');
|
|
this.isAdNonlinear = false;
|
|
};
|
|
|
|
/**
|
|
* Handles when a non-linear ad starts.
|
|
*/
|
|
AdUi.prototype.onNonLinearAdLoad = function () {
|
|
// For non-linear ads that show after a linear ad. For linear ads, we show the
|
|
// ad container in onAdBreakStart to prevent blinking in pods.
|
|
this.adContainerDiv.style.display = 'block';
|
|
// Bump container when controls are shown
|
|
this.addClass(this.adContainerDiv, 'bumpable-ima-ad-container');
|
|
this.isAdNonlinear = true;
|
|
};
|
|
|
|
AdUi.prototype.onPlayerEnterFullscreen = function () {
|
|
this.addClass(this.fullscreenDiv, 'ima-fullscreen');
|
|
this.removeClass(this.fullscreenDiv, 'ima-non-fullscreen');
|
|
};
|
|
|
|
AdUi.prototype.onPlayerExitFullscreen = function () {
|
|
this.addClass(this.fullscreenDiv, 'ima-non-fullscreen');
|
|
this.removeClass(this.fullscreenDiv, 'ima-fullscreen');
|
|
};
|
|
|
|
/**
|
|
* Called when the player volume changes.
|
|
*
|
|
* @param {number} volume The new player volume.
|
|
*/
|
|
AdUi.prototype.onPlayerVolumeChanged = function (volume) {
|
|
if (volume == 0) {
|
|
this.addClass(this.muteDiv, 'ima-muted');
|
|
this.removeClass(this.muteDiv, 'ima-non-muted');
|
|
this.sliderLevelDiv.style.width = '0%';
|
|
} else {
|
|
this.addClass(this.muteDiv, 'ima-non-muted');
|
|
this.removeClass(this.muteDiv, 'ima-muted');
|
|
this.sliderLevelDiv.style.width = volume * 100 + '%';
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Shows ad controls on mouseover.
|
|
*/
|
|
AdUi.prototype.showAdControls = function () {
|
|
var _controller$getSettin = this.controller.getSettings(),
|
|
disableAdControls = _controller$getSettin.disableAdControls;
|
|
|
|
if (!disableAdControls) {
|
|
this.addClass(this.controlsDiv, 'ima-controls-div-showing');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Hide the ad controls.
|
|
*/
|
|
AdUi.prototype.hideAdControls = function () {
|
|
this.removeClass(this.controlsDiv, 'ima-controls-div-showing');
|
|
};
|
|
|
|
/**
|
|
* Assigns the unique id and class names to the given element as well as the
|
|
* style class.
|
|
* @param {HTMLElement} element Element that needs the controlName assigned.
|
|
* @param {string} controlName Control name to assign.
|
|
*/
|
|
AdUi.prototype.assignControlAttributes = function (element, controlName) {
|
|
element.id = this.controlPrefix + controlName;
|
|
element.className = this.controlPrefix + controlName + ' ' + controlName;
|
|
};
|
|
|
|
/**
|
|
* Returns a regular expression to test a string for the given className.
|
|
*
|
|
* @param {string} className The name of the class.
|
|
* @return {RegExp} The regular expression used to test for that class.
|
|
*/
|
|
AdUi.prototype.getClassRegexp = function (className) {
|
|
// Matches on
|
|
// (beginning of string OR NOT word char)
|
|
// classname
|
|
// (negative lookahead word char OR end of string)
|
|
return new RegExp('(^|[^A-Za-z-])' + className + '((?![A-Za-z-])|$)', 'gi');
|
|
};
|
|
|
|
/**
|
|
* Returns whether or not the provided element has the provied class in its
|
|
* className.
|
|
* @param {HTMLElement} element Element to tes.t
|
|
* @param {string} className Class to look for.
|
|
* @return {boolean} True if element has className in class list. False
|
|
* otherwise.
|
|
*/
|
|
AdUi.prototype.elementHasClass = function (element, className) {
|
|
var classRegexp = this.getClassRegexp(className);
|
|
return classRegexp.test(element.className);
|
|
};
|
|
|
|
/**
|
|
* Adds a class to the given element if it doesn't already have the class
|
|
* @param {HTMLElement} element Element to which the class will be added.
|
|
* @param {string} classToAdd Class to add.
|
|
*/
|
|
AdUi.prototype.addClass = function (element, classToAdd) {
|
|
element.className = element.className.trim() + ' ' + classToAdd;
|
|
};
|
|
|
|
/**
|
|
* Removes a class from the given element if it has the given class
|
|
*
|
|
* @param {HTMLElement} element Element from which the class will be removed.
|
|
* @param {string} classToRemove Class to remove.
|
|
*/
|
|
AdUi.prototype.removeClass = function (element, classToRemove) {
|
|
var classRegexp = this.getClassRegexp(classToRemove);
|
|
element.className = element.className.trim().replace(classRegexp, '');
|
|
};
|
|
|
|
/**
|
|
* @return {HTMLElement} The div for the ad container.
|
|
*/
|
|
AdUi.prototype.getAdContainerDiv = function () {
|
|
return this.adContainerDiv;
|
|
};
|
|
|
|
/**
|
|
* Changes the flag to show or hide the ad countdown timer.
|
|
*
|
|
* @param {boolean} showCountdownIn Show or hide the countdown timer.
|
|
*/
|
|
AdUi.prototype.setShowCountdown = function (showCountdownIn) {
|
|
this.showCountdown = showCountdownIn;
|
|
this.countdownDiv.style.display = this.showCountdown ? 'block' : 'none';
|
|
};
|
|
|
|
var name = "videojs-ima";
|
|
var version = "2.0.0";
|
|
var license = "Apache-2.0";
|
|
var main = "./dist/videojs.ima.js";
|
|
var module$1 = "./dist/videojs.ima.es.js";
|
|
var author = { "name": "Google Inc." };
|
|
var engines = { "node": ">=0.8.0" };
|
|
var scripts = { "contBuild": "watch 'npm run rollup:max' src", "predevServer": "echo \"Starting up server on localhost:8000.\"", "devServer": "npm-run-all -p testServer contBuild", "lint": "eslint \"src/**/*.js\"", "rollup": "npm-run-all rollup:*", "rollup:max": "rollup -c configs/rollup.config.js", "rollup:es": "rollup -c configs/rollup.config.es.js", "rollup:min": "rollup -c configs/rollup.config.min.js", "pretest": "npm run rollup", "start": "npm run devServer", "test": "npm-run-all test:*", "test:vjs6": "npm install video.js@6 --no-save && npm-run-all -p -r testServer webdriver", "test:vjs7": "npm install video.js@7 --no-save && npm-run-all -p -r testServer webdriver", "testServer": "http-server --cors -p 8000 --silent", "preversion": "node scripts/preversion.js && npm run lint && npm test", "version": "node scripts/version.js", "postversion": "node scripts/postversion.js", "webdriver": "mocha test/webdriver/*.js --no-timeouts" };
|
|
var repository = { "type": "git", "url": "https://github.com/googleads/videojs-ima" };
|
|
var files = ["CHANGELOG.md", "LICENSE", "README.md", "dist/", "src/"];
|
|
var peerDependencies = { "video.js": "^5.19.2 || ^6 || ^7" };
|
|
var dependencies = { "@hapi/cryptiles": "^5.1.0", "@videojs/http-streaming": "^2.10.0", "can-autoplay": "^3.0.2", "extend": ">=3.0.2", "lodash": ">=4.17.19", "lodash.template": ">=4.5.0", "videojs-contrib-ads": "^6.9.0" };
|
|
var devDependencies = { "axios": "^0.25.0", "babel-core": "^6.26.3", "babel-preset-env": "^1.7.0", "child_process": "^1.0.2", "chromedriver": "^99.0.0", "conventional-changelog-cli": "^2.2.2", "conventional-changelog-videojs": "^3.0.2", "ecstatic": "^4.1.4", "eslint": "^8.8.0", "eslint-config-google": "^0.9.1", "eslint-plugin-jsdoc": "^3.15.1", "geckodriver": "^2.0.4", "http-server": "^14.1.0", "ini": ">=1.3.7", "mocha": "^9.2.0", "npm-run-all": "^4.1.5", "path": "^0.12.7", "protractor": "^7.0.0", "rimraf": "^2.7.1", "rollup": "^0.51.8", "rollup-plugin-babel": "^3.0.7", "rollup-plugin-copy": "^0.2.3", "rollup-plugin-json": "^2.3.1", "rollup-plugin-uglify": "^2.0.1", "selenium-webdriver": "^3.6.0", "uglify-es": "^3.3.9", "video.js": "^7.17.0", "watch": "^0.13.0", "webdriver-manager": "^12.1.7", "xmldom": "^0.6.0" };
|
|
var keywords = ["videojs", "videojs-plugin"];
|
|
var pkg = {
|
|
name: name,
|
|
version: version,
|
|
license: license,
|
|
main: main,
|
|
module: module$1,
|
|
author: author,
|
|
engines: engines,
|
|
scripts: scripts,
|
|
repository: repository,
|
|
files: files,
|
|
peerDependencies: peerDependencies,
|
|
dependencies: dependencies,
|
|
devDependencies: devDependencies,
|
|
keywords: keywords
|
|
};
|
|
|
|
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
|
|
/**
|
|
* Copyright 2017 Google Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* IMA SDK integration plugin for Video.js. For more information see
|
|
* https://www.github.com/googleads/videojs-ima
|
|
*/
|
|
|
|
/**
|
|
* Implementation of the IMA SDK for the plugin.
|
|
*
|
|
* @param {Object} controller Reference to the parent controller.
|
|
*
|
|
* @constructor
|
|
* @struct
|
|
* @final
|
|
*/
|
|
var SdkImpl = function SdkImpl(controller) {
|
|
/**
|
|
* Plugin controller.
|
|
*/
|
|
this.controller = controller;
|
|
|
|
/**
|
|
* IMA SDK AdDisplayContainer.
|
|
*/
|
|
this.adDisplayContainer = null;
|
|
|
|
/**
|
|
* True if the AdDisplayContainer has been initialized. False otherwise.
|
|
*/
|
|
this.adDisplayContainerInitialized = false;
|
|
|
|
/**
|
|
* IMA SDK AdsLoader
|
|
*/
|
|
this.adsLoader = null;
|
|
|
|
/**
|
|
* IMA SDK AdsManager
|
|
*/
|
|
this.adsManager = null;
|
|
|
|
/**
|
|
* IMA SDK AdsRenderingSettings.
|
|
*/
|
|
this.adsRenderingSettings = null;
|
|
|
|
/**
|
|
* VAST, VMAP, or ad rules response. Used in lieu of fetching a response
|
|
* from an ad tag URL.
|
|
*/
|
|
this.adsResponse = null;
|
|
|
|
/**
|
|
* Current IMA SDK Ad.
|
|
*/
|
|
this.currentAd = null;
|
|
|
|
/**
|
|
* Timer used to track ad progress.
|
|
*/
|
|
this.adTrackingTimer = null;
|
|
|
|
/**
|
|
* True if ALL_ADS_COMPLETED has fired, false until then.
|
|
*/
|
|
this.allAdsCompleted = false;
|
|
|
|
/**
|
|
* True if ads are currently displayed, false otherwise.
|
|
* True regardless of ad pause state if an ad is currently being displayed.
|
|
*/
|
|
this.adsActive = false;
|
|
|
|
/**
|
|
* True if ad is currently playing, false if ad is paused or ads are not
|
|
* currently displayed.
|
|
*/
|
|
this.adPlaying = false;
|
|
|
|
/**
|
|
* True if the ad is muted, false otherwise.
|
|
*/
|
|
this.adMuted = false;
|
|
|
|
/**
|
|
* Listener to be called to trigger manual ad break playback.
|
|
*/
|
|
this.adBreakReadyListener = undefined;
|
|
|
|
/**
|
|
* Tracks whether or not we have already called adsLoader.contentComplete().
|
|
*/
|
|
this.contentCompleteCalled = false;
|
|
|
|
/**
|
|
* True if the ad has timed out.
|
|
*/
|
|
this.isAdTimedOut = false;
|
|
|
|
/**
|
|
* Stores the dimensions for the ads manager.
|
|
*/
|
|
this.adsManagerDimensions = {
|
|
width: 0,
|
|
height: 0
|
|
};
|
|
|
|
/**
|
|
* Boolean flag to enable manual ad break playback.
|
|
*/
|
|
this.autoPlayAdBreaks = true;
|
|
if (this.controller.getSettings().autoPlayAdBreaks === false) {
|
|
this.autoPlayAdBreaks = false;
|
|
}
|
|
|
|
// Set SDK settings from plugin settings.
|
|
if (this.controller.getSettings().locale) {
|
|
/* eslint no-undef: 'error' */
|
|
/* global google */
|
|
google.ima.settings.setLocale(this.controller.getSettings().locale);
|
|
}
|
|
if (this.controller.getSettings().disableFlashAds) {
|
|
google.ima.settings.setDisableFlashAds(this.controller.getSettings().disableFlashAds);
|
|
}
|
|
if (this.controller.getSettings().disableCustomPlaybackForIOS10Plus) {
|
|
google.ima.settings.setDisableCustomPlaybackForIOS10Plus(this.controller.getSettings().disableCustomPlaybackForIOS10Plus);
|
|
}
|
|
|
|
if (this.controller.getSettings().ppid) {
|
|
google.ima.settings.setPpid(this.controller.getSettings().ppid);
|
|
}
|
|
|
|
if (this.controller.getSettings().featureFlags) {
|
|
google.ima.settings.setFeatureFlags(this.controller.getSettings().featureFlags);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Creates and initializes the IMA SDK objects.
|
|
*/
|
|
SdkImpl.prototype.initAdObjects = function () {
|
|
this.adDisplayContainer = new google.ima.AdDisplayContainer(this.controller.getAdContainerDiv(), this.controller.getContentPlayer());
|
|
|
|
this.adsLoader = new google.ima.AdsLoader(this.adDisplayContainer);
|
|
|
|
this.adsLoader.getSettings().setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.ENABLED);
|
|
if (this.controller.getSettings().vpaidAllowed == false) {
|
|
this.adsLoader.getSettings().setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.DISABLED);
|
|
}
|
|
if (this.controller.getSettings().vpaidMode !== undefined) {
|
|
this.adsLoader.getSettings().setVpaidMode(this.controller.getSettings().vpaidMode);
|
|
}
|
|
|
|
if (this.controller.getSettings().locale) {
|
|
this.adsLoader.getSettings().setLocale(this.controller.getSettings().locale);
|
|
}
|
|
|
|
if (this.controller.getSettings().numRedirects) {
|
|
this.adsLoader.getSettings().setNumRedirects(this.controller.getSettings().numRedirects);
|
|
}
|
|
|
|
if (this.controller.getSettings().sessionId) {
|
|
this.adsLoader.getSettings().setSessionId(this.controller.getSettings().sessionId);
|
|
}
|
|
|
|
this.adsLoader.getSettings().setPlayerType('videojs-ima');
|
|
this.adsLoader.getSettings().setPlayerVersion(pkg.version);
|
|
this.adsLoader.getSettings().setAutoPlayAdBreaks(this.autoPlayAdBreaks);
|
|
|
|
this.adsLoader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, this.onAdsManagerLoaded.bind(this), false);
|
|
this.adsLoader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdsLoaderError.bind(this), false);
|
|
|
|
this.controller.playerWrapper.vjsPlayer.trigger({
|
|
type: 'ads-loader',
|
|
adsLoader: this.adsLoader
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Creates the AdsRequest and request ads through the AdsLoader.
|
|
*/
|
|
SdkImpl.prototype.requestAds = function () {
|
|
var adsRequest = new google.ima.AdsRequest();
|
|
if (this.controller.getSettings().adTagUrl) {
|
|
adsRequest.adTagUrl = this.controller.getSettings().adTagUrl;
|
|
} else {
|
|
adsRequest.adsResponse = this.controller.getSettings().adsResponse;
|
|
}
|
|
if (this.controller.getSettings().forceNonLinearFullSlot) {
|
|
adsRequest.forceNonLinearFullSlot = true;
|
|
}
|
|
|
|
if (this.controller.getSettings().vastLoadTimeout) {
|
|
adsRequest.vastLoadTimeout = this.controller.getSettings().vastLoadTimeout;
|
|
}
|
|
|
|
if (this.controller.getSettings().omidMode) {
|
|
adsRequest.omidAccessModeRules = {};
|
|
var omidValues = this.controller.getSettings().omidMode;
|
|
|
|
if (omidValues.FULL) {
|
|
adsRequest.omidAccessModeRules[google.ima.OmidAccessMode.FULL] = omidValues.FULL;
|
|
}
|
|
if (omidValues.DOMAIN) {
|
|
adsRequest.omidAccessModeRules[google.ima.OmidAccessMode.DOMAIN] = omidValues.DOMAIN;
|
|
}
|
|
if (omidValues.LIMITED) {
|
|
adsRequest.omidAccessModeRules[google.ima.OmidAccessMode.LIMITED] = omidValues.LIMITED;
|
|
}
|
|
}
|
|
|
|
adsRequest.linearAdSlotWidth = this.controller.getPlayerWidth();
|
|
adsRequest.linearAdSlotHeight = this.controller.getPlayerHeight();
|
|
adsRequest.nonLinearAdSlotWidth = this.controller.getSettings().nonLinearWidth || this.controller.getPlayerWidth();
|
|
adsRequest.nonLinearAdSlotHeight = this.controller.getSettings().nonLinearHeight || this.controller.getPlayerHeight();
|
|
adsRequest.setAdWillAutoPlay(this.controller.adsWillAutoplay());
|
|
adsRequest.setAdWillPlayMuted(this.controller.adsWillPlayMuted());
|
|
|
|
// Populate the adsRequestproperties with those provided in the AdsRequest
|
|
// object in the settings.
|
|
var providedAdsRequest = this.controller.getSettings().adsRequest;
|
|
if (providedAdsRequest && (typeof providedAdsRequest === 'undefined' ? 'undefined' : _typeof(providedAdsRequest)) === 'object') {
|
|
Object.keys(providedAdsRequest).forEach(function (key) {
|
|
adsRequest[key] = providedAdsRequest[key];
|
|
});
|
|
}
|
|
|
|
this.adsLoader.requestAds(adsRequest);
|
|
this.controller.playerWrapper.vjsPlayer.trigger({
|
|
type: 'ads-request',
|
|
AdsRequest: adsRequest
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Listener for the ADS_MANAGER_LOADED event. Creates the AdsManager,
|
|
* sets up event listeners, and triggers the 'adsready' event for
|
|
* videojs-ads-contrib.
|
|
*
|
|
* @param {google.ima.AdsManagerLoadedEvent} adsManagerLoadedEvent Fired when
|
|
* the AdsManager loads.
|
|
*/
|
|
SdkImpl.prototype.onAdsManagerLoaded = function (adsManagerLoadedEvent) {
|
|
this.createAdsRenderingSettings();
|
|
|
|
this.adsManager = adsManagerLoadedEvent.getAdsManager(this.controller.getContentPlayheadTracker(), this.adsRenderingSettings);
|
|
|
|
this.adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdError.bind(this));
|
|
this.adsManager.addEventListener(google.ima.AdEvent.Type.AD_BREAK_READY, this.onAdBreakReady.bind(this));
|
|
this.adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, this.onContentPauseRequested.bind(this));
|
|
this.adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, this.onContentResumeRequested.bind(this));
|
|
this.adsManager.addEventListener(google.ima.AdEvent.Type.ALL_ADS_COMPLETED, this.onAllAdsCompleted.bind(this));
|
|
|
|
this.adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, this.onAdLoaded.bind(this));
|
|
this.adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, this.onAdStarted.bind(this));
|
|
this.adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, this.onAdComplete.bind(this));
|
|
this.adsManager.addEventListener(google.ima.AdEvent.Type.SKIPPED, this.onAdComplete.bind(this));
|
|
this.adsManager.addEventListener(google.ima.AdEvent.Type.LOG, this.onAdLog.bind(this));
|
|
this.adsManager.addEventListener(google.ima.AdEvent.Type.PAUSED, this.onAdPaused.bind(this));
|
|
this.adsManager.addEventListener(google.ima.AdEvent.Type.RESUMED, this.onAdResumed.bind(this));
|
|
|
|
this.controller.playerWrapper.vjsPlayer.trigger({
|
|
type: 'ads-manager',
|
|
adsManager: this.adsManager
|
|
});
|
|
|
|
if (!this.autoPlayAdBreaks) {
|
|
this.initAdsManager();
|
|
}
|
|
|
|
var _controller$getSettin = this.controller.getSettings(),
|
|
preventLateAdStart = _controller$getSettin.preventLateAdStart;
|
|
|
|
if (!preventLateAdStart) {
|
|
this.controller.onAdsReady();
|
|
} else if (preventLateAdStart && !this.isAdTimedOut) {
|
|
this.controller.onAdsReady();
|
|
}
|
|
|
|
if (this.controller.getSettings().adsManagerLoadedCallback) {
|
|
this.controller.getSettings().adsManagerLoadedCallback();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Listener for errors fired by the AdsLoader.
|
|
* @param {google.ima.AdErrorEvent} event The error event thrown by the
|
|
* AdsLoader. See
|
|
* https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/reference/js/google.ima.AdError#.Type
|
|
*/
|
|
SdkImpl.prototype.onAdsLoaderError = function (event) {
|
|
window.console.warn('AdsLoader error: ' + event.getError());
|
|
this.controller.onErrorLoadingAds(event);
|
|
if (this.adsManager) {
|
|
this.adsManager.destroy();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Initialize the ads manager.
|
|
*/
|
|
SdkImpl.prototype.initAdsManager = function () {
|
|
try {
|
|
var initWidth = this.controller.getPlayerWidth();
|
|
var initHeight = this.controller.getPlayerHeight();
|
|
this.adsManagerDimensions.width = initWidth;
|
|
this.adsManagerDimensions.height = initHeight;
|
|
this.adsManager.init(initWidth, initHeight, google.ima.ViewMode.NORMAL);
|
|
this.adsManager.setVolume(this.controller.getPlayerVolume());
|
|
this.initializeAdDisplayContainer();
|
|
} catch (adError) {
|
|
this.onAdError(adError);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Create AdsRenderingSettings for the IMA SDK.
|
|
*/
|
|
SdkImpl.prototype.createAdsRenderingSettings = function () {
|
|
this.adsRenderingSettings = new google.ima.AdsRenderingSettings();
|
|
this.adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;
|
|
if (this.controller.getSettings().adsRenderingSettings) {
|
|
for (var setting in this.controller.getSettings().adsRenderingSettings) {
|
|
if (setting !== '') {
|
|
this.adsRenderingSettings[setting] = this.controller.getSettings().adsRenderingSettings[setting];
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Listener for errors thrown by the AdsManager.
|
|
* @param {google.ima.AdErrorEvent} adErrorEvent The error event thrown by
|
|
* the AdsManager.
|
|
*/
|
|
SdkImpl.prototype.onAdError = function (adErrorEvent) {
|
|
var errorMessage = adErrorEvent.getError !== undefined ? adErrorEvent.getError() : adErrorEvent.stack;
|
|
window.console.warn('Ad error: ' + errorMessage);
|
|
|
|
this.adsManager.destroy();
|
|
this.controller.onAdError(adErrorEvent);
|
|
|
|
// reset these so consumers don't think we are still in an ad break,
|
|
// but reset them after any prior cleanup happens
|
|
this.adsActive = false;
|
|
this.adPlaying = false;
|
|
};
|
|
|
|
/**
|
|
* Listener for AD_BREAK_READY. Passes event on to publisher's listener.
|
|
* @param {google.ima.AdEvent} adEvent AdEvent thrown by the AdsManager.
|
|
*/
|
|
SdkImpl.prototype.onAdBreakReady = function (adEvent) {
|
|
this.adBreakReadyListener(adEvent);
|
|
};
|
|
|
|
/**
|
|
* Pauses the content video and displays the ad container so ads can play.
|
|
* @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
|
|
*/
|
|
SdkImpl.prototype.onContentPauseRequested = function (adEvent) {
|
|
this.adsActive = true;
|
|
this.adPlaying = true;
|
|
this.controller.onAdBreakStart(adEvent);
|
|
};
|
|
|
|
/**
|
|
* Resumes content video and hides the ad container.
|
|
* @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
|
|
*/
|
|
SdkImpl.prototype.onContentResumeRequested = function (adEvent) {
|
|
this.adsActive = false;
|
|
this.adPlaying = false;
|
|
this.controller.onAdBreakEnd();
|
|
// Hide controls in case of future non-linear ads. They'll be unhidden in
|
|
// content_pause_requested.
|
|
};
|
|
|
|
/**
|
|
* Records that ads have completed and calls contentAndAdsEndedListeners
|
|
* if content is also complete.
|
|
* @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
|
|
*/
|
|
SdkImpl.prototype.onAllAdsCompleted = function (adEvent) {
|
|
this.allAdsCompleted = true;
|
|
this.controller.onAllAdsCompleted();
|
|
};
|
|
|
|
/**
|
|
* Starts the content video when a non-linear ad is loaded.
|
|
* @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
|
|
*/
|
|
SdkImpl.prototype.onAdLoaded = function (adEvent) {
|
|
if (!adEvent.getAd().isLinear()) {
|
|
this.controller.onNonLinearAdLoad();
|
|
this.controller.playContent();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Starts the interval timer to check the current ad time when an ad starts
|
|
* playing.
|
|
* @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
|
|
*/
|
|
SdkImpl.prototype.onAdStarted = function (adEvent) {
|
|
this.currentAd = adEvent.getAd();
|
|
if (this.currentAd.isLinear()) {
|
|
this.adTrackingTimer = setInterval(this.onAdPlayheadTrackerInterval.bind(this), 250);
|
|
this.controller.onLinearAdStart();
|
|
} else {
|
|
this.controller.onNonLinearAdStart();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Handles an ad click. Puts the player UI in a paused state.
|
|
*/
|
|
SdkImpl.prototype.onAdPaused = function () {
|
|
this.controller.onAdsPaused();
|
|
};
|
|
|
|
/**
|
|
* Syncs controls when an ad resumes.
|
|
* @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
|
|
*/
|
|
SdkImpl.prototype.onAdResumed = function (adEvent) {
|
|
this.controller.onAdsResumed();
|
|
};
|
|
|
|
/**
|
|
* Clears the interval timer for current ad time when an ad completes.
|
|
*/
|
|
SdkImpl.prototype.onAdComplete = function () {
|
|
if (this.currentAd.isLinear()) {
|
|
clearInterval(this.adTrackingTimer);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Handles ad log messages.
|
|
* @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
|
|
*/
|
|
SdkImpl.prototype.onAdLog = function (adEvent) {
|
|
this.controller.onAdLog(adEvent);
|
|
};
|
|
|
|
/**
|
|
* Gets the current time and duration of the ad and calls the method to
|
|
* update the ad UI.
|
|
*/
|
|
SdkImpl.prototype.onAdPlayheadTrackerInterval = function () {
|
|
if (this.adsManager === null) return;
|
|
var remainingTime = this.adsManager.getRemainingTime();
|
|
var duration = this.currentAd.getDuration();
|
|
var currentTime = duration - remainingTime;
|
|
currentTime = currentTime > 0 ? currentTime : 0;
|
|
var totalAds = 0;
|
|
var adPosition = void 0;
|
|
if (this.currentAd.getAdPodInfo()) {
|
|
adPosition = this.currentAd.getAdPodInfo().getAdPosition();
|
|
totalAds = this.currentAd.getAdPodInfo().getTotalAds();
|
|
}
|
|
|
|
this.controller.onAdPlayheadUpdated(currentTime, remainingTime, duration, adPosition, totalAds);
|
|
};
|
|
|
|
/**
|
|
* Called by the player wrapper when content completes.
|
|
*/
|
|
SdkImpl.prototype.onContentComplete = function () {
|
|
if (this.adsLoader) {
|
|
this.adsLoader.contentComplete();
|
|
this.contentCompleteCalled = true;
|
|
}
|
|
|
|
if (this.adsManager && this.adsManager.getCuePoints() && !this.adsManager.getCuePoints().includes(-1) || !this.adsManager) {
|
|
this.controller.onNoPostroll();
|
|
}
|
|
|
|
if (this.allAdsCompleted) {
|
|
this.controller.onContentAndAdsCompleted();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Called when the player is disposed.
|
|
*/
|
|
SdkImpl.prototype.onPlayerDisposed = function () {
|
|
if (this.adTrackingTimer) {
|
|
clearInterval(this.adTrackingTimer);
|
|
}
|
|
if (this.adsManager) {
|
|
this.adsManager.destroy();
|
|
this.adsManager = null;
|
|
}
|
|
};
|
|
|
|
SdkImpl.prototype.onPlayerReadyForPreroll = function () {
|
|
if (this.autoPlayAdBreaks) {
|
|
this.initAdsManager();
|
|
try {
|
|
this.controller.showAdContainer();
|
|
// Sync ad volume with content volume.
|
|
this.adsManager.setVolume(this.controller.getPlayerVolume());
|
|
this.adsManager.start();
|
|
} catch (adError) {
|
|
this.onAdError(adError);
|
|
}
|
|
}
|
|
};
|
|
|
|
SdkImpl.prototype.onAdTimeout = function () {
|
|
this.isAdTimedOut = true;
|
|
};
|
|
|
|
SdkImpl.prototype.onPlayerReady = function () {
|
|
this.initAdObjects();
|
|
|
|
if ((this.controller.getSettings().adTagUrl || this.controller.getSettings().adsResponse) && this.controller.getSettings().requestMode === 'onLoad') {
|
|
this.requestAds();
|
|
}
|
|
};
|
|
|
|
SdkImpl.prototype.onPlayerEnterFullscreen = function () {
|
|
if (this.adsManager) {
|
|
this.adsManager.resize(window.screen.width, window.screen.height, google.ima.ViewMode.FULLSCREEN);
|
|
}
|
|
};
|
|
|
|
SdkImpl.prototype.onPlayerExitFullscreen = function () {
|
|
if (this.adsManager) {
|
|
this.adsManager.resize(this.controller.getPlayerWidth(), this.controller.getPlayerHeight(), google.ima.ViewMode.NORMAL);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Called when the player volume changes.
|
|
*
|
|
* @param {number} volume The new player volume.
|
|
*/
|
|
SdkImpl.prototype.onPlayerVolumeChanged = function (volume) {
|
|
if (this.adsManager) {
|
|
this.adsManager.setVolume(volume);
|
|
}
|
|
|
|
if (volume == 0) {
|
|
this.adMuted = true;
|
|
} else {
|
|
this.adMuted = false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Called when the player wrapper detects that the player has been resized.
|
|
*
|
|
* @param {number} width The post-resize width of the player.
|
|
* @param {number} height The post-resize height of the player.
|
|
*/
|
|
SdkImpl.prototype.onPlayerResize = function (width, height) {
|
|
if (this.adsManager) {
|
|
this.adsManagerDimensions.width = width;
|
|
this.adsManagerDimensions.height = height;
|
|
/* global google */
|
|
/* eslint no-undef: 'error' */
|
|
this.adsManager.resize(width, height, google.ima.ViewMode.NORMAL);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @return {Object} The current ad.
|
|
*/
|
|
SdkImpl.prototype.getCurrentAd = function () {
|
|
return this.currentAd;
|
|
};
|
|
|
|
/**
|
|
* Listener JSDoc for ESLint. This listener can be passed to
|
|
* setAdBreakReadyListener.
|
|
* @callback listener
|
|
*/
|
|
|
|
/**
|
|
* Sets the listener to be called to trigger manual ad break playback.
|
|
* @param {listener} listener The listener to be called to trigger manual ad
|
|
* break playback.
|
|
*/
|
|
SdkImpl.prototype.setAdBreakReadyListener = function (listener) {
|
|
this.adBreakReadyListener = listener;
|
|
};
|
|
|
|
/**
|
|
* @return {boolean} True if an ad is currently playing. False otherwise.
|
|
*/
|
|
SdkImpl.prototype.isAdPlaying = function () {
|
|
return this.adPlaying;
|
|
};
|
|
|
|
/**
|
|
* @return {boolean} True if an ad is currently playing. False otherwise.
|
|
*/
|
|
SdkImpl.prototype.isAdMuted = function () {
|
|
return this.adMuted;
|
|
};
|
|
|
|
/**
|
|
* Pause ads.
|
|
*/
|
|
SdkImpl.prototype.pauseAds = function () {
|
|
this.adsManager.pause();
|
|
this.adPlaying = false;
|
|
};
|
|
|
|
/**
|
|
* Resume ads.
|
|
*/
|
|
SdkImpl.prototype.resumeAds = function () {
|
|
this.adsManager.resume();
|
|
this.adPlaying = true;
|
|
};
|
|
|
|
/**
|
|
* Unmute ads.
|
|
*/
|
|
SdkImpl.prototype.unmute = function () {
|
|
this.adsManager.setVolume(1);
|
|
this.adMuted = false;
|
|
};
|
|
|
|
/**
|
|
* Mute ads.
|
|
*/
|
|
SdkImpl.prototype.mute = function () {
|
|
this.adsManager.setVolume(0);
|
|
this.adMuted = true;
|
|
};
|
|
|
|
/**
|
|
* Set the volume of the ads. 0-1.
|
|
*
|
|
* @param {number} volume The new volume.
|
|
*/
|
|
SdkImpl.prototype.setVolume = function (volume) {
|
|
this.adsManager.setVolume(volume);
|
|
if (volume == 0) {
|
|
this.adMuted = true;
|
|
} else {
|
|
this.adMuted = false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Initializes the AdDisplayContainer. On mobile, this must be done as a
|
|
* result of user action.
|
|
*/
|
|
SdkImpl.prototype.initializeAdDisplayContainer = function () {
|
|
if (this.adDisplayContainer) {
|
|
if (!this.adDisplayContainerInitialized) {
|
|
this.adDisplayContainer.initialize();
|
|
this.adDisplayContainerInitialized = true;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Called by publishers in manual ad break playback mode to start an ad
|
|
* break.
|
|
*/
|
|
SdkImpl.prototype.playAdBreak = function () {
|
|
if (!this.autoPlayAdBreaks) {
|
|
this.controller.showAdContainer();
|
|
// Sync ad volume with content volume.
|
|
this.adsManager.setVolume(this.controller.getPlayerVolume());
|
|
this.adsManager.start();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Callback JSDoc for ESLint. This callback can be passed to addEventListener.
|
|
* @callback callback
|
|
*/
|
|
|
|
/**
|
|
* Ads an EventListener to the AdsManager. For a list of available events,
|
|
* see
|
|
* https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/reference/js/google.ima.AdEvent#.Type
|
|
* @param {google.ima.AdEvent.Type} event The AdEvent.Type for which to
|
|
* listen.
|
|
* @param {callback} callback The method to call when the event is fired.
|
|
*/
|
|
SdkImpl.prototype.addEventListener = function (event, callback) {
|
|
if (this.adsManager) {
|
|
this.adsManager.addEventListener(event, callback);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns the instance of the AdsManager.
|
|
* @return {google.ima.AdsManager} The AdsManager being used by the plugin.
|
|
*/
|
|
SdkImpl.prototype.getAdsManager = function () {
|
|
return this.adsManager;
|
|
};
|
|
|
|
/**
|
|
* Reset the SDK implementation.
|
|
*/
|
|
SdkImpl.prototype.reset = function () {
|
|
this.adsActive = false;
|
|
this.adPlaying = false;
|
|
if (this.adTrackingTimer) {
|
|
// If this is called while an ad is playing, stop trying to get that
|
|
// ad's current time.
|
|
clearInterval(this.adTrackingTimer);
|
|
}
|
|
if (this.adsManager) {
|
|
this.adsManager.destroy();
|
|
this.adsManager = null;
|
|
}
|
|
if (this.adsLoader && !this.contentCompleteCalled) {
|
|
this.adsLoader.contentComplete();
|
|
}
|
|
this.contentCompleteCalled = false;
|
|
this.allAdsCompleted = false;
|
|
};
|
|
|
|
/**
|
|
* Copyright 2017 Google Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* IMA SDK integration plugin for Video.js. For more information see
|
|
* https://www.github.com/googleads/videojs-ima
|
|
*/
|
|
/**
|
|
* The grand coordinator of the plugin. Facilitates communication between all
|
|
* other plugin classes.
|
|
*
|
|
* @param {Object} player Instance of the video.js player.
|
|
* @param {Object} options Options provided by the implementation.
|
|
* @constructor
|
|
* @struct
|
|
* @final
|
|
*/
|
|
var Controller = function Controller(player, options) {
|
|
/**
|
|
* Stores user-provided settings.
|
|
* @type {Object}
|
|
*/
|
|
this.settings = {};
|
|
|
|
/**
|
|
* Content and ads ended listeners passed by the publisher to the plugin.
|
|
* These will be called when the plugin detects that content *and all
|
|
* ads* have completed. This differs from the contentEndedListeners in that
|
|
* contentEndedListeners will fire between content ending and a post-roll
|
|
* playing, whereas the contentAndAdsEndedListeners will fire after the
|
|
* post-roll completes.
|
|
*/
|
|
this.contentAndAdsEndedListeners = [];
|
|
|
|
/**
|
|
* Whether or not we are running on a mobile platform.
|
|
*/
|
|
this.isMobile = navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i) || navigator.userAgent.match(/Android/i);
|
|
|
|
/**
|
|
* Whether or not we are running on an iOS platform.
|
|
*/
|
|
this.isIos = navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i);
|
|
|
|
this.initWithSettings(options);
|
|
|
|
/**
|
|
* Stores contrib-ads default settings.
|
|
*/
|
|
var contribAdsDefaults = {
|
|
debug: this.settings.debug,
|
|
timeout: this.settings.timeout,
|
|
prerollTimeout: this.settings.prerollTimeout
|
|
};
|
|
var adsPluginSettings = this.extend({}, contribAdsDefaults, options.contribAdsSettings || {});
|
|
|
|
this.playerWrapper = new PlayerWrapper(player, adsPluginSettings, this);
|
|
this.adUi = new AdUi(this);
|
|
this.sdkImpl = new SdkImpl(this);
|
|
};
|
|
|
|
Controller.IMA_DEFAULTS = {
|
|
adLabel: 'Advertisement',
|
|
adLabelNofN: 'of',
|
|
debug: false,
|
|
disableAdControls: false,
|
|
prerollTimeout: 1000,
|
|
preventLateAdStart: false,
|
|
requestMode: 'onLoad',
|
|
showControlsForJSAds: true,
|
|
timeout: 5000
|
|
};
|
|
|
|
/**
|
|
* Extends the settings to include user-provided settings.
|
|
*
|
|
* @param {Object} options Options to be used in initialization.
|
|
*/
|
|
Controller.prototype.initWithSettings = function (options) {
|
|
this.settings = this.extend({}, Controller.IMA_DEFAULTS, options || {});
|
|
|
|
this.warnAboutDeprecatedSettings();
|
|
|
|
// Default showing countdown timer to true.
|
|
this.showCountdown = true;
|
|
if (this.settings.showCountdown === false) {
|
|
this.showCountdown = false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Logs console warnings when deprecated settings are used.
|
|
*/
|
|
Controller.prototype.warnAboutDeprecatedSettings = function () {
|
|
var _this = this;
|
|
|
|
var deprecatedSettings = ['adWillAutoplay', 'adsWillAutoplay', 'adWillPlayMuted', 'adsWillPlayMuted'];
|
|
deprecatedSettings.forEach(function (setting) {
|
|
if (_this.settings[setting] !== undefined) {
|
|
console.warn('WARNING: videojs.ima setting ' + setting + ' is deprecated');
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Return the settings object.
|
|
*
|
|
* @return {Object} The settings object.
|
|
*/
|
|
Controller.prototype.getSettings = function () {
|
|
return this.settings;
|
|
};
|
|
|
|
/**
|
|
* Return whether or not we're in a mobile environment.
|
|
*
|
|
* @return {boolean} True if running on mobile, false otherwise.
|
|
*/
|
|
Controller.prototype.getIsMobile = function () {
|
|
return this.isMobile;
|
|
};
|
|
|
|
/**
|
|
* Return whether or not we're in an iOS environment.
|
|
*
|
|
* @return {boolean} True if running on iOS, false otherwise.
|
|
*/
|
|
Controller.prototype.getIsIos = function () {
|
|
return this.isIos;
|
|
};
|
|
|
|
/**
|
|
* Inject the ad container div into the DOM.
|
|
*
|
|
* @param{HTMLElement} adContainerDiv The ad container div.
|
|
*/
|
|
Controller.prototype.injectAdContainerDiv = function (adContainerDiv) {
|
|
this.playerWrapper.injectAdContainerDiv(adContainerDiv);
|
|
};
|
|
|
|
/**
|
|
* @return {HTMLElement} The div for the ad container.
|
|
*/
|
|
Controller.prototype.getAdContainerDiv = function () {
|
|
return this.adUi.getAdContainerDiv();
|
|
};
|
|
|
|
/**
|
|
* @return {Object} The content player.
|
|
*/
|
|
Controller.prototype.getContentPlayer = function () {
|
|
return this.playerWrapper.getContentPlayer();
|
|
};
|
|
|
|
/**
|
|
* Returns the content playhead tracker.
|
|
*
|
|
* @return {Object} The content playhead tracker.
|
|
*/
|
|
Controller.prototype.getContentPlayheadTracker = function () {
|
|
return this.playerWrapper.getContentPlayheadTracker();
|
|
};
|
|
|
|
/**
|
|
* Requests ads.
|
|
*/
|
|
Controller.prototype.requestAds = function () {
|
|
this.sdkImpl.requestAds();
|
|
};
|
|
|
|
/**
|
|
* Add or modify a setting.
|
|
*
|
|
* @param {string} key Key to modify
|
|
* @param {Object} value Value to set at key.
|
|
*/
|
|
Controller.prototype.setSetting = function (key, value) {
|
|
this.settings[key] = value;
|
|
};
|
|
|
|
/**
|
|
* Called when there is an error loading ads.
|
|
*
|
|
* @param {Object} adErrorEvent The ad error event thrown by the IMA SDK.
|
|
*/
|
|
Controller.prototype.onErrorLoadingAds = function (adErrorEvent) {
|
|
this.adUi.onAdError();
|
|
this.playerWrapper.onAdError(adErrorEvent);
|
|
};
|
|
|
|
/**
|
|
* Called by the ad UI when the play/pause button is clicked.
|
|
*/
|
|
Controller.prototype.onAdPlayPauseClick = function () {
|
|
if (this.sdkImpl.isAdPlaying()) {
|
|
this.adUi.onAdsPaused();
|
|
this.sdkImpl.pauseAds();
|
|
} else {
|
|
this.adUi.onAdsPlaying();
|
|
this.sdkImpl.resumeAds();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Called by the ad UI when the mute button is clicked.
|
|
*
|
|
*/
|
|
Controller.prototype.onAdMuteClick = function () {
|
|
if (this.sdkImpl.isAdMuted()) {
|
|
this.playerWrapper.unmute();
|
|
this.adUi.unmute();
|
|
this.sdkImpl.unmute();
|
|
} else {
|
|
this.playerWrapper.mute();
|
|
this.adUi.mute();
|
|
this.sdkImpl.mute();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Set the volume of the player and ads. 0-1.
|
|
*
|
|
* @param {number} volume The new volume.
|
|
*/
|
|
Controller.prototype.setVolume = function (volume) {
|
|
this.playerWrapper.setVolume(volume);
|
|
this.sdkImpl.setVolume(volume);
|
|
};
|
|
|
|
/**
|
|
* @return {number} The volume of the content player.
|
|
*/
|
|
Controller.prototype.getPlayerVolume = function () {
|
|
return this.playerWrapper.getVolume();
|
|
};
|
|
|
|
/**
|
|
* Toggle fullscreen state.
|
|
*/
|
|
Controller.prototype.toggleFullscreen = function () {
|
|
this.playerWrapper.toggleFullscreen();
|
|
};
|
|
|
|
/**
|
|
* Relays ad errors to the player wrapper.
|
|
*
|
|
* @param {Object} adErrorEvent The ad error event thrown by the IMA SDK.
|
|
*/
|
|
Controller.prototype.onAdError = function (adErrorEvent) {
|
|
this.adUi.onAdError();
|
|
this.playerWrapper.onAdError(adErrorEvent);
|
|
};
|
|
|
|
/**
|
|
* Handles ad break starting.
|
|
*
|
|
* @param {Object} adEvent The event fired by the IMA SDK.
|
|
*/
|
|
Controller.prototype.onAdBreakStart = function (adEvent) {
|
|
this.playerWrapper.onAdBreakStart();
|
|
this.adUi.onAdBreakStart(adEvent);
|
|
};
|
|
|
|
/**
|
|
* Show the ad container.
|
|
*/
|
|
Controller.prototype.showAdContainer = function () {
|
|
this.adUi.showAdContainer();
|
|
};
|
|
|
|
/**
|
|
* Handles ad break ending.
|
|
*/
|
|
Controller.prototype.onAdBreakEnd = function () {
|
|
this.playerWrapper.onAdBreakEnd();
|
|
this.adUi.onAdBreakEnd();
|
|
};
|
|
|
|
/**
|
|
* Handles when all ads have finished playing.
|
|
*/
|
|
Controller.prototype.onAllAdsCompleted = function () {
|
|
this.adUi.onAllAdsCompleted();
|
|
this.playerWrapper.onAllAdsCompleted();
|
|
};
|
|
|
|
/**
|
|
* Handles the SDK firing an ad paused event.
|
|
*/
|
|
Controller.prototype.onAdsPaused = function () {
|
|
this.adUi.onAdsPaused();
|
|
};
|
|
|
|
/**
|
|
* Handles the SDK firing an ad resumed event.
|
|
*/
|
|
Controller.prototype.onAdsResumed = function () {
|
|
this.adUi.onAdsResumed();
|
|
};
|
|
|
|
/**
|
|
* Takes data from the sdk impl and passes it to the ad UI to update the UI.
|
|
*
|
|
* @param {number} currentTime Current time of the ad.
|
|
* @param {number} remainingTime Remaining time of the ad.
|
|
* @param {number} duration Duration of the ad.
|
|
* @param {number} adPosition Index of the ad in the pod.
|
|
* @param {number} totalAds Total number of ads in the pod.
|
|
*/
|
|
Controller.prototype.onAdPlayheadUpdated = function (currentTime, remainingTime, duration, adPosition, totalAds) {
|
|
this.adUi.updateAdUi(currentTime, remainingTime, duration, adPosition, totalAds);
|
|
};
|
|
|
|
/**
|
|
* Handles ad log messages.
|
|
* @param {google.ima.AdEvent} adEvent The AdEvent thrown by the IMA SDK.
|
|
*/
|
|
Controller.prototype.onAdLog = function (adEvent) {
|
|
this.playerWrapper.onAdLog(adEvent);
|
|
};
|
|
|
|
/**
|
|
* @return {Object} The current ad.
|
|
*/
|
|
Controller.prototype.getCurrentAd = function () {
|
|
return this.sdkImpl.getCurrentAd();
|
|
};
|
|
|
|
/**
|
|
* Play content.
|
|
*/
|
|
Controller.prototype.playContent = function () {
|
|
this.playerWrapper.play();
|
|
};
|
|
|
|
/**
|
|
* Handles when a linear ad starts.
|
|
*/
|
|
Controller.prototype.onLinearAdStart = function () {
|
|
this.adUi.onLinearAdStart();
|
|
this.playerWrapper.onAdStart();
|
|
};
|
|
|
|
/**
|
|
* Handles when a non-linear ad loads.
|
|
*/
|
|
Controller.prototype.onNonLinearAdLoad = function () {
|
|
this.adUi.onNonLinearAdLoad();
|
|
};
|
|
|
|
/**
|
|
* Handles when a non-linear ad starts.
|
|
*/
|
|
Controller.prototype.onNonLinearAdStart = function () {
|
|
this.adUi.onNonLinearAdLoad();
|
|
this.playerWrapper.onAdStart();
|
|
};
|
|
|
|
/**
|
|
* Get the player width.
|
|
*
|
|
* @return {number} The width of the player.
|
|
*/
|
|
Controller.prototype.getPlayerWidth = function () {
|
|
return this.playerWrapper.getPlayerWidth();
|
|
};
|
|
|
|
/**
|
|
* Get the player height.
|
|
*
|
|
* @return {number} The height of the player.
|
|
*/
|
|
Controller.prototype.getPlayerHeight = function () {
|
|
return this.playerWrapper.getPlayerHeight();
|
|
};
|
|
|
|
/**
|
|
* Tells the player wrapper that ads are ready.
|
|
*/
|
|
Controller.prototype.onAdsReady = function () {
|
|
this.playerWrapper.onAdsReady();
|
|
};
|
|
|
|
/**
|
|
* Called when the player wrapper detects that the player has been resized.
|
|
*
|
|
* @param {number} width The post-resize width of the player.
|
|
* @param {number} height The post-resize height of the player.
|
|
*/
|
|
Controller.prototype.onPlayerResize = function (width, height) {
|
|
this.sdkImpl.onPlayerResize(width, height);
|
|
};
|
|
|
|
/**
|
|
* Called by the player wrapper when content completes.
|
|
*/
|
|
Controller.prototype.onContentComplete = function () {
|
|
this.sdkImpl.onContentComplete();
|
|
};
|
|
|
|
/**
|
|
* Called by the player wrapper when it's time to play a post-roll but we don't
|
|
* have one to play.
|
|
*/
|
|
Controller.prototype.onNoPostroll = function () {
|
|
this.playerWrapper.onNoPostroll();
|
|
};
|
|
|
|
/**
|
|
* Called when content and all ads have completed.
|
|
*/
|
|
Controller.prototype.onContentAndAdsCompleted = function () {
|
|
for (var index in this.contentAndAdsEndedListeners) {
|
|
if (typeof this.contentAndAdsEndedListeners[index] === 'function') {
|
|
this.contentAndAdsEndedListeners[index]();
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Called when the player is disposed.
|
|
*/
|
|
Controller.prototype.onPlayerDisposed = function () {
|
|
this.contentAndAdsEndedListeners = [];
|
|
this.sdkImpl.onPlayerDisposed();
|
|
};
|
|
|
|
/**
|
|
* Called when the player is ready to play a pre-roll.
|
|
*/
|
|
Controller.prototype.onPlayerReadyForPreroll = function () {
|
|
this.sdkImpl.onPlayerReadyForPreroll();
|
|
};
|
|
|
|
/**
|
|
* Called if the ad times out.
|
|
*/
|
|
Controller.prototype.onAdTimeout = function () {
|
|
this.sdkImpl.onAdTimeout();
|
|
};
|
|
|
|
/**
|
|
* Called when the player is ready.
|
|
*/
|
|
Controller.prototype.onPlayerReady = function () {
|
|
this.sdkImpl.onPlayerReady();
|
|
};
|
|
|
|
/**
|
|
* Called when the player enters fullscreen.
|
|
*/
|
|
Controller.prototype.onPlayerEnterFullscreen = function () {
|
|
this.adUi.onPlayerEnterFullscreen();
|
|
this.sdkImpl.onPlayerEnterFullscreen();
|
|
};
|
|
|
|
/**
|
|
* Called when the player exits fullscreen.
|
|
*/
|
|
Controller.prototype.onPlayerExitFullscreen = function () {
|
|
this.adUi.onPlayerExitFullscreen();
|
|
this.sdkImpl.onPlayerExitFullscreen();
|
|
};
|
|
|
|
/**
|
|
* Called when the player volume changes.
|
|
*
|
|
* @param {number} volume The new player volume.
|
|
*/
|
|
Controller.prototype.onPlayerVolumeChanged = function (volume) {
|
|
this.adUi.onPlayerVolumeChanged(volume);
|
|
this.sdkImpl.onPlayerVolumeChanged(volume);
|
|
};
|
|
|
|
/**
|
|
* Sets the content of the video player. You should use this method instead
|
|
* of setting the content src directly to ensure the proper ad tag is
|
|
* requested when the video content is loaded.
|
|
* @param {?string} contentSrc The URI for the content to be played. Leave
|
|
* blank to use the existing content.
|
|
* @param {?string} adTag The ad tag to be requested when the content loads.
|
|
* Leave blank to use the existing ad tag.
|
|
*/
|
|
Controller.prototype.setContentWithAdTag = function (contentSrc, adTag) {
|
|
this.reset();
|
|
this.settings.adTagUrl = adTag ? adTag : this.settings.adTagUrl;
|
|
this.playerWrapper.changeSource(contentSrc);
|
|
};
|
|
|
|
/**
|
|
* Sets the content of the video player. You should use this method instead
|
|
* of setting the content src directly to ensure the proper ads response is
|
|
* used when the video content is loaded.
|
|
* @param {?string} contentSrc The URI for the content to be played. Leave
|
|
* blank to use the existing content.
|
|
* @param {?string} adsResponse The ads response to be requested when the
|
|
* content loads. Leave blank to use the existing ads response.
|
|
*/
|
|
Controller.prototype.setContentWithAdsResponse = function (contentSrc, adsResponse) {
|
|
this.reset();
|
|
this.settings.adsResponse = adsResponse ? adsResponse : this.settings.adsResponse;
|
|
this.playerWrapper.changeSource(contentSrc);
|
|
};
|
|
|
|
/**
|
|
* Sets the content of the video player. You should use this method instead
|
|
* of setting the content src directly to ensure the proper ads request is
|
|
* used when the video content is loaded.
|
|
* @param {?string} contentSrc The URI for the content to be played. Leave
|
|
* blank to use the existing content.
|
|
* @param {?Object} adsRequest The ads request to be requested when the
|
|
* content loads. Leave blank to use the existing ads request.
|
|
*/
|
|
Controller.prototype.setContentWithAdsRequest = function (contentSrc, adsRequest) {
|
|
this.reset();
|
|
this.settings.adsRequest = adsRequest ? adsRequest : this.settings.adsRequest;
|
|
this.playerWrapper.changeSource(contentSrc);
|
|
};
|
|
|
|
/**
|
|
* Resets the state of the plugin.
|
|
*/
|
|
Controller.prototype.reset = function () {
|
|
this.sdkImpl.reset();
|
|
this.playerWrapper.reset();
|
|
this.adUi.reset();
|
|
};
|
|
|
|
/**
|
|
* Listener JSDoc for ESLint. This listener can be passed to
|
|
* (add|remove)ContentEndedListener.
|
|
* @callback listener
|
|
*/
|
|
|
|
/**
|
|
* Adds a listener for the 'contentended' event of the video player. This should
|
|
* be used instead of setting an 'contentended' listener directly to ensure that
|
|
* the ima can do proper cleanup of the SDK before other event listeners are
|
|
* called.
|
|
* @param {listener} listener The listener to be called when content
|
|
* completes.
|
|
*/
|
|
Controller.prototype.addContentEndedListener = function (listener) {
|
|
this.playerWrapper.addContentEndedListener(listener);
|
|
};
|
|
|
|
/**
|
|
* Adds a listener that will be called when content and all ads have
|
|
* finished playing.
|
|
* @param {listener} listener The listener to be called when content and ads
|
|
* complete.
|
|
*/
|
|
Controller.prototype.addContentAndAdsEndedListener = function (listener) {
|
|
this.contentAndAdsEndedListeners.push(listener);
|
|
};
|
|
|
|
/**
|
|
* Sets the listener to be called to trigger manual ad break playback.
|
|
* @param {listener} listener The listener to be called to trigger manual ad
|
|
* break playback.
|
|
*/
|
|
Controller.prototype.setAdBreakReadyListener = function (listener) {
|
|
this.sdkImpl.setAdBreakReadyListener(listener);
|
|
};
|
|
|
|
/**
|
|
* Changes the flag to show or hide the ad countdown timer.
|
|
*
|
|
* @param {boolean} showCountdownIn Show or hide the countdown timer.
|
|
*/
|
|
Controller.prototype.setShowCountdown = function (showCountdownIn) {
|
|
this.adUi.setShowCountdown(showCountdownIn);
|
|
this.showCountdown = showCountdownIn;
|
|
this.adUi.countdownDiv.style.display = this.showCountdown ? 'block' : 'none';
|
|
};
|
|
|
|
/**
|
|
* Initializes the AdDisplayContainer. On mobile, this must be done as a
|
|
* result of user action.
|
|
*/
|
|
Controller.prototype.initializeAdDisplayContainer = function () {
|
|
this.sdkImpl.initializeAdDisplayContainer();
|
|
};
|
|
|
|
/**
|
|
* Called by publishers in manual ad break playback mode to start an ad
|
|
* break.
|
|
*/
|
|
Controller.prototype.playAdBreak = function () {
|
|
this.sdkImpl.playAdBreak();
|
|
};
|
|
|
|
/**
|
|
* Callback JSDoc for ESLint. This callback can be passed to addEventListener.
|
|
* @callback callback
|
|
*/
|
|
|
|
/**
|
|
* Adds an EventListener to the AdsManager. For a list of available events,
|
|
* see
|
|
* https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/reference/js/google.ima.AdEvent#.Type
|
|
* @param {google.ima.AdEvent.Type} event The AdEvent.Type for which to
|
|
* listen.
|
|
* @param {callback} callback The method to call when the event is fired.
|
|
*/
|
|
Controller.prototype.addEventListener = function (event, callback) {
|
|
this.sdkImpl.addEventListener(event, callback);
|
|
};
|
|
|
|
/**
|
|
* Returns the instance of the AdsManager.
|
|
* @return {google.ima.AdsManager} The AdsManager being used by the plugin.
|
|
*/
|
|
Controller.prototype.getAdsManager = function () {
|
|
return this.sdkImpl.getAdsManager();
|
|
};
|
|
|
|
/**
|
|
* Returns the instance of the player id.
|
|
* @return {string} The player id.
|
|
*/
|
|
Controller.prototype.getPlayerId = function () {
|
|
return this.playerWrapper.getPlayerId();
|
|
};
|
|
|
|
/**
|
|
* Changes the ad tag. You will need to call requestAds after this method
|
|
* for the new ads to be requested.
|
|
* @param {?string} adTag The ad tag to be requested the next time
|
|
* requestAds is called.
|
|
*/
|
|
Controller.prototype.changeAdTag = function (adTag) {
|
|
this.reset();
|
|
this.settings.adTagUrl = adTag;
|
|
};
|
|
|
|
/**
|
|
* Pauses the ad.
|
|
*/
|
|
Controller.prototype.pauseAd = function () {
|
|
this.adUi.onAdsPaused();
|
|
this.sdkImpl.pauseAds();
|
|
};
|
|
|
|
/**
|
|
* Resumes the ad.
|
|
*/
|
|
Controller.prototype.resumeAd = function () {
|
|
this.adUi.onAdsPlaying();
|
|
this.sdkImpl.resumeAds();
|
|
};
|
|
|
|
/**
|
|
* Toggles video/ad playback.
|
|
*/
|
|
Controller.prototype.togglePlayback = function () {
|
|
this.playerWrapper.togglePlayback();
|
|
};
|
|
|
|
/**
|
|
* @return {boolean} true if we expect that ads will autoplay. false otherwise.
|
|
*/
|
|
Controller.prototype.adsWillAutoplay = function () {
|
|
if (this.settings.adsWillAutoplay !== undefined) {
|
|
return this.settings.adsWillAutoplay;
|
|
} else if (this.settings.adWillAutoplay !== undefined) {
|
|
return this.settings.adWillAutoplay;
|
|
} else {
|
|
return !!this.playerWrapper.getPlayerOptions().autoplay;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @return {boolean} true if we expect that ads will autoplay. false otherwise.
|
|
*/
|
|
Controller.prototype.adsWillPlayMuted = function () {
|
|
if (this.settings.adsWillPlayMuted !== undefined) {
|
|
return this.settings.adsWillPlayMuted;
|
|
} else if (this.settings.adWillPlayMuted !== undefined) {
|
|
return this.settings.adWillPlayMuted;
|
|
} else if (this.playerWrapper.getPlayerOptions().muted !== undefined) {
|
|
return this.playerWrapper.getPlayerOptions().muted;
|
|
} else {
|
|
return this.playerWrapper.getVolume() == 0;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Triggers an event on the VJS player
|
|
* @param {string} name The event name.
|
|
* @param {Object} data The event data.
|
|
*/
|
|
Controller.prototype.triggerPlayerEvent = function (name, data) {
|
|
this.playerWrapper.triggerPlayerEvent(name, data);
|
|
};
|
|
|
|
/**
|
|
* Extends an object to include the contents of objects at parameters 2 onward.
|
|
*
|
|
* @param {Object} obj The object onto which the subsequent objects' parameters
|
|
* will be extended. This object will be modified.
|
|
* @param {...Object} var_args The objects whose properties are to be extended
|
|
* onto obj.
|
|
* @return {Object} The extended object.
|
|
*/
|
|
Controller.prototype.extend = function (obj) {
|
|
var arg = void 0;
|
|
var index = void 0;
|
|
var key = void 0;
|
|
|
|
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
args[_key - 1] = arguments[_key];
|
|
}
|
|
|
|
for (index = 0; index < args.length; index++) {
|
|
arg = args[index];
|
|
for (key in arg) {
|
|
if (arg.hasOwnProperty(key)) {
|
|
obj[key] = arg[key];
|
|
}
|
|
}
|
|
}
|
|
return obj;
|
|
};
|
|
|
|
/**
|
|
* Copyright 2021 Google Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* IMA SDK integration plugin for Video.js. For more information see
|
|
* https://www.github.com/googleads/videojs-ima
|
|
*/
|
|
|
|
/**
|
|
* Wraps the video.js stream player for the plugin.
|
|
*
|
|
* @param {!Object} player Video.js player instance.
|
|
* @param {!Object} adsPluginSettings Settings for the contrib-ads plugin.
|
|
* @param {!DaiController} daiController Reference to the parent controller.
|
|
*/
|
|
var PlayerWrapper$2 = function PlayerWrapper(player, adsPluginSettings, daiController) {
|
|
/**
|
|
* Instance of the video.js player.
|
|
*/
|
|
this.vjsPlayer = player;
|
|
|
|
/**
|
|
* Plugin DAI controller.
|
|
*/
|
|
this.daiController = daiController;
|
|
|
|
/**
|
|
* Video.js control bar.
|
|
*/
|
|
this.vjsControls = this.vjsPlayer.getChild('controlBar');
|
|
|
|
/**
|
|
* Vanilla HTML5 video player underneath the video.js player.
|
|
*/
|
|
this.h5Player = null;
|
|
|
|
this.vjsPlayer.on('dispose', this.playerDisposedListener.bind(this));
|
|
this.vjsPlayer.on('pause', this.onPause.bind(this));
|
|
this.vjsPlayer.on('play', this.onPlay.bind(this));
|
|
this.vjsPlayer.on('seeked', this.onSeekEnd.bind(this));
|
|
this.vjsPlayer.ready(this.onPlayerReady.bind(this));
|
|
this.vjsPlayer.ads(adsPluginSettings);
|
|
};
|
|
|
|
/**
|
|
* Called in response to the video.js player's 'disposed' event.
|
|
*/
|
|
PlayerWrapper$2.prototype.playerDisposedListener = function () {
|
|
this.contentEndedListeners = [];
|
|
this.daiController.onPlayerDisposed();
|
|
};
|
|
|
|
/**
|
|
* Called on the player 'pause' event. Handles displaying controls during
|
|
* paused ad breaks.
|
|
*/
|
|
PlayerWrapper$2.prototype.onPause = function () {
|
|
// This code will run if the stream is paused during an ad break. Since
|
|
// controls are usually hidden during ads, they will now show to allow
|
|
// users to resume ad playback.
|
|
if (this.daiController.isInAdBreak()) {
|
|
this.vjsControls.show();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Called on the player 'play' event. Handles hiding controls during
|
|
* ad breaks while playing.
|
|
*/
|
|
PlayerWrapper$2.prototype.onPlay = function () {
|
|
if (this.daiController.isInAdBreak()) {
|
|
this.vjsControls.hide();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Called on the player's 'seeked' event. Sets up handling for ad break
|
|
* snapback for VOD streams.
|
|
*/
|
|
PlayerWrapper$2.prototype.onSeekEnd = function () {
|
|
this.daiController.onSeekEnd(this.vjsPlayer.currentTime());
|
|
};
|
|
|
|
/**
|
|
* Called on the player's 'ready' event to begin initiating IMA.
|
|
*/
|
|
PlayerWrapper$2.prototype.onPlayerReady = function () {
|
|
this.h5Player = document.getElementById(this.getPlayerId()).getElementsByClassName('vjs-tech')[0];
|
|
this.daiController.onPlayerReady();
|
|
};
|
|
|
|
/**
|
|
* @return {!Object} The stream player.
|
|
*/
|
|
PlayerWrapper$2.prototype.getStreamPlayer = function () {
|
|
return this.h5Player;
|
|
};
|
|
|
|
/**
|
|
* @return {!Object} The video.js player.
|
|
*/
|
|
PlayerWrapper$2.prototype.getVjsPlayer = function () {
|
|
return this.vjsPlayer;
|
|
};
|
|
|
|
/**
|
|
* @return {!Object} The vjs player's options object.
|
|
*/
|
|
PlayerWrapper$2.prototype.getPlayerOptions = function () {
|
|
return this.vjsPlayer.options_;
|
|
};
|
|
|
|
/**
|
|
* Returns the instance of the player id.
|
|
* @return {string} The player id.
|
|
*/
|
|
PlayerWrapper$2.prototype.getPlayerId = function () {
|
|
return this.vjsPlayer.id();
|
|
};
|
|
|
|
/**
|
|
* Handles ad errors.
|
|
*
|
|
* @param {!Object} adErrorEvent The ad error event thrown by the IMA SDK.
|
|
*/
|
|
PlayerWrapper$2.prototype.onAdError = function (adErrorEvent) {
|
|
this.vjsControls.show();
|
|
var errorMessage = adErrorEvent.getError !== undefined ? adErrorEvent.getError() : adErrorEvent.stack;
|
|
this.vjsPlayer.trigger({ type: 'adserror', data: {
|
|
AdError: errorMessage,
|
|
AdErrorEvent: adErrorEvent
|
|
} });
|
|
};
|
|
|
|
/**
|
|
* Handles ad break starting.
|
|
*/
|
|
PlayerWrapper$2.prototype.onAdBreakStart = function () {
|
|
this.vjsControls.hide();
|
|
};
|
|
|
|
/**
|
|
* Handles ad break ending.
|
|
*/
|
|
PlayerWrapper$2.prototype.onAdBreakEnd = function () {
|
|
this.vjsControls.show();
|
|
};
|
|
|
|
/**
|
|
* Reset the player.
|
|
*/
|
|
PlayerWrapper$2.prototype.reset = function () {
|
|
this.vjsControls.show();
|
|
};
|
|
|
|
/**
|
|
* Copyright 2021 Google Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* IMA SDK integration plugin for Video.js. For more information see
|
|
* https://www.github.com/googleads/videojs-ima
|
|
*/
|
|
|
|
/**
|
|
* Implementation of the IMA DAI SDK for the plugin.
|
|
*
|
|
* @param {DaiController!} daiController Reference to the parent DAI controller.
|
|
*
|
|
* @constructor
|
|
* @struct
|
|
* @final
|
|
*/
|
|
var SdkImpl$2 = function SdkImpl(daiController) {
|
|
/**
|
|
* Plugin DAI controller.
|
|
*/
|
|
this.daiController = daiController;
|
|
|
|
/**
|
|
* The html5 stream player.
|
|
*/
|
|
this.streamPlayer = null;
|
|
|
|
/**
|
|
* The videoJS stream player.
|
|
*/
|
|
this.vjsPlayer = null;
|
|
|
|
/**
|
|
* IMA SDK StreamManager
|
|
*/
|
|
this.streamManager = null;
|
|
|
|
/**
|
|
* IMA stream UI settings.
|
|
*/
|
|
/* eslint no-undef: 'error' */
|
|
/* global google */
|
|
this.uiSettings = new google.ima.dai.api.UiSettings();
|
|
|
|
/**
|
|
* If the stream is currently in an ad break.
|
|
*/
|
|
this.isAdBreak = false;
|
|
|
|
/**
|
|
* If the stream is currently seeking from a snapback.
|
|
*/
|
|
this.isSnapback = false;
|
|
|
|
/**
|
|
* Originally seeked to time, to return stream to after ads.
|
|
*/
|
|
this.snapForwardTime = 0;
|
|
|
|
/**
|
|
* Timed metadata for the stream.
|
|
*/
|
|
this.timedMetadata;
|
|
|
|
/**
|
|
* Timed metadata record.
|
|
*/
|
|
this.metadataLoaded = {};
|
|
|
|
this.SOURCE_TYPES = {
|
|
hls: 'application/x-mpegURL',
|
|
dash: 'application/dash+xml'
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Creates and initializes the IMA DAI SDK objects.
|
|
*/
|
|
SdkImpl$2.prototype.initImaDai = function () {
|
|
this.streamPlayer = this.daiController.getStreamPlayer();
|
|
this.vjsPlayer = this.daiController.getVjsPlayer();
|
|
|
|
this.createAdUiDiv();
|
|
if (this.daiController.getSettings().locale) {
|
|
this.uiSettings.setLocale(this.daiController.getSettings().locale);
|
|
}
|
|
|
|
this.streamManager = new google.ima.dai.api.StreamManager(this.streamPlayer, this.adUiDiv, this.uiSettings);
|
|
|
|
this.streamPlayer.addEventListener('pause', this.onStreamPause);
|
|
this.streamPlayer.addEventListener('play', this.onStreamPlay);
|
|
|
|
this.streamManager.addEventListener([google.ima.dai.api.StreamEvent.Type.LOADED, google.ima.dai.api.StreamEvent.Type.ERROR, google.ima.dai.api.StreamEvent.Type.AD_BREAK_STARTED, google.ima.dai.api.StreamEvent.Type.AD_BREAK_ENDED], this.onStreamEvent.bind(this), false);
|
|
|
|
this.vjsPlayer.textTracks().onaddtrack = this.onAddTrack.bind(this);
|
|
|
|
this.vjsPlayer.trigger({
|
|
type: 'stream-manager',
|
|
StreamManager: this.streamManager
|
|
});
|
|
|
|
this.requestStream();
|
|
};
|
|
|
|
/**
|
|
* Called when the video player has metadata to process.
|
|
* @param {Event!} event The event that triggered this call.
|
|
*/
|
|
SdkImpl$2.prototype.onAddTrack = function (event) {
|
|
var _this = this;
|
|
|
|
var track = event.track;
|
|
console.log('TRACK', track);
|
|
if (track.kind === 'metadata') {
|
|
track.mode = 'hidden';
|
|
track.oncuechange = function (e) {
|
|
var _iteratorNormalCompletion = true;
|
|
var _didIteratorError = false;
|
|
var _iteratorError = undefined;
|
|
|
|
try {
|
|
for (var _iterator = track.activeCues_[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
var cue = _step.value;
|
|
|
|
var metadata = {};
|
|
metadata[cue.value.key] = cue.value.data;
|
|
_this.streamManager.onTimedMetadata(metadata);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError = true;
|
|
_iteratorError = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
_iterator.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError) {
|
|
throw _iteratorError;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Creates the ad UI container.
|
|
*/
|
|
SdkImpl$2.prototype.createAdUiDiv = function () {
|
|
var uiDiv = document.createElement('div');
|
|
uiDiv.id = 'ad-ui';
|
|
// 3em is the height of the control bar.
|
|
uiDiv.style.height = 'calc(100% - 3em)';
|
|
this.streamPlayer.parentNode.appendChild(uiDiv);
|
|
this.adUiDiv = uiDiv;
|
|
};
|
|
|
|
/**
|
|
* Called on pause to update the ad UI.
|
|
*/
|
|
SdkImpl$2.prototype.onStreamPause = function () {
|
|
if (this.isAdBreak) {
|
|
this.adUiDiv.style.display = 'none';
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Called on play to update the ad UI.
|
|
*/
|
|
SdkImpl$2.prototype.onStreamPlay = function () {
|
|
if (this.isAdBreak) {
|
|
this.adUiDiv.style.display = 'block';
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Called on play to update the ad UI.
|
|
* @param {number} currentTime the current time of the stream.
|
|
*/
|
|
SdkImpl$2.prototype.onSeekEnd = function (currentTime) {
|
|
var streamType = this.daiController.getSettings().streamType;
|
|
if (streamType === 'live') {
|
|
return;
|
|
}
|
|
if (this.isSnapback) {
|
|
this.isSnapback = false;
|
|
return;
|
|
}
|
|
var previousCuePoint = this.streamManager.previousCuePointForStreamTime(currentTime);
|
|
if (previousCuePoint && !previousCuePoint.played) {
|
|
this.isSnapback = true;
|
|
this.snapForwardTime = currentTime;
|
|
this.vjsPlayer.currentTime(previousCuePoint.start);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Handles IMA events.
|
|
* @param {google.ima.StreamEvent!} event the IMA event
|
|
*/
|
|
SdkImpl$2.prototype.onStreamEvent = function (event) {
|
|
switch (event.type) {
|
|
case google.ima.dai.api.StreamEvent.Type.LOADED:
|
|
this.loadUrl(event.getStreamData().url);
|
|
break;
|
|
case google.ima.dai.api.StreamEvent.Type.ERROR:
|
|
var errorMessage = event.getStreamData().errorMessage;
|
|
window.console.warn('Error loading stream, attempting to play backup stream. ' + errorMessage);
|
|
this.daiController.onErrorLoadingAds(event);
|
|
var fallbackUrl = this.daiController.getSettings().fallbackStreamUrl;
|
|
if (fallbackUrl) {
|
|
this.loadurl(fallbackUrl);
|
|
}
|
|
break;
|
|
case google.ima.dai.api.StreamEvent.Type.AD_BREAK_STARTED:
|
|
this.isAdBreak = true;
|
|
this.adUiDiv.style.display = 'block';
|
|
this.daiController.onAdBreakStart();
|
|
break;
|
|
case google.ima.dai.api.StreamEvent.Type.AD_BREAK_ENDED:
|
|
this.isAdBreak = false;
|
|
this.adUiDiv.style.display = 'none';
|
|
this.daiController.onAdBreakEnd();
|
|
var currentTime = this.vjsPlayer.currentTime();
|
|
if (this.snapForwardTime && this.snapForwardTime > currentTime) {
|
|
this.vjsPlayer.currentTime(this.snapForwardTime);
|
|
this.snapForwardTime = 0;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Loads the stream URL .
|
|
* @param {string} streamUrl the URL for the stream being loaded.
|
|
*/
|
|
SdkImpl$2.prototype.loadUrl = function (streamUrl) {
|
|
this.vjsPlayer.ready(function () {
|
|
var streamFormat = this.daiController.getSettings().streamFormat;
|
|
this.vjsPlayer.src({
|
|
src: streamUrl,
|
|
type: this.SOURCE_TYPES[streamFormat]
|
|
});
|
|
|
|
var bookmarkTime = this.daiController.getSettings().bookmarkTime;
|
|
if (bookmarkTime) {
|
|
var startTime = this.streamManager.streamTimeForContentTime(bookmarkTime);
|
|
// Seeking on load triggers the onSeekEnd event, so treat this seek as
|
|
// if it's snapback. Without this, resuming at a bookmark kicks you
|
|
// back to the ad before the bookmark.
|
|
this.isSnapback = true;
|
|
this.vjsPlayer.currentTime(startTime);
|
|
}
|
|
}.bind(this));
|
|
};
|
|
|
|
/**
|
|
* Creates the AdsRequest and request ads through the AdsLoader.
|
|
*/
|
|
SdkImpl$2.prototype.requestStream = function () {
|
|
var streamRequest = void 0;
|
|
var streamType = this.daiController.getSettings().streamType;
|
|
if (streamType === 'vod') {
|
|
streamRequest = new google.ima.dai.api.VODStreamRequest();
|
|
streamRequest.contentSourceId = this.daiController.getSettings().cmsId;
|
|
streamRequest.videoId = this.daiController.getSettings().videoId;
|
|
} else if (streamType === 'live') {
|
|
streamRequest = new google.ima.dai.api.LiveStreamRequest();
|
|
streamRequest.assetKey = this.daiController.getSettings().assetKey;
|
|
} else {
|
|
window.console.warn('No valid stream type selected');
|
|
}
|
|
streamRequest.format = this.daiController.getSettings().streamFormat;
|
|
|
|
if (this.daiController.getSettings().apiKey) {
|
|
streamRequest.apiKey = this.daiController.getSettings().apiKey;
|
|
}
|
|
if (this.daiController.getSettings().authKey) {
|
|
streamRequest.authKey = this.daiController.getSettings().authKey;
|
|
}
|
|
if (this.daiController.getSettings().adTagParameters) {
|
|
streamRequest.adTagParameters = this.daiController.getSettings().adTagParameters;
|
|
}
|
|
if (this.daiController.getSettings().streamActivityMonitorId) {
|
|
streamRequest.streamActivityMonitorId = this.daiController.getSettings().streamActivityMonitorId;
|
|
}
|
|
|
|
if (this.daiController.getSettings().omidMode) {
|
|
streamRequest.omidAccessModeRules = {};
|
|
var omidValues = this.daiController.getSettings().omidMode;
|
|
|
|
if (omidValues.FULL) {
|
|
streamRequest.omidAccessModeRules[google.ima.OmidAccessMode.FULL] = omidValues.FULL;
|
|
}
|
|
if (omidValues.DOMAIN) {
|
|
streamRequest.omidAccessModeRules[google.ima.OmidAccessMode.DOMAIN] = omidValues.DOMAIN;
|
|
}
|
|
if (omidValues.LIMITED) {
|
|
streamRequest.omidAccessModeRules[google.ima.OmidAccessMode.LIMITED] = omidValues.LIMITED;
|
|
}
|
|
}
|
|
|
|
this.streamManager.requestStream(streamRequest);
|
|
this.vjsPlayer.trigger({
|
|
type: 'stream-request',
|
|
StreamRequest: streamRequest
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Initiates IMA when the player is ready.
|
|
*/
|
|
SdkImpl$2.prototype.onPlayerReady = function () {
|
|
this.initImaDai();
|
|
};
|
|
|
|
/**
|
|
* Reset the StreamManager when the player is disposed.
|
|
*/
|
|
SdkImpl$2.prototype.onPlayerDisposed = function () {
|
|
if (this.streamManager) {
|
|
this.streamManager.reset();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns the instance of the StreamManager.
|
|
* @return {google.ima.StreamManager!} The StreamManager being used by the plugin.
|
|
*/
|
|
SdkImpl$2.prototype.getStreamManager = function () {
|
|
return this.StreamManager;
|
|
};
|
|
|
|
/**
|
|
* Reset the SDK implementation.
|
|
*/
|
|
SdkImpl$2.prototype.reset = function () {
|
|
if (this.StreamManager) {
|
|
this.StreamManager.reset();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Copyright 2021 Google Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* IMA SDK integration plugin for Video.js. For more information see
|
|
* https://www.github.com/googleads/videojs-ima
|
|
*/
|
|
/**
|
|
* The coordinator for the DAI portion of the plugin. Facilitates
|
|
* communication between all other plugin classes.
|
|
*
|
|
* @param {Object!} player Instance of the video.js player.
|
|
* @param {Object!} options Options provided by the implementation.
|
|
* @constructor
|
|
* @struct
|
|
* @final
|
|
*/
|
|
var DaiController = function DaiController(player, options) {
|
|
/**
|
|
* If the stream is currently in an ad break.
|
|
* @type {boolean}
|
|
*/
|
|
this.inAdBreak = false;
|
|
|
|
/**
|
|
* Stores user-provided settings.
|
|
* @type {Object!}
|
|
*/
|
|
this.settings = {};
|
|
|
|
/**
|
|
* Whether or not we are running on a mobile platform.
|
|
*/
|
|
this.isMobile = navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i) || navigator.userAgent.match(/Android/i);
|
|
|
|
/**
|
|
* Whether or not we are running on an iOS platform.
|
|
*/
|
|
this.isIos = navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i);
|
|
|
|
this.initWithSettings(options);
|
|
|
|
/**
|
|
* Stores contrib-ads default settings.
|
|
*/
|
|
var contribAdsDefaults = {
|
|
debug: this.settings.debug,
|
|
timeout: this.settings.timeout,
|
|
prerollTimeout: this.settings.prerollTimeout
|
|
};
|
|
var adsPluginSettings = Object.assign({}, contribAdsDefaults, options.contribAdsSettings || {});
|
|
|
|
this.playerWrapper = new PlayerWrapper$2(player, adsPluginSettings, this);
|
|
this.sdkImpl = new SdkImpl$2(this);
|
|
};
|
|
|
|
DaiController.IMA_DEFAULTS = {
|
|
adLabel: 'Advertisement',
|
|
adLabelNofN: 'of',
|
|
debug: false,
|
|
disableAdControls: false,
|
|
showControlsForJSAds: true
|
|
};
|
|
|
|
/**
|
|
* Extends the settings to include user-provided settings.
|
|
*
|
|
* @param {Object!} options Options to be used in initialization.
|
|
*/
|
|
DaiController.prototype.initWithSettings = function (options) {
|
|
this.settings = Object.assign({}, DaiController.IMA_DEFAULTS, options || {});
|
|
|
|
this.warnAboutDeprecatedSettings();
|
|
|
|
// Default showing countdown timer to true.
|
|
this.showCountdown = true;
|
|
if (this.settings.showCountdown === false) {
|
|
this.showCountdown = false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Logs console warnings when deprecated settings are used.
|
|
*/
|
|
DaiController.prototype.warnAboutDeprecatedSettings = function () {
|
|
var _this = this;
|
|
|
|
var deprecatedSettings = [
|
|
// Currently no DAI plugin settings are deprecated.
|
|
];
|
|
deprecatedSettings.forEach(function (setting) {
|
|
if (_this.settings[setting] !== undefined) {
|
|
console.warn('WARNING: videojs.imaDai setting ' + setting + ' is deprecated');
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Return the settings object.
|
|
*
|
|
* @return {Object!} The settings object.
|
|
*/
|
|
DaiController.prototype.getSettings = function () {
|
|
return this.settings;
|
|
};
|
|
|
|
/**
|
|
* Return whether or not we're in a mobile environment.
|
|
*
|
|
* @return {boolean} True if running on mobile, false otherwise.
|
|
*/
|
|
DaiController.prototype.getIsMobile = function () {
|
|
return this.isMobile;
|
|
};
|
|
|
|
/**
|
|
* Return whether or not we're in an iOS environment.
|
|
*
|
|
* @return {boolean} True if running on iOS, false otherwise.
|
|
*/
|
|
DaiController.prototype.getIsIos = function () {
|
|
return this.isIos;
|
|
};
|
|
|
|
/**
|
|
* @return {Object!} The html5 player.
|
|
*/
|
|
DaiController.prototype.getStreamPlayer = function () {
|
|
return this.playerWrapper.getStreamPlayer();
|
|
};
|
|
|
|
/**
|
|
* @return {Object!} The video.js player.
|
|
*/
|
|
DaiController.prototype.getVjsPlayer = function () {
|
|
return this.playerWrapper.getVjsPlayer();
|
|
};
|
|
|
|
/**
|
|
* Requests the stream.
|
|
*/
|
|
DaiController.prototype.requestStream = function () {
|
|
this.sdkImpl.requestStream();
|
|
};
|
|
|
|
/**
|
|
* Add or modify a setting.
|
|
*
|
|
* @param {string} key Key to modify
|
|
* @param {Object!} value Value to set at key.
|
|
*/
|
|
DaiController.prototype.setSetting = function (key, value) {
|
|
this.settings[key] = value;
|
|
};
|
|
|
|
/**
|
|
* Called when there is an error loading ads.
|
|
*
|
|
* @param {Object!} adErrorEvent The ad error event thrown by the IMA SDK.
|
|
*/
|
|
DaiController.prototype.onErrorLoadingAds = function (adErrorEvent) {
|
|
this.playerWrapper.onAdError(adErrorEvent);
|
|
};
|
|
|
|
/**
|
|
* Relays ad errors to the player wrapper.
|
|
*
|
|
* @param {Object!} adErrorEvent The ad error event thrown by the IMA SDK.
|
|
*/
|
|
DaiController.prototype.onAdError = function (adErrorEvent) {
|
|
this.playerWrapper.onAdError(adErrorEvent);
|
|
};
|
|
|
|
/**
|
|
* Signals player that an ad break has started.
|
|
*/
|
|
DaiController.prototype.onAdBreakStart = function () {
|
|
this.inAdBreak = true;
|
|
this.playerWrapper.onAdBreakStart();
|
|
};
|
|
|
|
/**
|
|
* Signals player that an ad break has ended.
|
|
*/
|
|
DaiController.prototype.onAdBreakEnd = function () {
|
|
this.inAdBreak = false;
|
|
this.playerWrapper.onAdBreakEnd();
|
|
};
|
|
|
|
/**
|
|
* Called when the player is disposed.
|
|
*/
|
|
DaiController.prototype.onPlayerDisposed = function () {
|
|
this.contentAndAdsEndedListeners = [];
|
|
this.sdkImpl.onPlayerDisposed();
|
|
};
|
|
|
|
/**
|
|
* Returns if the stream is currently in an ad break.
|
|
* @return {boolean} If the stream is currently in an ad break.
|
|
*/
|
|
DaiController.prototype.isInAdBreak = function () {
|
|
return this.inAdBreak;
|
|
};
|
|
|
|
/**
|
|
* Called on seek end to check for ad snapback.
|
|
* @param {number} currentTime the current time of the stream.
|
|
*/
|
|
DaiController.prototype.onSeekEnd = function (currentTime) {
|
|
this.sdkImpl.onSeekEnd(currentTime);
|
|
};
|
|
|
|
/**
|
|
* Called when the player is ready.
|
|
*/
|
|
DaiController.prototype.onPlayerReady = function () {
|
|
this.sdkImpl.onPlayerReady();
|
|
};
|
|
|
|
/**
|
|
* Resets the state of the plugin.
|
|
*/
|
|
DaiController.prototype.reset = function () {
|
|
this.sdkImpl.reset();
|
|
this.playerWrapper.reset();
|
|
};
|
|
|
|
/**
|
|
* Adds an EventListener to the StreamManager. For a list of available events,
|
|
* see
|
|
* https://developers.google.com/interactive-media-ads/docs/sdks/html5/dai/reference/js/StreamEvent
|
|
* @param {google.ima.StreamEvent.Type!} event The AdEvent.Type for which to
|
|
* listen.
|
|
* @param {callback!} callback The method to call when the event is fired.
|
|
*/
|
|
DaiController.prototype.addEventListener = function (event, callback) {
|
|
this.sdkImpl.addEventListener(event, callback);
|
|
};
|
|
|
|
/**
|
|
* Returns the instance of the StreamManager.
|
|
* @return {google.ima.StreamManager!} The StreamManager being used by the plugin.
|
|
*/
|
|
DaiController.prototype.getStreamManager = function () {
|
|
return this.sdkImpl.getStreamManager();
|
|
};
|
|
|
|
/**
|
|
* Returns the instance of the player id.
|
|
* @return {string} The player id.
|
|
*/
|
|
DaiController.prototype.getPlayerId = function () {
|
|
return this.playerWrapper.getPlayerId();
|
|
};
|
|
|
|
/**
|
|
* @return {boolean} true if we expect that the stream will autoplay. false otherwise.
|
|
*/
|
|
DaiController.prototype.streamWillAutoplay = function () {
|
|
if (this.settings.streamWillAutoplay !== undefined) {
|
|
return this.settings.streamWillAutoplay;
|
|
} else {
|
|
return !!this.playerWrapper.getPlayerOptions().autoplay;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Triggers an event on the VJS player
|
|
* @param {string} name The event name.
|
|
* @param {Object!} data The event data.
|
|
*/
|
|
DaiController.prototype.triggerPlayerEvent = function (name, data) {
|
|
this.playerWrapper.triggerPlayerEvent(name, data);
|
|
};
|
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
/**
|
|
* Copyright 2017 Google Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* IMA SDK integration plugin for Video.js. For more information see
|
|
* https://www.github.com/googleads/videojs-ima
|
|
*/
|
|
|
|
/**
|
|
* Exposes the ImaPlugin to a publisher implementation.
|
|
*
|
|
* @param {Object} player Instance of the video.js player to which this plugin
|
|
* will be added.
|
|
* @param {Object} options Options provided by the implementation.
|
|
* @constructor
|
|
* @struct
|
|
* @final
|
|
*/
|
|
var ImaPlugin = function ImaPlugin(player, options) {
|
|
this.controller = new Controller(player, options);
|
|
|
|
/**
|
|
* Listener JSDoc for ESLint. This listener can be passed to
|
|
* addContent(AndAds)EndedListener.
|
|
* @callback listener
|
|
*/
|
|
|
|
/**
|
|
* Adds a listener that will be called when content and all ads have
|
|
* finished playing.
|
|
* @param {listener} listener The listener to be called when content and ads
|
|
* complete.
|
|
*/
|
|
this.addContentAndAdsEndedListener = function (listener) {
|
|
this.controller.addContentAndAdsEndedListener(listener);
|
|
}.bind(this);
|
|
|
|
/**
|
|
* Adds a listener for the 'contentended' event of the video player. This
|
|
* should be used instead of setting an 'contentended' listener directly to
|
|
* ensure that the ima can do proper cleanup of the SDK before other event
|
|
* listeners are called.
|
|
* @param {listener} listener The listener to be called when content
|
|
* completes.
|
|
*/
|
|
this.addContentEndedListener = function (listener) {
|
|
this.controller.addContentEndedListener(listener);
|
|
}.bind(this);
|
|
|
|
/**
|
|
* Callback JSDoc for ESLint. This callback can be passed to addEventListener.
|
|
* @callback callback
|
|
*/
|
|
|
|
/**
|
|
* Ads an EventListener to the AdsManager. For a list of available events,
|
|
* see
|
|
* https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/reference/js/google.ima.AdEvent#.Type
|
|
* @param {google.ima.AdEvent.Type} event The AdEvent.Type for which to
|
|
* listen.
|
|
* @param {callback} callback The method to call when the event is fired.
|
|
*/
|
|
this.addEventListener = function (event, callback) {
|
|
this.controller.addEventListener(event, callback);
|
|
}.bind(this);
|
|
|
|
/**
|
|
* Changes the ad tag. You will need to call requestAds after this method
|
|
* for the new ads to be requested.
|
|
* @param {?string} adTag The ad tag to be requested the next time requestAds
|
|
* is called.
|
|
*/
|
|
this.changeAdTag = function (adTag) {
|
|
this.controller.changeAdTag(adTag);
|
|
}.bind(this);
|
|
|
|
/**
|
|
* Returns the instance of the AdsManager.
|
|
* @return {google.ima.AdsManager} The AdsManager being used by the plugin.
|
|
*/
|
|
this.getAdsManager = function () {
|
|
return this.controller.getAdsManager();
|
|
}.bind(this);
|
|
|
|
/**
|
|
* Initializes the AdDisplayContainer. On mobile, this must be done as a
|
|
* result of user action.
|
|
*/
|
|
this.initializeAdDisplayContainer = function () {
|
|
this.controller.initializeAdDisplayContainer();
|
|
}.bind(this);
|
|
|
|
/**
|
|
* Pauses the ad.
|
|
*/
|
|
this.pauseAd = function () {
|
|
this.controller.pauseAd();
|
|
}.bind(this);
|
|
|
|
/**
|
|
* Called by publishers in manual ad break playback mode to start an ad
|
|
* break.
|
|
*/
|
|
this.playAdBreak = function () {
|
|
this.controller.playAdBreak();
|
|
}.bind(this);
|
|
|
|
/**
|
|
* Creates the AdsRequest and request ads through the AdsLoader.
|
|
*/
|
|
this.requestAds = function () {
|
|
this.controller.requestAds();
|
|
}.bind(this);
|
|
|
|
/**
|
|
* Resumes the ad.
|
|
*/
|
|
this.resumeAd = function () {
|
|
this.controller.resumeAd();
|
|
}.bind(this);
|
|
|
|
/**
|
|
* Sets the listener to be called to trigger manual ad break playback.
|
|
* @param {listener} listener The listener to be called to trigger manual ad
|
|
* break playback.
|
|
*/
|
|
this.setAdBreakReadyListener = function (listener) {
|
|
this.controller.setAdBreakReadyListener(listener);
|
|
}.bind(this);
|
|
|
|
/**
|
|
* Sets the content of the video player. You should use this method instead
|
|
* of setting the content src directly to ensure the proper ad tag is
|
|
* requested when the video content is loaded.
|
|
* @param {?string} contentSrc The URI for the content to be played. Leave
|
|
* blank to use the existing content.
|
|
* @param {?string} adTag The ad tag to be requested when the content loads.
|
|
* Leave blank to use the existing ad tag.
|
|
*/
|
|
this.setContentWithAdTag = function (contentSrc, adTag) {
|
|
this.controller.setContentWithAdTag(contentSrc, adTag);
|
|
}.bind(this);
|
|
|
|
/**
|
|
* Sets the content of the video player. You should use this method instead
|
|
* of setting the content src directly to ensure the proper ads response is
|
|
* used when the video content is loaded.
|
|
* @param {?string} contentSrc The URI for the content to be played. Leave
|
|
* blank to use the existing content.
|
|
* @param {?string} adsResponse The ads response to be requested when the
|
|
* content loads. Leave blank to use the existing ads response.
|
|
*/
|
|
this.setContentWithAdsResponse = function (contentSrc, adsResponse) {
|
|
this.controller.setContentWithAdsResponse(contentSrc, adsResponse);
|
|
}.bind(this);
|
|
|
|
/**
|
|
* Sets the content of the video player. You should use this method instead
|
|
* of setting the content src directly to ensure the proper ads request is
|
|
* used when the video content is loaded.
|
|
* @param {?string} contentSrc The URI for the content to be played. Leave
|
|
* blank to use the existing content.
|
|
* @param {?Object} adsRequest The ads request to be requested when the
|
|
* content loads. Leave blank to use the existing ads request.
|
|
*/
|
|
this.setContentWithAdsRequest = function (contentSrc, adsRequest) {
|
|
this.controller.setContentWithAdsRequest(contentSrc, adsRequest);
|
|
}.bind(this);
|
|
|
|
/**
|
|
* Changes the flag to show or hide the ad countdown timer.
|
|
*
|
|
* @param {boolean} showCountdownIn Show or hide the countdown timer.
|
|
*/
|
|
this.setShowCountdown = function (showCountdownIn) {
|
|
this.controller.setShowCountdown(showCountdownIn);
|
|
}.bind(this);
|
|
};
|
|
|
|
/**
|
|
* Exposes the ImaDaiPlugin to a publisher implementation.
|
|
*
|
|
* @param {Object} player Instance of the video.js player to which this plugin
|
|
* will be added.
|
|
* @param {Object} options Options provided by the implementation.
|
|
* @constructor
|
|
* @struct
|
|
* @final
|
|
*/
|
|
var ImaDaiPlugin = function ImaDaiPlugin(player, options) {
|
|
this.controller = new DaiController(player, options);
|
|
|
|
/**
|
|
* Adds a listener that will be called when content and all ads in the
|
|
* stream have finished playing. VOD stream only.
|
|
* @param {listener} listener The listener to be called when content and ads
|
|
* complete.
|
|
*/
|
|
this.streamEndedListener = function (listener) {
|
|
this.controller.addStreamEndedListener(listener);
|
|
}.bind(this);
|
|
|
|
/**
|
|
* Adds an EventListener to the StreamManager. For a list of available events,
|
|
* see
|
|
* https://developers.google.com/interactive-media-ads/docs/sdks/html5/dai/reference/js/StreamEvent
|
|
* @param {google.ima.StreamEvent.Type} event The StreamEvent.Type for which to
|
|
* listen.
|
|
* @param {callback} callback The method to call when the event is fired.
|
|
*/
|
|
this.addEventListener = function (event, callback) {
|
|
this.controller.addEventListener(event, callback);
|
|
}.bind(this);
|
|
|
|
/**
|
|
* Returns the instance of the StreamManager.
|
|
* @return {google.ima.StreamManager} The StreamManager being used by the plugin.
|
|
*/
|
|
this.getStreamManager = function () {
|
|
return this.controller.getStreamManager();
|
|
}.bind(this);
|
|
};
|
|
|
|
var init = function init(options) {
|
|
/* eslint no-invalid-this: 'off' */
|
|
this.ima = new ImaPlugin(this, options);
|
|
};
|
|
|
|
var LiveStream = function LiveStream(streamFormat, assetKey) {
|
|
_classCallCheck(this, LiveStream);
|
|
|
|
streamFormat = streamFormat.toLowerCase();
|
|
if (streamFormat !== 'hls' && streamFormat !== 'dash') {
|
|
window.console.error('VodStream error: incorrect streamFormat.');
|
|
return;
|
|
} else if (streamFormat === 'dash') {
|
|
window.console.error('streamFormat error: DASH streams are not' + 'currently supported by this plugin.');
|
|
return;
|
|
} else if (typeof assetKey !== 'string') {
|
|
window.console.error('assetKey error: value must be string.');
|
|
return;
|
|
}
|
|
this.streamFormat = streamFormat;
|
|
this.assetKey = assetKey;
|
|
};
|
|
|
|
var VodStream = function VodStream(streamFormat, cmsId, videoId) {
|
|
_classCallCheck(this, VodStream);
|
|
|
|
streamFormat = streamFormat.toLowerCase();
|
|
if (streamFormat !== 'hls' && streamFormat !== 'dash') {
|
|
window.console.error('VodStream error: incorrect streamFormat.');
|
|
return;
|
|
} else if (streamFormat === 'dash') {
|
|
window.console.error('streamFormat error: DASH streams are not' + 'currently supported by this plugin.');
|
|
return;
|
|
} else if (typeof cmsId !== 'string') {
|
|
window.console.error('cmsId error: value must be string.');
|
|
return;
|
|
} else if (typeof videoId !== 'string') {
|
|
window.console.error('videoId error: value must be string.');
|
|
return;
|
|
}
|
|
|
|
this.streamFormat = streamFormat;
|
|
this.cmsId = cmsId;
|
|
this.videoId = videoId;
|
|
};
|
|
|
|
var initDai = function initDai(stream, options) {
|
|
if (stream instanceof LiveStream) {
|
|
options.streamType = 'live';
|
|
options.assetKey = stream.assetKey;
|
|
} else if (stream instanceof VodStream) {
|
|
options.streamType = 'vod';
|
|
options.cmsId = stream.cmsId;
|
|
options.videoId = stream.videoId;
|
|
} else {
|
|
window.console.error('initDai() first parameter must be an instance of LiveStream or VodStream.');
|
|
return;
|
|
}
|
|
|
|
options.streamFormat = stream.streamFormat;
|
|
/* eslint no-invalid-this: 'off' */
|
|
this.imaDai = new ImaDaiPlugin(this, options);
|
|
};
|
|
|
|
var registerPlugin = videojs.registerPlugin || videojs.plugin;
|
|
registerPlugin('ima', init);
|
|
registerPlugin('imaDai', initDai);
|
|
|
|
exports['default'] = ImaPlugin;
|
|
exports.VodStream = VodStream;
|
|
exports.LiveStream = LiveStream;
|
|
|
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
|
})));
|