Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fix prefixing events when player is preload none #158

Merged
merged 5 commits into from
May 11, 2016
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,16 @@ Here are the events that communicate information to your integration from the ad

And here are the interaction points you use to send information to the ads plugin:

* `adsready` (EVENT) — Trigger this event after to signal that your integration is ready to play ads.
* `adscanceled` (EVENT) — Trigger this event after starting up the player or setting a new video to skip ads entirely. This event is optional; if you always plan on displaying ads, you don't need to worry about triggering it.
* `adserror` (EVENT) - Trigger this event to indicate that an error in the ad integration has ocurred and any ad states should abort so that content can resume.
* `adsready` (EVENT) — Trigger this event after to signal that your integration is ready to play ads.
* `adscanceled` (EVENT) — Trigger this event after starting up the player or setting a new video to skip ads entirely. This event is optional; if you always plan on displaying ads, you don't need to worry about triggering it.
* `adserror` (EVENT) - Trigger this event to indicate that an error in the ad integration has ocurred and any ad states should abort so that content can resume.
* `nopreroll` (EVENT) - Trigger this event to indicate that there will be no preroll ad. Otherwise, the player will wait until a timeout occurs before playing content. This event is optional, but can improve user experience.
* `nopostroll` (EVENT) - Trigger this event to indicate that there will be no postroll ad. Otherwise, contrib-ads will trigger an adtimeout event after content ends if there is no postroll.
* `ads.startLinearAdMode()` (METHOD) — Call this method to signal that your integration is about to play a linear ad. This method triggers `adstart` to be emitted by the player.
* `ads.endLinearAdMode()` (METHOD) — Call this method to signal that your integration is finished playing linear ads, ready for content video to resume. This method triggers `adend` to be emitted by the player.
* `ads.skipLinearAdMode()` (METHOD) — Call this method to signal that your integration has received an ad response but is not going to play a linear ad. This method triggers `adskip` to be emitted by the player.

* `ads.startLinearAdMode()` (METHOD) — Call this method to signal that your integration is about to play a linear ad. This method triggers `adstart` to be emitted by the player.
* `ads.endLinearAdMode()` (METHOD) — Call this method to signal that your integration is finished playing linear ads, ready for content video to resume. This method triggers `adend` to be emitted by the player.
* `ads.skipLinearAdMode()` (METHOD) — Call this method to signal that your integration has received an ad response but is not going to play a linear ad. This method triggers `adskip` to be emitted by the player.
* `ads.stitchedAds()` (METHOD) — Get or set the `stitchedAds` setting.
* `ads.videoElementRecycled()` (METHOD) - Returns true if ad playback is taking place in the content element.

In addition, video.js provides a number of events and APIs that might be useful to you.
For example, the `ended` event signals that the content video has played to completion.
Expand Down Expand Up @@ -181,6 +182,12 @@ The postrollTimeout should be as short as possible so that the viewer does not h
Make this longer if your ad integration needs a long time to decide whether it has postroll inventory to play or not.
Ideally, your ad integration should already know if it wants to play a postroll before the `contentended` event.

### stitchedAds

Type: `boolean`
Default Value: `false`

Set this to true if you are using ads stitched into the content video. This is necessary for ad events to be sent correctly.

### debug

Expand Down
38 changes: 28 additions & 10 deletions src/videojs.ads.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,11 +229,7 @@ var
}
})();
}
},

// whether the video element has been modified since the
// snapshot was taken
srcChanged;
};

