Skip to content

Commit

Permalink
Merge pull request #7 from jwplayer/feat/AD-1499
Browse files Browse the repository at this point in the history
[AD-1499] Integrate Video Module with Ad Server
  • Loading branch information
karimMourra authored Oct 14, 2021
2 parents f474b41 + b473747 commit 00977b0
Show file tree
Hide file tree
Showing 12 changed files with 390 additions and 101 deletions.
22 changes: 22 additions & 0 deletions modules/gamAdServerSubmodule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { GAM_VENDOR } from './videoModule/constants/vendorCodes.js';
import { adServerDirectory } from './videoModule/vendorDirectory.js';

function GamAdServerProvider(dfpModule_) {
const dfp = dfpModule_;

function getAdTagUrl(adUnit, baseAdTag) {
return dfp.buildVideoUrl({ adUnit: adUnit, url: baseAdTag });
}

return {
getAdTagUrl
}
}
function gamSubmoduleFactory() {
const dfp = $$PREBID_GLOBAL$$.adServers.dfp;
const gamProvider = GamAdServerProvider(dfp);
return gamProvider;
}

gamSubmoduleFactory.vendorCode = GAM_VENDOR;
adServerDirectory[GAM_VENDOR] = gamSubmoduleFactory;
8 changes: 4 additions & 4 deletions modules/jwplayerVideoProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from './videoModule/constants/events.js';
import stateFactory from './videoModule/shared/state.js';
import { JWPLAYER_VENDOR } from './videoModule/constants/vendorCodes.js';
import { vendorDirectory } from './videoModule/vendorDirectory.js';
import { videoVendorDirectory } from './videoModule/vendorDirectory.js';

export function JWPlayerProvider(config, jwplayer_, adState_, timeState_, callbackStorage_, utils) {
const jwplayer = jwplayer_;
Expand Down Expand Up @@ -124,11 +124,11 @@ export function JWPlayerProvider(config, jwplayer_, adState_, timeState_, callba
}
}

