Skip to content

Commit

Permalink
dfpAdServerVideo: add several parameters do DFP URLs (prebid#10977)
Browse files Browse the repository at this point in the history
  • Loading branch information
dgirardi authored Jan 29, 2024
1 parent ba42d90 commit 17f3468
Show file tree
Hide file tree
Showing 2 changed files with 405 additions and 183 deletions.
103 changes: 84 additions & 19 deletions modules/dfpAdServerVideo.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,28 @@
* This module adds [DFP support]{@link https://www.doubleclickbygoogle.com/} for Video to Prebid.
*/

import { registerVideoSupport } from '../src/adServerManager.js';
import { targeting } from '../src/targeting.js';
import { deepAccess, isEmpty, logError, parseSizesInput, formatQS, parseUrl, buildUrl } from '../src/utils.js';
import { config } from '../src/config.js';
import { getHook, submodule } from '../src/hook.js';
import { auctionManager } from '../src/auctionManager.js';
import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../src/adapterManager.js';
import {registerVideoSupport} from '../src/adServerManager.js';
import {targeting} from '../src/targeting.js';
import {
isNumber,
buildUrl,
deepAccess,
formatQS,
isEmpty,
logError,
parseSizesInput,
parseUrl,
uniques
} from '../src/utils.js';
import {config} from '../src/config.js';
import {getHook, submodule} from '../src/hook.js';
import {auctionManager} from '../src/auctionManager.js';
import {gdprDataHandler} from '../src/adapterManager.js';
import * as events from '../src/events.js';
import CONSTANTS from '../src/constants.json';
import {getPPID} from '../src/adserver.js';
import {getRefererInfo} from '../src/refererDetection.js';
import {CLIENT_SECTIONS} from '../src/fpd/oneClient.js';

/**
* @typedef {Object} DfpVideoParams
Expand Down Expand Up @@ -113,29 +124,84 @@ export function buildDfpVideoUrl(options) {

const descriptionUrl = getDescriptionUrl(bid, options, 'params');
if (descriptionUrl) { queryParams.description_url = descriptionUrl; }

const gdprConsent = gdprDataHandler.getConsentData();
if (gdprConsent) {
if (typeof gdprConsent.gdprApplies === 'boolean') { queryParams.gdpr = Number(gdprConsent.gdprApplies); }
if (gdprConsent.consentString) { queryParams.gdpr_consent = gdprConsent.consentString; }
if (gdprConsent.addtlConsent) { queryParams.addtl_consent = gdprConsent.addtlConsent; }
}

const uspConsent = uspDataHandler.getConsentData();
if (uspConsent) { queryParams.us_privacy = uspConsent; }

const gppConsent = gppDataHandler.getConsentData();
if (gppConsent) {
// TODO - need to know what to set here for queryParams...
}

if (!queryParams.ppid) {
const ppid = getPPID();
if (ppid != null) {
queryParams.ppid = ppid;
}
}

const video = options.adUnit?.mediaTypes?.video;
Object.entries({
plcmt: () => video?.plcmt,
min_ad_duration: () => isNumber(video?.minduration) ? video.minduration * 1000 : null,
max_ad_duration: () => isNumber(video?.maxduration) ? video.maxduration * 1000 : null,
vpos() {
const startdelay = video?.startdelay;
if (isNumber(startdelay)) {
if (startdelay === -2) return 'postroll';
if (startdelay === -1 || startdelay > 0) return 'midroll';
return 'preroll';
}
},
vconp: () => Array.isArray(video?.playbackmethod) && video.playbackmethod.every(m => m === 7) ? '2' : undefined,
vpa() {
// playbackmethod = 3 is play on click; 1, 2, 4, 5, 6 are autoplay
if (Array.isArray(video?.playbackmethod)) {
const click = video.playbackmethod.some(m => m === 3);
const auto = video.playbackmethod.some(m => [1, 2, 4, 5, 6].includes(m));
if (click && !auto) return 'click';
if (auto && !click) return 'auto';
}
},
vpmute() {
// playbackmethod = 2, 6 are muted; 1, 3, 4, 5 are not
if (Array.isArray(video?.playbackmethod)) {
const muted = video.playbackmethod.some(m => [2, 6].includes(m));
const talkie = video.playbackmethod.some(m => [1, 3, 4, 5].includes(m));
if (muted && !talkie) return '1';
if (talkie && !muted) return '0';
}
}
}).forEach(([param, getter]) => {
if (!queryParams.hasOwnProperty(param)) {
const val = getter();
if (val != null) {
queryParams[param] = val;
}
}
});
const fpd = auctionManager.index.getBidRequest(options.bid || {})?.ortb2 ??
auctionManager.index.getAuction(options.bid || {})?.getFPD()?.global;

function getSegments(sections, segtax) {
return sections
.flatMap(section => deepAccess(fpd, section) || [])
.filter(datum => datum.ext?.segtax === segtax)
.flatMap(datum => datum.segment?.map(seg => seg.id))
.filter(ob => ob)
.filter(uniques)
}

const signals = Object.entries({
IAB_AUDIENCE_1_1: getSegments(['user.data'], 4),
IAB_CONTENT_2_2: getSegments(CLIENT_SECTIONS.map(section => `${section}.content.data`), 6)
}).map(([taxonomy, values]) => values.length ? {taxonomy, values} : null)
.filter(ob => ob);

if (signals.length) {
queryParams.ppsj = btoa(JSON.stringify({
PublisherProvidedTaxonomySignals: signals
}))
}

return buildUrl(Object.assign({
protocol: 'https',
host: 'securepubads.g.doubleclick.net',
Expand Down Expand Up @@ -164,6 +230,8 @@ if (config.getConfig('brandCategoryTranslation.translationFile')) { getHook('reg
* @returns {string} A URL which calls DFP with custom adpod targeting key values to compete with rest of the demand in DFP
*/
export function buildAdpodVideoUrl({code, params, callback} = {}) {
// TODO: the public API for this does not take in enough info to fill all DFP params (adUnit/bid),
// and is marked "alpha": https://docs.prebid.org/dev-docs/publisher-api-reference/adServers.dfp.buildAdpodVideoUrl.html
if (!params || !callback) {
logError(`A params object and a callback is required to use pbjs.adServers.dfp.buildAdpodVideoUrl`);
return;
Expand Down Expand Up @@ -225,9 +293,6 @@ export function buildAdpodVideoUrl({code, params, callback} = {}) {
if (gdprConsent.addtlConsent) { queryParams.addtl_consent = gdprConsent.addtlConsent; }
}

const uspConsent = uspDataHandler.getConsentData();
if (uspConsent) { queryParams.us_privacy = uspConsent; }

const masterTag = buildUrl({
protocol: 'https',
host: 'securepubads.g.doubleclick.net',
Expand Down
Loading

0 comments on commit 17f3468

Please sign in to comment.