Skip to content

Commit

Permalink
Video support in PubMatic adapter (#2807)
Browse files Browse the repository at this point in the history
* merging latest prebid master with video changes

* fixed linting errors

* moved data types to constants

* fixes for code review comments on PR
  • Loading branch information
PubMatic-OpenWrap authored and jaiminpanchal27 committed Jul 10, 2018
1 parent 8995c80 commit 4ffc421
Show file tree
Hide file tree
Showing 4 changed files with 368 additions and 14 deletions.
132 changes: 121 additions & 11 deletions modules/pubmaticBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as utils from 'src/utils';
import { registerBidder } from 'src/adapters/bidderFactory';
import { BANNER, VIDEO } from 'src/mediaTypes';
const constants = require('src/constants.json');

const BIDDER_CODE = 'pubmatic';
Expand All @@ -18,6 +19,28 @@ const CUSTOM_PARAMS = {
'profId': '', // OpenWrap Legacy: Profile ID
'verId': '' // OpenWrap Legacy: version ID
};
const DATA_TYPES = {
'NUMBER': 'number',
'STRING': 'string',
'BOOLEAN': 'boolean',
'ARRAY': 'array'
};
const VIDEO_CUSTOM_PARAMS = {
'mimes': DATA_TYPES.ARRAY,
'minduration': DATA_TYPES.NUMBER,
'maxduration': DATA_TYPES.NUMBER,
'startdelay': DATA_TYPES.NUMBER,
'playbackmethod': DATA_TYPES.ARRAY,
'api': DATA_TYPES.ARRAY,
'protocols': DATA_TYPES.ARRAY,
'w': DATA_TYPES.NUMBER,
'h': DATA_TYPES.NUMBER,
'battr': DATA_TYPES.ARRAY,
'linearity': DATA_TYPES.NUMBER,
'placement': DATA_TYPES.NUMBER,
'minbitrate': DATA_TYPES.NUMBER,
'maxbitrate': DATA_TYPES.NUMBER
}
const NET_REVENUE = false;
const dealChannelValues = {
1: 'PMP',
Expand Down Expand Up @@ -152,27 +175,90 @@ function _createOrtbTemplate(conf) {
};
}

// similar functionality as parseSlotParam. Check if the 2 functions can be clubbed.
function _checkParamDataType(key, value, datatype) {
var errMsg = 'PubMatic: Ignoring param key: ' + key + ', expects ' + datatype + ', found ' + typeof value;
switch (datatype) {
case DATA_TYPES.BOOLEAN:
if (!utils.isBoolean(value)) {
utils.logWarn(errMsg);
return UNDEFINED;
}
return value;
case DATA_TYPES.NUMBER:
if (!utils.isNumber(value)) {
utils.logWarn(errMsg);
return UNDEFINED;
}
return value;
case DATA_TYPES.STRING:
if (!utils.isStr(value)) {
utils.logWarn(errMsg);
return UNDEFINED;
}
return value;
case DATA_TYPES.ARRAY:
if (!utils.isArray(value)) {
utils.logWarn(errMsg);
return UNDEFINED;
}
return value;
}
}

function _createImpressionObject(bid, conf) {
return {
var impObj = {};
var bannerObj = {};
var videoObj = {};

impObj = {
id: bid.bidId,
tagid: bid.params.adUnit,
bidfloor: _parseSlotParam('kadfloor', bid.params.kadfloor),
secure: window.location.protocol === 'https:' ? 1 : 0,
banner: {
ext: {
pmZoneId: _parseSlotParam('pmzoneid', bid.params.pmzoneid)
}
};

if (bid.params.hasOwnProperty('video')) {
var videoData = bid.params.video;

for (var key in VIDEO_CUSTOM_PARAMS) {
if (videoData.hasOwnProperty(key)) {
videoObj[key] = _checkParamDataType(key, videoData[key], VIDEO_CUSTOM_PARAMS[key])
}
}
// read playersize and assign to h and w.
if (utils.isArray(bid.mediaTypes.video.playerSize[0])) {
videoObj.w = bid.mediaTypes.video.playerSize[0][0];
videoObj.h = bid.mediaTypes.video.playerSize[0][1];
} else if (utils.isNumber(bid.mediaTypes.video.playerSize[0])) {
videoObj.w = bid.mediaTypes.video.playerSize[0];
videoObj.h = bid.mediaTypes.video.playerSize[1];
}
if (bid.params.video.hasOwnProperty('skippable')) {
videoObj.ext = {
'video_skippable': bid.params.video.skippable ? 1 : 0
}
}

impObj.video = videoObj;
} else {
bannerObj = {
pos: 0,
w: bid.params.width,
h: bid.params.height,
topframe: utils.inIframe() ? 0 : 1,
},
ext: {
pmZoneId: _parseSlotParam('pmzoneid', bid.params.pmzoneid)
}
};
impObj.banner = bannerObj;
}
return impObj;
}

export const spec = {
code: BIDDER_CODE,

supportedMediaTypes: [BANNER, VIDEO],
/**
* Determines whether or not the given bid request is valid. Valid bid request must have placementId and hbid
*
Expand All @@ -189,6 +275,13 @@ export const spec = {
utils.logWarn('PubMatic: adSlotId is mandatory and cannot be numeric. Call to OpenBid will not be sent.');
return false;
}
// video ad validation
if (bid.params.hasOwnProperty('video')) {
if (!bid.params.video.hasOwnProperty('mimes') || !utils.isArray(bid.params.video.mimes) || bid.params.video.mimes.length === 0) {
utils.logWarn('PubMatic: For video ads, mimes is mandatory and must specify atlease 1 mime value. Call to OpenBid will not be sent.');
return false;
}
}
return true;
}
return false;
Expand All @@ -205,9 +298,16 @@ export const spec = {
var payload = _createOrtbTemplate(conf);
validBidRequests.forEach(bid => {
_parseAdSlot(bid);
if (!(bid.params.adSlot && bid.params.adUnit && bid.params.adUnitIndex && bid.params.width && bid.params.height)) {
utils.logWarn('PubMatic: Skipping the non-standard adslot:', bid.params.adSlot, bid);
return;
if (bid.params.hasOwnProperty('video')) {
if (!(bid.params.adSlot && bid.params.adUnit && bid.params.adUnitIndex)) {
utils.logWarn('PubMatic: Skipping the non-standard adslot: ', bid.params.adSlot, bid);
return;
}
} else {
if (!(bid.params.adSlot && bid.params.adUnit && bid.params.adUnitIndex && bid.params.width && bid.params.height)) {
utils.logWarn('PubMatic: Skipping the non-standard adslot: ', bid.params.adSlot, bid);
return;
}
}
conf.pubId = conf.pubId || bid.params.publisherId;
conf = _handleCustomParams(bid.params, conf);
Expand Down Expand Up @@ -287,7 +387,17 @@ export const spec = {
referrer: utils.getTopWindowUrl(),
ad: bid.adm
};

let parsedRequest = JSON.parse(request.data);
if (parsedRequest.imp && parsedRequest.imp.length > 0) {
parsedRequest.imp.forEach(req => {
if (bid.impid === req.id && req.hasOwnProperty('video')) {
newBid.mediaType = 'video';
newBid.width = bid.hasOwnProperty('w') ? bid.w : req.video.w;
newBid.height = bid.hasOwnProperty('h') ? bid.h : req.video.h;
newBid.vastXml = bid.adm;
}
});
}
if (bid.ext && bid.ext.deal_channel) {
newBid['dealChannel'] = dealChannelValues[bid.ext.deal_channel] || null;
}
Expand Down
52 changes: 49 additions & 3 deletions modules/pubmaticBidAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ Maintainer: [email protected]

Connects to PubMatic exchange for bids.

PubMatic bid adapter supports Banner currently.
PubMatic bid adapter supports Video and Banner currently.

# Sample Ad Unit: For Publishers
# Sample Banner Ad Unit: For Publishers
```
var adUnits = [
{
Expand All @@ -35,7 +35,43 @@ var adUnits = [
kadfloor: '0.50' // optional
}
}]
}
}];
```

# Sample Video Ad Unit: For Publishers
```
var adVideoAdUnits = [
{
code: 'test-div-video',
mediaTypes: {
video: {
playerSize: [640, 480], // required
context: 'instream'
}
},
bids: [{
bidder: 'pubmatic',
params: {
publisherId: '351', // required
adSlot: '1363568@300x250', // required
video: {
mimes: ['video/mp4','video/x-flv'], // required
skippable: true, // optional
minduration: 5, // optional
maxduration: 30, // optional
startdelay: 5, // optional
playbackmethod: [1,3], // optional
api: [ 1, 2 ], // optional
protocols: [ 2, 3 ], // optional
battr: [ 13, 14 ], // optional
linearity: 1, // optional
placement: 2, // optional
minbitrate: 10, // optional
maxbitrate: 10 // optional
}
}
}]
}]
```

### Configuration
Expand All @@ -49,5 +85,15 @@ pbjs.setConfig({
enabledBidders: ['pubmatic'],
syncDelay: 6000
}});


For Video ads, prebid cache needs to be enabled for PubMatic adapter.
pbjs.setConfig({
debug: true,
cache: {
url: 'https://prebid.adnxs.com/pbc/v1/cache'
}
});

```
Note: Combine the above the configuration with any other UserSync configuration. Multiple setConfig() calls overwrite each other and only last call for a given attribute will take effect.
5 changes: 5 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var t_Str = 'String';
var t_Fn = 'Function';
var t_Numb = 'Number';
var t_Object = 'Object';
var t_Boolean = 'Boolean';
var toString = Object.prototype.toString;
let infoLogger = null;
let warnLogger = null;
Expand Down Expand Up @@ -423,6 +424,10 @@ exports.isPlainObject = function(object) {
return exports.isA(object, t_Object);
}

exports.isBoolean = function(object) {
return exports.isA(object, t_Boolean);
}

/**
* Return if the object is "empty";
* this includes falsey, no keys, or no items at indices
Expand Down
Loading

0 comments on commit 4ffc421

Please sign in to comment.