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

Core: fill in video.plcmt when possible #10438

Merged
merged 1 commit into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/prebid.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {newMetrics, useMetrics} from './utils/perfMetrics.js';
import {defer, GreedyPromise} from './utils/promise.js';
import {enrichFPD} from './fpd/enrichment.js';
import {allConsent} from './consentHandler.js';
import {fillVideoDefaults} from './video.js';

const pbjsInstance = getGlobal();
const { triggerUserSyncs } = userSync;
Expand Down Expand Up @@ -269,6 +270,12 @@ export const checkAdUnitSetup = hook('sync', function (adUnits) {
return validatedAdUnits;
}, 'checkAdUnitSetup');

function fillAdUnitDefaults(adUnits) {
if (FEATURES.VIDEO) {
adUnits.forEach(au => fillVideoDefaults(au))
}
}

/// ///////////////////////////////
// //
// Start Public APIs //
Expand Down Expand Up @@ -658,6 +665,7 @@ pbjsInstance.requestBids = (function() {

export const startAuction = hook('async', function ({ bidsBackHandler, timeout: cbTimeout, adUnits, ttlBuffer, adUnitCodes, labels, auctionId, ortb2Fragments, metrics, defer } = {}) {
const s2sBidders = getS2SBidderSet(config.getConfig('s2sConfig') || []);
fillAdUnitDefaults(adUnits);
adUnits = useMetrics(metrics).measureTime('requestBids.validate', () => checkAdUnitSetup(adUnits));

function auctionDone(bids, timedOut, auctionId) {
Expand Down
30 changes: 13 additions & 17 deletions src/video.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
import adapterManager from './adapterManager.js';
import { deepAccess, logError } from './utils.js';
import { config } from '../src/config.js';
import {includes} from './polyfill.js';
import { hook } from './hook.js';
import {deepAccess, logError} from './utils.js';
import {config} from '../src/config.js';
import {hook} from './hook.js';
import {auctionManager} from './auctionManager.js';

const VIDEO_MEDIA_TYPE = 'video';
export const OUTSTREAM = 'outstream';
export const INSTREAM = 'instream';

/**
* Helper functions for working with video-enabled adUnits
*/
export const videoAdUnit = adUnit => {
const mediaType = adUnit.mediaType === VIDEO_MEDIA_TYPE;
const mediaTypes = deepAccess(adUnit, 'mediaTypes.video');
return mediaType || mediaTypes;
};
export const videoBidder = bid => includes(adapterManager.videoAdapters, bid.bidder);
export const hasNonVideoBidder = adUnit =>
adUnit.bids.filter(bid => !videoBidder(bid)).length;
export function fillVideoDefaults(adUnit) {
const video = adUnit?.mediaTypes?.video;
if (video != null && video.plcmt == null) {
if (video.context === OUTSTREAM || [2, 3, 4].includes(video.placement)) {
video.plcmt = 4;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this not playbackmethod?

} else if (video.context !== OUTSTREAM && [2, 6].includes(video.playmethod)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Was this supposed to be playbackmethod ?

video.plcmt = 2;
}
}
}

/**
* @typedef {object} VideoBid
Expand Down
244 changes: 158 additions & 86 deletions test/spec/video_spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isValidVideoBid } from 'src/video.js';
import {fillVideoDefaults, isValidVideoBid} from 'src/video.js';
import {hook} from '../../src/hook.js';
import {stubAuctionIndex} from '../helpers/indexStub.js';

Expand All @@ -7,97 +7,169 @@ describe('video.js', function () {
hook.ready();
});

it('validates valid instream bids', function () {
const bid = {
adId: '456xyz',
vastUrl: 'http://www.example.com/vastUrl',
transactionId: 'au'
};
const adUnits = [{
transactionId: 'au',
mediaTypes: {
video: {context: 'instream'}
}
}];
const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
expect(valid).to.equal(true);
});
describe('fillVideoDefaults', () => {
function fillDefaults(videoMediaType = {}) {
const adUnit = {mediaTypes: {video: videoMediaType}};
fillVideoDefaults(adUnit);
return adUnit.mediaTypes.video;
}

it('catches invalid instream bids', function () {
const bid = {
transactionId: 'au'
};
const adUnits = [{
transactionId: 'au',
mediaTypes: {
video: {context: 'instream'}
}
}];
const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
expect(valid).to.equal(false);
});
describe('should set plcmt = 4 when', () => {
it('context is "outstream"', () => {
expect(fillDefaults({context: 'outstream'})).to.eql({
context: 'outstream',
plcmt: 4
})
});
[2, 3, 4].forEach(placement => {
it(`placemement is "${placement}"`, () => {
expect(fillDefaults({placement})).to.eql({
placement,
plcmt: 4
});
})
});
});
describe('should set plcmt = 2 when', () => {
[2, 6].forEach(playmethod => {
it(`playmethod is "${playmethod}"`, () => {
expect(fillDefaults({playmethod})).to.eql({
playmethod,
plcmt: 2,
});
});
});
});
describe('should not set plcmt when', () => {
Object.entries({
'it was set by pub (context=outstream)': {
expected: 1,
video: {
context: 'outstream',
plcmt: 1
}
},
'it was set by pub (placement=2)': {
expected: 1,
video: {
placement: 2,
plcmt: 1
}
},
'placement not in 2, 3, 4': {
expected: undefined,
video: {
placement: 1
}
},
'it was set by pub (playmethod=2)': {
expected: 1,
video: {
plcmt: 1,
playmethod: 2
}
}
}).forEach(([t, {expected, video}]) => {
it(t, () => {
expect(fillDefaults(video).plcmt).to.eql(expected);
})
})
})
})

it('catches invalid bids when prebid-cache is disabled', function () {
const adUnits = [{
transactionId: 'au',
bidder: 'vastOnlyVideoBidder',
mediaTypes: {video: {}},
}];
describe('isValidVideoBid', () => {
it('validates valid instream bids', function () {
const bid = {
adId: '456xyz',
vastUrl: 'http://www.example.com/vastUrl',
transactionId: 'au'
};
const adUnits = [{
transactionId: 'au',
mediaTypes: {
video: {context: 'instream'}
}
}];
const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
expect(valid).to.equal(true);
});

const valid = isValidVideoBid({ transactionId: 'au', vastXml: '<xml>vast</xml>' }, {index: stubAuctionIndex({adUnits})});
it('catches invalid instream bids', function () {
const bid = {
transactionId: 'au'
};
const adUnits = [{
transactionId: 'au',
mediaTypes: {
video: {context: 'instream'}
}
}];
const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
expect(valid).to.equal(false);
});

expect(valid).to.equal(false);
});
it('catches invalid bids when prebid-cache is disabled', function () {
const adUnits = [{
transactionId: 'au',
bidder: 'vastOnlyVideoBidder',
mediaTypes: {video: {}},
}];

it('validates valid outstream bids', function () {
const bid = {
transactionId: 'au',
renderer: {
url: 'render.url',
render: () => true,
}
};
const adUnits = [{
transactionId: 'au',
mediaTypes: {
video: {context: 'outstream'}
}
}];
const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
expect(valid).to.equal(true);
});
const valid = isValidVideoBid({ transactionId: 'au', vastXml: '<xml>vast</xml>' }, {index: stubAuctionIndex({adUnits})});

it('validates valid outstream bids with a publisher defined renderer', function () {
const bid = {
transactionId: 'au',
};
const adUnits = [{
transactionId: 'au',
mediaTypes: {
video: {
context: 'outstream',
expect(valid).to.equal(false);
});

it('validates valid outstream bids', function () {
const bid = {
transactionId: 'au',
renderer: {
url: 'render.url',
render: () => true,
}
},
renderer: {
url: 'render.url',
render: () => true,
}
}];
const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
expect(valid).to.equal(true);
});
};
const adUnits = [{
transactionId: 'au',
mediaTypes: {
video: {context: 'outstream'}
}
}];
const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
expect(valid).to.equal(true);
});

it('catches invalid outstream bids', function () {
const bid = {
transactionId: 'au',
};
const adUnits = [{
transactionId: 'au',
mediaTypes: {
video: {context: 'outstream'}
}
}];
const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
expect(valid).to.equal(false);
});
it('validates valid outstream bids with a publisher defined renderer', function () {
const bid = {
transactionId: 'au',
};
const adUnits = [{
transactionId: 'au',
mediaTypes: {
video: {
context: 'outstream',
}
},
renderer: {
url: 'render.url',
render: () => true,
}
}];
const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
expect(valid).to.equal(true);
});

it('catches invalid outstream bids', function () {
const bid = {
transactionId: 'au',
};
const adUnits = [{
transactionId: 'au',
mediaTypes: {
video: {context: 'outstream'}
}
}];
const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
expect(valid).to.equal(false);
});
})
});