Ads improvements for volume and race condition fix

This commit is contained in:
Sam Potts 2019-04-25 12:03:04 +10:00
parent 9b81e776fb
commit a1b2c0419f

View File

@ -14,6 +14,20 @@ import loadScript from '../utils/loadScript';
import { formatTime } from '../utils/time'; import { formatTime } from '../utils/time';
import { buildUrlParams } from '../utils/urls'; import { buildUrlParams } from '../utils/urls';
const destroy = instance => {
// Destroy our adsManager
if (instance.manager) {
instance.manager.destroy();
}
// Destroy our adsManager
if (instance.elements.displayContainer) {
instance.elements.displayContainer.destroy();
}
instance.elements.container.remove();
};
class Ads { class Ads {
/** /**
* Ads constructor. * Ads constructor.
@ -63,20 +77,22 @@ class Ads {
* Load the IMA SDK * Load the IMA SDK
*/ */
load() { load() {
if (this.enabled) { if (!this.enabled) {
// Check if the Google IMA3 SDK is loaded or load it ourselves return;
if (!is.object(window.google) || !is.object(window.google.ima)) { }
loadScript(this.player.config.urls.googleIMA.sdk)
.then(() => { // Check if the Google IMA3 SDK is loaded or load it ourselves
this.ready(); if (!is.object(window.google) || !is.object(window.google.ima)) {
}) loadScript(this.player.config.urls.googleIMA.sdk)
.catch(() => { .then(() => {
// Script failed to load or is blocked this.ready();
this.trigger('error', new Error('Google IMA SDK failed to load')); })
}); .catch(() => {
} else { // Script failed to load or is blocked
this.ready(); this.trigger('error', new Error('Google IMA SDK failed to load'));
} });
} else {
this.ready();
} }
} }
@ -84,6 +100,11 @@ class Ads {
* Get the ads instance ready * Get the ads instance ready
*/ */
ready() { ready() {
// Double check we're enabled
if (!this.enabled) {
destroy(this);
}
// Start ticking our safety timer. If the whole advertisement // Start ticking our safety timer. If the whole advertisement
// thing doesn't resolve within our set time; we bail // thing doesn't resolve within our set time; we bail
this.startSafetyTimer(12000, 'ready()'); this.startSafetyTimer(12000, 'ready()');
@ -240,9 +261,6 @@ class Ads {
// Get the cue points for any mid-rolls by filtering out the pre- and post-roll // Get the cue points for any mid-rolls by filtering out the pre- and post-roll
this.cuePoints = this.manager.getCuePoints(); this.cuePoints = this.manager.getCuePoints();
// Set volume to match player
this.manager.setVolume(this.player.volume);
// Add listeners to the required events // Add listeners to the required events
// Advertisement error events // Advertisement error events
this.manager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, error => this.onAdError(error)); this.manager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, error => this.onAdError(error));
@ -297,15 +315,15 @@ class Ads {
triggerEvent.call(this.player, this.player.media, event); triggerEvent.call(this.player, this.player.media, event);
}; };
// Bubble the event
dispatchEvent(event.type);
switch (event.type) { switch (event.type) {
case google.ima.AdEvent.Type.LOADED: case google.ima.AdEvent.Type.LOADED:
// This is the first event sent for an ad - it is possible to determine whether the // This is the first event sent for an ad - it is possible to determine whether the
// ad is a video ad or an overlay // ad is a video ad or an overlay
this.trigger('loaded'); this.trigger('loaded');
// Bubble event
dispatchEvent(event.type);
// Start countdown // Start countdown
this.pollCountdown(true); this.pollCountdown(true);
@ -317,15 +335,19 @@ class Ads {
// console.info('Ad type: ' + event.getAd().getAdPodInfo().getPodIndex()); // console.info('Ad type: ' + event.getAd().getAdPodInfo().getPodIndex());
// console.info('Ad time: ' + event.getAd().getAdPodInfo().getTimeOffset()); // console.info('Ad time: ' + event.getAd().getAdPodInfo().getTimeOffset());
break;
case google.ima.AdEvent.Type.STARTED:
// Set volume to match player
this.manager.setVolume(this.player.volume);
break; break;
case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: case google.ima.AdEvent.Type.ALL_ADS_COMPLETED:
// All ads for the current videos are done. We can now request new advertisements // All ads for the current videos are done. We can now request new advertisements
// in case the video is re-played // in case the video is re-played
// Fire event
dispatchEvent(event.type);
// TODO: Example for what happens when a next video in a playlist would be loaded. // TODO: Example for what happens when a next video in a playlist would be loaded.
// So here we load a new video when all ads are done. // So here we load a new video when all ads are done.
// Then we load new ads within a new adsManager. When the video // Then we load new ads within a new adsManager. When the video
@ -350,6 +372,7 @@ class Ads {
// playing when the IMA SDK is ready or has failed // playing when the IMA SDK is ready or has failed
this.loadAds(); this.loadAds();
break; break;
case google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED: case google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED:
@ -357,8 +380,6 @@ class Ads {
// for example display a pause button and remaining time. Fired when content should // for example display a pause button and remaining time. Fired when content should
// be paused. This usually happens right before an ad is about to cover the content // be paused. This usually happens right before an ad is about to cover the content
dispatchEvent(event.type);
this.pauseContent(); this.pauseContent();
break; break;
@ -369,26 +390,17 @@ class Ads {
// Fired when content should be resumed. This usually happens when an ad finishes // Fired when content should be resumed. This usually happens when an ad finishes
// or collapses // or collapses
dispatchEvent(event.type);
this.pollCountdown(); this.pollCountdown();
this.resumeContent(); this.resumeContent();
break; break;
case google.ima.AdEvent.Type.STARTED:
case google.ima.AdEvent.Type.MIDPOINT:
case google.ima.AdEvent.Type.COMPLETE:
case google.ima.AdEvent.Type.IMPRESSION:
case google.ima.AdEvent.Type.CLICK:
dispatchEvent(event.type);
break;
case google.ima.AdEvent.Type.LOG: case google.ima.AdEvent.Type.LOG:
if (adData.adError) { if (adData.adError) {
this.player.debug.warn(`Non-fatal ad error: ${adData.adError.getMessage()}`); this.player.debug.warn(`Non-fatal ad error: ${adData.adError.getMessage()}`);
} }
break; break;
default: default:
@ -463,6 +475,9 @@ class Ads {
// Play the requested advertisement whenever the adsManager is ready // Play the requested advertisement whenever the adsManager is ready
this.managerPromise this.managerPromise
.then(() => { .then(() => {
// Set volume to match player
this.manager.setVolume(this.player.volume);
// Initialize the container. Must be done via a user action on mobile devices // Initialize the container. Must be done via a user action on mobile devices
this.elements.displayContainer.initialize(); this.elements.displayContainer.initialize();