Skip to content

Commit

Permalink
Merge branch 'master' into 9.0
Browse files Browse the repository at this point in the history
  • Loading branch information
dgirardi committed May 7, 2024
2 parents 3749ca7 + eb5ae98 commit 10d8c88
Show file tree
Hide file tree
Showing 14 changed files with 818 additions and 133 deletions.
5 changes: 0 additions & 5 deletions modules/ajaBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,6 @@ export const spec = {

for (let i = 0, len = validBidRequests.length; i < len; i++) {
const bidRequest = validBidRequests[i];
if (
(bidRequest.mediaTypes?.native || bidRequest.mediaTypes?.video) &&
bidRequest.mediaTypes?.banner) {
continue
}

let queryString = '';

Expand Down
105 changes: 74 additions & 31 deletions modules/dailymotionBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,46 +9,59 @@ import { deepAccess } from '../src/utils.js';
* @return video metadata
*/
function getVideoMetadata(bidRequest, bidderRequest) {
const videoAdUnit = deepAccess(bidRequest, 'mediaTypes.video', {});
const videoBidderParams = deepAccess(bidRequest, 'params.video', {});
const videoParams = deepAccess(bidRequest, 'params.video', {});

const videoParams = {
...videoAdUnit,
...videoBidderParams, // Bidder Specific overrides
};
// As per oRTB 2.5 spec, "A bid request must not contain both an App and a Site object."
// See section 3.2.14
// Content object is either from Object: Site or Object: App
const contentObj = deepAccess(bidderRequest, 'ortb2.site')
? deepAccess(bidderRequest, 'ortb2.site.content')
: deepAccess(bidderRequest, 'ortb2.app.content');

// Store as object keys to ensure uniqueness
const iabcat1 = {};
const iabcat2 = {};
const parsedContentData = {
// Store as object keys to ensure uniqueness
iabcat1: {},
iabcat2: {},
};

deepAccess(bidderRequest, 'ortb2.site.content.data', []).forEach((data) => {
deepAccess(contentObj, 'data', []).forEach((data) => {
if ([4, 5, 6, 7].includes(data?.ext?.segtax)) {
(Array.isArray(data.segment) ? data.segment : []).forEach((segment) => {
if (typeof segment.id === 'string') {
// See https://docs.prebid.org/features/firstPartyData.html#segments-and-taxonomy
// Only take IAB cats of taxonomy V1
if (data.ext.segtax === 4) iabcat1[segment.id] = 1;
// Only take IAB cats of taxonomy V2 or higher
if ([5, 6, 7].includes(data.ext.segtax)) iabcat2[segment.id] = 1;
if (data.ext.segtax === 4) {
parsedContentData.iabcat1[segment.id] = 1;
} else {
// Only take IAB cats of taxonomy V2 or higher
parsedContentData.iabcat2[segment.id] = 1;
}
}
});
}
});

const videoMetadata = {
description: videoParams.description || '',
duration: videoParams.duration || 0,
iabcat1: Object.keys(iabcat1),
duration: videoParams.duration || deepAccess(contentObj, 'len', 0),
iabcat1: Array.isArray(videoParams.iabcat1)
? videoParams.iabcat1
: Array.isArray(deepAccess(contentObj, 'cat'))
? contentObj.cat
: Object.keys(parsedContentData.iabcat1),
iabcat2: Array.isArray(videoParams.iabcat2)
? videoParams.iabcat2
: Object.keys(iabcat2),
id: videoParams.id || '',
lang: videoParams.lang || '',
: Object.keys(parsedContentData.iabcat2),
id: videoParams.id || deepAccess(contentObj, 'id', ''),
lang: videoParams.lang || deepAccess(contentObj, 'language', ''),
private: videoParams.private || false,
tags: videoParams.tags || '',
title: videoParams.title || '',
tags: videoParams.tags || deepAccess(contentObj, 'keywords', ''),
title: videoParams.title || deepAccess(contentObj, 'title', ''),
topics: videoParams.topics || '',
xid: videoParams.xid || '',
livestream: typeof videoParams.livestream === 'number'
? !!videoParams.livestream
: !!deepAccess(contentObj, 'livestream', 0),
};

return videoMetadata;
Expand All @@ -67,7 +80,24 @@ export const spec = {
* @return boolean True if this is a valid bid, and false otherwise.
*/
isBidRequestValid: function (bid) {
return typeof bid?.params?.apiKey === 'string' && bid.params.apiKey.length > 10;
if (bid?.params) {
// We only accept video adUnits
if (!bid?.mediaTypes?.[VIDEO]) return false;

// As `context`, `placement` & `plcmt` are optional (although recommended)
// values, we check the 3 of them to see if we are in an instream video context
const isInstream = bid.mediaTypes[VIDEO].context === 'instream' ||
bid.mediaTypes[VIDEO].placement === 1 ||
bid.mediaTypes[VIDEO].plcmt === 1;

// We only accept instream video context
if (!isInstream) return false;

// We need API key
return typeof bid.params.apiKey === 'string' && bid.params.apiKey.length > 10;
}

return false;
},

/**
Expand All @@ -83,15 +113,15 @@ export const spec = {
data: {
bidder_request: {
gdprConsent: {
apiVersion: bidderRequest?.gdprConsent?.apiVersion || 1,
consentString: bidderRequest?.gdprConsent?.consentString || '',
apiVersion: deepAccess(bidderRequest, 'gdprConsent.apiVersion', 1),
consentString: deepAccess(bidderRequest, 'gdprConsent.consentString', ''),
// Cast boolean in any case (eg: if value is int) to ensure type
gdprApplies: !!bidderRequest?.gdprConsent?.gdprApplies,
gdprApplies: !!deepAccess(bidderRequest, 'gdprConsent.gdprApplies'),
},
refererInfo: {
page: bidderRequest?.refererInfo?.page || '',
page: deepAccess(bidderRequest, 'refererInfo.page', ''),
},
uspConsent: bidderRequest?.uspConsent || '',
uspConsent: deepAccess(bidderRequest, 'uspConsent', ''),
gppConsent: {
gppString: deepAccess(bidderRequest, 'gppConsent.gppString') ||
deepAccess(bidderRequest, 'ortb2.regs.gpp', ''),
Expand All @@ -104,15 +134,28 @@ export const spec = {
},
// Cast boolean in any case (value should be 0 or 1) to ensure type
coppa: !!deepAccess(bidderRequest, 'ortb2.regs.coppa'),
// In app context, we need to retrieve additional informations
...(!deepAccess(bidderRequest, 'ortb2.site') && !!deepAccess(bidderRequest, 'ortb2.app') ? {
appBundle: deepAccess(bidderRequest, 'ortb2.app.bundle', ''),
appStoreUrl: deepAccess(bidderRequest, 'ortb2.app.storeurl', ''),
} : {}),
request: {
adUnitCode: bid.adUnitCode || '',
auctionId: bid.auctionId || '',
bidId: bid.bidId || '',
adUnitCode: deepAccess(bid, 'adUnitCode', ''),
auctionId: deepAccess(bid, 'auctionId', ''),
bidId: deepAccess(bid, 'bidId', ''),
mediaTypes: {
video: {
playerSize: bid.mediaTypes?.[VIDEO]?.playerSize || [],
api: bid.mediaTypes?.[VIDEO]?.api || [],
startDelay: bid.mediaTypes?.[VIDEO]?.startdelay || 0,
mimes: bid.mediaTypes?.[VIDEO]?.mimes || [],
minduration: bid.mediaTypes?.[VIDEO]?.minduration || 0,
maxduration: bid.mediaTypes?.[VIDEO]?.maxduration || 0,
protocols: bid.mediaTypes?.[VIDEO]?.protocols || [],
skip: bid.mediaTypes?.[VIDEO]?.skip || 0,
skipafter: bid.mediaTypes?.[VIDEO]?.skipafter || 0,
skipmin: bid.mediaTypes?.[VIDEO]?.skipmin || 0,
startdelay: bid.mediaTypes?.[VIDEO]?.startdelay || 0,
w: bid.mediaTypes?.[VIDEO]?.w || 0,
h: bid.mediaTypes?.[VIDEO]?.h || 0,
},
},
sizes: bid.sizes || [],
Expand Down
83 changes: 57 additions & 26 deletions modules/dailymotionBidAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ Maintainer: ad-leo-engineering@dailymotion.com
# Description

Dailymotion prebid adapter.
Supports video ad units in instream context.

# Configuration options

Before calling this adapter, you need to set at least the API key in the bid parameters:
Before calling this adapter, you need to at least set a video adUnit in an instream context and the API key in the bid parameters:

```javascript
const adUnits = [
Expand All @@ -21,8 +22,14 @@ const adUnits = [
bidder: 'dailymotion',
params: {
apiKey: 'fake_api_key'
}
}]
},
}],
code: 'test-ad-unit',
mediaTypes: {
video: {
context: 'instream',
},
},
}
];
```
Expand All @@ -39,9 +46,15 @@ const adUnits = [
bids: [{
bidder: 'dailymotion',
params: {
apiKey: 'dailymotion-testing'
}
}]
apiKey: 'dailymotion-testing',
},
}],
code: 'test-ad-unit',
mediaTypes: {
video: {
context: 'instream',
},
},
}
];
```
Expand All @@ -51,7 +64,7 @@ Please note that failing to set these will result in the adapter not bidding at
# Sample video AdUnit

To allow better targeting, you should provide as much context about the video as possible.
There are two ways of doing this depending on if you're using Dailymotion player or a third party one.
There are three ways of doing this depending on if you're using Dailymotion player or a third party one.

If you are using the Dailymotion player, you should only provide the video `xid` in your ad unit, example:

Expand All @@ -61,17 +74,20 @@ const adUnits = [
bids: [{
bidder: 'dailymotion',
params: {
apiKey: 'dailymotion-testing'
apiKey: 'dailymotion-testing',
video: {
xid: 'x123456' // Dailymotion infrastructure unique video ID
},
}
}],
code: 'test-ad-unit',
mediaTypes: {
video: {
api: [2, 7],
context: 'instream',
playerSize: [ [1280, 720] ],
startDelay: 0,
xid: 'x123456' // Dailymotion infrastructure unique video ID
startdelay: 0,
w: 1280,
h: 720,
},
}
}
Expand All @@ -91,7 +107,17 @@ const adUnits = [
params: {
apiKey: 'dailymotion-testing',
video: {
description: 'overriden video description'
description: 'this is a video description',
duration: 556,
iabcat1: ['IAB-2'],
iabcat2: ['6', '17'],
id: '54321',
lang: 'FR',
livestream: 0,
private: false,
tags: 'tag_1,tag_2,tag_3',
title: 'test video',
topics: 'topic_1, topic_2',
}
}
}],
Expand All @@ -100,37 +126,42 @@ const adUnits = [
video: {
api: [2, 7],
context: 'instream',
description: 'this is a video description',
duration: 556,
iabcat2: ['6', '17'],
id: '54321',
lang: 'FR',
playerSize: [ [1280, 720] ],
private: false,
startDelay: 0,
tags: 'tag_1,tag_2,tag_3',
title: 'test video',
topics: 'topic_1, topic_2',
startdelay: 0,
w: 1280,
h: 720,
},
}
}
];
```

Each of the following video metadata fields can be added in mediaTypes.video or bids.params.video.
If a field exists in both places, it will be overridden by bids.params.video.
Each of the following video metadata fields can be added in bids.params.video.

* `description` - Video description
* `duration` - Video duration in seconds
* `iabcat2` - List of IAB category IDs from the [2.0 taxonomy](https://github.com/InteractiveAdvertisingBureau/Taxonomies/blob/main/Content%20Taxonomies/Content%20Taxonomy%202.0.tsv)
* `iabcat1` - List of IAB category IDs from the [1.0 taxonomy](https://github.com/InteractiveAdvertisingBureau/Taxonomies/blob/main/Content%20Taxonomies/Content%20Taxonomy%201.0.tsv)
* `iabcat2` - List of IAB category IDs from the [2.0 taxonomy](https://github.com/InteractiveAdvertisingBureau/Taxonomies/blob/main/Content%20Taxonomies/Content%20Taxonomy%202.0.tsv) and above
* `id` - Video unique ID in host video infrastructure
* `lang` - ISO 639-1 code for main language used in the video
* `livestream` - 0 = not live, 1 = content is live
* `private` - True if video is not publicly available
* `tags` - Tags for the video, comma separated
* `title` - Video title
* `topics` - Main topics for the video, comma separated
* `xid` - Dailymotion video identifier (only applicable if using the Dailymotion player)

If you already specify [First-Party data](https://docs.prebid.org/features/firstPartyData.html) through the `ortb2` object when calling [`pbjs.requestBids(requestObj)`](https://docs.prebid.org/dev-docs/publisher-api-reference/requestBids.html), we will fallback to those values when possible. See the mapping below.

| From ortb2 | Metadata fields |
|---------------------------------------------------------------------------------|-----------------|
| `ortb2.site.content.cat` OR `ortb2.site.content.data` where `ext.segtax` is `4` | `iabcat1` |
| `ortb2.site.content.data` where `ext.segtax` is `5`, `6` or `7` | `iabcat2` |
| `ortb2.site.content.id` | `id` |
| `ortb2.site.content.language` | `lang` |
| `ortb2.site.content.livestream` | `livestream` |
| `ortb2.site.content.keywords` | `tags` |
| `ortb2.site.content.title` | `title` |

# Integrating the adapter

To use the adapter with any non-test request, you first need to ask an API key from Dailymotion. Please contact us through **DailymotionPrebid.js@dailymotion.com**.
Expand Down
9 changes: 5 additions & 4 deletions modules/exadsBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -319,24 +319,25 @@ function getEnvParams() {
language: ''
};

// TODO: all of this is already in first party data
envParams.domain = window.location.hostname;
envParams.page = window.location.protocol + '//' + window.location.host + window.location.pathname;
envParams.lang = navigator.language.indexOf('-') > -1
? navigator.language.split('-')[0]
: navigator.language;
envParams.userAgent = navigator.userAgent;

if (envParams.userAgent.match(/Windows/i)) {
envParams.osName = 'Windows';
} else if (envParams.userAgent.match(/Mac OS|Macintosh/i)) {
envParams.osName = 'MacOS';
} else if (envParams.userAgent.match(/Unix/i)) {
envParams.osName = 'Unix';
} else if (envParams.userAgent.userAgent.match(/Android/i)) {
// TODO: what is userAgent.userAgent supposed to be?
} else if (envParams.userAgent.userAgent?.match(/Android/i)) {
envParams.osName = 'Android';
} else if (envParams.userAgent.userAgent.match(/iPhone|iPad|iPod/i)) {
} else if (envParams.userAgent.userAgent?.match(/iPhone|iPad|iPod/i)) {
envParams.osName = 'iOS';
} else if (envParams.userAgent.userAgent.match(/Linux/i)) {
} else if (envParams.userAgent.userAgent?.match(/Linux/i)) {
envParams.osName = 'Linux';
} else {
envParams.osName = 'Unknown';
Expand Down
8 changes: 6 additions & 2 deletions modules/mabidderBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER } from '../src/mediaTypes.js';
import {getGlobal} from '../src/prebidGlobal.js';
import { getGlobal } from '../src/prebidGlobal.js';
import { ortbConverter } from '../libraries/ortbConverter/converter.js';

const BIDDER_CODE = 'mabidder';
export const baseUrl = 'https://prebid.ecdrsvc.com/bid';
const converter = ortbConverter({})

export const spec = {
supportedMediaTypes: [BANNER],
code: BIDDER_CODE,
Expand All @@ -14,7 +17,8 @@ export const spec = {
return !!(bid.params.ppid && bid.sizes && Array.isArray(bid.sizes) && Array.isArray(bid.sizes[0]))
},
buildRequests: function(validBidRequests, bidderRequest) {
const fpd = bidderRequest.ortb2;
const fpd = converter.toORTB({ bidRequests: validBidRequests, bidderRequest: bidderRequest });

const bids = [];
validBidRequests.forEach(bidRequest => {
const sizes = [];
Expand Down
Loading

0 comments on commit 10d8c88

Please sign in to comment.