diff --git a/modules/videofyBidAdapter.js b/modules/videofyBidAdapter.js
new file mode 100644
index 00000000000..07e95689ab4
--- /dev/null
+++ b/modules/videofyBidAdapter.js
@@ -0,0 +1,262 @@
+import { VIDEO } from '../src/mediaTypes.js';
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { Renderer } from '../src/Renderer.js';
+
+const BIDDER_CODE = 'videofy';
+const TTL = 600;
+
+function avRenderer(bid) {
+ bid.renderer.push(function() {
+ let eventCallback = bid && bid.renderer && bid.renderer.handleVideoEvent ? bid.renderer.handleVideoEvent : null;
+ window.aniviewRenderer.renderAd({
+ id: bid.adUnitCode + '_' + bid.adId,
+ debug: window.location.href.indexOf('pbjsDebug') >= 0,
+ placement: bid.adUnitCode,
+ width: bid.width,
+ height: bid.height,
+ vastUrl: bid.vastUrl,
+ vastXml: bid.vastXml,
+ config: bid.params[0].rendererConfig,
+ eventsCallback: eventCallback,
+ bid: bid
+ });
+ });
+}
+
+function newRenderer(bidRequest) {
+ const renderer = Renderer.install({
+ url: 'https://player.srv-mars.com/script/6.1/prebidRenderer.js',
+ config: {},
+ loaded: false,
+ });
+
+ try {
+ renderer.setRender(avRenderer);
+ } catch (err) {
+ }
+
+ return renderer;
+}
+
+function isBidRequestValid(bid) {
+ if (!bid.params || !bid.params.AV_PUBLISHERID || !bid.params.AV_CHANNELID) { return false; }
+
+ return true;
+}
+let irc = 0;
+function buildRequests(validBidRequests, bidderRequest) {
+ let bidRequests = [];
+
+ for (let i = 0; i < validBidRequests.length; i++) {
+ let bidRequest = validBidRequests[i];
+ var sizes = [[640, 480]];
+
+ if (bidRequest.mediaTypes && bidRequest.mediaTypes.video && bidRequest.mediaTypes.video.playerSize) {
+ sizes = bidRequest.mediaTypes.video.playerSize;
+ } else {
+ if (bidRequest.sizes) {
+ sizes = bidRequest.sizes;
+ }
+ }
+ if (sizes.length === 2 && typeof sizes[0] === 'number') {
+ sizes = [[sizes[0], sizes[1]]];
+ }
+
+ for (let j = 0; j < sizes.length; j++) {
+ let size = sizes[j];
+ let playerWidth;
+ let playerHeight;
+
+ if (size && size.length == 2) {
+ playerWidth = size[0];
+ playerHeight = size[1];
+ } else {
+ playerWidth = 640;
+ playerHeight = 480;
+ }
+
+ let s2sParams = {};
+
+ for (var attrname in bidRequest.params) {
+ if (bidRequest.params.hasOwnProperty(attrname) && attrname.indexOf('AV_') == 0) {
+ s2sParams[attrname] = bidRequest.params[attrname];
+ }
+ };
+
+ if (s2sParams.AV_APPPKGNAME && !s2sParams.AV_URL) { s2sParams.AV_URL = s2sParams.AV_APPPKGNAME; }
+ if (!s2sParams.AV_IDFA && !s2sParams.AV_URL) {
+ if (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer) {
+ s2sParams.AV_URL = bidderRequest.refererInfo.referer;
+ } else {
+ s2sParams.AV_URL = window.location.href;
+ }
+ }
+ if (s2sParams.AV_IDFA && !s2sParams.AV_AID) { s2sParams.AV_AID = s2sParams.AV_IDFA; }
+ if (s2sParams.AV_AID && !s2sParams.AV_IDFA) { s2sParams.AV_IDFA = s2sParams.AV_AID; }
+
+ s2sParams.cb = Math.floor(Math.random() * 999999999);
+ s2sParams.AV_WIDTH = playerWidth;
+ s2sParams.AV_HEIGHT = playerHeight;
+ s2sParams.bidWidth = playerWidth;
+ s2sParams.bidHeight = playerHeight;
+ s2sParams.bidId = bidRequest.bidId;
+ s2sParams.pbjs = 1;
+ s2sParams.tgt = 10;
+ s2sParams.s2s = '1';
+ s2sParams.irc = irc;
+ irc++;
+ s2sParams.wpm = 1;
+
+ if (bidderRequest && bidderRequest.gdprConsent) {
+ if (bidderRequest.gdprConsent.gdprApplies) {
+ s2sParams.AV_GDPR = 1;
+ s2sParams.AV_CONSENT = bidderRequest.gdprConsent.consentString;
+ }
+ }
+ if (bidderRequest && bidderRequest.uspConsent) {
+ s2sParams.AV_CCPA = bidderRequest.uspConsent;
+ }
+
+ let serverDomain = (bidRequest.params && bidRequest.params.serverDomain) ? bidRequest.params.serverDomain : 'servx.srv-mars.com';
+ let servingUrl = 'https://' + serverDomain + '/api/adserver/vast3/';
+
+ bidRequests.push({
+ method: 'GET',
+ url: servingUrl,
+ data: s2sParams,
+ bidRequest
+ });
+ }
+ }
+
+ return bidRequests;
+}
+function getCpmData(xml) {
+ let ret = {cpm: 0, currency: 'USD'};
+ if (xml) {
+ let ext = xml.getElementsByTagName('Extensions');
+ if (ext && ext.length > 0) {
+ ext = ext[0].getElementsByTagName('Extension');
+ if (ext && ext.length > 0) {
+ for (var i = 0; i < ext.length; i++) {
+ if (ext[i].getAttribute('type') == 'ANIVIEW') {
+ let price = ext[i].getElementsByTagName('Cpm');
+ if (price && price.length == 1) {
+ ret.cpm = price[0].textContent;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ return ret;
+}
+function interpretResponse(serverResponse, bidRequest) {
+ let bidResponses = [];
+ if (serverResponse && serverResponse.body) {
+ if (serverResponse.error) {
+ return bidResponses;
+ } else {
+ try {
+ let bidResponse = {};
+ if (bidRequest && bidRequest.data && bidRequest.data.bidId && bidRequest.data.bidId !== '') {
+ let xmlStr = serverResponse.body;
+ let xml = new window.DOMParser().parseFromString(xmlStr, 'text/xml');
+ if (xml && xml.getElementsByTagName('parsererror').length == 0) {
+ let cpmData = getCpmData(xml);
+ if (cpmData && cpmData.cpm > 0) {
+ bidResponse.requestId = bidRequest.data.bidId;
+ bidResponse.bidderCode = BIDDER_CODE;
+ bidResponse.ad = '';
+ bidResponse.cpm = cpmData.cpm;
+ bidResponse.width = bidRequest.data.AV_WIDTH;
+ bidResponse.height = bidRequest.data.AV_HEIGHT;
+ bidResponse.ttl = TTL;
+ bidResponse.creativeId = xml.getElementsByTagName('Ad') && xml.getElementsByTagName('Ad')[0] && xml.getElementsByTagName('Ad')[0].getAttribute('id') ? xml.getElementsByTagName('Ad')[0].getAttribute('id') : 'creativeId';
+ bidResponse.currency = cpmData.currency;
+ bidResponse.netRevenue = true;
+ var blob = new Blob([xmlStr], {
+ type: 'application/xml'
+ });
+ bidResponse.vastUrl = window.URL.createObjectURL(blob);
+ bidResponse.vastXml = xmlStr;
+ bidResponse.mediaType = VIDEO;
+ if (bidRequest.bidRequest && bidRequest.bidRequest.mediaTypes && bidRequest.bidRequest.mediaTypes.video && bidRequest.bidRequest.mediaTypes.video.context === 'outstream') { bidResponse.renderer = newRenderer(bidRequest); }
+
+ bidResponses.push(bidResponse);
+ }
+ } else {}
+ } else {}
+ } catch (e) {}
+ }
+ } else {}
+
+ return bidResponses;
+}
+
+function getSyncData(xml, options) {
+ let ret = [];
+ if (xml) {
+ let ext = xml.getElementsByTagName('Extensions');
+ if (ext && ext.length > 0) {
+ ext = ext[0].getElementsByTagName('Extension');
+ if (ext && ext.length > 0) {
+ for (var i = 0; i < ext.length; i++) {
+ if (ext[i].getAttribute('type') == 'ANIVIEW') {
+ let syncs = ext[i].getElementsByTagName('AdServingSync');
+ if (syncs && syncs.length == 1) {
+ try {
+ let data = JSON.parse(syncs[0].textContent);
+ if (data && data.trackers && data.trackers.length) {
+ data = data.trackers;
+ for (var j = 0; j < data.length; j++) {
+ if (typeof data[j] === 'object' && typeof data[j].url === 'string' && data[j].e === 'inventory') {
+ if (data[j].t == 1 && options.pixelEnabled) {
+ ret.push({url: data[j].url, type: 'image'});
+ } else {
+ if (data[j].t == 3 && options.iframeEnabled) {
+ ret.push({url: data[j].url, type: 'iframe'});
+ }
+ }
+ }
+ }
+ }
+ } catch (e) {}
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+function getUserSyncs(syncOptions, serverResponses) {
+ if (serverResponses && serverResponses[0] && serverResponses[0].body) {
+ if (serverResponses.error) {
+ return [];
+ } else {
+ try {
+ let xmlStr = serverResponses[0].body;
+ let xml = new window.DOMParser().parseFromString(xmlStr, 'text/xml');
+ if (xml && xml.getElementsByTagName('parsererror').length == 0) {
+ let syncData = getSyncData(xml, syncOptions);
+ return syncData;
+ }
+ } catch (e) {}
+ }
+ }
+}
+
+export const spec = {
+ code: BIDDER_CODE,
+ supportedMediaTypes: [VIDEO],
+ isBidRequestValid,
+ buildRequests,
+ interpretResponse,
+ getUserSyncs
+};
+
+registerBidder(spec);
diff --git a/modules/videofyBidAdapter.md b/modules/videofyBidAdapter.md
new file mode 100644
index 00000000000..b50eaf5672e
--- /dev/null
+++ b/modules/videofyBidAdapter.md
@@ -0,0 +1,36 @@
+# Overview
+
+```
+Module Name: Videofy Bidder Adapter
+Module Type: Bidder Adapter
+Maintainer: support1@videofy.ai
+```
+
+# Description
+
+Connects to Videofy for bids.
+
+Videofy bid adapter supports Video ads currently.
+
+# Sample Ad Unit: For Publishers
+```javascript
+var videoAdUnit = [
+{
+ code: 'video1',
+ mediaTypes: {
+ video: {
+ playerSize: [[640, 480]],
+ context: 'outstream'
+ },
+ },
+ bids: [{
+ bidder: 'videofy',
+ params: {
+ AV_PUBLISHERID: '55b78633181f4603178b4568',
+ AV_CHANNELID: '5d19dfca4b6236688c0a2fc4'
+ }
+ }]
+}];
+```
+
+```
diff --git a/test/spec/modules/videofyBidAdapter_spec.js b/test/spec/modules/videofyBidAdapter_spec.js
new file mode 100644
index 00000000000..e221ece45b8
--- /dev/null
+++ b/test/spec/modules/videofyBidAdapter_spec.js
@@ -0,0 +1,200 @@
+import { spec } from 'modules/videofyBidAdapter.js';
+import { newBidder } from 'src/adapters/bidderFactory.js';
+const { expect } = require('chai');
+
+describe('Videofy Bid Adapter Test', function () {
+ const adapter = newBidder(spec);
+
+ describe('inherited functions', function () {
+ it('exists and is a function', function () {
+ expect(adapter.callBids).to.exist.and.to.be.a('function');
+ });
+ });
+
+ describe('isBidRequestValid', function () {
+ let bid = {
+ 'bidder': 'videofy',
+ 'params': {
+ 'AV_PUBLISHERID': '123456',
+ 'AV_CHANNELID': '123456'
+ },
+ 'adUnitCode': 'video1',
+ 'sizes': [[300, 250], [640, 480]],
+ 'bidId': '30b31c1838de1e',
+ 'bidderRequestId': '22edbae2733bf6',
+ 'requestId': 'a09c66c3-53e3-4428-b296-38fc08e7cd2a',
+ 'transactionId': 'd6f6b392-54a9-454c-85fb-a2fd882c4a2d',
+ };
+
+ it('should return true when required params found', function () {
+ expect(spec.isBidRequestValid(bid)).to.equal(true);
+ });
+
+ it('should return false when required params are not passed', function () {
+ let bid = Object.assign({}, bid);
+ delete bid.params;
+ bid.params = {
+ something: 'is wrong'
+ };
+ expect(spec.isBidRequestValid(bid)).to.equal(false);
+ });
+ });
+
+ describe('buildRequests', function () {
+ let bid2Requests = [
+ {
+ 'bidder': 'videofy',
+ 'params': {
+ 'AV_PUBLISHERID': '123456',
+ 'AV_CHANNELID': '123456'
+ },
+ 'adUnitCode': 'test1',
+ 'sizes': [[300, 250], [640, 480]],
+ 'bidId': '30b31c1838de1e',
+ 'bidderRequestId': '22edbae2733bf6',
+ 'requestId': 'a09c66c3-53e3-4428-b296-38fc08e7cd2a',
+ 'transactionId': 'd6f6b392-54a9-454c-85fb-a2fd882c4a2d',
+ }
+ ];
+ let bid1Request = [
+ {
+ 'bidder': 'videofy',
+ 'params': {
+ 'AV_PUBLISHERID': '123456',
+ 'AV_CHANNELID': '123456'
+ },
+ 'adUnitCode': 'test1',
+ 'sizes': [640, 480],
+ 'bidId': '30b31c1838de1e',
+ 'bidderRequestId': '22edbae2733bf6',
+ 'requestId': 'a09c66c3-53e3-4428-b296-38fc08e7cd2a',
+ 'transactionId': 'd6f6b392-54a9-454c-85fb-a2fd882c4a2d',
+ }
+ ];
+
+ it('Test 2 requests', function () {
+ const requests = spec.buildRequests(bid2Requests);
+ expect(requests.length).to.equal(2);
+ const r1 = requests[0];
+ const d1 = requests[0].data;
+ expect(d1).to.have.property('AV_PUBLISHERID');
+ expect(d1.AV_PUBLISHERID).to.equal('123456');
+ expect(d1).to.have.property('AV_CHANNELID');
+ expect(d1.AV_CHANNELID).to.equal('123456');
+ expect(d1).to.have.property('AV_WIDTH');
+ expect(d1.AV_WIDTH).to.equal(300);
+ expect(d1).to.have.property('AV_HEIGHT');
+ expect(d1.AV_HEIGHT).to.equal(250);
+ expect(d1).to.have.property('AV_URL');
+ expect(d1).to.have.property('cb');
+ expect(d1).to.have.property('s2s');
+ expect(d1.s2s).to.equal('1');
+ expect(d1).to.have.property('pbjs');
+ expect(d1.pbjs).to.equal(1);
+ expect(r1).to.have.property('url');
+ expect(r1.url).to.contain('https://servx.srv-mars.com/api/adserver/vast3/');
+ const r2 = requests[1];
+ const d2 = requests[1].data;
+ expect(d2).to.have.property('AV_PUBLISHERID');
+ expect(d2.AV_PUBLISHERID).to.equal('123456');
+ expect(d2).to.have.property('AV_CHANNELID');
+ expect(d2.AV_CHANNELID).to.equal('123456');
+ expect(d2).to.have.property('AV_WIDTH');
+ expect(d2.AV_WIDTH).to.equal(640);
+ expect(d2).to.have.property('AV_HEIGHT');
+ expect(d2.AV_HEIGHT).to.equal(480);
+ expect(d2).to.have.property('AV_URL');
+ expect(d2).to.have.property('cb');
+ expect(d2).to.have.property('s2s');
+ expect(d2.s2s).to.equal('1');
+ expect(d2).to.have.property('pbjs');
+ expect(d2.pbjs).to.equal(1);
+ expect(r2).to.have.property('url');
+ expect(r2.url).to.contain('https://servx.srv-mars.com/api/adserver/vast3/');
+ });
+
+ it('Test 1 request', function () {
+ const requests = spec.buildRequests(bid1Request);
+ expect(requests.length).to.equal(1);
+ const r = requests[0];
+ const d = requests[0].data;
+ expect(d).to.have.property('AV_PUBLISHERID');
+ expect(d.AV_PUBLISHERID).to.equal('123456');
+ expect(d).to.have.property('AV_CHANNELID');
+ expect(d.AV_CHANNELID).to.equal('123456');
+ expect(d).to.have.property('AV_WIDTH');
+ expect(d.AV_WIDTH).to.equal(640);
+ expect(d).to.have.property('AV_HEIGHT');
+ expect(d.AV_HEIGHT).to.equal(480);
+ expect(d).to.have.property('AV_URL');
+ expect(d).to.have.property('cb');
+ expect(d).to.have.property('s2s');
+ expect(d.s2s).to.equal('1');
+ expect(d).to.have.property('pbjs');
+ expect(d.pbjs).to.equal(1);
+ expect(r).to.have.property('url');
+ expect(r.url).to.contain('https://servx.srv-mars.com/api/adserver/vast3/');
+ });
+ });
+
+ describe('interpretResponse', function () {
+ let bidRequest = {
+ 'url': 'https://servx.srv-mars.com/api/adserver/vast3/',
+ 'data': {
+ 'bidId': '253dcb69fb2577',
+ AV_PUBLISHERID: '55b78633181f4603178b4568',
+ AV_CHANNELID: '55b7904d181f46410f8b4568',
+ }
+ };
+ let serverResponse = {};
+ serverResponse.body = 'FORDFORD00:00:15';
+
+ it('Check bid interpretResponse', function () {
+ const BIDDER_CODE = 'videofy';
+ let bidResponses = spec.interpretResponse(serverResponse, bidRequest);
+ expect(bidResponses.length).to.equal(1);
+ let bidResponse = bidResponses[0];
+ expect(bidResponse.requestId).to.equal(bidRequest.data.bidId);
+ expect(bidResponse.bidderCode).to.equal(BIDDER_CODE);
+ expect(bidResponse.cpm).to.equal('2');
+ expect(bidResponse.ttl).to.equal(600);
+ expect(bidResponse.currency).to.equal('USD');
+ expect(bidResponse.netRevenue).to.equal(true);
+ expect(bidResponse.mediaType).to.equal('video');
+ });
+
+ it('safely handles XML parsing failure from invalid bid response', function () {
+ let invalidServerResponse = {};
+ invalidServerResponse.body = '';
+
+ let result = spec.interpretResponse(invalidServerResponse, bidRequest);
+ expect(result.length).to.equal(0);
+ });
+
+ it('handles nobid responses', function () {
+ let nobidResponse = {};
+ nobidResponse.body = '';
+
+ let result = spec.interpretResponse(nobidResponse, bidRequest);
+ expect(result.length).to.equal(0);
+ });
+ });
+
+ describe('getUserSyncs', function () {
+ it('Check get sync pixels from response', function () {
+ let pixelUrl = 'https://sync.pixel.url/sync';
+ let pixelEvent = 'inventory';
+ let pixelType = '3';
+ let pixelStr = '{"url":"' + pixelUrl + '", "e":"' + pixelEvent + '", "t":' + pixelType + '}';
+ let bidResponse = 'FORDFORD00:00:15';
+ let serverResponse = [
+ {body: bidResponse}
+ ];
+ let syncPixels = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, serverResponse);
+ expect(syncPixels.length).to.equal(1);
+ let pixel = syncPixels[0];
+ expect(pixel.url).to.equal(pixelUrl);
+ expect(pixel.type).to.equal('iframe');
+ });
+ });
+});