function setAdTagUrl(adTagUrl) {
function setAdTagUrl(adTagUrl, options) {
if (!player) {
return;
}
player.playAd(adTagUrl);
player.playAd(adTagUrl || options.adXml, options);
}

function onEvents(events, callback) {
Expand Down Expand Up @@ -632,7 +632,7 @@ const jwplayerSubmoduleFactory = function (config) {
}

jwplayerSubmoduleFactory.vendorCode = JWPLAYER_VENDOR;
vendorDirectory[JWPLAYER_VENDOR] = jwplayerSubmoduleFactory;
videoVendorDirectory[JWPLAYER_VENDOR] = jwplayerSubmoduleFactory;
export default jwplayerSubmoduleFactory;

// HELPERS
Expand Down
28 changes: 28 additions & 0 deletions modules/videoModule/adServer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { adServerDirectory } from './vendorDirectory.js';
import { ParentModule, SubmoduleBuilder } from './shared/parentModule.js';

export function AdServerCore(parentModule_) {
const parentModule = parentModule_;

function registerAdServer(config) {
const vendorCode = config.vendorCode;
parentModule.registerSubmodule(vendorCode, vendorCode, config);
}

function getAdTagUrl(vendorCode, adUnit, baseAdTagUrl) {
const submodule = parentModule.getSubmodule(vendorCode);
return submodule && submodule.getAdTagUrl(adUnit, baseAdTagUrl);
}

return {
registerAdServer,
getAdTagUrl
}
}

export function coreAdServerFactory() {
const adServerSubmoduleBuilder = SubmoduleBuilder(adServerDirectory);
const parentModule = ParentModule(adServerSubmoduleBuilder);
const adServerCore = AdServerCore(parentModule);
return adServerCore;
}
4 changes: 4 additions & 0 deletions modules/videoModule/constants/vendorCodes.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
// Video Vendors
export const JWPLAYER_VENDOR = 1;
export const VIDEO_JS_VENDOR = 2;

// Ad Server Vendors
export const GAM_VENDOR = 'gam';
57 changes: 14 additions & 43 deletions modules/videoModule/coreVideo.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,30 @@
import { vendorDirectory } from './vendorDirectory.js';
import { videoVendorDirectory } from './vendorDirectory.js';
import { ParentModule, SubmoduleBuilder } from './shared/parentModule.js';

export function VideoCore(submoduleBuilder_) {
const submodules = {};
const submoduleBuilder = submoduleBuilder_;
export function VideoCore(parentModule_) {
const parentModule = parentModule_;

function registerProvider(providerConfig) {
const divId = providerConfig.divId;
if (submodules[divId]) {
return;
}

let submodule;
try {
submodule = submoduleBuilder.build(providerConfig);
} catch (e) {
throw e;
}
submodules[divId] = submodule;
parentModule.registerSubmodule(providerConfig.divId, providerConfig.vendorCode, providerConfig);
}

function getOrtbParams(divId) {
const submodule = submodules[divId];
const submodule = parentModule.getSubmodule(divId);
return submodule && submodule.getOrtbParams();
}

function setAdTagUrl(adTagUrl, divId) {
const submodule = submodules[divId];
return submodule && submodule.setAdTagUrl(adTagUrl);
function setAdTagUrl(adTagUrl, divId, options) {
const submodule = parentModule.getSubmodule(divId);
return submodule && submodule.setAdTagUrl(adTagUrl, options);
}

function onEvents(events, callback, divId) {
const submodule = submodules[divId];
const submodule = parentModule.getSubmodule(divId);
return submodule && submodule.onEvents(events, callback);
}

function offEvents(events, callback, divId) {
const submodule = submodules[divId];
const submodule = parentModule.getSubmodule(divId);
return submodule && submodule.offEvents(events, callback);
}

Expand All @@ -49,25 +38,7 @@ export function VideoCore(submoduleBuilder_) {
}

export function videoCoreFactory() {
const submoduleBuilder = VideoSubmoduleBuilder(vendorDirectory);
return VideoCore(submoduleBuilder);
}

export function VideoSubmoduleBuilder(vendorDirectory_) {
const vendorDirectory = vendorDirectory_;

function build(providerConfig) {
const submoduleFactory = vendorDirectory[providerConfig.vendorCode];
if (!submoduleFactory) {
throw new Error('Unrecognized vendor code');
}

const submodule = submoduleFactory(providerConfig);
submodule && submodule.init && submodule.init();
return submodule;
}

return {
build
};
const videoSubmoduleBuilder = SubmoduleBuilder(videoVendorDirectory);
const parentModule = ParentModule(videoSubmoduleBuilder);
return VideoCore(parentModule);
}
55 changes: 45 additions & 10 deletions modules/videoModule/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import {config} from '../../src/config.js';
import { config } from '../../src/config.js';
import events from '../../src/events.js';
import { allVideoEvents } from './constants/events.js';
import CONSTANTS from '../../src/constants.json';
import { videoCoreFactory } from './coreVideo.js';
import { coreAdServerFactory } from './adServer.js';

events.addEvents(allVideoEvents);

export function PbVideo(videoCore_, getConfig_, pbGlobal_, pbEvents_, videoEvents_) {
export function PbVideo(videoCore_, getConfig_, pbGlobal_, pbEvents_, videoEvents_, adServerCore_) {
const videoCore = videoCore_;
const getConfig = getConfig_;
const pbGlobal = pbGlobal_;
const requestBids = pbGlobal.requestBids;
const pbEvents = pbEvents_;
const videoEvents = videoEvents_;
const adServerCore = adServerCore_;

function init() {
getConfig('video', ({ video }) => {
Expand All @@ -23,18 +25,27 @@ export function PbVideo(videoCore_, getConfig_, pbGlobal_, pbEvents_, videoEvent
pbEvents.emit(type, payload);
}, provider.divId);
} catch (e) {}

const adServerConfig = provider.adServer;
if (adServerConfig) {
adServerCore.registerAdServer(adServerConfig.vendorCode, adServerConfig.params);
}
});
});

requestBids.before(enrichAdUnits, 40);

pbEvents.on(CONSTANTS.EVENTS.AUCTION_END, function(auctionResult) {
// TODO: requires AdServer Module.
// get ad tag from adServer - auctionResult.winningBids
// coreVideo.setAdTagUrl(adTag, divId);
auctionResult.adUnits.forEach(adUnit => {
if (adUnit.video) {
renderWinningBid(adUnit);
}
});
});
}

return { init };

function enrichAdUnits(nextFn, bidRequest) {
const adUnits = bidRequest.adUnits || pbGlobal.adUnits || [];
adUnits.forEach(adUnit => {
Expand All @@ -44,14 +55,38 @@ export function PbVideo(videoCore_, getConfig_, pbGlobal_, pbEvents_, videoEvent
return nextFn.call(this, bidRequest);
}

return { init };
function renderWinningBid(adUnit) {
const videoConfig = adUnit.video;
const divId = videoConfig.divId;
const adServerConfig = videoConfig.adServer;
let adTagUrl;
if (adServerConfig) {
adTagUrl = adServerCore.getAdTagUrl(adServerConfig.vendorCode, adUnit, adServerConfig.baseAdTagUrl);
}

const adUnitCode = adUnit.code;
const options = { adUnitCode };
if (adTagUrl) {
videoCore.setAdTagUrl(adTagUrl, divId, options);
return;
}

const highestCpmBids = pbGlobal.getHighestCpmBids(adUnit.code);
const highestBid = highestCpmBids && highestCpmBids.shift();
if (!highestBid) {
return;
}

adTagUrl = highestBid.vastUrl;
options.adXml = highestBid.vastXml;
videoCore.setAdTagUrl(adTagUrl, divId, options);
}
}

function pbVideoFactory() {
export function pbVideoFactory() {
const videoCore = videoCoreFactory();
const pbVideo = PbVideo(videoCore, config.getConfig, $$PREBID_GLOBAL$$, events, allVideoEvents);
const adServerCore = coreAdServerFactory();
const pbVideo = PbVideo(videoCore, config.getConfig, $$PREBID_GLOBAL$$, events, allVideoEvents, adServerCore);
pbVideo.init();
return pbVideo;
}

pbVideoFactory();
55 changes: 55 additions & 0 deletions modules/videoModule/shared/parentModule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@

/*
Used by any module to manage the relationship with its submodules.
*/
export function ParentModule(submoduleBuilder_) {
const submoduleBuilder = submoduleBuilder_;
const submodules = {};

/*
id: identifies the submodule instance
vendorCode: identifies the submodule type that must be built
config: additional information necessary to instantiate the instance
*/
function registerSubmodule(id, vendorCode, config) {
if (submodules[id]) {
return;
}

let submodule;
try {
submodule = submoduleBuilder.build(vendorCode, config);
} catch (e) {
throw e;
}
submodules[id] = submodule;
}

function getSubmodule(id) {
return submodules[id];
}

return {
registerSubmodule,
getSubmodule
}
}

export function SubmoduleBuilder(submoduleDirectory_) {
const submoduleDirectory = submoduleDirectory_;

function build(vendorCode, config) {
const submoduleFactory = submoduleDirectory[vendorCode];
if (!submoduleFactory) {
throw new Error('Unrecognized submodule vendor code: ' + vendorCode);
}

const submodule = submoduleFactory(config);
submodule && submodule.init && submodule.init();
return submodule;
}

return {
build
};
}
3 changes: 2 additions & 1 deletion modules/videoModule/vendorDirectory.js
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const vendorDirectory = {};
export const videoVendorDirectory = {};
export const adServerDirectory = {};
28 changes: 28 additions & 0 deletions test/spec/modules/videoModule/adServer_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { expect } from 'chai';
import { AdServerCore } from 'modules/videoModule/adServer.js';

describe('Ad Server Core', function () {
const parentModuleMock = {
registerSubmodule: sinon.spy(),
getSubmodule: sinon.spy()
};
const testVendorCode = 'test';
const adServerCore = AdServerCore(parentModuleMock);
adServerCore.registerAdServer({ vendorCode: testVendorCode });

describe('Register Ad Server', function () {
it('should use the vendor code as an id as well as a vendor code', function () {
expect(parentModuleMock.registerSubmodule.calledOnce).to.be.true;
expect(parentModuleMock.registerSubmodule.args[0][0]).to.be.equal(testVendorCode);
expect(parentModuleMock.registerSubmodule.args[0][1]).to.be.equal(testVendorCode);
});
});

describe('Get Ad Tag Url', function () {
it('should request the right submodule', function () {
adServerCore.getAdTagUrl(testVendorCode);
expect(parentModuleMock.getSubmodule.calledOnce).to.be.true;
expect(parentModuleMock.getSubmodule.args[0][0]).to.be.equal(testVendorCode);
});
});
});
Loading

0 comments on commit 00977b0

Please sign in to comment.