forked from prebid/Prebid.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinstreamTracking.js
121 lines (105 loc) · 4.33 KB
/
instreamTracking.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { deepClone, getBidRequest, deepAccess } from '../src/utils.js';
import { config } from '../src/config.js';
import { auctionManager } from '../src/auctionManager.js';
import { INSTREAM } from '../src/video.js';
import * as events from '../src/events.js';
import { EVENTS, TARGETING_KEYS, BID_STATUS } from '../src/constants.js'
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
* @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid
* @typedef {import('../src/adapters/bidderFactory.js').AdUnit} AdUnit
*/
const { CACHE_ID, UUID } = TARGETING_KEYS;
const { BID_WON, AUCTION_END } = EVENTS;
const { RENDERED } = BID_STATUS;
const INSTREAM_TRACKING_DEFAULT_CONFIG = {
enabled: false,
maxWindow: 1000 * 60, // the time in ms after which polling for instream delivery stops
pollingFreq: 500 // the frequency of polling
};
// Set instreamTracking default values
config.setDefaults({
'instreamTracking': deepClone(INSTREAM_TRACKING_DEFAULT_CONFIG)
});
const whitelistedResources = /video|fetch|xmlhttprequest|other/;
/**
* Here the idea is
* find all network entries via performance.getEntriesByType()
* filter it by video cache key in the url
* and exclude the ad server urls so that we dont match twice
* eg:
* dfp ads call: https://securepubads.g.doubleclick.net/gampad/ads?...hb_cache_id%3D55e85cd3-6ea4-4469-b890-84241816b131%26...
* prebid cache url: https://prebid.adnxs.com/pbc/v1/cache?uuid=55e85cd3-6ea4-4469-b890-84241816b131
*
* if the entry exists, emit the BID_WON
*
* Note: this is a workaround till a better approach is engineered.
*
* @param {object} config
* @param {Array<AdUnit>} config.adUnits
* @param {Array<Bid>} config.bidsReceived
* @param {Array<BidRequest>} config.bidderRequests
*
* @return {boolean} returns TRUE if tracking started
*/
export function trackInstreamDeliveredImpressions({adUnits, bidsReceived, bidderRequests}) {
const instreamTrackingConfig = config.getConfig('instreamTracking') || {};
// check if instreamTracking is enabled and performance api is available
if (!instreamTrackingConfig.enabled || !window.performance || !window.performance.getEntriesByType) {
return false;
}
// filter for video bids
const instreamBids = bidsReceived.filter(bid => {
const bidderRequest = getBidRequest(bid.requestId, bidderRequests);
return bidderRequest && deepAccess(bidderRequest, 'mediaTypes.video.context') === INSTREAM && bid.videoCacheKey;
});
if (!instreamBids.length) {
return false;
}
// find unique instream ad units
const instreamAdUnitMap = {};
adUnits.forEach(adUnit => {
if (!instreamAdUnitMap[adUnit.code] && deepAccess(adUnit, 'mediaTypes.video.context') === INSTREAM) {
instreamAdUnitMap[adUnit.code] = true;
}
});
const instreamAdUnitsCount = Object.keys(instreamAdUnitMap).length;
const start = Date.now();
const {maxWindow, pollingFreq, urlPattern} = instreamTrackingConfig;
let instreamWinningBidsCount = 0;
let lastRead = 0; // offset for performance.getEntriesByType
function poll() {
// get network entries using the last read offset
const entries = window.performance.getEntriesByType('resource').splice(lastRead);
for (const resource of entries) {
const url = resource.name;
// check if the resource is of whitelisted resource to avoid checking img or css or script urls
if (!whitelistedResources.test(resource.initiatorType)) {
continue;
}
instreamBids.forEach((bid) => {
// match the video cache key excluding ad server call
const matches = !(url.indexOf(CACHE_ID) !== -1 || url.indexOf(UUID) !== -1) && url.indexOf(bid.videoCacheKey) !== -1;
if (urlPattern && urlPattern instanceof RegExp && !urlPattern.test(url)) {
return;
}
if (matches && bid.status !== RENDERED) {
// video found
instreamWinningBidsCount++;
auctionManager.addWinningBid(bid);
events.emit(BID_WON, bid);
}
});
}
// update offset
lastRead += entries.length;
const timeElapsed = Date.now() - start;
if (timeElapsed < maxWindow && instreamWinningBidsCount < instreamAdUnitsCount) {
setTimeout(poll, pollingFreq);
}
}
// start polling for network entries
setTimeout(poll, pollingFreq);
return true;
}
events.on(AUCTION_END, trackInstreamDeliveredImpressions)