kemono2/client/fluid-player/src/modules/vpaid.js
2025-04-02 16:32:47 +02:00

544 lines
20 KiB
JavaScript

// VPAID support module
export default function (playerInstance, options) {
const callbacks = {
AdStarted: () => playerInstance.onStartVpaidAd,
AdStopped: () => playerInstance.onStopVpaidAd,
AdSkipped: () => playerInstance.onSkipVpaidAd,
AdLoaded: () => playerInstance.onVpaidAdLoaded,
AdLinearChange: () => playerInstance.onVpaidAdLinearChange,
AdSizeChange: () => playerInstance.onVpaidAdSizeChange,
AdExpandedChange: () => playerInstance.onVpaidAdExpandedChange,
AdSkippableStateChange: () => playerInstance.onVpaidAdSkippableStateChange,
AdDurationChange: () => playerInstance.onVpaidAdDurationChange,
AdRemainingTimeChange: () => playerInstance.onVpaidAdRemainingTimeChange,
AdVolumeChange: () => playerInstance.onVpaidAdVolumeChange,
AdImpression: () => playerInstance.onVpaidAdImpression,
AdClickThru: () => playerInstance.onVpaidAdClickThru,
AdInteraction: () => playerInstance.onVpaidAdInteraction,
AdVideoStart: () => playerInstance.onVpaidAdVideoStart,
AdVideoFirstQuartile: () => playerInstance.onVpaidAdVideoFirstQuartile,
AdVideoMidpoint: () => playerInstance.onVpaidAdVideoMidpoint,
AdVideoThirdQuartile: () => playerInstance.onVpaidAdVideoThirdQuartile,
AdVideoComplete: () => playerInstance.onVpaidAdVideoComplete,
AdUserAcceptInvitation: () => playerInstance.onVpaidAdUserAcceptInvitation,
AdUserMinimize: () => playerInstance.onVpaidAdUserMinimize,
AdUserClose: () => playerInstance.onVpaidAdUserClose,
AdPaused: () => playerInstance.onVpaidAdPaused,
AdPlaying: () => playerInstance.onVpaidAdPlaying,
AdError: () => playerInstance.onVpaidAdError,
AdLog: () => playerInstance.onVpaidAdLog
};
playerInstance.checkVPAIDInterface = (vpaidAdUnit) => {
const VPAIDCreative = vpaidAdUnit;
// checks if all the mandatory params present
return !!(VPAIDCreative.handshakeVersion && typeof VPAIDCreative.handshakeVersion == "function"
&& VPAIDCreative.initAd && typeof VPAIDCreative.initAd == "function"
&& VPAIDCreative.startAd && typeof VPAIDCreative.startAd == "function"
&& VPAIDCreative.stopAd && typeof VPAIDCreative.stopAd == "function"
&& VPAIDCreative.skipAd && typeof VPAIDCreative.skipAd == "function"
&& VPAIDCreative.resizeAd && typeof VPAIDCreative.resizeAd == "function"
&& VPAIDCreative.pauseAd && typeof VPAIDCreative.pauseAd == "function"
&& VPAIDCreative.resumeAd && typeof VPAIDCreative.resumeAd == "function"
&& VPAIDCreative.expandAd && typeof VPAIDCreative.expandAd == "function"
&& VPAIDCreative.collapseAd && typeof VPAIDCreative.collapseAd == "function"
&& VPAIDCreative.subscribe && typeof VPAIDCreative.subscribe == "function"
&& VPAIDCreative.unsubscribe && typeof VPAIDCreative.unsubscribe == "function");
};
// Callback for AdPaused
playerInstance.onVpaidAdPaused = () => {
playerInstance.vpaidTimeoutTimerClear();
playerInstance.debugMessage("onAdPaused");
};
// Callback for AdPlaying
playerInstance.onVpaidAdPlaying = () => {
playerInstance.vpaidTimeoutTimerClear();
playerInstance.debugMessage("onAdPlaying");
};
// Callback for AdError
playerInstance.onVpaidAdError = (message) => {
playerInstance.debugMessage("onAdError: " + message);
playerInstance.vpaidTimeoutTimerClear();
playerInstance.onVpaidEnded();
};
// Callback for AdLog
playerInstance.onVpaidAdLog = (message) => {
playerInstance.debugMessage("onAdLog: " + message);
};
// Callback for AdUserAcceptInvitation
playerInstance.onVpaidAdUserAcceptInvitation = () => {
playerInstance.debugMessage("onAdUserAcceptInvitation");
};
// Callback for AdUserMinimize
playerInstance.onVpaidAdUserMinimize = () => {
playerInstance.debugMessage("onAdUserMinimize");
};
// Callback for AdUserClose
playerInstance.onVpaidAdUserClose = () => {
playerInstance.debugMessage("onAdUserClose");
};
// Callback for AdUserClose
playerInstance.onVpaidAdSkippableStateChange = () => {
if (!playerInstance.vpaidAdUnit) {
return;
}
playerInstance.debugMessage("Ad Skippable State Changed to: " + playerInstance.vpaidAdUnit.getAdSkippableState());
};
// Callback for AdUserClose
playerInstance.onVpaidAdExpandedChange = () => {
if (!playerInstance.vpaidAdUnit) {
return;
}
playerInstance.debugMessage("Ad Expanded Changed to: " + playerInstance.vpaidAdUnit.getAdExpanded());
};
// Pass through for getAdExpanded
playerInstance.getVpaidAdExpanded = () => {
playerInstance.debugMessage("getAdExpanded");
if (!playerInstance.vpaidAdUnit) {
return;
}
return playerInstance.vpaidAdUnit.getAdExpanded();
};
// Pass through for getAdSkippableState
playerInstance.getVpaidAdSkippableState = () => {
playerInstance.debugMessage("getAdSkippableState");
if (!playerInstance.vpaidAdUnit) {
return;
}
return playerInstance.vpaidAdUnit.getAdSkippableState();
};
// Callback for AdSizeChange
playerInstance.onVpaidAdSizeChange = () => {
if (!playerInstance.vpaidAdUnit) {
return;
}
playerInstance.debugMessage("Ad size changed to: w=" + playerInstance.vpaidAdUnit.getAdWidth() + " h=" + playerInstance.vpaidAdUnit.getAdHeight());
};
// Callback for AdDurationChange
playerInstance.onVpaidAdDurationChange = () => {
if (!playerInstance.vpaidAdUnit) {
return;
}
playerInstance.debugMessage("Ad Duration Changed to: " + playerInstance.vpaidAdUnit.getAdDuration());
};
// Callback for AdRemainingTimeChange
playerInstance.onVpaidAdRemainingTimeChange = () => {
if (!playerInstance.vpaidAdUnit) {
return;
}
playerInstance.debugMessage("Ad Remaining Time Changed to: " + playerInstance.vpaidAdUnit.getAdRemainingTime());
};
// Pass through for getAdRemainingTime
playerInstance.getVpaidAdRemainingTime = () => {
playerInstance.debugMessage("getAdRemainingTime");
if (!playerInstance.vpaidAdUnit) {
return;
}
return playerInstance.vpaidAdUnit.getAdRemainingTime();
};
// Callback for AdImpression
playerInstance.onVpaidAdImpression = () => {
playerInstance.debugMessage("Ad Impression");
//Announce the impressions
playerInstance.trackSingleEvent('impression');
};
// Callback for AdClickThru
playerInstance.onVpaidAdClickThru = (url, id, playerHandles) => {
playerInstance.debugMessage("Clickthrough portion of the ad was clicked");
// if playerHandles flag is set to true
// then player need to open click thorough url in new window
if (playerHandles) {
window.open(playerInstance.vastOptions.clickthroughUrl);
}
playerInstance.pauseVpaidAd();
// fire click tracking
playerInstance.callUris(playerInstance.vastOptions.clicktracking);
};
// Callback for AdInteraction
playerInstance.onVpaidAdInteraction = (id) => {
playerInstance.debugMessage("A non-clickthrough event has occured");
};
// Callback for AdVideoStart
playerInstance.onVpaidAdVideoStart = () => {
playerInstance.debugMessage("Video 0% completed");
playerInstance.trackSingleEvent('start');
};
// Callback for AdUserClose
playerInstance.onVpaidAdVideoFirstQuartile = () => {
playerInstance.debugMessage("Video 25% completed");
playerInstance.trackSingleEvent('firstQuartile');
};
// Callback for AdUserClose
playerInstance.onVpaidAdVideoMidpoint = () => {
playerInstance.debugMessage("Video 50% completed");
playerInstance.trackSingleEvent('midpoint');
};
// Callback for AdUserClose
playerInstance.onVpaidAdVideoThirdQuartile = () => {
playerInstance.debugMessage("Video 75% completed");
playerInstance.trackSingleEvent('thirdQuartile');
};
// Callback for AdVideoComplete
playerInstance.onVpaidAdVideoComplete = () => {
playerInstance.debugMessage("Video 100% completed");
playerInstance.trackSingleEvent('complete');
};
// Callback for AdLinearChange
playerInstance.onVpaidAdLinearChange = () => {
const vpaidNonLinearSlot = playerInstance.domRef.wrapper.getElementsByClassName("fluid_vpaidNonLinear_ad")[0];
const closeBtn = playerInstance.domRef.wrapper.querySelector('.close_button');
const adListId = vpaidNonLinearSlot.getAttribute('adlistid');
playerInstance.debugMessage("Ad linear has changed: " + playerInstance.vpaidAdUnit.getAdLinear());
if (!playerInstance.vpaidAdUnit.getAdLinear()) {
return;
}
playerInstance.backupMainVideoContentTime(adListId.split('_')[0]);
playerInstance.isCurrentlyPlayingAd = true;
if (closeBtn) {
closeBtn.remove();
}
vpaidNonLinearSlot.className = 'fluid_vpaid_slot';
playerInstance.domRef.player.loop = false;
playerInstance.domRef.player.removeAttribute('controls');
const progressbarContainer = playerInstance.domRef.player.parentNode.getElementsByClassName('fluid_controls_currentprogress');
for (let i = 0; i < progressbarContainer.length; i++) {
progressbarContainer[i].style.backgroundColor = playerInstance.displayOptions.layoutControls.adProgressColor;
}
playerInstance.toggleLoader(false);
};
// Pass through for getAdLinear
playerInstance.getVpaidAdLinear = () => {
playerInstance.debugMessage("getAdLinear");
return playerInstance.vpaidAdUnit.getAdLinear();
};
// Pass through for startAd()
playerInstance.startVpaidAd = () => {
playerInstance.debugMessage("startAd");
playerInstance.vpaidTimeoutTimerStart();
playerInstance.vpaidAdUnit.startAd();
};
// Callback for AdLoaded
playerInstance.onVpaidAdLoaded = () => {
playerInstance.debugMessage("ad has been loaded");
// start the video play as vpaid is loaded successfully
playerInstance.vpaidTimeoutTimerClear();
playerInstance.startVpaidAd();
};
// Callback for StartAd()
playerInstance.onStartVpaidAd = () => {
playerInstance.debugMessage("Ad has started");
playerInstance.vpaidTimeoutTimerClear();
};
// Pass through for stopAd()
playerInstance.stopVpaidAd = () => {
playerInstance.vpaidTimeoutTimerStart();
playerInstance.vpaidAdUnit.stopAd();
};
// Hard Pass through for stopAd() excluding deleteOtherVpaidAdsApart
playerInstance.hardStopVpaidAd = (ad) => {
// this is hard stop of vpaid ads
// we delete all the vpaid assets so the new one can be loaded
// delete all assets apart from the ad from deleteOtherVpaidAdsApart
if (playerInstance.vpaidAdUnit) {
playerInstance.vpaidAdUnit.stopAd();
playerInstance.vpaidAdUnit = null;
}
const vpaidIframes = playerInstance.domRef.wrapper.getElementsByClassName("fluid_vpaid_iframe");
const vpaidSlots = playerInstance.domRef.wrapper.getElementsByClassName("fluid_vpaid_slot");
const vpaidNonLinearSlots = playerInstance.domRef.wrapper.getElementsByClassName("fluid_vpaidNonLinear_ad");
for (let i = 0; i < vpaidIframes.length; i++) {
if (vpaidIframes[i].getAttribute('adListId') !== ad.id) {
vpaidIframes[i].remove();
}
}
for (let j = 0; j < vpaidSlots.length; j++) {
if (vpaidSlots[j].getAttribute('adListId') !== ad.id) {
vpaidSlots[j].remove();
}
}
for (let k = 0; k < vpaidNonLinearSlots.length; k++) {
if (vpaidNonLinearSlots[k].getAttribute('adListId') !== ad.id) {
vpaidNonLinearSlots[k].remove();
}
}
};
// Callback for AdUserClose
playerInstance.onStopVpaidAd = () => {
playerInstance.debugMessage("Ad has stopped");
playerInstance.vpaidTimeoutTimerClear();
playerInstance.onVpaidEnded();
};
// Callback for AdUserClose
playerInstance.onSkipVpaidAd = () => {
playerInstance.debugMessage("Ad was skipped");
playerInstance.vpaidTimeoutTimerClear();
playerInstance.onVpaidEnded();
};
// Passthrough for skipAd
playerInstance.skipVpaidAd = () => {
playerInstance.vpaidTimeoutTimerStart();
if (!playerInstance.vpaidAdUnit) {
return;
}
playerInstance.vpaidAdUnit.skipAd()
playerInstance.vpaidTimeoutTimerClear();
playerInstance.onVpaidEnded();
};
// Passthrough for setAdVolume
playerInstance.setVpaidAdVolume = (val) => {
if (!playerInstance.vpaidAdUnit) {
return;
}
playerInstance.vpaidAdUnit.setAdVolume(val);
};
// Passthrough for getAdVolume
playerInstance.getVpaidAdVolume = () => {
if (!playerInstance.vpaidAdUnit) {
return;
}
return playerInstance.vpaidAdUnit.getAdVolume();
};
// Callback for AdVolumeChange
playerInstance.onVpaidAdVolumeChange = () => {
if (!playerInstance.vpaidAdUnit) {
return;
}
playerInstance.debugMessage("Ad Volume has changed to - " + playerInstance.vpaidAdUnit.getAdVolume());
};
playerInstance.resizeVpaidAuto = () => {
if (playerInstance.vastOptions !== null && playerInstance.vastOptions.vpaid && playerInstance.vastOptions.linear) {
const adWidth = playerInstance.domRef.player.offsetWidth;
const adHeight = playerInstance.domRef.player.offsetHeight;
const mode = (playerInstance.fullscreenMode ? 'fullscreen' : 'normal');
playerInstance.resizeVpaidAd(adWidth, adHeight, mode);
}
};
// Passthrough for resizeAd
playerInstance.resizeVpaidAd = (width, height, viewMode) => {
if (!playerInstance.vpaidAdUnit) {
return;
}
playerInstance.vpaidAdUnit.resizeAd(width, height, viewMode);
};
// Passthrough for pauseAd()
playerInstance.pauseVpaidAd = () => {
playerInstance.vpaidTimeoutTimerStart();
if (!playerInstance.vpaidAdUnit) {
return;
}
playerInstance.vpaidAdUnit.pauseAd();
};
// Passthrough for resumeAd()
playerInstance.resumeVpaidAd = () => {
playerInstance.vpaidTimeoutTimerStart();
if (!playerInstance.vpaidAdUnit) {
return;
}
playerInstance.vpaidAdUnit.resumeAd();
};
// Passthrough for expandAd()
playerInstance.expandVpaidAd = () => {
if (!playerInstance.vpaidAdUnit) {
return;
}
playerInstance.vpaidAdUnit.expandAd();
};
// Passthrough for collapseAd()
playerInstance.collapseVpaidAd = () => {
if (!playerInstance.vpaidAdUnit) {
return;
}
playerInstance.vpaidAdUnit.collapseAd();
};
playerInstance.vpaidTimeoutTimerClear = () => {
if (playerInstance.vpaidTimer) {
clearTimeout(playerInstance.vpaidTimer);
}
};
// placeholder for timer function
playerInstance.vpaidTimeoutTimerStart = () => {
// clear previous timer if any
playerInstance.vpaidTimeoutTimerClear();
playerInstance.vpaidTimer = setTimeout(function () {
playerInstance.announceLocalError('901');
playerInstance.onVpaidEnded();
}, playerInstance.displayOptions.vastOptions.vpaidTimeout);
};
playerInstance.vpaidCallbackListenersAttach = () => {
// The key of the object is the event name and the value is a reference to the callback function that is registered with the creative
// Looping through the object and registering each of the callbacks with the creative
for (let eventName in callbacks) {
playerInstance.vpaidAdUnit.subscribe(callbacks[eventName](), eventName, playerInstance);
}
};
playerInstance.vpaidCallbackListenersDetach = () => {
if (!playerInstance.vpaidAdUnit) {
return;
}
for (let eventName in callbacks) {
playerInstance.vpaidAdUnit.unsubscribe(callbacks[eventName](), eventName, playerInstance);
}
};
playerInstance.loadVpaid = (ad, vpaidJsUrl) => {
const vpaidIframe = document.createElement('iframe');
vpaidIframe.id = "fp_" + ad.id + "_fluid_vpaid_iframe";
vpaidIframe.className = 'fluid_vpaid_iframe';
vpaidIframe.setAttribute('adListId', ad.id);
vpaidIframe.setAttribute('frameborder', '0');
playerInstance.domRef.player.parentNode.insertBefore(vpaidIframe, playerInstance.domRef.player.nextSibling);
const vpaidJsScriptElement = document.createElement('script');
vpaidJsScriptElement.src = vpaidJsUrl;
vpaidIframe.contentWindow.document.head.append(vpaidJsScriptElement);
// set interval with timeout
playerInstance.tempVpaidCounter = 0;
playerInstance.getVPAIDAdInterval = setInterval(function () {
if (vpaidIframe && vpaidIframe.contentWindow) {
const fn = vpaidIframe.contentWindow['getVPAIDAd'];
// check if JS is loaded fully in iframe
if (fn && typeof fn == 'function') {
if (playerInstance.vpaidAdUnit) {
playerInstance.hardStopVpaidAd(ad);
}
playerInstance.vpaidAdUnit = fn();
clearInterval(playerInstance.getVPAIDAdInterval);
if (playerInstance.checkVPAIDInterface(playerInstance.vpaidAdUnit)) {
if (playerInstance.getVpaidAdLinear()) {
playerInstance.isCurrentlyPlayingAd = true;
playerInstance.switchPlayerToVpaidMode(ad);
} else {
playerInstance.debugMessage('non linear vpaid ad is loaded');
playerInstance.loadVpaidNonlinearAssets(ad);
}
}
} else {
// video player will wait for 2seconds if vpaid is not loaded, then it will declare vast error and move ahead
playerInstance.tempVpaidCounter++;
if (playerInstance.tempVpaidCounter >= 20) {
clearInterval(playerInstance.getVPAIDAdInterval);
playerInstance.rollsById[ad.rollListId].error = true;
playerInstance.playMainVideoWhenVpaidFails(403);
return false;
} else {
playerInstance.debugMessage(playerInstance.tempVpaidCounter);
}
}
}
}, 100);
playerInstance.destructors.push(() => clearInterval(playerInstance.getVPAIDAdInterval));
};
playerInstance.onVpaidEnded = (event) => {
if (event) {
event.stopImmediatePropagation();
}
if (!playerInstance.vpaidAdUnit) {
return;
}
const vpaidSlot = playerInstance.domRef.wrapper.querySelector('.fluid_vpaid_slot');
playerInstance.vpaidCallbackListenersDetach();
playerInstance.vpaidAdUnit = null;
clearInterval(playerInstance.getVPAIDAdInterval);
if (!!vpaidSlot) {
vpaidSlot.remove();
}
playerInstance.checkForNextAd();
};
playerInstance.playMainVideoWhenVpaidFails = (errorCode) => {
const vpaidSlot = playerInstance.domRef.wrapper.querySelector('.fluid_vpaid_slot');
if (vpaidSlot) {
vpaidSlot.remove();
}
clearInterval(playerInstance.getVPAIDAdInterval);
playerInstance.playMainVideoWhenVastFails(errorCode);
};
}