-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
ShowHeroes adapter - expanded outstream support #4222
Changes from all commits
d0b644d
b6c0154
bfcdb91
c2e4686
6c57d09
f10b01f
6b36284
2f7f6e6
c701851
3ea3260
02ce519
041268c
fa9d95c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -1,5 +1,6 @@ | ||||||||||
import * as utils from '../src/utils'; | ||||||||||
import { config } from '../src/config'; | ||||||||||
import { Renderer } from '../src/Renderer'; | ||||||||||
import { registerBidder } from '../src/adapters/bidderFactory'; | ||||||||||
import { VIDEO, BANNER } from '../src/mediaTypes'; | ||||||||||
|
||||||||||
|
@@ -12,6 +13,13 @@ const STAGE_VL = 'https://video-library.stage.showheroes.com'; | |||||||||
const BIDDER_CODE = 'showheroes-bs'; | ||||||||||
const TTL = 300; | ||||||||||
|
||||||||||
function getEnvURLs(isStage) { | ||||||||||
return { | ||||||||||
pubTag: isStage ? STAGE_PUBLISHER_TAG : PROD_PUBLISHER_TAG, | ||||||||||
vlHost: isStage ? STAGE_VL : PROD_VL | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
export const spec = { | ||||||||||
code: BIDDER_CODE, | ||||||||||
aliases: ['showheroesBs'], | ||||||||||
|
@@ -22,16 +30,22 @@ export const spec = { | |||||||||
buildRequests: function(validBidRequests, bidderRequest) { | ||||||||||
const pageURL = validBidRequests[0].params.contentPageUrl || bidderRequest.refererInfo.referer; | ||||||||||
const isStage = !!validBidRequests[0].params.stage; | ||||||||||
const isBanner = !!validBidRequests[0].mediaTypes.banner; | ||||||||||
const isOutstream = utils.deepAccess(validBidRequests[0], 'mediaTypes.video.context') === 'outstream'; | ||||||||||
const isCustomRender = utils.deepAccess(validBidRequests[0], 'params.outstreamOptions.customRender'); | ||||||||||
const isNodeRender = utils.deepAccess(validBidRequests[0], 'params.outstreamOptions.slot') || utils.deepAccess(validBidRequests[0], 'params.outstreamOptions.iframe'); | ||||||||||
const isNativeRender = utils.deepAccess(validBidRequests[0], 'renderer'); | ||||||||||
const outstreamOptions = utils.deepAccess(validBidRequests[0], 'params.outstreamOptions'); | ||||||||||
const isBanner = !!validBidRequests[0].mediaTypes.banner || (isOutstream && !(isCustomRender || isNativeRender || isNodeRender)); | ||||||||||
|
||||||||||
let adUnits = validBidRequests.map((bid) => { | ||||||||||
const vpaidMode = utils.getBidIdParameter('vpaidMode', bid.params); | ||||||||||
|
||||||||||
let sizes = bid.sizes.length === 1 ? bid.sizes[0] : bid.sizes; | ||||||||||
if (sizes && !sizes.length) { | ||||||||||
let mediaSize; | ||||||||||
if (!isBanner) { | ||||||||||
mediaSize = bid.mediaTypes.video.playerSize; | ||||||||||
let mediaVideoSize = utils.deepAccess(bid, 'mediaTypes.video.playerSize'); | ||||||||||
if (utils.isArray(mediaVideoSize)) { | ||||||||||
mediaSize = mediaVideoSize; | ||||||||||
} else { | ||||||||||
mediaSize = bid.mediaTypes.banner.sizes; | ||||||||||
} | ||||||||||
|
@@ -49,14 +63,15 @@ export const spec = { | |||||||||
if (vpaidMode && context === 'instream') { | ||||||||||
streamType = 1; | ||||||||||
} | ||||||||||
if (context === 'outstream' || isBanner) { | ||||||||||
if (isBanner || context === 'outstream') { | ||||||||||
streamType = 5; | ||||||||||
} | ||||||||||
|
||||||||||
return { | ||||||||||
type: streamType, | ||||||||||
bidId: bid.bidId, | ||||||||||
mediaType: isBanner ? BANNER : VIDEO, | ||||||||||
context: context, | ||||||||||
playerId: utils.getBidIdParameter('playerId', bid.params), | ||||||||||
auctionId: bidderRequest.auctionId, | ||||||||||
bidderCode: BIDDER_CODE, | ||||||||||
|
@@ -67,6 +82,7 @@ export const spec = { | |||||||||
width: sizes[0], | ||||||||||
height: sizes[1] | ||||||||||
}, | ||||||||||
params: bid.params, | ||||||||||
}; | ||||||||||
}); | ||||||||||
|
||||||||||
|
@@ -78,8 +94,9 @@ export const spec = { | |||||||||
'user': [], | ||||||||||
'meta': { | ||||||||||
'pageURL': encodeURIComponent(pageURL), | ||||||||||
'vastCacheEnabled': (!!config.getConfig('cache') && !isBanner) || false, | ||||||||||
'vastCacheEnabled': (!!config.getConfig('cache') && !isBanner && !outstreamOptions) || false, | ||||||||||
'isDesktop': utils.getWindowTop().document.documentElement.clientWidth > 700, | ||||||||||
'xmlAndTag': !!(isOutstream && isCustomRender) || false, | ||||||||||
'stage': isStage || undefined | ||||||||||
}, | ||||||||||
'requests': adUnits, | ||||||||||
|
@@ -133,6 +150,7 @@ function createBids(bidRes, reqData) { | |||||||||
|
||||||||||
bidRes.bids.forEach(function (bid) { | ||||||||||
const reqBid = bidMap[bid.bidId]; | ||||||||||
const currentBidParams = reqBid.params; | ||||||||||
let bidUnit = {}; | ||||||||||
bidUnit.cpm = bid.cpm; | ||||||||||
bidUnit.requestId = bid.bidId; | ||||||||||
|
@@ -154,25 +172,95 @@ function createBids(bidRes, reqData) { | |||||||||
} | ||||||||||
if (reqBid.mediaType === BANNER) { | ||||||||||
bidUnit.ad = getBannerHtml(bid, reqBid, reqData); | ||||||||||
} else if (reqBid.context === 'outstream') { | ||||||||||
const renderer = Renderer.install({ | ||||||||||
id: bid.bidId, | ||||||||||
url: '//', | ||||||||||
config: { | ||||||||||
playerId: reqBid.playerId, | ||||||||||
width: bid.video.width, | ||||||||||
height: bid.video.height, | ||||||||||
vastUrl: bid.vastTag, | ||||||||||
vastXml: bid.vastXml, | ||||||||||
debug: reqData.debug, | ||||||||||
isStage: !!reqData.meta.stage, | ||||||||||
customRender: utils.getBidIdParameter('customRender', currentBidParams.outstreamOptions), | ||||||||||
slot: utils.getBidIdParameter('slot', currentBidParams.outstreamOptions), | ||||||||||
iframe: utils.getBidIdParameter('iframe', currentBidParams.outstreamOptions), | ||||||||||
} | ||||||||||
}); | ||||||||||
renderer.setRender(outstreamRender); | ||||||||||
bidUnit.renderer = renderer; | ||||||||||
} | ||||||||||
bids.push(bidUnit); | ||||||||||
}); | ||||||||||
|
||||||||||
return bids; | ||||||||||
} | ||||||||||
|
||||||||||
function outstreamRender(bid) { | ||||||||||
const embedCode = createOutstreamEmbedCode(bid); | ||||||||||
if (typeof bid.renderer.config.customRender === 'function') { | ||||||||||
bid.renderer.config.customRender(bid, embedCode); | ||||||||||
} else { | ||||||||||
try { | ||||||||||
const inIframe = utils.getBidIdParameter('iframe', bid.renderer.config); | ||||||||||
if (inIframe && window.document.getElementById(inIframe).nodeName === 'IFRAME') { | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How should this work with DFP? The iframe option is hard, because the iframe ID is assigned by The slot option doesn't work either as DFP creatives are always delivered in an iframe and the standard prebid creative doesn't have any div elements. From other implementations there are better options: inFrame flagIf you really need to know if you are in an iFrame or not use a flag instead. Then append the necessary code to the slot_idIf you want to break out of the iFrame, then try to do something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These options are not for DFP integration. They need for our publishers, which want to set our outstream directly on their page. They can use id of existing iframe or slot id (can be used any html element). If you would like to integrate our outstream with DFP, please, give me a link with an example of your bidding. We will try to found a solution. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tl;dr IMHO the
I'm not sure how prevalent DFP is as an Ad Server, but I guess there's a fair amount of publisher that use DFP as an Ad Server along with Prebid. Your solution is what is documented as Option 2: Serving without an ad server in the outstream-video-ads documentation. We haven't heard of any publisher doing this.
This is not necessarily DFP relevant. Delivering ad creatives in an iFrame is IMHO a good practice to avoid ad fraud and illegal page access by creatives. My point here is that the implementation is unnecessary complex and hinders the integration with any Ad Server that delivers in iFrames. At least this is how I understand the code 😉 . Please correct me if I missed something obvious. Here's the example page from prebid outstream working examples section: http://prebid.org/dev-docs/show-outstream-video-ads.html The appNexus outstream renders everything inside the iframe and only adds a small There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @muuki88! We created a demo page how we can work with DFP http://test.showheroes.com/schema/sh-prebid-outstream-google There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @veranevera 😃 Thanks a lot for the example page 👍 The banner solutions sounds good to me. Still unsure about the use case of the other options in Prebid, but you know what you are doing 😁 I'll give it a try and keep you posted. I saw that you were using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @muuki88 we are looking forward 💃 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope. Responsive is perfect 😄 |
||||||||||
const iframe = window.document.getElementById(inIframe); | ||||||||||
let framedoc = iframe.contentDocument || (iframe.contentWindow && iframe.contentWindow.document); | ||||||||||
framedoc.body.appendChild(embedCode); | ||||||||||
return; | ||||||||||
} | ||||||||||
|
||||||||||
const slot = utils.getBidIdParameter('slot', bid.renderer.config); | ||||||||||
if (slot && window.document.getElementById(slot)) { | ||||||||||
window.document.getElementById(slot).appendChild(embedCode); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
} else if (slot) { | ||||||||||
utils.logError('[ShowHeroes][renderer] Error: spot not found'); | ||||||||||
} | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
The last case would be to just insert the |
||||||||||
} catch (err) { | ||||||||||
utils.logError('[ShowHeroes][renderer] Error:' + err.message) | ||||||||||
} | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
function createOutstreamEmbedCode(bid) { | ||||||||||
const isStage = utils.getBidIdParameter('isStage', bid.renderer.config); | ||||||||||
const urls = getEnvURLs(isStage); | ||||||||||
|
||||||||||
const fragment = window.document.createDocumentFragment(); | ||||||||||
|
||||||||||
const script = window.document.createElement('script'); | ||||||||||
script.type = 'text/javascript'; | ||||||||||
script.src = urls.pubTag; | ||||||||||
script.onload = function () { | ||||||||||
window.ShowheroesTag = this; | ||||||||||
}; | ||||||||||
script.setAttribute('data-player-host', urls.vlHost); | ||||||||||
|
||||||||||
const spot = window.document.createElement('div'); | ||||||||||
spot.setAttribute('class', 'showheroes-spot'); | ||||||||||
spot.setAttribute('data-player', utils.getBidIdParameter('playerId', bid.renderer.config)); | ||||||||||
spot.setAttribute('data-debug', utils.getBidIdParameter('debug', bid.renderer.config)); | ||||||||||
spot.setAttribute('data-ad-vast-tag', utils.getBidIdParameter('vastUrl', bid.renderer.config)); | ||||||||||
spot.setAttribute('data-stream-type', 'outstream'); | ||||||||||
|
||||||||||
fragment.appendChild(spot); | ||||||||||
fragment.appendChild(script); | ||||||||||
return fragment; | ||||||||||
} | ||||||||||
|
||||||||||
function getBannerHtml (bid, reqBid, reqData) { | ||||||||||
const isStage = !!reqData.meta.stage; | ||||||||||
const pubTag = isStage ? STAGE_PUBLISHER_TAG : PROD_PUBLISHER_TAG; | ||||||||||
const vlHost = isStage ? STAGE_VL : PROD_VL; | ||||||||||
const urls = getEnvURLs(isStage); | ||||||||||
return `<html> | ||||||||||
<head></head> | ||||||||||
<body> | ||||||||||
<script async src="${pubTag}" | ||||||||||
<script async src="${urls.pubTag}" | ||||||||||
data-canvas="" | ||||||||||
data-noad-passback-listener="" | ||||||||||
onload="window.ShowheroesTag=this" | ||||||||||
data-player-host="${vlHost}"></script> | ||||||||||
data-player-host="${urls.vlHost}"></script> | ||||||||||
<div class="showheroes-spot" | ||||||||||
data-debug="${reqData.debug ? '1' : ''}" | ||||||||||
data-player="${reqBid.playerId}" | ||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,8 @@ Module Name: ShowHeroes Bidder Adapter | |
|
||
Module Type: Bidder Adapter | ||
|
||
Alias: showheroesBs | ||
|
||
Maintainer: [email protected] | ||
|
||
# Description | ||
|
@@ -32,6 +34,7 @@ Module that connects to ShowHeroes demand source to fetch bids. | |
] | ||
}, | ||
{ | ||
// if you have adSlot renderer or oustream should be returned as banner | ||
code: 'video', | ||
mediaTypes: { | ||
video: { | ||
|
@@ -44,7 +47,62 @@ Module that connects to ShowHeroes demand source to fetch bids. | |
bidder: "showheroes-bs", | ||
params: { | ||
playerId: '0151f985-fb1a-4f37-bb26-cfc62e43ec05', | ||
vpaidMode: true // by default is 'false' | ||
} | ||
} | ||
] | ||
}, | ||
{ | ||
code: 'video', | ||
mediaTypes: { | ||
video: { | ||
playerSize: [640, 480], | ||
context: 'outstream', | ||
} | ||
}, | ||
bids: [ | ||
{ | ||
bidder: "showheroes-bs", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. During my testing I found that this bidder code causes issue with DFP as I used the alias There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this something we want to fix / update? |
||
params: { | ||
playerId: '0151f985-fb1a-4f37-bb26-cfc62e43ec05', | ||
|
||
outstreamOptions: { | ||
// Required for the outstream renderer to exact node, one of | ||
iframe: 'iframe_id', | ||
// or | ||
slot: 'slot_id' | ||
} | ||
} | ||
} | ||
] | ||
}, | ||
{ | ||
code: 'video', | ||
mediaTypes: { | ||
video: { | ||
playerSize: [640, 480], | ||
context: 'outstream', | ||
} | ||
}, | ||
bids: [ | ||
{ | ||
bidder: "showheroes-bs", | ||
params: { | ||
playerId: '0151f985-fb1a-4f37-bb26-cfc62e43ec05', | ||
|
||
outstreamOptions: { | ||
// Custom outstream rendering function | ||
customRender: function(bid, embedCode) { | ||
// Example with embedCode | ||
someContainer.appendChild(embedCode); | ||
|
||
// bid config data | ||
var vastUrl = bid.renderer.config.vastUrl; | ||
var vastXML = bid.renderer.config.vastXML; | ||
var videoWidth = bid.renderer.config.width; | ||
var videoHeight = bid.renderer.config.height; | ||
var playerId = bid.renderer.config.playerId; | ||
}, | ||
} | ||
} | ||
} | ||
] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
out of curiosity:
will video ads still be displayed if the media type is banner and video?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our player will work and show video or display ad as banner in an iframe, in this case.