1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 | 1× 1× 1× 1× 1× 1× 1× 1× 1× 1× 1× 62× 62× 62× 62× 62× 62× 62× 62× 8× 62× 1× 61× 1× 60× 60× 60× 60× 21× 4× 2× 60× 60× 7× 7× 60× 4× 2× 5× 60× 1× 50× 50× 49× 49× 50× 48× 46× 2× 1× 49× 21× 1× 49× 49× 39× 39× 1× 49× 9× 3× 6× 6× 6× 2× 1× 47× 43× 4× 1× 43× 42× 42× 42× 42× 40× 2× 1× 1× 43× 1× 40× 40× 40× 2× 40× 1× 39× 39× 39× 40× 1× 42× 42× 1× 41× 6× 1× 57× 57× 1× 40× 1× 40× 1× 19× 1× 18× 18× 18× 18× 6× 18× 18× 17× 18× 18× 18× 1× 6× 6× 1× 18× 18× 1× 17× 17× 17× 17× 17× 17× 1× 2× 1× 1× 1× 1× 15× 15× 15× 4× 4× 2× 4× 11× 1× 17× 17× 1× 50× 50× 50× 1× 18× 18× 18× 18× 18× | 'use strict'; var VASTClient = require('../ads/vast/VASTClient'); var VASTError = require('../ads/vast/VASTError'); var vastUtil = require('../ads/vast/vastUtil'); var VASTIntegrator = require('../ads/vast/VASTIntegrator'); var VPAIDIntegrator = require('../ads/vpaid/VPAIDIntegrator'); var async = require('../utils/async'); var dom = require('../utils/dom'); var playerUtils = require('../utils/playerUtils'); var utilities = require('../utils/utilityFunctions'); var logger = require ('../utils/consoleLogger'); module.exports = function VASTPlugin(options) { var snapshot; var player = this; var vast = new VASTClient(); var adsCanceled = false; var defaultOpts = { // maximum amount of time in ms to wait to receive `adsready` from the ad // implementation after play has been requested. Ad implementations are // expected to load any dynamic libraries and make any requests to determine // ad policies for a video during this time. timeout: 500, //TODO:finish this IOS FIX //Whenever you play an add on IOS, the native player kicks in and we loose control of it. On very heavy pages the 'play' event // May occur after the video content has already started. This is wrong if you want to play a preroll ad that needs to happen before the user // starts watching the content. To prevent this usec iosPrerollCancelTimeout: 2000, // maximun amount of time for the ad to actually start playing. If this timeout gets // triggered the ads will be cancelled adCancelTimeout: 3000, // Boolean flag that configures the player to play a new ad before the user sees the video again // the current video playAdAlways: false, // Flag to enable or disable the ads by default. adsEnabled: true, // Boolean flag to enable or disable the resize with window.resize or orientationchange autoResize: true, // Path to the VPAID flash ad's loader vpaidFlashLoaderPath: '/VPAIDFlash.swf', // verbosity of console logging: // 0 - error // 1 - error, warn // 2 - error, warn, info // 3 - error, warn, info, log // 4 - error, warn, info, log, debug verbosity: 0 }; var settings = utilities.extend({}, defaultOpts, options || {}); Iif(utilities.isUndefined(settings.adTagUrl) && utilities.isDefined(settings.url)){ settings.adTagUrl = settings.url; } if (utilities.isString(settings.adTagUrl)) { settings.adTagUrl = utilities.echoFn(settings.adTagUrl); } if (utilities.isDefined(settings.adTagXML) && !utilities.isFunction(settings.adTagXML)) { return trackAdError(new VASTError('on VideoJS VAST plugin, the passed adTagXML option does not contain a function')); } if (!utilities.isDefined(settings.adTagUrl) && !utilities.isFunction(settings.adTagXML)) { return trackAdError(new VASTError('on VideoJS VAST plugin, missing adTagUrl on options object')); } logger.setVerbosity (settings.verbosity); vastUtil.runFlashSupportCheck(settings.vpaidFlashLoaderPath);// Necessary step for VPAIDFLASHClient to work. playerUtils.prepareForAds(player); if (settings.playAdAlways) { // No matter what happens we play a new ad before the user sees the video again. player.on('vast.contentEnd', function () { setTimeout(function () { player.trigger('vast.reset'); }, 0); }); } player.on('vast.firstPlay', tryToPlayPrerollAd); player.on('vast.reset', function () { //If we are reseting the plugin, we don't want to restore the content snapshot = null; cancelAds(); }); player.vast = { isEnabled: function () { return settings.adsEnabled; }, enable: function () { settings.adsEnabled = true; }, disable: function () { settings.adsEnabled = false; } }; return player.vast; /**** Local functions ****/ function tryToPlayPrerollAd() { //We remove the poster to prevent flickering whenever the content starts playing playerUtils.removeNativePoster(player); playerUtils.once(player, ['vast.adsCancel', 'vast.adEnd'], function () { removeAdUnit(); restoreVideoContent(); }); async.waterfall([ checkAdsEnabled, preparePlayerForAd, startAdCancelTimeout, playPrerollAd ], function (error, response) { if (error) { trackAdError(error, response); } else { player.trigger('vast.adEnd'); } }); /*** Local functions ***/ function removeAdUnit() { if (player.vast && player.vast.adUnit) { player.vast.adUnit = null; //We remove the adUnit } } function restoreVideoContent() { setupContentEvents(); if (snapshot) { playerUtils.restorePlayerSnapshot(player, snapshot); snapshot = null; } } function setupContentEvents() { playerUtils.once(player, ['playing', 'vast.reset', 'vast.firstPlay'], function (evt) { if (evt.type !== 'playing') { return; } player.trigger('vast.contentStart'); playerUtils.once(player, ['ended', 'vast.reset', 'vast.firstPlay'], function (evt) { if (evt.type === 'ended') { player.trigger('vast.contentEnd'); } }); }); } function checkAdsEnabled(next) { if (settings.adsEnabled) { return next(null); } next(new VASTError('Ads are not enabled')); } function preparePlayerForAd(next) { if (canPlayPrerollAd()) { snapshot = playerUtils.getPlayerSnapshot(player); player.pause(); addSpinnerIcon(); if(player.paused()) { next(null); } else { playerUtils.once(player, ['playing'], function() { player.pause(); next(null); }); } } else { next(new VASTError('video content has been playing before preroll ad')); } } function canPlayPrerollAd() { return !utilities.isIPhone() || player.currentTime() <= settings.iosPrerollCancelTimeout; } function startAdCancelTimeout(next) { var adCancelTimeoutId; adsCanceled = false; adCancelTimeoutId = setTimeout(function () { trackAdError(new VASTError('timeout while waiting for the video to start playing', 402)); }, settings.adCancelTimeout); playerUtils.once(player, ['vast.adStart', 'vast.adsCancel'], clearAdCancelTimeout); /*** local functions ***/ function clearAdCancelTimeout() { Eif (adCancelTimeoutId) { clearTimeout(adCancelTimeoutId); adCancelTimeoutId = null; } } next(null); } function addSpinnerIcon() { dom.addClass(player.el(), 'vjs-vast-ad-loading'); playerUtils.once(player, ['vast.adStart', 'vast.adsCancel'], removeSpinnerIcon); } function removeSpinnerIcon() { //IMPORTANT NOTE: We remove the spinnerIcon asynchronously to give time to the browser to start the video. // If we remove it synchronously we see a flash of the content video before the ad starts playing. setTimeout(function () { dom.removeClass(player.el(), 'vjs-vast-ad-loading'); }, 100); } } function cancelAds() { player.trigger('vast.adsCancel'); adsCanceled = true; } function playPrerollAd(callback) { async.waterfall([ getVastResponse, playAd ], callback); } function getVastResponse(callback) { vast.getVASTResponse(settings.adTagUrl ? settings.adTagUrl() : settings.adTagXML, callback); } function playAd(vastResponse, callback) { //TODO: Find a better way to stop the play. The 'playPrerollWaterfall' ends in an inconsistent situation //If the state is not 'preroll?' it means the ads were canceled therefore, we break the waterfall if (adsCanceled) { return; } var adIntegrator = isVPAID(vastResponse) ? new VPAIDIntegrator(player, settings) : new VASTIntegrator(player); var adFinished = false; playerUtils.once(player, ['vast.adStart', 'vast.adsCancel'], function (evt) { if (evt.type === 'vast.adStart') { addAdsLabel(); } }); playerUtils.once(player, ['vast.adEnd', 'vast.adsCancel'], removeAdsLabel); if (utilities.isIDevice()) { preventManualProgress(); } player.vast.vastResponse = vastResponse; logger.debug ("calling adIntegrator.playAd() with vastResponse:", vastResponse); player.vast.adUnit = adIntegrator.playAd(vastResponse, callback); /*** Local functions ****/ function addAdsLabel() { Iif (adFinished || player.controlBar.getChild('AdsLabel')) { return; } player.controlBar.addChild('AdsLabel'); } function removeAdsLabel() { player.controlBar.removeChild('AdsLabel'); adFinished = true; } function preventManualProgress() { //IOS video clock is very unreliable and we need a 3 seconds threshold to ensure that the user forwarded/rewound the ad var PROGRESS_THRESHOLD = 3; var previousTime = 0; var skipad_attempts = 0; player.on('timeupdate', preventAdSeek); player.on('ended', preventAdSkip); playerUtils.once(player, ['vast.adEnd', 'vast.adsCancel', 'vast.adError'], stopPreventManualProgress); /*** Local functions ***/ function preventAdSkip() { // Ignore ended event if the Ad time was not 'near' the end // and revert time to the previous 'valid' time if ((player.duration() - previousTime) > PROGRESS_THRESHOLD) { player.pause(true); // this reduce the video jitter if the IOS skip button is pressed player.play(true); // we need to trigger the play to put the video element back in a valid state player.currentTime(previousTime); } } function preventAdSeek() { var currentTime = player.currentTime(); var progressDelta = Math.abs(currentTime - previousTime); if (progressDelta > PROGRESS_THRESHOLD) { skipad_attempts += 1; if (skipad_attempts >= 2) { player.pause(true); } player.currentTime(previousTime); } else { previousTime = currentTime; } } function stopPreventManualProgress() { player.off('timeupdate', preventAdSeek); player.off('ended', preventAdSkip); } } } function trackAdError(error, vastResponse) { player.trigger({type: 'vast.adError', error: error}); cancelAds(); logger.error ('AD ERROR:', error.message, error, vastResponse); } function isVPAID(vastResponse) { var i, len; var mediaFiles = vastResponse.mediaFiles; for (i = 0, len = mediaFiles.length; i < len; i++) { Iif (vastUtil.isVPAID(mediaFiles[i])) { return true; } } return false; } }; |