From 6d8720202d25d3f7f71c59a6613944f667f2137b Mon Sep 17 00:00:00 2001 From: MHk | Maros Hauk Date: Tue, 29 Apr 2025 16:10:40 +0200 Subject: [PATCH] Adscript changes and fixes - Fixed attribute value for ad metadata - Added player state visibility flooring for decimal numbers - Fixed adscript quartile events naming - Added adscript buffer/multiple player support - Added ASMEA code loading from the vast akaCode param - Fixed ad/content `attributes` property to `attribute` - JHMT load script null reference fixes - Moved ad start to playing from adbegin --- .changeset/curvy-deers-kiss.md | 5 ++ .changeset/fluffy-files-clap.md | 5 ++ .changeset/khaki-chairs-work.md | 5 ++ .changeset/rare-rabbits-travel.md | 5 ++ .changeset/seven-ties-press.md | 5 ++ .changeset/shy-apes-boil.md | 5 ++ .changeset/sixty-lemons-own.md | 5 ++ .changeset/thin-years-pull.md | 5 ++ adscript/src/adscript/AdScript.d.ts | 9 +++ .../src/integration/AdScriptConfiguration.ts | 4 +- adscript/src/integration/AdScriptConnector.ts | 3 +- .../integration/AdScriptTHEOIntegration.ts | 72 +++++++++++++------ adscript/src/integration/LoadAdScriptSDK.ts | 8 ++- 13 files changed, 110 insertions(+), 26 deletions(-) create mode 100644 .changeset/curvy-deers-kiss.md create mode 100644 .changeset/fluffy-files-clap.md create mode 100644 .changeset/khaki-chairs-work.md create mode 100644 .changeset/rare-rabbits-travel.md create mode 100644 .changeset/seven-ties-press.md create mode 100644 .changeset/shy-apes-boil.md create mode 100644 .changeset/sixty-lemons-own.md create mode 100644 .changeset/thin-years-pull.md diff --git a/.changeset/curvy-deers-kiss.md b/.changeset/curvy-deers-kiss.md new file mode 100644 index 00000000..5cd8465c --- /dev/null +++ b/.changeset/curvy-deers-kiss.md @@ -0,0 +1,5 @@ +--- +"@theoplayer/adscript-connector-web": minor +--- + +Fixed attribute value for ad metadata diff --git a/.changeset/fluffy-files-clap.md b/.changeset/fluffy-files-clap.md new file mode 100644 index 00000000..bd5da009 --- /dev/null +++ b/.changeset/fluffy-files-clap.md @@ -0,0 +1,5 @@ +--- +"@theoplayer/adscript-connector-web": minor +--- + +Added player state visibility flooring for decimal numbers diff --git a/.changeset/khaki-chairs-work.md b/.changeset/khaki-chairs-work.md new file mode 100644 index 00000000..f2b2e83f --- /dev/null +++ b/.changeset/khaki-chairs-work.md @@ -0,0 +1,5 @@ +--- +"@theoplayer/adscript-connector-web": minor +--- + +Fixed adscript quartile events naming diff --git a/.changeset/rare-rabbits-travel.md b/.changeset/rare-rabbits-travel.md new file mode 100644 index 00000000..357b3bdc --- /dev/null +++ b/.changeset/rare-rabbits-travel.md @@ -0,0 +1,5 @@ +--- +"@theoplayer/adscript-connector-web": minor +--- + +Added adscript buffer/multiple player support diff --git a/.changeset/seven-ties-press.md b/.changeset/seven-ties-press.md new file mode 100644 index 00000000..723c15e3 --- /dev/null +++ b/.changeset/seven-ties-press.md @@ -0,0 +1,5 @@ +--- +"@theoplayer/adscript-connector-web": minor +--- + +Added ASMEA code loading from the vast akaCode param diff --git a/.changeset/shy-apes-boil.md b/.changeset/shy-apes-boil.md new file mode 100644 index 00000000..961e0d43 --- /dev/null +++ b/.changeset/shy-apes-boil.md @@ -0,0 +1,5 @@ +--- +"@theoplayer/adscript-connector-web": minor +--- + +Fixed ad/content `attributes` property to `attribute` diff --git a/.changeset/sixty-lemons-own.md b/.changeset/sixty-lemons-own.md new file mode 100644 index 00000000..c2956bbb --- /dev/null +++ b/.changeset/sixty-lemons-own.md @@ -0,0 +1,5 @@ +--- +"@theoplayer/adscript-connector-web": minor +--- + +JHMT load script null reference fixes diff --git a/.changeset/thin-years-pull.md b/.changeset/thin-years-pull.md new file mode 100644 index 00000000..a8aa6d5b --- /dev/null +++ b/.changeset/thin-years-pull.md @@ -0,0 +1,5 @@ +--- +"@theoplayer/adscript-connector-web": minor +--- + +Moved ad start to playing from adbegin diff --git a/adscript/src/adscript/AdScript.d.ts b/adscript/src/adscript/AdScript.d.ts index e64ae1b0..0f59ed3e 100644 --- a/adscript/src/adscript/AdScript.d.ts +++ b/adscript/src/adscript/AdScript.d.ts @@ -22,6 +22,11 @@ interface StaticContentMetadata { ref: string; } +interface BufferMetadata { + contentMetadata?: MainVideoContentMetadata; + playerState?: PlayerState; +} + export interface PlayerState { muted: number; volume: number; @@ -54,4 +59,8 @@ export interface JHMTApi { setContentMetadata(contentMetadata: ContentMetadata); setPlayerState(playerState: PlayerState); + + addBuffer(bufferName: string, playerId: string, bufferMetadata: BufferMetadata); + + setBuffer(bufferName: string, bufferMetadata: BufferMetadata); } diff --git a/adscript/src/integration/AdScriptConfiguration.ts b/adscript/src/integration/AdScriptConfiguration.ts index 4c0581be..d44fddd9 100644 --- a/adscript/src/integration/AdScriptConfiguration.ts +++ b/adscript/src/integration/AdScriptConfiguration.ts @@ -13,7 +13,7 @@ export interface MainVideoContentMetadata { crossId: string; livestream: string; channelId: string; - attributes: string; + attribute: string; } /** @@ -25,7 +25,7 @@ export interface EmbeddedContentMetadata { length: string; title: string; asmea: string; - attributes: string; + attribute: string; } /** diff --git a/adscript/src/integration/AdScriptConnector.ts b/adscript/src/integration/AdScriptConnector.ts index 31d61ae8..bdb6cd3f 100644 --- a/adscript/src/integration/AdScriptConnector.ts +++ b/adscript/src/integration/AdScriptConnector.ts @@ -44,11 +44,10 @@ export class AdScriptConnector { return; } if (typeof window.JHMTApi === 'object') { - this.adScriptIntegration = new AdScriptTHEOIntegration(this.player, this.configuration); + this.adScriptIntegration = new AdScriptTHEOIntegration(this.player, this.configuration, this.metadata); if (this.i12n) { this.adScriptIntegration.updateUser(this.i12n); } - this.adScriptIntegration.updateMetadata(this.metadata); this.adScriptIntegration.start(); return; } diff --git a/adscript/src/integration/AdScriptTHEOIntegration.ts b/adscript/src/integration/AdScriptTHEOIntegration.ts index 7360a7ff..ff6f8320 100644 --- a/adscript/src/integration/AdScriptTHEOIntegration.ts +++ b/adscript/src/integration/AdScriptTHEOIntegration.ts @@ -8,13 +8,14 @@ import type { Event, GoogleImaAd, PlayEvent, + PlayingEvent, RateChangeEvent, SourceChangeEvent, TimeUpdateEvent, VolumeChangeEvent } from 'theoplayer'; import { AdScriptConfiguration, EmbeddedContentMetadata, MainVideoContentMetadata } from './AdScriptConfiguration'; -import { EmbeddedContentType } from './../adscript/AdScript'; +import { EmbeddedContentType, JHMTArray, PlayerState } from './../adscript/AdScript'; import { Logger } from '../utils/Logger'; interface LogPoint { @@ -34,12 +35,23 @@ export class AdScriptTHEOIntegration { private currentAdLogPoints: LogPoint[] = []; private JHMTApi = window.JHMTApi; - private JHMT = window.JHMT; + private JHMTBuffer: JHMTArray; + private bufferName: string; - constructor(player: ChromelessPlayer, configuration: AdScriptConfiguration) { + constructor(player: ChromelessPlayer, configuration: AdScriptConfiguration, metadata: MainVideoContentMetadata) { this.player = player; this.logger = new Logger(Boolean(configuration.debug)); this.adProcessor = configuration?.adProcessor; + this.mainContentMetadata = metadata; + this.player.element.querySelectorAll('video').forEach((element: HTMLVideoElement) => { + element.setAttribute('data-jhmt-player-id', String(player.uid)); + }); + this.bufferName = `__theo${String(this.player.uid)}`; + this.JHMTApi.addBuffer(this.bufferName, this.player.uid, { + contentMetadata: this.mainContentMetadata, + playerState: this.getPlayerState() + }); + this.JHMTBuffer = (window as any)[this.bufferName] as JHMTArray; } public start() { @@ -53,7 +65,7 @@ export class AdScriptTHEOIntegration { public updateMetadata(metadata: MainVideoContentMetadata) { this.mainContentMetadata = metadata; this.logger.onSetMainVideoContentMetadata(this.mainContentMetadata); - this.JHMTApi.setContentMetadata(this.mainContentMetadata); + this.JHMTApi.setBuffer(this.bufferName, { contentMetadata: this.mainContentMetadata }); } public updateUser(i12n: { [key: string]: string }): void { @@ -159,7 +171,7 @@ export class AdScriptTHEOIntegration { const isBeforePreroll = this.player.ads?.scheduledAdBreaks.find((adBreak) => adBreak.timeOffset === 0); if (this.player.ads?.playing || isBeforePreroll) return; this.logger.onAdScriptEvent('start', this.mainContentMetadata); - this.JHMT.push(['start', this.mainContentMetadata]); + this.JHMTBuffer.push(['start', this.mainContentMetadata]); this.player.removeEventListener('playing', this.onFirstMainContentPlaying); }; @@ -171,7 +183,7 @@ export class AdScriptTHEOIntegration { private onEnded = (event: EndedEvent) => { this.logger.onEvent(event); this.logger.onAdScriptEvent('complete', this.mainContentMetadata); - this.JHMT.push(['complete', this.mainContentMetadata]); + this.JHMTBuffer.push(['complete', this.mainContentMetadata]); }; private onVolumeChange = (event: VolumeChangeEvent) => { @@ -202,23 +214,23 @@ export class AdScriptTHEOIntegration { private onAdFirstQuartile = (event: AdEvent<'adfirstquartile'>) => { this.logger.onEvent(event); - this.logger.onAdScriptEvent('firstquartile', this.currentAdMetadata); - this.JHMT.push(['firstquartile', this.currentAdMetadata]); + this.logger.onAdScriptEvent('firstQuartile', this.currentAdMetadata); + this.JHMTBuffer.push(['firstQuartile', this.currentAdMetadata]); }; private onAdMidpoint = (event: AdEvent<'admidpoint'>) => { this.logger.onEvent(event); this.logger.onAdScriptEvent('midpoint', this.currentAdMetadata); - this.JHMT.push(['midpoint', this.currentAdMetadata]); + this.JHMTBuffer.push(['midpoint', this.currentAdMetadata]); }; private onAdTirdQuartile = (event: AdEvent<'adthirdquartile'>) => { this.logger.onEvent(event); - this.logger.onAdScriptEvent('thirdquartile', this.currentAdMetadata); - this.JHMT.push(['thirdquartile', this.currentAdMetadata]); + this.logger.onAdScriptEvent('thirdQuartile', this.currentAdMetadata); + this.JHMTBuffer.push(['thirdQuartile', this.currentAdMetadata]); }; private onAdEnd = (event: AdEvent<'adend'>) => { this.logger.onEvent(event); this.logger.onAdScriptEvent('complete', this.currentAdMetadata); - this.JHMT.push(['complete', this.currentAdMetadata]); + this.JHMTBuffer.push(['complete', this.currentAdMetadata]); }; private onAdBegin = (event: AdEvent<'adbegin'>) => { @@ -226,8 +238,14 @@ export class AdScriptTHEOIntegration { if (event.ad.type !== 'linear') return; this.currentAdMetadata = this.buildAdMetadataObject(event); this.currentAdLogPoints = this.buildAdLogPoints(event.ad); + this.player.removeEventListener('playing', this.onAdFirstTimePlaying); + this.player.addEventListener('playing', this.onAdFirstTimePlaying); + }; + + private onAdFirstTimePlaying = (event: PlayingEvent) => { this.logger.onAdScriptEvent('start', this.currentAdMetadata); - this.JHMT.push(['start', this.currentAdMetadata]); + this.JHMTBuffer.push(['start', this.currentAdMetadata]); + this.player.removeEventListener('playing', this.onAdFirstTimePlaying); }; private buildAdLogPoints = (ad: Ad) => { @@ -253,13 +271,23 @@ export class AdScriptTHEOIntegration { length: ad.duration?.toString() ?? '' }; } + let asmeaCode = ''; + try { + const traffickingParametersString = (ad as GoogleImaAd)?.traffickingParametersString; + if (traffickingParametersString) { + const adParams: any = JSON.parse(traffickingParametersString); + asmeaCode = adParams ? adParams.akaCode : ''; + } + } catch (error) { + // skip error + } return { assetid: ad.id ?? '', type: this.getAdType(adBreak.timeOffset, this.player.duration, adBreak.integration), length: ad.duration?.toString() ?? '', title: ad.integration?.includes('google') ? (ad as GoogleImaAd).title ?? '' : '', - asmea: '', - attributes: '' + asmea: asmeaCode, + attribute: '2' }; }; @@ -271,19 +299,23 @@ export class AdScriptTHEOIntegration { return 'midroll'; }; - private reportPlayerState = () => { - const playerState = { + private getPlayerState(): PlayerState { + return { muted: this.player.muted ? 1 : 0, volume: this.player.volume * 100, triggeredByUser: this.player.autoplay ? 1 : 0, normalSpeed: this.player.playbackRate === 1 ? 1 : 0, fullscreen: this.player.presentation.currentMode === 'fullscreen' ? 1 : 0, - visibility: this.player.visibility.ratio * 100, + visibility: Math.floor(this.player.visibility.ratio * 10000) / 100, width: this.player.element.clientWidth, height: this.player.element.clientHeight }; + } + + private reportPlayerState = () => { + const playerState = this.getPlayerState(); this.logger.onPlayerStateChange(playerState); - this.JHMTApi.setPlayerState(playerState); + this.JHMTApi.setBuffer(this.bufferName, { playerState: playerState }); }; private maybeReportLogPoint = ( @@ -296,7 +328,7 @@ export class AdScriptTHEOIntegration { if (!reported && currentTime >= offset && currentTime < offset + 1) { logPoint.reported = true; this.logger.onAdScriptEvent(name, metadata); - this.JHMT.push([name, metadata]); + this.JHMTBuffer.push([name, metadata]); } }); }; diff --git a/adscript/src/integration/LoadAdScriptSDK.ts b/adscript/src/integration/LoadAdScriptSDK.ts index 06eb224b..a0f111d6 100644 --- a/adscript/src/integration/LoadAdScriptSDK.ts +++ b/adscript/src/integration/LoadAdScriptSDK.ts @@ -31,7 +31,9 @@ function loadAdScriptInternal(j, h, m, t, c, z) { }), (b.src = j['JHMTApiProtocol'] + '//cm' + i + '.jhmt.cz/api.js'), (b.onerror = function () { - b.parentNode.removeChild(b); + if (b.parentNode !== 'undefined') { + b.parentNode.removeChild(b); + } z++; i = (z % 3) + 1; loadAdScriptInternal(j, h, m, t, c, i); @@ -43,7 +45,9 @@ function loadAdScriptInternal(j, h, m, t, c, z) { if (typeof j.JHMTApi !== 'undefined') { clearInterval(it); } else { - b.parentNode.removeChild(b); + if (b.parentNode !== 'undefined') { + b.parentNode.removeChild(b); + } z++; i = (z % 3) + 1; loadAdScriptInternal(j, h, m, t, c, i);