From 29bb63c0430d508dd9a013933d82b14c692aa8e1 Mon Sep 17 00:00:00 2001 From: mkomorski Date: Tue, 26 Nov 2024 13:22:49 +0100 Subject: [PATCH 1/8] Local cache for video bids --- .../adPlayerPro/bidRequestScheduling.html | 5 +- .../adPlayerPro/eventListeners.html | 5 +- .../adPlayerPro/localVideoCache.html | 166 ++++++++++++++++++ .../videoModule/jwplayer/bidMarkedAsUsed.html | 4 +- .../jwplayer/bidsBackHandlerOverride.html | 4 +- .../videoModule/jwplayer/eventListeners.html | 4 +- .../videoModule/jwplayer/eventsUI.html | 4 +- .../jwplayer/gamAdServerMediation.html | 4 +- .../videoModule/jwplayer/localVideoCache.html | 136 ++++++++++++++ .../videoModule/jwplayer/mediaMetadata.html | 4 +- .../videoModule/jwplayer/playlist.html | 4 +- .../videoModule/videoImpressionVerifier.js | 5 +- src/auction.js | 16 +- src/video.js | 3 +- src/videoCache.js | 12 +- test/spec/videoCache_spec.js | 32 +++- 16 files changed, 391 insertions(+), 17 deletions(-) create mode 100644 integrationExamples/videoModule/adPlayerPro/localVideoCache.html create mode 100644 integrationExamples/videoModule/jwplayer/localVideoCache.html diff --git a/integrationExamples/videoModule/adPlayerPro/bidRequestScheduling.html b/integrationExamples/videoModule/adPlayerPro/bidRequestScheduling.html index 7c73312c5c3..4274f256b42 100644 --- a/integrationExamples/videoModule/adPlayerPro/bidRequestScheduling.html +++ b/integrationExamples/videoModule/adPlayerPro/bidRequestScheduling.html @@ -13,7 +13,10 @@ var adUnits = [{ code: 'div-gpt-ad-51545-0', mediaTypes: { - video:{"context":"outstream"} + video:{ + "context":"outstream", + playerSize: [640, 360] + } }, video: { divId: 'player', // required to indicate which player is being used to render this ad unit. diff --git a/integrationExamples/videoModule/adPlayerPro/eventListeners.html b/integrationExamples/videoModule/adPlayerPro/eventListeners.html index 3c26ef42bee..684dd40a43b 100644 --- a/integrationExamples/videoModule/adPlayerPro/eventListeners.html +++ b/integrationExamples/videoModule/adPlayerPro/eventListeners.html @@ -13,7 +13,10 @@ var adUnits = [{ code: 'div-gpt-ad-51545-0', mediaTypes: { - video:{"context":"outstream"} + video: { + "context":"outstream", + playerSize: [640, 360] + } }, video: { divId: 'player', // required to indicate which player is being used to render this ad unit. diff --git a/integrationExamples/videoModule/adPlayerPro/localVideoCache.html b/integrationExamples/videoModule/adPlayerPro/localVideoCache.html new file mode 100644 index 00000000000..6fe9c3f9c9f --- /dev/null +++ b/integrationExamples/videoModule/adPlayerPro/localVideoCache.html @@ -0,0 +1,166 @@ + + + + + + + + + AdPlayer.Pro Event Listeners + + + + + + + + +

AdPlayer.Pro with Local Cache

+ +
Div-1: Player placeholder div
+
+ + + diff --git a/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html b/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html index d0b261043e4..7dc6ba6afd1 100644 --- a/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html +++ b/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html @@ -13,7 +13,9 @@ var adUnits = [{ code: 'div-gpt-ad-51545-0', mediaTypes: { - video: {} + video: { + playerSize: [640, 360] + } }, video: { divId: 'player', // required to indicate which player is being used to render this ad unit. diff --git a/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html b/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html index 75a72ba3501..71f40ab3400 100644 --- a/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html +++ b/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html @@ -12,7 +12,9 @@ var videoAdUnit = { code: videoAdUnitCode, mediaTypes: { - video: {} + video: { + playerSize: [640, 360] + } }, video: { divId: 'player', // required to indicate which player is being used to render this ad unit. diff --git a/integrationExamples/videoModule/jwplayer/eventListeners.html b/integrationExamples/videoModule/jwplayer/eventListeners.html index 6f04f37264b..6bd323568c2 100644 --- a/integrationExamples/videoModule/jwplayer/eventListeners.html +++ b/integrationExamples/videoModule/jwplayer/eventListeners.html @@ -15,7 +15,9 @@ var adUnits = [{ code: 'div-gpt-ad-51545-0', mediaTypes: { - video: {} + video: { + playerSize: [640, 360] + } }, video: { divId: 'player', diff --git a/integrationExamples/videoModule/jwplayer/eventsUI.html b/integrationExamples/videoModule/jwplayer/eventsUI.html index cfd1efe7624..6642cb46b47 100644 --- a/integrationExamples/videoModule/jwplayer/eventsUI.html +++ b/integrationExamples/videoModule/jwplayer/eventsUI.html @@ -15,7 +15,9 @@ var adUnits = [{ code: 'testUnit', mediaTypes: { - video: {} + video: { + playerSize: [640, 360] + } }, video: { divId: 'player', // required to indicate which player is being used to render this ad unit. diff --git a/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html b/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html index 1f4331785ea..98b9a52f47d 100644 --- a/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html +++ b/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html @@ -13,7 +13,9 @@ var adUnits = [{ code: 'div-gpt-ad-51545-0', mediaTypes: { - video: {} + video: { + playerSize: [640, 360] + } }, video: { divId: 'player', // required to indicate which player is being used to render this ad unit. diff --git a/integrationExamples/videoModule/jwplayer/localVideoCache.html b/integrationExamples/videoModule/jwplayer/localVideoCache.html new file mode 100644 index 00000000000..fba7aa7901b --- /dev/null +++ b/integrationExamples/videoModule/jwplayer/localVideoCache.html @@ -0,0 +1,136 @@ + + + + + + + JW Player with Playlist + + + + + + + + +

JW Player with Local cache

+ +
Div-1: Player placeholder div
+
+ + + diff --git a/integrationExamples/videoModule/jwplayer/mediaMetadata.html b/integrationExamples/videoModule/jwplayer/mediaMetadata.html index 63e62aa4b82..d0a572ee8cf 100644 --- a/integrationExamples/videoModule/jwplayer/mediaMetadata.html +++ b/integrationExamples/videoModule/jwplayer/mediaMetadata.html @@ -13,7 +13,9 @@ var adUnits = [{ code: 'div-gpt-ad-51545-0', mediaTypes: { - video: {} + video: { + playerSize: [640, 360] + } }, video: { divId: 'player', // required to indicate which player is being used to render this ad unit. diff --git a/integrationExamples/videoModule/jwplayer/playlist.html b/integrationExamples/videoModule/jwplayer/playlist.html index 9e89f606f23..dd2c79aaf7c 100644 --- a/integrationExamples/videoModule/jwplayer/playlist.html +++ b/integrationExamples/videoModule/jwplayer/playlist.html @@ -13,7 +13,9 @@ var adUnits = [{ code: 'div-gpt-ad-51545-0', mediaTypes: { - video: {} + video: { + playerSize: [640, 360] + } }, video: { divId: 'player', // required to indicate which player is being used to render this ad unit. diff --git a/modules/videoModule/videoImpressionVerifier.js b/modules/videoModule/videoImpressionVerifier.js index 60717c0f855..945928ecb04 100644 --- a/modules/videoModule/videoImpressionVerifier.js +++ b/modules/videoModule/videoImpressionVerifier.js @@ -1,6 +1,7 @@ import { find } from '../../src/polyfill.js'; import { vastXmlEditorFactory } from '../../libraries/video/shared/vastXmlEditor.js'; import { generateUUID } from '../../src/utils.js'; +import { config } from '../../src/config.js'; export const PB_PREFIX = 'pb_'; export const UUID_MARKER = PB_PREFIX + 'uuid'; @@ -65,7 +66,9 @@ export function videoImpressionVerifier(vastXmlEditor_, bidTracker_) { if (vastUrl) { const url = new URL(vastUrl); - url.searchParams.append(UUID_MARKER, uuid); + if (!config.getConfig('cache.useLocal')) { + url.searchParams.append(UUID_MARKER, uuid); + } bid.vastUrl = url.toString(); } else if (vastXml) { bid.vastXml = vastXmlEditor.getVastXmlWithTracking(vastXml, uuid); diff --git a/src/auction.js b/src/auction.js index 759397275d5..97f67e03ea5 100644 --- a/src/auction.js +++ b/src/auction.js @@ -81,7 +81,7 @@ import { } from './utils.js'; import {getPriceBucketString} from './cpmBucketManager.js'; import {getNativeTargeting, isNativeResponse, setNativeResponseProperties} from './native.js'; -import {batchAndStore} from './videoCache.js'; +import {storeLocally} from './videoCache.js'; import {Renderer} from './Renderer.js'; import {config} from './config.js'; import {userSync} from './userSync.js'; @@ -566,15 +566,23 @@ function tryAddVideoBid(auctionInstance, bidResponse, afterBidAdded, {index = au }), 'video'); const context = videoMediaType && deepAccess(videoMediaType, 'context'); const useCacheKey = videoMediaType && deepAccess(videoMediaType, 'useCacheKey'); - - if (config.getConfig('cache.url') && (useCacheKey || context !== OUTSTREAM)) { - if (!bidResponse.videoCacheKey || config.getConfig('cache.ignoreBidderCacheKey')) { + const { + useLocal, + url: cacheUrl, + ignoreBidderCacheKey + } = config.getConfig('cache') || {}; + + if (cacheUrl && (useCacheKey || context !== OUTSTREAM)) { + if (!bidResponse.videoCacheKey || ignoreBidderCacheKey) { addBid = false; callPrebidCache(auctionInstance, bidResponse, afterBidAdded, videoMediaType); } else if (!bidResponse.vastUrl) { logError('videoCacheKey specified but not required vastUrl for video bid'); addBid = false; } + } else if (useLocal) { + // stores video bid vast as local blob in the browser + storeLocally(bidResponse); } if (addBid) { addBidToAuction(auctionInstance, bidResponse); diff --git a/src/video.js b/src/video.js index 9be9adec4c5..bc3413d2256 100644 --- a/src/video.js +++ b/src/video.js @@ -120,7 +120,8 @@ export function isValidVideoBid(bid, {index = auctionManager.index} = {}) { export const checkVideoBidSetup = hook('sync', function(bid, adUnit, videoMediaType, context, useCacheKey) { if (videoMediaType && (useCacheKey || context !== OUTSTREAM)) { // xml-only video bids require a prebid cache url - if (!config.getConfig('cache.url') && bid.vastXml && !bid.vastUrl) { + const { url, useLocal } = config.getConfig('cache') || {}; + if ((!url && !useLocal) && bid.vastXml && !bid.vastUrl) { logError(` This bid contains only vastXml and will not work when a prebid cache url is not specified. Try enabling prebid cache with $$PREBID_GLOBAL$$.setConfig({ cache: {url: "..."} }); diff --git a/src/videoCache.js b/src/videoCache.js index cf39c1c9452..0b24c989329 100644 --- a/src/videoCache.js +++ b/src/videoCache.js @@ -72,7 +72,7 @@ function wrapURI(uri, impTrackerURLs) { * @return {Object|null} - The payload to be sent to the prebid-server endpoints, or null if the bid can't be converted cleanly. */ function toStorageRequest(bid, {index = auctionManager.index} = {}) { - const vastValue = bid.vastXml ? bid.vastXml : wrapURI(bid.vastUrl, bid.vastImpUrl); + const vastValue = getVastValue(bid); const auction = index.getAuction(bid); const ttlWithBuffer = Number(bid.ttl) + ttlBufferInSeconds; let payload = { @@ -140,6 +140,10 @@ function shimStorageCallback(done) { } } +function getVastValue(bid) { + return bid.vastXml ? bid.vastXml : wrapURI(bid.vastUrl, bid.vastImpUrl); +}; + /** * If the given bid is for a Video ad, generate a unique ID and cache it somewhere server-side. * @@ -162,6 +166,12 @@ export function getCacheUrl(id) { return `${config.getConfig('cache.url')}?uuid=${id}`; } +export const storeLocally = (bid) => { + const vastValue = getVastValue(bid); + const url = URL.createObjectURL(new Blob([vastValue], { type: 'text/xml' })); + bid.vastUrl = url; +}; + export const _internal = { store } diff --git a/test/spec/videoCache_spec.js b/test/spec/videoCache_spec.js index 7d07da9de90..69a30b158a3 100644 --- a/test/spec/videoCache_spec.js +++ b/test/spec/videoCache_spec.js @@ -1,5 +1,5 @@ import chai from 'chai'; -import {batchingCache, getCacheUrl, store, _internal, storeBatch} from 'src/videoCache.js'; +import {batchingCache, getCacheUrl, store, _internal, storeBatch, storeLocally} from 'src/videoCache.js'; import {config} from 'src/config.js'; import {server} from 'test/mocks/xhr.js'; import {auctionManager} from '../../src/auctionManager.js'; @@ -396,6 +396,36 @@ describe('The video cache', function () { sinon.assert.called(utils.logError); }) }) + + describe('storeLocally', () => { + let sandbox; + + beforeEach(function () { + sandbox = sinon.createSandbox(); + config.setConfig({ + cache: { + useLocal: true + } + }); + }); + + afterEach(function () { + config.resetConfig(); + sandbox.restore(); + }); + + it('should set blob url to bid.vastUrl', () => { + const bid = { + vastXml: '' + }; + const currentHost = `${window.location.protocol}//${window.location.host}`; + const blobUrlPattern = new RegExp(`^blob:${currentHost}/[a-f0-9\\-]+$`); + + storeLocally(bid); + + expect(bid.vastUrl).to.match(blobUrlPattern); + }); + }) }); describe('The getCache function', function () { From a93a356a140c568c98c11391d460911a18b60816 Mon Sep 17 00:00:00 2001 From: mkomorski Date: Tue, 26 Nov 2024 13:25:02 +0100 Subject: [PATCH 2/8] clean up --- integrationExamples/videoModule/jwplayer/localVideoCache.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/integrationExamples/videoModule/jwplayer/localVideoCache.html b/integrationExamples/videoModule/jwplayer/localVideoCache.html index fba7aa7901b..52384fcd949 100644 --- a/integrationExamples/videoModule/jwplayer/localVideoCache.html +++ b/integrationExamples/videoModule/jwplayer/localVideoCache.html @@ -37,8 +37,6 @@ }]; const vastXml = "GDFPDemo00:00:11" - const url = URL.createObjectURL(new Blob([vastXml], { type: 'text/xml' })); - var pbjs = pbjs || {}; pbjs.que = pbjs.que || []; From ced6862290870ab6139b4a1e44f16b9946a50c4b Mon Sep 17 00:00:00 2001 From: mkomorski Date: Tue, 26 Nov 2024 13:26:24 +0100 Subject: [PATCH 3/8] clean up --- .../videoModule/adPlayerPro/localVideoCache.html | 5 +---- .../videoModule/jwplayer/localVideoCache.html | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/integrationExamples/videoModule/adPlayerPro/localVideoCache.html b/integrationExamples/videoModule/adPlayerPro/localVideoCache.html index 6fe9c3f9c9f..88a6b02c6f3 100644 --- a/integrationExamples/videoModule/adPlayerPro/localVideoCache.html +++ b/integrationExamples/videoModule/adPlayerPro/localVideoCache.html @@ -3,12 +3,9 @@ - - AdPlayer.Pro Event Listeners - - + AdPlayer.Pro with Local Cache - JW Player with Playlist - - + JW Player with Local Cache