if (snapshot.nativePoster) {
tech.poster = snapshot.nativePoster;
Expand All @@ -249,9 +245,7 @@ var
// ads, the content player state hasn't been modified and so no
// restoration is required

srcChanged = player.src() !== snapshot.src || player.currentSrc() !== snapshot.currentSrc;

if (srcChanged) {
if (player.ads.videoElementRecycled()) {
// on ios7, fiddling with textTracks too early will cause safari to crash
player.one('contentloadedmetadata', restoreTracks);

Expand Down Expand Up @@ -311,7 +305,10 @@ var
// when truthy, instructs the plugin to output additional information about
// plugin state to the video.js log. On most devices, the video.js log is
// the same as the developer console.
debug: false
debug: false,

// set this to true when using ads that are part of the content video
stitchedAds: false
},

adFramework = function(options) {
Expand Down Expand Up @@ -347,7 +344,9 @@ var

player.on(videoEvents, function redispatch(event) {
if (player.ads.state === 'ad-playback') {
triggerEvent('ad', event);
if (player.ads.videoElementRecycled() || player.ads.stitchedAds()) {
triggerEvent('ad', event);
}
} else if (player.ads.state === 'content-playback' && event.type === 'ended') {
triggerEvent('content', event);
} else if (player.ads.state === 'content-resuming') {
Expand Down Expand Up @@ -430,9 +429,28 @@ var
if (player.ads.state !== 'ad-playback') {
player.trigger('adskip');
}
},

stitchedAds: function(arg) {
if (arg) {
Copy link
Member

Choose a reason for hiding this comment

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

Should this be arg !== undefined? It doesn't look like there would be a way to set it to false.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

this._stitchedAds = !!arg;
}
return this._stitchedAds;
},

// Returns whether the video element has been modified since the
// snapshot was taken.
// We test both src and currentSrc because changing the src attribute to a URL that
// AdBlocker is intercepting doesn't update currentSrc.
videoElementRecycled: function() {
var srcChanged = player.src() !== this.snapshot.src;
var currentSrcChanged = player.currentSrc() !== this.snapshot.currentSrc;
return srcChanged || currentSrcChanged;
}
};

player.ads.stitchedAds(settings.stitchedAds);

fsmHandler = function(event) {
// Ad Playback State Machine
var fsm = {
Expand Down
47 changes: 44 additions & 3 deletions test/videojs.ads.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -856,7 +856,7 @@ QUnit.test('adsready in content-playback triggers readyforpreroll', function(ass
// Event prefixing during ad playback
// ----------------------------------

QUnit.test('player events during prerolls are prefixed', function(assert) {
QUnit.test('player events during prerolls are prefixed if tech is reused for ad', function(assert) {
var prefixed, unprefixed;

assert.expect(2);
Expand All @@ -872,6 +872,10 @@ QUnit.test('player events during prerolls are prefixed', function(assert) {
this.player.trigger('play');
this.player.trigger('adsready');

this.player.ads.snapshot = {
currentSrc: 'something'
};

// simulate video events that should be prefixed
this.player.on(['loadstart', 'playing', 'pause', 'ended', 'firstplay', 'loadedalldata'], unprefixed);
this.player.on(['adloadstart', 'adplaying', 'adpause', 'adended', 'adfirstplay', 'adloadedalldata'], prefixed);
Expand All @@ -885,7 +889,7 @@ QUnit.test('player events during prerolls are prefixed', function(assert) {
assert.strictEqual(prefixed.callCount, 6, 'prefixed events fired');
});

QUnit.test('player events during midrolls are prefixed', function(assert) {
QUnit.test('player events during midrolls are prefixed if tech is reused for ad', function(assert) {
var prefixed, unprefixed;

assert.expect(2);
Expand All @@ -899,6 +903,10 @@ QUnit.test('player events during midrolls are prefixed', function(assert) {
this.player.trigger('adtimeout');
this.player.ads.startLinearAdMode();

this.player.ads.snapshot = {
currentSrc: 'something'
};

// simulate video events that should be prefixed
this.player.on(['loadstart', 'playing', 'pause', 'ended', 'firstplay', 'loadedalldata'], unprefixed);
this.player.on(['adloadstart', 'adplaying', 'adpause', 'adended', 'adfirstplay', 'adloadedalldata'], prefixed);
Expand All @@ -912,7 +920,7 @@ QUnit.test('player events during midrolls are prefixed', function(assert) {
assert.strictEqual(prefixed.callCount, 6, 'prefixed events fired');
});

QUnit.test('player events during postrolls are prefixed', function(assert) {
QUnit.test('player events during postrolls are prefixed if tech is reused for ad', function(assert) {
var prefixed, unprefixed;

assert.expect(2);
Expand All @@ -927,6 +935,39 @@ QUnit.test('player events during postrolls are prefixed', function(assert) {
this.player.trigger('ended');
this.player.ads.startLinearAdMode();

this.player.ads.snapshot = {
currentSrc: 'something'
};

// simulate video events that should be prefixed
this.player.on(['loadstart', 'playing', 'pause', 'ended', 'firstplay', 'loadedalldata'], unprefixed);
this.player.on(['adloadstart', 'adplaying', 'adpause', 'adended', 'adfirstplay', 'adloadedalldata'], prefixed);
this.player.trigger('firstplay');
this.player.trigger('loadstart');
this.player.trigger('playing');
this.player.trigger('loadedalldata');
this.player.trigger('pause');
this.player.trigger('ended');
assert.strictEqual(unprefixed.callCount, 0, 'no unprefixed events fired');
assert.strictEqual(prefixed.callCount, 6, 'prefixed events fired');
});

QUnit.test('player events during stitched ads are prefixed', function(assert) {
var prefixed, unprefixed;

assert.expect(2);

prefixed = sinon.spy();
unprefixed = sinon.spy();

this.player.ads.stitchedAds(true);

// play a midroll
this.player.trigger('play');
this.player.trigger('adsready');
this.player.trigger('adtimeout');
this.player.ads.startLinearAdMode();

// simulate video events that should be prefixed
this.player.on(['loadstart', 'playing', 'pause', 'ended', 'firstplay', 'loadedalldata'], unprefixed);
this.player.on(['adloadstart', 'adplaying', 'adpause', 'adended', 'adfirstplay', 'adloadedalldata'], prefixed);
Expand Down