mk9-prebid
Version:
Header Bidding Management Library
408 lines (388 loc) • 15 kB
JavaScript
import * as utils from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
const BIDDER_CODE = 'oneVideo';
export const spec = {
code: 'oneVideo',
VERSION: '3.1.2',
ENDPOINT: 'https://ads.adaptv.advertising.com/rtb/openrtb?ext_id=',
E2ETESTENDPOINT: 'https://ads-wc.v.ssp.yahoo.com/rtb/openrtb?ext_id=',
SYNC_ENDPOINT1: 'https://pixel.advertising.com/ups/57304/sync?gdpr=&gdpr_consent=&_origin=0&redir=true',
SYNC_ENDPOINT2: 'https://match.adsrvr.org/track/cmf/generic?ttd_pid=adaptv&ttd_tpi=1',
supportedMediaTypes: ['video', 'banner'],
/**
* Determines whether or not the given bid request is valid.
*
* @param {BidRequest} bid The bid params to validate.
* @return boolean True if this is a valid bid, and false otherwise.
*/
isBidRequestValid: function(bid) {
// Bidder code validation
if (bid.bidder !== BIDDER_CODE || typeof bid.params === 'undefined') {
return false;
}
// E2E test skip validations
if (bid.params && bid.params.video && bid.params.video.e2etest) {
return true;
}
// MediaTypes Video / Banner validation
if (typeof bid.mediaTypes.video === 'undefined' && typeof bid.mediaTypes.banner === 'undefined') {
utils.logError('Failed validation: adUnit mediaTypes.video OR mediaTypes.banner not declared');
return false;
};
if (bid.mediaTypes.video) {
// Player size validation
if (typeof bid.mediaTypes.video.playerSize === 'undefined') {
if (bid.params.video && (typeof bid.params.video.playerWidth === 'undefined' || typeof bid.params.video.playerHeight === 'undefined')) {
utils.logError('Failed validation: Player size not declared in either mediaTypes.playerSize OR bid.params.video.plauerWidth & bid.params.video.playerHeight.');
return false;
};
};
// Mimes validation
if (typeof bid.mediaTypes.video.mimes === 'undefined') {
if (!bid.params.video || typeof bid.params.video.mimes === 'undefined') {
utils.logError('Failed validation: adUnit mediaTypes.mimes OR params.video.mimes not declared');
return false;
};
};
// Prevend DAP Outstream validation, Banner DAP validation & Multi-Format adUnit support
if (bid.mediaTypes.video.context === 'outstream' && bid.params.video && bid.params.video.display === 1) {
utils.logError('Failed validation: Dynamic Ad Placement cannot be used with context Outstream (params.video.display=1)');
return false;
};
};
// Publisher Id (Exchange) validation
if (typeof bid.params.pubId === 'undefined') {
utils.logError('Failed validation: Adapter cannot send requests without bid.params.pubId');
return false;
}
return true;
},
/**
* Make a server request from the list of BidRequests.
*
* @param {validBidRequests[]} - an array of bids
* @param bidderRequest
* @return ServerRequest Info describing the request to the server.
*/
buildRequests: function (bids, bidRequest) {
let consentData = bidRequest ? bidRequest.gdprConsent : null;
return bids.map(bid => {
let url = spec.ENDPOINT
let pubId = bid.params.pubId;
if (bid.params.video.e2etest) {
url = spec.E2ETESTENDPOINT;
pubId = 'HBExchange';
}
return {
method: 'POST',
/** removing adding local protocal since we
* can get cookie data only if we call with https. */
url: url + pubId,
data: getRequestData(bid, consentData, bidRequest),
bidRequest: bid
}
})
},
/**
* Unpack the response from the server into a list of bids.
*
* @param {*} serverResponse A successful response from the server.
* @return {Bid[]} An array of bids which were nested inside the server.
*/
interpretResponse: function(response, {bidRequest}) {
let bid;
let size;
let bidResponse;
try {
response = response.body;
bid = response.seatbid[0].bid[0];
} catch (e) {
response = null;
}
if (!response || !bid || (!bid.adm && !bid.nurl) || !bid.price) {
utils.logWarn(`No valid bids from ${spec.code} bidder`);
return [];
}
size = getSize(bidRequest.sizes);
bidResponse = {
requestId: bidRequest.bidId,
bidderCode: spec.code,
cpm: bid.price,
creativeId: bid.crid,
width: size.width,
height: size.height,
currency: response.cur,
ttl: (bidRequest.params.video.ttl > 0 && bidRequest.params.video.ttl <= 3600) ? bidRequest.params.video.ttl : 300,
netRevenue: true,
adUnitCode: bidRequest.adUnitCode,
meta: {
advertiserDomains: bid.adomain
}
};
bidResponse.mediaType = (bidRequest.mediaTypes.banner) ? 'banner' : 'video'
if (bid.nurl) {
bidResponse.vastUrl = bid.nurl;
} else if (bid.adm && bidRequest.params.video.display === 1) {
bidResponse.ad = bid.adm
} else if (bid.adm) {
bidResponse.vastXml = bid.adm;
}
if (bidRequest.mediaTypes.video) {
bidResponse.renderer = (bidRequest.mediaTypes.video.context === 'outstream') ? newRenderer(bidRequest, bidResponse) : undefined;
}
return bidResponse;
},
/**
* Register the user sync pixels which should be dropped after the auction.
*
* @param {SyncOptions} syncOptions Which user syncs are allowed?
* @param {ServerResponse[]} serverResponses List of server's responses.
* @return {UserSync[]} The user syncs which should be dropped.
*/
getUserSyncs: function(syncOptions, responses, consentData = {}) {
let {
gdprApplies,
consentString = ''
} = consentData;
if (syncOptions.pixelEnabled) {
return [{
type: 'image',
url: spec.SYNC_ENDPOINT1
},
{
type: 'image',
url: `https://sync-tm.everesttech.net/upi/pid/m7y5t93k?gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}&redir=https%3A%2F%2Fpixel.advertising.com%2Fups%2F55986%2Fsync%3Fuid%3D%24%7BUSER_ID%7D%26_origin%3D0` + encodeURI(`&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}`)
},
{
type: 'image',
url: spec.SYNC_ENDPOINT2
}];
}
}
};
function getSize(sizes) {
let parsedSizes = utils.parseSizesInput(sizes);
let [ width, height ] = parsedSizes.length ? parsedSizes[0].split('x') : [];
return {
width: parseInt(width, 10) || undefined,
height: parseInt(height, 10) || undefined
};
}
function isConsentRequired(consentData) {
return !!(consentData && consentData.gdprApplies);
}
function getRequestData(bid, consentData, bidRequest) {
let loc = bidRequest.refererInfo.referer;
let page = (bid.params.site && bid.params.site.page) ? (bid.params.site.page) : (loc.href);
let ref = (bid.params.site && bid.params.site.referrer) ? bid.params.site.referrer : bidRequest.refererInfo.referer;
let getFloorRequestObject = {
currency: bid.params.cur || 'USD',
mediaType: 'video',
size: '*'
};
let bidData = {
id: utils.generateUUID(),
at: 2,
imp: [{
id: '1',
secure: isSecure(),
ext: {
hb: 1,
prebidver: '$prebid.version$',
adapterver: spec.VERSION,
}
}],
site: {
page: page,
ref: ref
},
device: {
ua: navigator.userAgent
},
tmax: 200
};
if (bid.params.video.display == undefined || bid.params.video.display != 1) {
bidData.imp[0].video = {
linearity: 1
};
if (bid.params.video.playerWidth && bid.params.video.playerHeight) {
bidData.imp[0].video.w = bid.params.video.playerWidth;
bidData.imp[0].video.h = bid.params.video.playerHeight;
} else {
const playerSize = getSize(bid.mediaTypes.video.playerSize);
bidData.imp[0].video.w = playerSize.width;
bidData.imp[0].video.h = playerSize.height;
};
if (bid.params.video.mimes) {
bidData.imp[0].video.mimes = bid.params.video.mimes;
} else {
bidData.imp[0].video.mimes = bid.mediaTypes.video.mimes;
};
if (bid.mediaTypes.video.maxbitrate || bid.params.video.maxbitrate) {
bidData.imp[0].video.maxbitrate = bid.params.video.maxbitrate || bid.mediaTypes.video.maxbitrate;
}
if (bid.mediaTypes.video.maxduration || bid.params.video.maxduration) {
bidData.imp[0].video.maxduration = bid.params.video.maxduration || bid.mediaTypes.video.maxduration;
}
if (bid.mediaTypes.video.minduration || bid.params.video.minduration) {
bidData.imp[0].video.minduration = bid.params.video.minduration || bid.mediaTypes.video.minduration;
}
if (bid.mediaTypes.video.api || bid.params.video.api) {
bidData.imp[0].video.api = bid.params.video.api || bid.mediaTypes.video.api;
}
if (bid.mediaTypes.video.delivery || bid.params.video.delivery) {
bidData.imp[0].video.delivery = bid.params.video.delivery || bid.mediaTypes.video.delivery;
}
if (bid.mediaTypes.video.position || bid.params.video.position) {
bidData.imp[0].video.pos = bid.params.video.position || bid.mediaTypes.video.position;
}
if (bid.mediaTypes.video.playbackmethod || bid.params.video.playbackmethod) {
bidData.imp[0].video.playbackmethod = bid.params.video.playbackmethod || bid.mediaTypes.video.playbackmethod;
}
if (bid.mediaTypes.video.placement || bid.params.video.placement) {
bidData.imp[0].video.placement = bid.params.video.placement || bid.mediaTypes.video.placement;
}
if (bid.params.video.rewarded) {
bidData.imp[0].ext.rewarded = bid.params.video.rewarded
}
if (bid.mediaTypes.video.linearity || bid.params.video.linearity) {
bidData.imp[0].video.linearity = bid.params.video.linearity || bid.mediaTypes.video.linearity || 1;
}
if (bid.mediaTypes.video.protocols || bid.params.video.protocols) {
bidData.imp[0].video.protocols = bid.params.video.protocols || bid.mediaTypes.video.protocols || [2, 5];
}
} else if (bid.params.video.display == 1) {
getFloorRequestObject.mediaType = 'banner';
bidData.imp[0].banner = {
mimes: bid.params.video.mimes,
w: bid.params.video.playerWidth,
h: bid.params.video.playerHeight,
pos: bid.params.video.position,
};
if (bid.params.video.placement) {
bidData.imp[0].banner.placement = bid.params.video.placement
}
if (bid.params.video.maxduration) {
bidData.imp[0].banner.ext = bidData.imp[0].banner.ext || {}
bidData.imp[0].banner.ext.maxduration = bid.params.video.maxduration
}
if (bid.params.video.minduration) {
bidData.imp[0].banner.ext = bidData.imp[0].banner.ext || {}
bidData.imp[0].banner.ext.minduration = bid.params.video.minduration
}
}
if (utils.isFn(bid.getFloor)) {
let floorData = bid.getFloor(getFloorRequestObject);
bidData.imp[0].bidfloor = floorData.floor;
bidData.cur = floorData.currency;
} else {
bidData.imp[0].bidfloor = bid.params.bidfloor;
};
if (bid.params.video.inventoryid) {
bidData.imp[0].ext.inventoryid = bid.params.video.inventoryid
}
if (bid.params.video.sid) {
bidData.source = {
ext: {
schain: {
complete: 1,
nodes: [{
sid: bid.params.video.sid,
rid: bidData.id,
}]
}
}
}
if (bid.params.video.hp == 1) {
bidData.source.ext.schain.nodes[0].hp = bid.params.video.hp;
}
} else if (bid.schain) {
bidData.source = {
ext: {
schain: bid.schain
}
}
bidData.source.ext.schain.nodes[0].rid = bidData.id;
}
if (bid.params.site && bid.params.site.id) {
bidData.site.id = bid.params.site.id
}
if (isConsentRequired(consentData) || (bidRequest && bidRequest.uspConsent)) {
bidData.regs = {
ext: {}
};
if (isConsentRequired(consentData)) {
bidData.regs.ext.gdpr = 1
}
if (consentData && consentData.consentString) {
bidData.user = {
ext: {
consent: consentData.consentString
}
};
}
// ccpa support
if (bidRequest && bidRequest.uspConsent) {
bidData.regs.ext.us_privacy = bidRequest.uspConsent
}
}
if (bid.params.video.e2etest) {
utils.logMessage('E2E test mode enabled: \n The following parameters are being overridden by e2etest mode:\n* bidfloor:null\n* width:300\n* height:250\n* mimes: video/mp4, application/javascript\n* api:2\n* site.page/ref: verizonmedia.com\n* tmax:1000');
bidData.imp[0].bidfloor = null;
bidData.imp[0].video.w = 300;
bidData.imp[0].video.h = 250;
bidData.imp[0].video.mimes = ['video/mp4', 'application/javascript'];
bidData.imp[0].video.api = [2];
bidData.site.page = 'https://verizonmedia.com';
bidData.site.ref = 'https://verizonmedia.com';
bidData.tmax = 1000;
}
if (bid.params.video.custom && utils.isPlainObject(bid.params.video.custom)) {
bidData.imp[0].ext.custom = {};
for (const key in bid.params.video.custom) {
if (utils.isStr(bid.params.video.custom[key]) || utils.isNumber(bid.params.video.custom[key])) {
bidData.imp[0].ext.custom[key] = bid.params.video.custom[key];
}
}
}
if (bid.params.video.content && utils.isPlainObject(bid.params.video.content)) {
bidData.site.content = {};
const contentStringKeys = ['id', 'title', 'series', 'season', 'genre', 'contentrating', 'language'];
const contentNumberkeys = ['episode', 'prodq', 'context', 'livestream', 'len'];
const contentArrayKeys = ['cat'];
const contentObjectKeys = ['ext'];
for (const contentKey in bid.params.video.content) {
if (
(contentStringKeys.indexOf(contentKey) > -1 && utils.isStr(bid.params.video.content[contentKey])) ||
(contentNumberkeys.indexOf(contentKey) > -1 && utils.isNumber(bid.params.video.content[contentKey])) ||
(contentObjectKeys.indexOf(contentKey) > -1 && utils.isPlainObject(bid.params.video.content[contentKey])) ||
(contentArrayKeys.indexOf(contentKey) > -1 && utils.isArray(bid.params.video.content[contentKey]) &&
bid.params.video.content[contentKey].every(catStr => utils.isStr(catStr)))) {
bidData.site.content[contentKey] = bid.params.video.content[contentKey];
} else {
utils.logMessage('oneVideo bid adapter validation error: ', contentKey, ' is either not supported is OpenRTB V2.5 or value is undefined');
}
}
}
return bidData;
}
function isSecure() {
return document.location.protocol === 'https:';
}
/**
* Create oneVideo renderer
* @returns {*}
*/
function newRenderer(bidRequest, bid) {
if (!bidRequest.renderer) {
bidRequest.renderer = {};
bidRequest.renderer.url = 'https://cdn.vidible.tv/prod/hb-outstream-renderer/renderer.js';
bidRequest.renderer.render = function(bid) {
setTimeout(function() {
// eslint-disable-next-line no-undef
o2PlayerRender(bid);
}, 700)
};
}
}
registerBidder(spec);