Skip to content

Commit

Permalink
Ad state refactor (#315)
Browse files Browse the repository at this point in the history
  • Loading branch information
incompl committed Feb 23, 2018
1 parent f4e4440 commit d11ef39
Show file tree
Hide file tree
Showing 43 changed files with 2,087 additions and 1,054 deletions.
171 changes: 85 additions & 86 deletions README.md

Large diffs are not rendered by default.

Binary file removed ad-states.graffle
Binary file not shown.
Binary file modified ad-states.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 5 additions & 3 deletions example/app.js
Expand Up @@ -68,11 +68,13 @@
li.className = 'content-event';
}

str = '[' + (d) + '] ' + padRight(19, '[' + (event.state ? event.state : player.ads.state + '*') + ']', ' ') + ' ' + evt;
str = evt;

if (evt === 'contentupdate') {
str += "\toldValue: " + event.oldValue + "\n" +
"\tnewValue: " + event.newValue + "\n";
str += ' ' + event.oldValue + " -> " + event.newValue;
li.className = 'content-adplugin-event';
}
if (evt === 'contentchanged') {
li.className = 'content-adplugin-event';
}
if (evt === 'contentplayback') {
Expand Down
13 changes: 7 additions & 6 deletions example/example-integration.js
Expand Up @@ -92,15 +92,16 @@
// initialize the ads plugin, passing in any relevant options
player.ads(options);

// request ad inventory whenever the player gets new content to play
player.on('contentupdate', requestAds);
// if there's already content loaded, request an add immediately
if (player.currentSrc()) {
// request ads right away
requestAds();

// request ad inventory whenever the player gets content to play
player.on('contentchanged', () => {
requestAds();
}
});

player.on('contentended', function() {
if (!state.postrollPlayed && player.ads.state === 'postroll?' && playPostroll) {
if (!state.postrollPlayed && playPostroll) {
state.postrollPlayed = true;
playAd();
}
Expand Down
Binary file added logo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 53 additions & 0 deletions migration-guides/migrating-to-6.0.md
@@ -0,0 +1,53 @@
# Migrating to videojs-contrib-ads 6.0.0

Version 6 of videojs-contrib-ads includes a major refactor and cleanup of the state management logic.

## Migration

* Timeouts have a more intuitive behavior. See the next section for more information.
* Ended events are no longer delayed by 1 second.
* Ended events due to an ad ending will no longer be allowed to replace the ended event
that is triggered by linear ad mode ending. Integrations must not emit ended events
after the end of linear ad mode.
* There will no longer be a `contentended` event when content ends after the first time content ends.
* `ads.state` has been removed. Methods have been added to replace state checks, such as `ads.isInAdMode()`. See the README for a list of available methods. `ads._state` has been
added, but it is not compatible with the old `ads.state` and should not be inspected
by integrations.
* The event parameter `triggerevent` has been removed. It is unlikely that integrations used it, but any usage must be migrated.
* We no longer trigger a `readyforpreroll` event after receiving a `nopreroll` event.
* adTimeoutTimeout has been removed. It was not part of the documented interface, but make note if your integration inspected it.
* There is no longer a snapshot object while checking for postrolls. Now a snapshot is only taken when a postroll ad break actually begins.
* The `contentplayback` event (removed in [4.0.0](https://github.com/videojs/videojs-contrib-ads/blob/cc664517aa0d07398decc0aa5d41974330efc4e4/CHANGELOG.md#400), re-added as deprecated in [4.1.1](https://github.com/videojs/videojs-contrib-ads/blob/cc664517aa0d07398decc0aa5d41974330efc4e4/CHANGELOG.md#411)), has been removed. Use the `playing` event instead.

## Deprecation

Deprecated interfaces will be removed in a future major version update.

* `contentupdate` is now deprecated. It has been replaced by `contentchanged`. `contentupdate` was never intended to fire for the initial source, but over time its behavior eroded. To make migration easier for anyone who depends on the current behavior, we're providing a deprecation period and a new event with correct behavior.
* `adscanceled` is now deprecated. Instead, use `nopreroll` and `nopostroll`. `adscanceled` was initially intended to function similarly to calling both `nopreroll` and `nopostroll` but it was never fully implemented.

## Timeout behavior changes

Previous behavior:

* The `timeout` setting was the number of milliseconds that we waited for `adsready` after the `play` event if `adsready` was not before `play`.
* The `prerollTimeout` setting was the number of milliseconds we waited for `startLinearAdMode` after `readyforpreroll`. It was a separate timeout period after `timeout`.
* The `postrollTimeout` setting was the number of milliseconds we waited for `startLinearAdMode` after `contentended`.

Previous Defaults:

* timeout: 5000
* prerollTimeout: 100
* postrollTimeout: 100

New Behavior:

* The `timeout` setting is now the default setting for all timeouts. It can be overridden by `prerollTimeout` and/or `postrollTimeout`.
* `prerollTimeout` overrides `timeout` for the number of milliseconds we wait for a preroll ad (the time between `play` and `startLinearAdMode`).
* `postrollTimeout` overrides `timeout` for the number of milliseconds we wait for a postroll ad (the time between `contentended` and `startLinearAdMode`).

New Defaults:

* timeout: 5000
* prerollTimeout: no default
* postrollTimeout: no default
5 changes: 3 additions & 2 deletions package.json
Expand Up @@ -75,7 +75,7 @@
"node-sass": "^4.5.3",
"node-static": "^0.7.9",
"npm-run-all": "^4.0.2",
"qunitjs": "^1.23.1",
"qunitjs": "^2.1.1",
"rimraf": "^2.6.1",
"rollup": "^0.50",
"rollup-plugin-babel": "^2.7.1",
Expand All @@ -96,7 +96,8 @@
"eslintConfig": {
"extends": "videojs",
"rules": {
"require-jsdoc": "off"
"require-jsdoc": "off",
"consistent-this": "off"
}
},
"files": [
Expand Down
2 changes: 1 addition & 1 deletion scripts/umd.rollup.config.js
Expand Up @@ -40,7 +40,7 @@ export default {
}]
],
plugins: [
// 'external-helpers',
'external-helpers',
'transform-object-assign'
]
})
Expand Down
76 changes: 76 additions & 0 deletions src/adBreak.js
@@ -0,0 +1,76 @@
/*
* Encapsulates logic for starting and ending ad breaks. An ad break
* is the time between startLinearAdMode and endLinearAdMode. The ad
* integration may play 0 or more ads during this time.
*/

import * as snapshot from './snapshot.js';

function start(player) {

player.ads.debug('Starting ad break');

player.ads._inLinearAdMode = true;

// No longer does anything, used to move us to ad-playback
player.trigger('adstart');

// Capture current player state snapshot
if (!player.ads.shouldPlayContentBehindAd(player)) {
player.ads.snapshot = snapshot.getPlayerSnapshot(player);
}

// Mute the player behind the ad
if (player.ads.shouldPlayContentBehindAd(player)) {
player.ads.preAdVolume_ = player.volume();
player.volume(0);
}

// Add css to the element to indicate and ad is playing.
player.addClass('vjs-ad-playing');

// We should remove the vjs-live class if it has been added in order to
// show the adprogress control bar on Android devices for falsely
// determined LIVE videos due to the duration incorrectly reported as Infinity
if (player.hasClass('vjs-live')) {
player.removeClass('vjs-live');
}

// This removes the native poster so the ads don't show the content
// poster if content element is reused for ad playback. The snapshot
// will restore it afterwards.
player.ads.removeNativePoster();
}

function end(player) {
player.ads.debug('Ending ad break');

player.ads.adType = null;

player.ads._inLinearAdMode = false;

// Signals the end of the ad break to anyone listening.
player.trigger('adend');

player.removeClass('vjs-ad-playing');

// We should add the vjs-live class back if the video is a LIVE video
// If we dont do this, then for a LIVE Video, we will get an incorrect
// styled control, which displays the time for the video
if (player.ads.isLive(player)) {
player.addClass('vjs-live');
}
if (!player.ads.shouldPlayContentBehindAd(player)) {
snapshot.restorePlayerSnapshot(player, player.ads.snapshot);
}

// Reset the volume to pre-ad levels
if (player.ads.shouldPlayContentBehindAd(player)) {
player.volume(player.ads.preAdVolume_);
}

}

const obj = {start, end};

export default obj;
8 changes: 5 additions & 3 deletions src/cancelContentPlay.js
Expand Up @@ -5,8 +5,6 @@ It does this by pausing the player immediately after a "play" where ads will be
then signalling that we should play after the ad is done.
*/

import window from 'global/window';

export default function cancelContentPlay(player) {
if (player.ads.cancelPlayTimeout) {
// another cancellation is already in flight, so do nothing
Expand All @@ -16,10 +14,14 @@ export default function cancelContentPlay(player) {
// The timeout is necessary because pausing a video element while processing a `play`
// event on iOS can cause the video element to continuously toggle between playing and
// paused states.
player.ads.cancelPlayTimeout = window.setTimeout(function() {
player.ads.cancelPlayTimeout = player.setTimeout(function() {
// deregister the cancel timeout so subsequent cancels are scheduled
player.ads.cancelPlayTimeout = null;

if (!player.ads.isInAdMode()) {
return;
}

// pause playback so ads can be handled.
if (!player.paused()) {
player.pause();
Expand Down
17 changes: 12 additions & 5 deletions src/contentupdate.js
Expand Up @@ -2,8 +2,6 @@
This feature sends a `contentupdate` event when the player source changes.
*/

import window from 'global/window';

// Start sending contentupdate events
export default function initializeContentupdate(player) {

Expand All @@ -13,24 +11,33 @@ export default function initializeContentupdate(player) {
// modifying the player's source
player.ads.contentSrc = player.currentSrc();

player.ads._seenInitialLoadstart = false;

// Check if a new src has been set, if so, trigger contentupdate
const checkSrc = function() {
if (player.ads.state !== 'ad-playback') {
if (!player.ads.inAdBreak()) {
const src = player.currentSrc();

if (src !== player.ads.contentSrc) {

if (player.ads._seenInitialLoadstart) {
player.trigger({
type: 'contentchanged'
});
}

player.trigger({
type: 'contentupdate',
oldValue: player.ads.contentSrc,
newValue: src
});
player.ads.contentSrc = src;
}

player.ads._seenInitialLoadstart = true;
}
};

// loadstart reliably indicates a new src has been set
player.on('loadstart', checkSrc);
// check immediately in case we missed the loadstart
window.setTimeout(checkSrc, 1);
}

0 comments on commit d11ef39

Please sign in to comment.