videojs-contrib-ads
Version:
A framework that provides common functionality needed by video advertisement libraries working with video.js.
229 lines (195 loc) • 5.75 kB
JavaScript
import videojs from 'video.js';
import {AdState, ContentPlayback} from '../states.js';
import cancelContentPlay from '../cancelContentPlay.js';
import adBreak from '../adBreak.js';
/*
* This state encapsulates waiting for prerolls, preroll playback, and
* content restoration after a preroll.
*/
export default class Preroll extends AdState {
init(player, adsReady) {
// Loading spinner from now until ad start or end of ad break.
player.addClass('vjs-ad-loading');
// Determine preroll timeout based on plugin settings
let timeout = player.ads.settings.timeout;
if (typeof player.ads.settings.prerollTimeout === 'number') {
timeout = player.ads.settings.prerollTimeout;
}
// Start the clock ticking for ad timeout
this._timeout = player.setTimeout(function() {
player.trigger('adtimeout');
}, timeout);
// If adsready already happened, lets get started. Otherwise,
// wait until onAdsReady.
if (adsReady) {
this.handleAdsReady();
} else {
this.adsReady = false;
}
}
onAdsReady(player) {
if (!player.ads.inAdBreak() && !player.ads.isContentResuming()) {
player.ads.debug('Received adsready event (Preroll)');
this.handleAdsReady();
} else {
videojs.log.warn('Unexpected adsready event (Preroll)');
}
}
/*
* Ad integration is ready. Let's get started on this preroll.
*/
handleAdsReady() {
this.adsReady = true;
if (this.player.ads.nopreroll_) {
this.noPreroll();
} else {
this.readyForPreroll();
}
}
/*
* Helper to call a callback only after a loadstart event.
* If we start content or ads before loadstart, loadstart
* will not be prefixed correctly.
*/
afterLoadStart(callback) {
const player = this.player;
if (player.ads._hasThereBeenALoadStartDuringPlayerLife) {
callback();
} else {
player.ads.debug('Waiting for loadstart...');
player.one('loadstart', () => {
player.ads.debug('Received loadstart event');
callback();
});
}
}
/*
* If there is no preroll, play content instead.
*/
noPreroll() {
this.afterLoadStart(() => {
this.player.ads.debug('Skipping prerolls due to nopreroll event (Preroll)');
this.transitionTo(ContentPlayback);
});
}
/*
* Fire the readyforpreroll event. If loadstart hasn't happened yet,
* wait until loadstart first.
*/
readyForPreroll() {
const player = this.player;
this.afterLoadStart(() => {
player.ads.debug('Triggered readyforpreroll event (Preroll)');
player.trigger('readyforpreroll');
});
}
/*
* Don't allow the content to start playing while we're dealing with ads.
*/
onPlay(player) {
player.ads.debug('Received play event (Preroll)');
if (!this.inAdBreak() && !this.isContentResuming()) {
cancelContentPlay(this.player);
}
}
/*
* adscanceled cancels all ads for the source. Play content now.
*/
onAdsCanceled(player) {
player.ads.debug('adscanceled (Preroll)');
this.afterLoadStart(() => {
this.transitionTo(ContentPlayback);
});
}
/*
* An ad error occured. Play content instead.
*/
onAdsError(player) {
videojs.log('adserror (Preroll)');
// In the future, we may not want to do this automatically.
// Integrations should be able to choose to continue the ad break
// if there was an error.
if (this.inAdBreak()) {
player.ads.endLinearAdMode();
}
this.afterLoadStart(() => {
this.transitionTo(ContentPlayback);
});
}
/*
* Integration invoked startLinearAdMode, the ad break starts now.
*/
startLinearAdMode() {
const player = this.player;
if (this.adsReady && !player.ads.inAdBreak() && !this.isContentResuming()) {
player.clearTimeout(this._timeout);
player.ads.adType = 'preroll';
adBreak.start(player);
} else {
videojs.log.warn('Unexpected startLinearAdMode invocation (Preroll)');
}
}
/*
* An ad has actually started playing.
* Remove the loading spinner.
*/
onAdStarted(player) {
player.removeClass('vjs-ad-loading');
}
/*
* Integration invoked endLinearAdMode, the ad break ends now.
*/
endLinearAdMode() {
const player = this.player;
if (this.inAdBreak()) {
player.removeClass('vjs-ad-loading');
adBreak.end(player);
this.contentResuming = true;
}
}
/*
* Ad skipped by integration. Play content instead.
*/
skipLinearAdMode() {
const player = this.player;
if (player.ads.inAdBreak() || this.isContentResuming()) {
videojs.log.warn('Unexpected skipLinearAdMode invocation');
} else {
this.afterLoadStart(() => {
player.trigger('adskip');
player.ads.debug('skipLinearAdMode (Preroll)');
this.transitionTo(ContentPlayback);
});
}
}
/*
* Prerolls took too long! Play content instead.
*/
onAdTimeout(player) {
this.afterLoadStart(() => {
player.ads.debug('adtimeout (Preroll)');
this.transitionTo(ContentPlayback);
});
}
/*
* Check if nopreroll event was too late before handling it.
*/
onNoPreroll(player) {
if (player.ads.inAdBreak() || this.isContentResuming()) {
videojs.log.warn('Unexpected nopreroll event (Preroll)');
} else {
this.noPreroll();
}
}
/*
* Cleanup timeouts and spinner.
*/
cleanup() {
const player = this.player;
if (!player.ads._hasThereBeenALoadStartDuringPlayerLife) {
videojs.log.warn('Leaving Preroll state before loadstart event can cause issues.');
}
player.removeClass('vjs-ad-loading');
player.clearTimeout(this._timeout);
}
}