Adsmanager is now re/pre-loaded with new ads when the video is done or an ad error appears. Will make it possible to request ads when a new video is loaded. Added comments and missing events within the adsmanagerloader method.
This commit is contained in:
		| @ -42,8 +42,8 @@ class Ads { | |||||||
|     ready() { |     ready() { | ||||||
|         this.time = Date.now(); |         this.time = Date.now(); | ||||||
|         this.startEvents = getStartEvents(); |         this.startEvents = getStartEvents(); | ||||||
|  |         this.adsContainer = null; | ||||||
|         this.adDisplayContainer = null; |         this.adDisplayContainer = null; | ||||||
|         this.adsDisplayElement = null; |  | ||||||
|         this.adsManager = null; |         this.adsManager = null; | ||||||
|         this.adsLoader = null; |         this.adsLoader = null; | ||||||
|         this.adsCuePoints = null; |         this.adsCuePoints = null; | ||||||
| @ -54,61 +54,125 @@ class Ads { | |||||||
|         // Set listeners on the Plyr instance. |         // Set listeners on the Plyr instance. | ||||||
|         this.setupListeners(); |         this.setupListeners(); | ||||||
|  |  | ||||||
|  |         // Start ticking our safety timer. If the whole advertisement | ||||||
|  |         // thing doesn't resolve within our set time; we bail. | ||||||
|  |         this.startSafetyTimer(12000, 'ready()'); | ||||||
|  |  | ||||||
|         // Setup a simple promise to resolve if the IMA loader is ready. |         // Setup a simple promise to resolve if the IMA loader is ready. | ||||||
|         this.adsLoaderPromise = new Promise((resolve) => { |         this.adsLoaderPromise = new Promise((resolve) => { | ||||||
|             this.on('ADS_LOADER_LOADED', () => resolve()); |             this.on('ADS_LOADER_LOADED', () => resolve()); | ||||||
|         }); |  | ||||||
|         this.adsLoaderPromise.then(() => { |  | ||||||
|             this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK] adsLoader resolved!`, this.adsLoader); |             this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK] adsLoader resolved!`, this.adsLoader); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         // Setup a promise to resolve if the IMA manager is ready. |         // Setup a promise to resolve if the IMA manager is ready. | ||||||
|         this.adsManagerPromise = new Promise((resolve) => { |         this.adsManagerPromise = new Promise((resolve) => { | ||||||
|             this.on('ADS_MANAGER_LOADED', () => resolve()); |             this.on('ADS_MANAGER_LOADED', () => resolve()); | ||||||
|         }); |             this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK] adsManager resolved!`, this.adsManager); | ||||||
|         this.adsManagerPromise.then(() => { |  | ||||||
|             // Clear the safety timer. |             // Clear the safety timer. | ||||||
|             this.clearSafetyTimer('onAdsManagerLoaded()'); |             this.clearSafetyTimer('onAdsManagerLoaded()'); | ||||||
|             this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK] adsManager resolved!`, this.adsManager); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         // Start ticking our safety timer. If the whole advertisement |  | ||||||
|         // thing doesn't resolve within our set time; we bail. |  | ||||||
|         this.startSafetyTimer(12000, 'ready()'); |  | ||||||
|  |  | ||||||
|         // Setup the ad display container. |  | ||||||
|         this.setupAdDisplayContainer(); |  | ||||||
|  |  | ||||||
|         // Setup the IMA SDK. |         // Setup the IMA SDK. | ||||||
|         this.setupIMA(); |         this.setupIMA(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * setupIMA | ||||||
|  |      * In order for the SDK to display ads for our video, we need to tell it | ||||||
|  |      * where to put them, so here we define our ad container. This div is set | ||||||
|  |      * up to render on top of the video player. Using the code below, we tell | ||||||
|  |      * the SDK to render ads within that div. We also provide a handle to the | ||||||
|  |      * content video player - the SDK will poll the current time of our player | ||||||
|  |      * to properly place mid-rolls. After we create the ad display container, | ||||||
|  |      * we initialize it. On mobile devices, this initialization is done as the | ||||||
|  |      * result of a user action. | ||||||
|  |      */ | ||||||
|     setupIMA() { |     setupIMA() { | ||||||
|         const { container } = this.player.elements; |         // Create the container for our advertisements. | ||||||
|  |         this.adsContainer = utils.createElement('div', { | ||||||
|  |             class: this.player.config.classNames.ads, | ||||||
|  |         }); | ||||||
|  |         this.player.elements.container.appendChild(this.adsContainer); | ||||||
|  |  | ||||||
|         // Create ads loader. |         // So we can run VPAID2. | ||||||
|         this.adsLoader = new google.ima.AdsLoader(this.adDisplayContainer); |         google.ima.settings.setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.ENABLED); | ||||||
|  |  | ||||||
|         // Listen and respond to ads loaded and error events. |         // Set language. | ||||||
|         this.adsLoader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, event => this.onAdsManagerLoaded(event), false); |         // Todo: Could make a config option out of this locale value. | ||||||
|         this.adsLoader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, error => this.onAdError(error), false); |         google.ima.settings.setLocale('en'); | ||||||
|  |  | ||||||
|         // Request video ads. |         // We assume the adContainer is the video container of the plyr element | ||||||
|         const adsRequest = new google.ima.AdsRequest(); |         // that will house the ads. | ||||||
|         adsRequest.adTagUrl = this.player.config.ads.tagUrl; |         this.adDisplayContainer = new google.ima.AdDisplayContainer(this.adsContainer); | ||||||
|  |  | ||||||
|         // Specify the linear and nonlinear slot sizes. This helps the SDK to |         const adsDisplayElement = this.adsContainer.firstChild; | ||||||
|         // select the correct creative if multiple are returned. |  | ||||||
|         adsRequest.linearAdSlotWidth = container.offsetWidth; |  | ||||||
|         adsRequest.linearAdSlotHeight = container.offsetHeight; |  | ||||||
|         adsRequest.nonLinearAdSlotWidth = container.offsetWidth; |  | ||||||
|         adsRequest.nonLinearAdSlotHeight = container.offsetHeight; |  | ||||||
|  |  | ||||||
|         this.adsLoader.requestAds(adsRequest); |         // The AdDisplayContainer call from google IMA sets the style attribute | ||||||
|  |         // by default. We remove the inline style and set it through the stylesheet. | ||||||
|  |         adsDisplayElement.removeAttribute('style'); | ||||||
|  |  | ||||||
|         this.handleEventListeners('ADS_LOADER_LOADED'); |         // Set class name on the adDisplayContainer element. | ||||||
|  |         adsDisplayElement.setAttribute('class', this.player.config.classNames.ads); | ||||||
|  |  | ||||||
|  |         // Play ads when clicked. Wait until the adsManager and adsLoader | ||||||
|  |         // are both resolved. | ||||||
|  |         Promise.all([ | ||||||
|  |             this.adsManagerPromise, | ||||||
|  |             this.adsLoaderPromise, | ||||||
|  |         ]).then(() => { | ||||||
|  |             this.setOnClickHandler(adsDisplayElement, this.play); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         // Request video ads to be pre-loaded. | ||||||
|  |         this.requestAds(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Request advertisements. | ||||||
|  |      */ | ||||||
|  |     requestAds() { | ||||||
|  |         const { container } = this.player.elements; | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |             // Create ads loader. | ||||||
|  |             this.adsLoader = new google.ima.AdsLoader(this.adDisplayContainer); | ||||||
|  |  | ||||||
|  |             // Listen and respond to ads loaded and error events. | ||||||
|  |             this.adsLoader.addEventListener( | ||||||
|  |                 google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, | ||||||
|  |                 event => this.onAdsManagerLoaded(event), false); | ||||||
|  |             this.adsLoader.addEventListener( | ||||||
|  |                 google.ima.AdErrorEvent.Type.AD_ERROR, | ||||||
|  |                 error => this.onAdError(error), false); | ||||||
|  |  | ||||||
|  |             // Request video ads. | ||||||
|  |             const adsRequest = new google.ima.AdsRequest(); | ||||||
|  |             adsRequest.adTagUrl = this.player.config.ads.tagUrl; | ||||||
|  |  | ||||||
|  |             // Specify the linear and nonlinear slot sizes. This helps the SDK | ||||||
|  |             // to select the correct creative if multiple are returned. | ||||||
|  |             adsRequest.linearAdSlotWidth = container.offsetWidth; | ||||||
|  |             adsRequest.linearAdSlotHeight = container.offsetHeight; | ||||||
|  |             adsRequest.nonLinearAdSlotWidth = container.offsetWidth; | ||||||
|  |             adsRequest.nonLinearAdSlotHeight = container.offsetHeight; | ||||||
|  |  | ||||||
|  |             // We only overlay ads as we only support video. | ||||||
|  |             adsRequest.forceNonLinearFullSlot = false; | ||||||
|  |  | ||||||
|  |             this.adsLoader.requestAds(adsRequest); | ||||||
|  |  | ||||||
|  |             this.handleEventListeners('ADS_LOADER_LOADED'); | ||||||
|  |         } catch (e) { | ||||||
|  |             this.onAdError(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * This method is called whenever the ads are ready inside | ||||||
|  |      * the AdDisplayContainer. | ||||||
|  |      * @param {Event} adsManagerLoadedEvent | ||||||
|  |      */ | ||||||
|     onAdsManagerLoaded(adsManagerLoadedEvent) { |     onAdsManagerLoaded(adsManagerLoadedEvent) { | ||||||
|  |  | ||||||
|         // Get the ads manager. |         // Get the ads manager. | ||||||
| @ -126,21 +190,44 @@ class Ads { | |||||||
|         this.adsCuePoints = this.adsManager.getCuePoints(); |         this.adsCuePoints = this.adsManager.getCuePoints(); | ||||||
|  |  | ||||||
|         // Add listeners to the required events. |         // Add listeners to the required events. | ||||||
|  |         // Advertisement error events. | ||||||
|         this.adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, error => this.onAdError(error)); |         this.adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, error => this.onAdError(error)); | ||||||
|  |  | ||||||
|  |         // Advertisement regular events. | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.AD_BREAK_READY, event => this.onAdEvent(event)); | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.AD_METADATA, event => this.onAdEvent(event)); | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.ALL_ADS_COMPLETED, event => this.onAdEvent(event)); | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.CLICK, event => this.onAdEvent(event)); | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, event => this.onAdEvent(event)); | ||||||
|         this.adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, event => this.onAdEvent(event)); |         this.adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, event => this.onAdEvent(event)); | ||||||
|         this.adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, event => this.onAdEvent(event)); |         this.adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, event => this.onAdEvent(event)); | ||||||
|         this.adsManager.addEventListener(google.ima.AdEvent.Type.ALL_ADS_COMPLETED, event => this.onAdEvent(event)); |  | ||||||
|         this.adsManager.addEventListener(google.ima.AdEvent.Type.AD_BREAK_READY, event => this.onAdEvent(event)); |  | ||||||
|  |  | ||||||
|         // Listen to any additional events, if necessary. |  | ||||||
|         this.adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, event => this.onAdEvent(event)); |         this.adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, event => this.onAdEvent(event)); | ||||||
|         this.adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, event => this.onAdEvent(event)); |         this.adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, event => this.onAdEvent(event)); | ||||||
|         this.adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, event => this.onAdEvent(event)); |         this.adsManager.addEventListener(google.ima.AdEvent.Type.DURATION_CHANGE, event => this.onAdEvent(event)); | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.FIRST_QUARTILE, event => this.onAdEvent(event)); | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.IMPRESSION, event => this.onAdEvent(event)); | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.INTERACTION, event => this.onAdEvent(event)); | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.LINEAR_CHANGED, event => this.onAdEvent(event)); | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.MIDPOINT, event => this.onAdEvent(event)); | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.PAUSED, event => this.onAdEvent(event)); | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.RESUMED, event => this.onAdEvent(event)); | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.SKIPPABLE_STATE_CHANGED, event => this.onAdEvent(event)); | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.SKIPPED, event => this.onAdEvent(event)); | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.THIRD_QUARTILE, event => this.onAdEvent(event)); | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.USER_CLOSE, event => this.onAdEvent(event)); | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.VOLUME_CHANGED, event => this.onAdEvent(event)); | ||||||
|  |         this.adsManager.addEventListener(google.ima.AdEvent.Type.VOLUME_MUTED, event => this.onAdEvent(event)); | ||||||
|  |  | ||||||
|         // Resolve our adsManager. |         // Resolve our adsManager. | ||||||
|         this.handleEventListeners('ADS_MANAGER_LOADED'); |         this.handleEventListeners('ADS_MANAGER_LOADED'); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * This is where all the event handling takes place. Retrieve the ad from | ||||||
|  |      * the event. Some events (e.g. ALL_ADS_COMPLETED) don't have ad | ||||||
|  |      * object associated. | ||||||
|  |      * @param {Event} event | ||||||
|  |      */ | ||||||
|     onAdEvent(event) { |     onAdEvent(event) { | ||||||
|         const { container } = this.player.elements; |         const { container } = this.player.elements; | ||||||
|  |  | ||||||
| @ -163,8 +250,45 @@ class Ads { | |||||||
|                 this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK] AD_METADATA |`, 'Fired when an ads list is loaded.'); |                 this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK] AD_METADATA |`, 'Fired when an ads list is loaded.'); | ||||||
|                 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 in case the video is re-played. | ||||||
|                 this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK] ALL_ADS_COMPLETED |`, 'Fired when the ads manager is done playing all the ads.'); |                 this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK] ALL_ADS_COMPLETED |`, 'Fired when the ads manager is done playing all the ads.'); | ||||||
|                 this.handleEventListeners('ALL_ADS_COMPLETED'); |                 this.handleEventListeners('ALL_ADS_COMPLETED'); | ||||||
|  |  | ||||||
|  |                 // 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. | ||||||
|  |                 // Then we load new ads within a new adsManager. When the video | ||||||
|  |                 // Is started - after - the ads are loaded, then we get ads. | ||||||
|  |                 // You can also easily test cancelling and reloading by running | ||||||
|  |                 // player.ads.cancel() and player.ads.play from the console I guess. | ||||||
|  |                 // this.player.source = { | ||||||
|  |                 //     type: 'video', | ||||||
|  |                 //     title: 'View From A Blue Moon', | ||||||
|  |                 //     sources: [{ | ||||||
|  |                 //         src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.mp4', | ||||||
|  |                 //         type: 'video/mp4', | ||||||
|  |                 //     }], | ||||||
|  |                 //     poster: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.jpg', | ||||||
|  |                 //     tracks: [ | ||||||
|  |                 //         { | ||||||
|  |                 //             kind: 'captions', | ||||||
|  |                 //             label: 'English', | ||||||
|  |                 //             srclang: 'en', | ||||||
|  |                 //             src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.en.vtt', | ||||||
|  |                 //             default: true, | ||||||
|  |                 //         }, | ||||||
|  |                 //         { | ||||||
|  |                 //             kind: 'captions', | ||||||
|  |                 //             label: 'French', | ||||||
|  |                 //             srclang: 'fr', | ||||||
|  |                 //             src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.fr.vtt', | ||||||
|  |                 //         }, | ||||||
|  |                 //     ], | ||||||
|  |                 // }; | ||||||
|  |  | ||||||
|  |                 // Todo: So there is still this thing where a video should only be allowed to start playing when the IMA SDK is ready or has failed. | ||||||
|  |  | ||||||
|  |                 this.loadAds(); | ||||||
|                 break; |                 break; | ||||||
|             case google.ima.AdEvent.Type.CLICK: |             case google.ima.AdEvent.Type.CLICK: | ||||||
|                 this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK] CLICK |`, 'Fired when the ad is clicked.'); |                 this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK] CLICK |`, 'Fired when the ad is clicked.'); | ||||||
| @ -178,14 +302,7 @@ class Ads { | |||||||
|                 // remaining time. |                 // remaining time. | ||||||
|                 this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK] CONTENT_PAUSE_REQUESTED |`, 'Fired when content should be paused. This usually happens right before an ad is about to cover the content.'); |                 this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK] CONTENT_PAUSE_REQUESTED |`, 'Fired when content should be paused. This usually happens right before an ad is about to cover the content.'); | ||||||
|                 this.handleEventListeners('CONTENT_PAUSE_REQUESTED'); |                 this.handleEventListeners('CONTENT_PAUSE_REQUESTED'); | ||||||
|  |                 this.contentPause(); | ||||||
|                 // Show our advertiment container. |  | ||||||
|                 this.adsDisplayElement.style.display = 'block'; |  | ||||||
|  |  | ||||||
|                 this.playing = true; |  | ||||||
|  |  | ||||||
|                 // Pause our video. |  | ||||||
|                 this.player.pause(); |  | ||||||
|                 break; |                 break; | ||||||
|             case google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED: |             case google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED: | ||||||
|                 // This event indicates the ad has finished - the video player |                 // This event indicates the ad has finished - the video player | ||||||
| @ -193,16 +310,7 @@ class Ads { | |||||||
|                 // remaining time detection. |                 // remaining time detection. | ||||||
|                 this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK] CONTENT_RESUME_REQUESTED |`, 'Fired when content should be resumed. This usually happens when an ad finishes or collapses.'); |                 this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK] CONTENT_RESUME_REQUESTED |`, 'Fired when content should be resumed. This usually happens when an ad finishes or collapses.'); | ||||||
|                 this.handleEventListeners('CONTENT_RESUME_REQUESTED'); |                 this.handleEventListeners('CONTENT_RESUME_REQUESTED'); | ||||||
|  |                 this.contentResume(); | ||||||
|                 // Hide the advertisement container. |  | ||||||
|                 this.adsDisplayElement.style.display = 'none'; |  | ||||||
|  |  | ||||||
|                 this.playing = false; |  | ||||||
|  |  | ||||||
|                 // Play our video. |  | ||||||
|                 if (this.player.currentTime < this.player.duration) { |  | ||||||
|                     this.player.play(); |  | ||||||
|                 } |  | ||||||
|                 break; |                 break; | ||||||
|             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 |                 // This is the first event sent for an ad - it is possible to | ||||||
| @ -270,56 +378,15 @@ class Ads { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Any ad error handling comes through here. | ||||||
|  |      * @param {Event} adErrorEvent | ||||||
|  |      */ | ||||||
|     onAdError(adErrorEvent) { |     onAdError(adErrorEvent) { | ||||||
|         this.cancel(); |         this.cancel(); | ||||||
|         this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK] ERROR |`, adErrorEvent); |         this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK] ERROR |`, adErrorEvent); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     setupAdDisplayContainer() { |  | ||||||
|         // Create the container for our advertisements. |  | ||||||
|         const container = utils.createElement('div', { |  | ||||||
|             class: this.player.config.classNames.ads, |  | ||||||
|         }); |  | ||||||
|         this.player.elements.container.appendChild(container); |  | ||||||
|  |  | ||||||
|         // So we can run VPAID2. |  | ||||||
|         google.ima.settings.setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.ENABLED); |  | ||||||
|  |  | ||||||
|         // Set language. |  | ||||||
|         // Todo: Could make a config option out of this locale value. |  | ||||||
|         google.ima.settings.setLocale('en'); |  | ||||||
|  |  | ||||||
|         // We assume the adContainer is the video container of the plyr element |  | ||||||
|         // that will house the ads. |  | ||||||
|         this.adDisplayContainer = new google.ima.AdDisplayContainer(container); |  | ||||||
|  |  | ||||||
|         this.adsDisplayElement = container.firstChild; |  | ||||||
|  |  | ||||||
|         // The AdDisplayContainer call from google IMA sets the style attribute |  | ||||||
|         // by default. We remove the inline style and set it through the stylesheet. |  | ||||||
|         this.adsDisplayElement.removeAttribute('style'); |  | ||||||
|  |  | ||||||
|         // Set class name on the adDisplayContainer element. |  | ||||||
|         this.adsDisplayElement.setAttribute('class', this.player.config.classNames.ads); |  | ||||||
|  |  | ||||||
|         // Make sure our advertisement container has the right z-index. |  | ||||||
|         this.on('CONTENT_PAUSE_REQUESTED', () => { |  | ||||||
|             container.style.zIndex = '3'; |  | ||||||
|         }); |  | ||||||
|         this.on('CONTENT_RESUME_REQUESTED', () => { |  | ||||||
|             container.style.zIndex = '1'; |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         // Play ads when clicked. Wait until the adsManager and adsLoader |  | ||||||
|         // are both resolved. |  | ||||||
|         Promise.all([ |  | ||||||
|             this.adsManagerPromise, |  | ||||||
|             this.adsLoaderPromise, |  | ||||||
|         ]).then(() => { |  | ||||||
|             this.setOnClickHandler(this.adsDisplayElement, this.play); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Setup hooks for Plyr and window events. This ensures |      * Setup hooks for Plyr and window events. This ensures | ||||||
|      * the mid- and post-roll launch at the correct time. And |      * the mid- and post-roll launch at the correct time. And | ||||||
| @ -380,17 +447,49 @@ class Ads { | |||||||
|  |  | ||||||
|                 this.initialized = true; |                 this.initialized = true; | ||||||
|             } catch (adError) { |             } catch (adError) { | ||||||
|                 // An error may be thrown if there was a problem with the VAST response. |                 // An error may be thrown if there was a problem with the | ||||||
|                 this.adsDisplayElement.remove(); |                 // VAST response. | ||||||
|  |                 this.onAdError(adError); | ||||||
|                 if (this.player.debug) { |  | ||||||
|                     throw new Error(adError); |  | ||||||
|                 } |  | ||||||
|                 this.player.play(); |  | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Resume our video. | ||||||
|  |      */ | ||||||
|  |     contentResume() { | ||||||
|  |         this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK]`, 'Resume video.'); | ||||||
|  |  | ||||||
|  |         // Hide our ad container. | ||||||
|  |         this.adsContainer.style.display = 'none'; | ||||||
|  |         this.adsContainer.style.zIndex = '1'; | ||||||
|  |  | ||||||
|  |         // Ad is stopped. | ||||||
|  |         this.playing = false; | ||||||
|  |  | ||||||
|  |         // Play our video. | ||||||
|  |         if (this.player.currentTime < this.player.duration) { | ||||||
|  |             this.player.play(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Pause our video. | ||||||
|  |      */ | ||||||
|  |     contentPause() { | ||||||
|  |         this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK]`, 'Pause video.'); | ||||||
|  |  | ||||||
|  |         // Show our ad container. | ||||||
|  |         this.adsContainer.style.display = 'block'; | ||||||
|  |         this.adsContainer.style.zIndex = '3'; | ||||||
|  |  | ||||||
|  |         // Ad is playing. | ||||||
|  |         this.playing = true; | ||||||
|  |  | ||||||
|  |         // Pause our video. | ||||||
|  |         this.player.pause(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Destroy the adsManager so we can grab new ads after this. |      * Destroy the adsManager so we can grab new ads after this. | ||||||
|      * If we don't then we're not allowed to call new ads based |      * If we don't then we're not allowed to call new ads based | ||||||
| @ -401,14 +500,40 @@ class Ads { | |||||||
|     cancel() { |     cancel() { | ||||||
|         this.player.debug.warn(`[${(Date.now() - this.time) / 1000}s][IMA SDK]`, 'Advertisement cancelled.'); |         this.player.debug.warn(`[${(Date.now() - this.time) / 1000}s][IMA SDK]`, 'Advertisement cancelled.'); | ||||||
|  |  | ||||||
|         // Todo: Removing the ad container might be problematic if we were to recreate the adsManager. Think of playlists. Every new video you need to request a new VAST xml and preload the advertisement. |         // Pause our video. | ||||||
|         this.adsDisplayElement.remove(); |         this.contentResume(); | ||||||
|  |  | ||||||
|  |         // Tell our instance that we're done for now. | ||||||
|  |         this.handleEventListeners('ERROR'); | ||||||
|  |  | ||||||
|  |         // Re-create our adsManager. | ||||||
|  |         this.loadAds(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Re-create our adsManager. | ||||||
|  |      */ | ||||||
|  |     loadAds() { | ||||||
|  |         this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK]`, 'Re-loading advertisements.'); | ||||||
|  |  | ||||||
|         // Tell our adsManager to go bye bye. |         // Tell our adsManager to go bye bye. | ||||||
|         this.adsManagerPromise.then(() => { |         this.adsManagerPromise.then(() => { | ||||||
|  |             // Destroy our adsManager. | ||||||
|             if (this.adsManager) { |             if (this.adsManager) { | ||||||
|                 this.adsManager.destroy(); |                 this.adsManager.destroy(); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             // Re-set our adsManager promises. | ||||||
|  |             this.adsManagerPromise = new Promise((resolve) => { | ||||||
|  |                 this.on('ADS_MANAGER_LOADED', () => resolve()); | ||||||
|  |                 this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK] adsManager resolved!`, this.adsManager); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             // Make sure we can re-call advertisements. | ||||||
|  |             this.initialized = false; | ||||||
|  |  | ||||||
|  |             // Now request some new advertisements. | ||||||
|  |             this.requestAds(); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -453,15 +578,12 @@ class Ads { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * startSafetyTimer |      * Setup a safety timer for when the ad network doesn't respond for | ||||||
|      * Setup a safety timer for when the ad network |      * whatever reason. The advertisement has 12 seconds to get its things | ||||||
|      * doesn't respond for whatever reason. The advertisement has 12 seconds |      * together. We stop this timer when the advertisement is playing, or when | ||||||
|      * to get its shit together. We stop this timer when the advertisement |      * a user action is required to start, then we clear the timer on ad ready. | ||||||
|      * is playing, or when a user action is required to start, then we |  | ||||||
|      * clear the timer on ad ready. |  | ||||||
|      * @param {Number} time |      * @param {Number} time | ||||||
|      * @param {String} from |      * @param {String} from | ||||||
|      * @private |  | ||||||
|      */ |      */ | ||||||
|     startSafetyTimer(time, from) { |     startSafetyTimer(time, from) { | ||||||
|         this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK]`, `Safety timer invoked timer from: ${from}`); |         this.player.debug.log(`[${(Date.now() - this.time) / 1000}s][IMA SDK]`, `Safety timer invoked timer from: ${from}`); | ||||||
| @ -472,9 +594,8 @@ class Ads { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * clearSafetyTimer |      * Clear our safety timer(s). | ||||||
|      * @param {String} from |      * @param {String} from | ||||||
|      * @private |  | ||||||
|      */ |      */ | ||||||
|     clearSafetyTimer(from) { |     clearSafetyTimer(from) { | ||||||
|         if (typeof this.safetyTimer !== 'undefined' && this.safetyTimer !== null) { |         if (typeof this.safetyTimer !== 'undefined' && this.safetyTimer !== null) { | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user