Skip to content

Commit

Permalink
Merge pull request #10 from THEOplayer/feature/conviva_custom_events
Browse files Browse the repository at this point in the history
Feature/conviva custom events
  • Loading branch information
tvanlaerhoven authored Apr 3, 2024
2 parents 3f19267 + b1f71c0 commit 355e92a
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 48 deletions.
5 changes: 5 additions & 0 deletions .changeset/tough-hornets-sneeze.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@theoplayer/conviva-connector-web": patch
---

Added functionality to listen for external ad events using the `convivaAdEventsExtension` property.
6 changes: 3 additions & 3 deletions conviva/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@
"package.json"
],
"dependencies": {
"@convivainc/conviva-js-coresdk": "^4.6.1"
"@convivainc/conviva-js-coresdk": "^4.7.4"
},
"peerDependencies": {
"theoplayer": "^5.0.0 || ^6.0.0",
"@theoplayer/yospace-connector-web": "^2.1.0"
"@theoplayer/yospace-connector-web": "^2.1.0",
"theoplayer": "^5.0.0 || ^6.0.0 || ^7.0.0"
},
"peerDependenciesMeta": {
"@theoplayer/yospace-connector-web": {
Expand Down
9 changes: 9 additions & 0 deletions conviva/src/integration/ConvivaConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ export class ConvivaConnector {
this.convivaHandler.reportPlaybackFailed(errorMessage);
}

/**
* Reports a custom event to the current Conviva session.
* @param eventType the type of the custom event.
* @param eventDetail an optional object containing event details.
*/
reportPlaybackEvent(eventType: string, eventDetail?: object): void {
this.convivaHandler.reportPlaybackEvent(eventType, eventDetail);
}

/**
* Explicitly stop the current session and start a new one.
*
Expand Down
22 changes: 12 additions & 10 deletions conviva/src/integration/ConvivaHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
collectPlayerInfo,
flattenAndStringifyObject
} from '../utils/Utils';
import { CsaiAdReporter } from './ads/CsaiAdReporter';
import { AdReporter } from './ads/AdReporter';
import { YospaceAdReporter } from './ads/YospaceAdReporter';
import { VerizonAdReporter } from './ads/VerizonAdReporter';

Expand All @@ -29,7 +29,7 @@ export class ConvivaHandler {
private convivaVideoAnalytics: VideoAnalytics | undefined;
private convivaAdAnalytics: AdAnalytics | undefined;

private adReporter: CsaiAdReporter | undefined;
private adReporter: AdReporter | undefined;
private yospaceAdReporter: YospaceAdReporter | undefined;
private verizonAdReporter: VerizonAdReporter | undefined;

Expand Down Expand Up @@ -62,14 +62,12 @@ export class ConvivaHandler {

this.convivaAdAnalytics = Analytics.buildAdAnalytics(this.convivaVideoAnalytics);

if (this.player.ads !== undefined) {
this.adReporter = new CsaiAdReporter(
this.player,
this.convivaVideoAnalytics,
this.convivaAdAnalytics,
() => this.customMetadata
);
}
this.adReporter = new AdReporter(
this.player,
this.convivaVideoAnalytics,
this.convivaAdAnalytics,
() => this.customMetadata
);

if (this.player.verizonMedia !== undefined) {
this.verizonAdReporter = new VerizonAdReporter(
Expand Down Expand Up @@ -123,6 +121,10 @@ export class ConvivaHandler {
this.releaseSession();
}

reportPlaybackEvent(eventType: string, eventDetail?: object): void {
this.convivaVideoAnalytics?.reportPlaybackEvent(eventType, eventDetail);
}

stopAndStartNewSession(metadata: ConvivaMetadata): void {
this.maybeReportPlaybackEnded();
this.maybeReportPlaybackRequested();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Ad, AdBreak, ChromelessPlayer, GoogleImaAd } from 'theoplayer';
import { AdAnalytics, Constants, ConvivaMetadata, VideoAnalytics } from '@convivainc/conviva-js-coresdk';
import { calculateCurrentAdBreakInfo, collectAdMetadata, collectPlayerInfo } from '../../utils/Utils';
import { calculateAdType, calculateCurrentAdBreakInfo, collectAdMetadata, collectPlayerInfo } from '../../utils/Utils';

export class CsaiAdReporter {
export class AdReporter {
private readonly player: ChromelessPlayer;
private readonly convivaVideoAnalytics: VideoAnalytics;
private readonly convivaAdAnalytics: AdAnalytics;
Expand All @@ -29,7 +29,7 @@ export class CsaiAdReporter {
private readonly onAdBreakBegin = (event: any) => {
this.currentAdBreak = event.ad as AdBreak;
this.convivaVideoAnalytics.reportAdBreakStarted(
Constants.AdType.CLIENT_SIDE,
calculateAdType(this.player),
Constants.AdPlayer.CONTENT,
calculateCurrentAdBreakInfo(this.currentAdBreak, this.adBreakCounter)
);
Expand Down Expand Up @@ -57,6 +57,9 @@ export class CsaiAdReporter {
adMetadata.contentAssetName =
this.contentInfo()[Constants.ASSET_NAME] ?? this.player.source?.metadata?.title ?? 'NA';

// [Required] The ad technology as CLIENT_SIDE/SERVER_SIDE
adMetadata['c3.ad.technology'] = calculateAdType(this.player);

this.convivaAdAnalytics.setAdInfo(adMetadata);
this.convivaAdAnalytics.reportAdLoaded(adMetadata);
this.convivaAdAnalytics.reportAdStarted(adMetadata);
Expand All @@ -66,6 +69,11 @@ export class CsaiAdReporter {
this.player.videoHeight
);
this.convivaAdAnalytics.reportAdMetric(Constants.Playback.BITRATE, (currentAd as GoogleImaAd).bitrate || 0);

// Report playing state in case of SSAI.
if (calculateAdType(this.player) === Constants.AdType.SERVER_SIDE) {
this.convivaAdAnalytics.reportAdMetric(Constants.Playback.PLAYER_STATE, Constants.PlayerState.PLAYING);
}
};

private readonly onAdEnd = (event: any) => {
Expand Down Expand Up @@ -116,33 +124,29 @@ export class CsaiAdReporter {
private addEventListeners(): void {
this.player.addEventListener('playing', this.onPlaying);
this.player.addEventListener('pause', this.onPause);
if (this.player.ads === undefined) {
// should not happen
return;
}
this.player.ads.addEventListener('adbreakbegin', this.onAdBreakBegin);
this.player.ads.addEventListener('adbreakend', this.onAdBreakEnd);
this.player.ads.addEventListener('adbegin', this.onAdBegin);
this.player.ads.addEventListener('adend', this.onAdEnd);
this.player.ads.addEventListener('adskip', this.onAdSkip);
this.player.ads.addEventListener('adbuffering', this.onAdBuffering);
this.player.ads.addEventListener('aderror', this.onAdError);
[this.player.ads, this.player.ads?.convivaAdEventsExtension].forEach((dispatcher) => {
dispatcher?.addEventListener('adbreakbegin', this.onAdBreakBegin);
dispatcher?.addEventListener('adbreakend', this.onAdBreakEnd);
dispatcher?.addEventListener('adbegin', this.onAdBegin);
dispatcher?.addEventListener('adend', this.onAdEnd);
dispatcher?.addEventListener('adskip', this.onAdSkip);
dispatcher?.addEventListener('adbuffering', this.onAdBuffering);
dispatcher?.addEventListener('aderror', this.onAdError);
});
}

private removeEventListeners(): void {
this.player.removeEventListener('playing', this.onPlaying);
this.player.removeEventListener('pause', this.onPause);
if (this.player.ads === undefined) {
// should not happen
return;
}
this.player.ads.removeEventListener('adbreakbegin', this.onAdBreakBegin);
this.player.ads.removeEventListener('adbreakend', this.onAdBreakEnd);
this.player.ads.removeEventListener('adbegin', this.onAdBegin);
this.player.ads.removeEventListener('adend', this.onAdEnd);
this.player.ads.removeEventListener('adskip', this.onAdSkip);
this.player.ads.removeEventListener('adbuffering', this.onAdBuffering);
this.player.ads.removeEventListener('aderror', this.onAdError);
[this.player.ads, this.player.ads?.convivaAdEventsExtension].forEach((dispatcher) => {
dispatcher?.removeEventListener('adbreakbegin', this.onAdBreakBegin);
dispatcher?.removeEventListener('adbreakend', this.onAdBreakEnd);
dispatcher?.removeEventListener('adbegin', this.onAdBegin);
dispatcher?.removeEventListener('adend', this.onAdEnd);
dispatcher?.removeEventListener('adskip', this.onAdSkip);
dispatcher?.removeEventListener('adbuffering', this.onAdBuffering);
dispatcher?.removeEventListener('aderror', this.onAdError);
});
}

reset(): void {
Expand Down
10 changes: 10 additions & 0 deletions conviva/src/integration/extension/ConvivaAdEventsExtension.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { AdsEventMap, EventDispatcher } from 'theoplayer';

declare module 'theoplayer' {
interface Ads extends EventDispatcher<AdsEventMap> {
convivaAdEventsExtension?: EventDispatcher<AdsEventMap>;
}
class ChromelessPlayer {
ads?: Ads;
}
}
9 changes: 6 additions & 3 deletions conviva/src/utils/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ export function collectDeviceMetadata(): ConvivaDeviceMetadata {
};
}

type AdBreakPosition = 'preroll' | 'midroll' | 'postroll';

export function calculateAdType(player: ChromelessPlayer) {
return player.source?.ads?.length ? Constants.AdType.CLIENT_SIDE : Constants.AdType.SERVER_SIDE;
}

export function calculateVerizonAdBreakInfo(adBreak: VerizonMediaAdBreak, adBreakIndex: number): ConvivaAdBreakInfo {
return {
[Constants.POD_DURATION]: adBreak.duration!,
Expand Down Expand Up @@ -132,9 +138,6 @@ export function collectAdMetadata(ad: Ad): ConvivaMetadata {
// For wrapper ads, this is the last creative id at the end of the wrapper chain. Set to "NA" if not available.
adMetadata['c3.ad.creativeId'] = ad.creativeId || 'NA';

// [Required] The ad technology as CLIENT_SIDE/SERVER_SIDE
adMetadata['c3.ad.technology'] = Constants.AdType.CLIENT_SIDE;

// [Required] The ad position as a string "Pre-roll", "Mid-roll" or "Post-roll"
adMetadata['c3.ad.position'] = calculateCurrentAdBreakPosition(ad.adBreak);

Expand Down
8 changes: 1 addition & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 355e92a

Please sign in to comment.