Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support multiple media formats within a single ad unit #1991

Merged
merged 8 commits into from
Jan 23, 2018
2 changes: 1 addition & 1 deletion modules/adkernelAdnBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export const spec = {

code: 'adkernelAdn',

supportedMediaTypes: [VIDEO],
supportedMediaTypes: [BANNER, VIDEO],

isBidRequestValid: function(bidRequest) {
return 'params' in bidRequest && (typeof bidRequest.params.host === 'undefined' || typeof bidRequest.params.host === 'string') &&
Expand Down
2 changes: 1 addition & 1 deletion modules/adkernelBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const spec = {

code: 'adkernel',
aliases: ['headbidding'],
supportedMediaTypes: [VIDEO],
supportedMediaTypes: [BANNER, VIDEO],
isBidRequestValid: function(bidRequest) {
return 'params' in bidRequest && typeof bidRequest.params.host !== 'undefined' &&
'zoneId' in bidRequest.params && !isNaN(Number(bidRequest.params.zoneId));
Expand Down
4 changes: 2 additions & 2 deletions modules/adxcgBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import * as utils from 'src/utils';
import * as url from 'src/url';
import {registerBidder} from 'src/adapters/bidderFactory';
import {NATIVE, VIDEO} from 'src/mediaTypes';
import {BANNER, NATIVE, VIDEO} from 'src/mediaTypes';

/**
* Adapter for requesting bids from adxcg.net
* updated to latest prebid repo on 2017.10.20
*/

const BIDDER_CODE = 'adxcg';
const SUPPORTED_AD_TYPES = [VIDEO, NATIVE];
const SUPPORTED_AD_TYPES = [BANNER, VIDEO, NATIVE];
const SOURCE = 'pbjs10';
export const spec = {
code: BIDDER_CODE,
Expand Down
47 changes: 29 additions & 18 deletions modules/appnexusBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { Renderer } from 'src/Renderer';
import * as utils from 'src/utils';
import { registerBidder } from 'src/adapters/bidderFactory';
import { NATIVE, VIDEO } from 'src/mediaTypes';
import { BANNER, NATIVE, VIDEO } from 'src/mediaTypes';
import find from 'core-js/library/fn/array/find';
import includes from 'core-js/library/fn/array/includes';

const BIDDER_CODE = 'appnexus';
const URL = '//ib.adnxs.com/ut/v3/prebid';
const SUPPORTED_AD_TYPES = ['banner', 'video', 'native'];
const VIDEO_TARGETING = ['id', 'mimes', 'minduration', 'maxduration',
'startdelay', 'skippable', 'playback_method', 'frameworks'];
const USER_PARAMS = ['age', 'external_uid', 'segments', 'gender', 'dnt', 'language'];
Expand All @@ -31,7 +30,7 @@ const SOURCE = 'pbjs';
export const spec = {
code: BIDDER_CODE,
aliases: ['appnexusAst', 'brealtime', 'pagescience', 'defymedia', 'gourmetads', 'matomy', 'featureforward', 'oftmedia', 'districtm'],
supportedMediaTypes: [VIDEO, NATIVE],
supportedMediaTypes: [BANNER, VIDEO, NATIVE],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matthewlane If i am not wrong all other adapters supporting video or native also needs to be updated. As of now https://github.com/prebid/Prebid.js/blob/master/modules/rubiconBidAdapter.js#L76 Rubicon will not work for banner

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, looking in to this

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good callout @jaiminpanchal27. I went through all adapters that have supportedMediaTypes and added 'banner' to those that do support it in addition to 'native' or 'video'. Some adapters only support 'native' or 'video', determined by looking at code and corresponding .md files, and in those cases supportedMediaTypes are left unchanged


/**
* Determines whether or not the given bid request is valid.
Expand Down Expand Up @@ -103,7 +102,7 @@ export const spec = {
serverResponse.tags.forEach(serverBid => {
const rtbBid = getRtbBid(serverBid);
if (rtbBid) {
if (rtbBid.cpm !== 0 && includes(SUPPORTED_AD_TYPES, rtbBid.ad_type)) {
if (rtbBid.cpm !== 0 && includes(this.supportedMediaTypes, rtbBid.ad_type)) {
const bid = newBid(serverBid, rtbBid);
bid.mediaType = parseMediaType(rtbBid);
bids.push(bid);
Expand Down Expand Up @@ -197,7 +196,6 @@ function newBid(serverBid, rtbBid) {
width: rtbBid.rtb.video.player_width,
height: rtbBid.rtb.video.player_height,
vastUrl: rtbBid.rtb.video.asset_url,
descriptionUrl: rtbBid.rtb.video.asset_url,
ttl: 3600
});
// This supports Outstream Video
Expand All @@ -209,9 +207,9 @@ function newBid(serverBid, rtbBid) {
bid.adResponse.ad = bid.adResponse.ads[0];
bid.adResponse.ad.video = bid.adResponse.ad.rtb.video;
}
} else if (rtbBid.rtb['native']) {
const nativeAd = rtbBid.rtb['native'];
bid['native'] = {
} else if (rtbBid.rtb[NATIVE]) {
const nativeAd = rtbBid.rtb[NATIVE];
bid[NATIVE] = {
title: nativeAd.title,
body: nativeAd.desc,
cta: nativeAd.ctatext,
Expand Down Expand Up @@ -256,6 +254,7 @@ function bidToTag(bid) {
const tag = {};
tag.sizes = transformSizes(bid.sizes);
tag.primary_size = tag.sizes[0];
tag.ad_types = [];
tag.uuid = bid.bidId;
if (bid.params.placementId) {
tag.id = parseInt(bid.params.placementId, 10);
Expand Down Expand Up @@ -294,19 +293,24 @@ function bidToTag(bid) {
tag.keywords = getKeywords(bid.params.keywords);
}

if (bid.mediaType === 'native' || utils.deepAccess(bid, 'mediaTypes.native')) {
tag.ad_types = ['native'];
if (bid.mediaType === NATIVE || utils.deepAccess(bid, `mediaTypes.${NATIVE}`)) {
tag.ad_types.push(NATIVE);

if (bid.nativeParams) {
const nativeRequest = buildNativeRequest(bid.nativeParams);
tag['native'] = {layouts: [nativeRequest]};
tag[NATIVE] = {layouts: [nativeRequest]};
}
}

const videoMediaType = utils.deepAccess(bid, 'mediaTypes.video');
const videoMediaType = utils.deepAccess(bid, `mediaTypes.${VIDEO}`);
const context = utils.deepAccess(bid, 'mediaTypes.video.context');

if (bid.mediaType === 'video' || (videoMediaType && context !== 'outstream')) {
if (bid.mediaType === VIDEO || videoMediaType) {
tag.ad_types.push(VIDEO);
}

// instream gets vastUrl, outstream gets vastXml
if (bid.mediaType === VIDEO || (videoMediaType && context !== 'outstream')) {
tag.require_asset_url = true;
}

Expand All @@ -318,6 +322,13 @@ function bidToTag(bid) {
.forEach(param => tag.video[param] = bid.params.video[param]);
}

if (
(utils.isEmpty(bid.mediaType) && utils.isEmpty(bid.mediaTypes)) ||
(bid.mediaType === BANNER || (bid.mediaTypes && bid.mediaTypes[BANNER]))
) {
tag.ad_types.push(BANNER);
}

return tag;
}

Expand Down Expand Up @@ -414,12 +425,12 @@ function handleOutstreamRendererEvents(bid, id, eventName) {

function parseMediaType(rtbBid) {
const adType = rtbBid.ad_type;
if (adType === 'video') {
return 'video';
} else if (adType === 'native') {
return 'native';
if (adType === VIDEO) {
return VIDEO;
} else if (adType === NATIVE) {
return NATIVE;
} else {
return 'banner';
return BANNER;
}
}

Expand Down
2 changes: 1 addition & 1 deletion modules/audienceNetworkBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const code = 'audienceNetwork';
const currency = 'USD';
const method = 'GET';
const url = 'https://an.facebook.com/v2/placementbid.json';
const supportedMediaTypes = ['video'];
const supportedMediaTypes = ['banner', 'video'];
const netRevenue = true;
const hb_bidder = 'fan';

Expand Down
4 changes: 2 additions & 2 deletions modules/conversantBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as utils from 'src/utils';
import {registerBidder} from 'src/adapters/bidderFactory';
import { VIDEO } from 'src/mediaTypes';
import { BANNER, VIDEO } from 'src/mediaTypes';

const BIDDER_CODE = 'conversant';
const URL = '//media.msg.dotomi.com/s2s/header/24';
Expand All @@ -10,7 +10,7 @@ const VERSION = '2.2.1';
export const spec = {
code: BIDDER_CODE,
aliases: ['cnvr'], // short code
supportedMediaTypes: [VIDEO],
supportedMediaTypes: [BANNER, VIDEO],

/**
* Determines whether or not the given bid request is valid.
Expand Down
2 changes: 1 addition & 1 deletion modules/freewheelSSPBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ var getOutstreamScript = function(bid) {

export const spec = {
code: BIDDER_CODE,
supportedMediaTypes: ['video'],
supportedMediaTypes: ['banner', 'video'],
aliases: ['stickyadstv'], // former name for freewheel-ssp
/**
* Determines whether or not the given bid request is valid.
Expand Down
2 changes: 1 addition & 1 deletion modules/pulsepointBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const spec = {

aliases: ['pulseLite', 'pulsepointLite'],

supportedMediaTypes: ['native'],
supportedMediaTypes: ['banner', 'native'],

isBidRequestValid: bid => (
!!(bid && bid.params && bid.params.cp && bid.params.ct)
Expand Down
2 changes: 1 addition & 1 deletion modules/rubiconBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ utils._each(sizeMap, (item, key) => sizeMap[item] = key);
export const spec = {
code: 'rubicon',
aliases: ['rubiconLite'],
supportedMediaTypes: ['video'],
supportedMediaTypes: ['banner', 'video'],
/**
* @param {object} bid
* @return boolean
Expand Down
2 changes: 1 addition & 1 deletion modules/sekindoUMBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as utils from 'src/utils';
import {registerBidder} from 'src/adapters/bidderFactory';
export const spec = {
code: 'sekindoUM',
supportedMediaTypes: ['video'],
supportedMediaTypes: ['banner', 'video'],
/**
* Determines whether or not the given bid request is valid.
*
Expand Down
12 changes: 9 additions & 3 deletions src/adapters/bidderFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,14 @@ export function newBidder(spec) {
// After all the responses have come back, call done() and
// register any required usersync pixels.
const responses = [];
function afterAllResponses() {
done();
function afterAllResponses(bids) {
const videoBid = bids && bids[0] && bids[0].mediaType && bids[0].mediaType === 'video';
const cacheEnabled = config.getConfig('cache.url');

// video bids with cache enabled need to be cached first before they are considered done
if (!(videoBid && cacheEnabled)) {
done();
}
registerSyncs(responses);
}

Expand Down Expand Up @@ -281,7 +287,7 @@ export function newBidder(spec) {
addBidUsingRequestMap(bids);
}
}
onResponse();
onResponse(bids);

function addBidUsingRequestMap(bid) {
const bidRequest = bidRequestMap[bid.requestId];
Expand Down
26 changes: 10 additions & 16 deletions src/auction.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ import { Renderer } from 'src/Renderer';
import { config } from 'src/config';
import { userSync } from 'src/userSync';
import { createHook } from 'src/hook';
import { videoAdUnit } from 'src/video';
import find from 'core-js/library/fn/array/find';
import includes from 'core-js/library/fn/array/includes';

Expand Down Expand Up @@ -153,20 +152,9 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels})
return innerBidRequestId === bidRequest.bidderRequestId;
});

const nonVideoBid = request.bids.filter(videoAdUnit).length === 0;
const videoBid = request.bids.filter(videoAdUnit).length > 0;
const videoBidNoCache = videoBid && !config.getConfig('cache.url');
const videoBidWithCache = videoBid && config.getConfig('cache.url');

// video bids with cache enabled need to be cached first before saying they are done
if (!videoBidWithCache) {
request.doneCbCallCount += 1;
}

// in case of mediaType video and prebidCache enabled, call bidsBackHandler after cache is stored.
if (nonVideoBid || videoBidNoCache) {
bidsBackAll()
}
// this is done for cache-enabled video bids in tryAddVideoBids, after the cache is stored
request.doneCbCallCount += 1;
bidsBackAll();
}, 1);
}

Expand Down Expand Up @@ -379,7 +367,13 @@ export function getStandardBidderSettings() {
val: function (bidResponse) {
return bidResponse.source;
}
}
},
{
key: 'hb_format',
val: function (bidResponse) {
return bidResponse.mediaType;
}
},
]
}
return bidder_settings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD];
Expand Down
3 changes: 2 additions & 1 deletion src/constants.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@
"hb_pb",
"hb_size",
"hb_deal",
"hb_source"
"hb_source",
"hb_format"
],
"S2S" : {
"SRC" : "s2s",
Expand Down
48 changes: 28 additions & 20 deletions src/prebid.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import { getGlobal } from './prebidGlobal';
import { flatten, uniques, isGptPubadsDefined, adUnitsFilter, removeRequestId } from './utils';
import { videoAdUnit, videoBidder, hasNonVideoBidder } from './video';
import { nativeAdUnit, nativeBidder, hasNonNativeBidder } from './native';
import { listenMessagesFromCreative } from './secureCreatives';
import { userSync } from 'src/userSync.js';
import { loadScript } from './adloader';
Expand Down Expand Up @@ -298,24 +296,34 @@ $$PREBID_GLOBAL$$.requestBids = function ({ bidsBackHandler, timeout, adUnits, a
adUnitCodes = adUnits && adUnits.map(unit => unit.code);
}

// for video-enabled adUnits, only request bids for bidders that support video
adUnits.filter(videoAdUnit).filter(hasNonVideoBidder).forEach(adUnit => {
const nonVideoBidders = adUnit.bids
.filter(bid => !videoBidder(bid))
.map(bid => bid.bidder);

utils.logWarn(utils.unsupportedBidderMessage(adUnit, nonVideoBidders));
adUnit.bids = adUnit.bids.filter(videoBidder);
});

// for native-enabled adUnits, only request bids for bidders that support native
adUnits.filter(nativeAdUnit).filter(hasNonNativeBidder).forEach(adUnit => {
const nonNativeBidders = adUnit.bids
.filter(bid => !nativeBidder(bid))
.map(bid => bid.bidder);

utils.logWarn(utils.unsupportedBidderMessage(adUnit, nonNativeBidders));
adUnit.bids = adUnit.bids.filter(nativeBidder);
/*
* for a given adunit which supports a set of mediaTypes
* and a given bidder which supports a set of mediaTypes
* a bidder is eligible to participate on the adunit
* if it supports at least one of the mediaTypes on the adunit
*/
adUnits.forEach(adUnit => {
// get the adunit's mediaTypes, defaulting to banner if mediaTypes isn't present
const adUnitMediaTypes = Object.keys(adUnit.mediaTypes || {'banner': 'banner'});

// get the bidder's mediaTypes
const bidders = adUnit.bids.map(bid => bid.bidder);
const bidderRegistry = adaptermanager.bidderRegistry;

bidders.forEach(bidder => {
const adapter = bidderRegistry[bidder];
const spec = adapter && adapter.getSpec && adapter.getSpec()
// banner is default if not specified in spec
const bidderMediaTypes = (spec && spec.supportedMediaTypes) || ['banner'];

// check if the bidder's mediaTypes are not in the adUnit's mediaTypes
const bidderEligible = adUnitMediaTypes.some(type => bidderMediaTypes.includes(type));
if (!bidderEligible) {
// drop the bidder from the ad unit if it's not compatible
utils.logWarn(utils.unsupportedBidderMessage(adUnit, bidder));
adUnit.bids = adUnit.bids.filter(bid => bid.bidder !== bidder);
}
});
});

if (!adUnits || adUnits.length === 0) {
Expand Down
11 changes: 5 additions & 6 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -832,17 +832,16 @@ export function isSlotMatchingAdUnitCode(adUnitCode) {
/**
* Constructs warning message for when unsupported bidders are dropped from an adunit
* @param {Object} adUnit ad unit from which the bidder is being dropped
* @param {Array} unSupportedBidders arrary of bidder codes that are not compatible with the adUnit
* @param {string} bidder bidder code that is not compatible with the adUnit
* @return {string} warning message to display when condition is met
*/
export function unsupportedBidderMessage(adUnit, unSupportedBidders) {
const mediaType = adUnit.mediaType || Object.keys(adUnit.mediaTypes).join(', ');
const plural = unSupportedBidders.length === 1 ? 'This bidder' : 'These bidders';
export function unsupportedBidderMessage(adUnit, bidder) {
const mediaType = Object.keys(adUnit.mediaTypes || {'banner': 'banner'}).join(', ');

return `
${adUnit.code} is a ${mediaType} ad unit
containing bidders that don't support ${mediaType}: ${unSupportedBidders.join(', ')}.
${plural} won't fetch demand.
containing bidders that don't support ${mediaType}: ${bidder}.
This bidder won't fetch demand.
`;
}

Expand Down
Loading