From 02da1359725cabb7fe04de099f626399809e24ca Mon Sep 17 00:00:00 2001
From: Peter Perlepes
Date: Wed, 20 Mar 2024 13:36:02 +0200
Subject: [PATCH] Add Event Specification option map to Snowplow Media Plugin
---
plugins/browser-plugin-media/src/api.ts | 24 ++++++++++---
plugins/browser-plugin-media/src/types.ts | 7 ++++
plugins/browser-plugin-media/src/utils.ts | 42 +++++++++++++++++++++++
3 files changed, 68 insertions(+), 5 deletions(-)
create mode 100644 plugins/browser-plugin-media/src/utils.ts
diff --git a/plugins/browser-plugin-media/src/api.ts b/plugins/browser-plugin-media/src/api.ts
index 8ef0bbf91..1abfc6c9c 100644
--- a/plugins/browser-plugin-media/src/api.ts
+++ b/plugins/browser-plugin-media/src/api.ts
@@ -21,17 +21,20 @@ import {
MediaTrackErrorArguments,
MediaTrackSelfDescribingEventArguments,
EventWithContext,
+ MediaPluginOptions,
} from './types';
+import { setEventSpecificationContext } from './utils';
export { MediaAdBreakType as MediaPlayerAdBreakType };
const _trackers: Record = {};
const _context: Record = {};
+const defaultPluginOptions = {};
/**
* Adds media tracking
*/
-export function SnowplowMediaPlugin(): BrowserPlugin {
+export function SnowplowMediaPlugin(pluginOptions: MediaPluginOptions = defaultPluginOptions): BrowserPlugin {
let trackerId: string;
return {
activateBrowserPlugin: (tracker) => {
@@ -39,6 +42,9 @@ export function SnowplowMediaPlugin(): BrowserPlugin {
_trackers[trackerId] = tracker;
_context[trackerId] = [];
},
+ beforeTrack(payloadBuilder) {
+ setEventSpecificationContext(payloadBuilder, pluginOptions.eventSpecificationIds);
+ },
contexts: () => {
return _context[trackerId] || [];
},
@@ -396,7 +402,9 @@ export function trackMediaAdSkip(
trackers: Array = Object.keys(_trackers)
) {
let { percentProgress } = args;
- if (percentProgress !== undefined) { percentProgress = Math.floor(percentProgress); }
+ if (percentProgress !== undefined) {
+ percentProgress = Math.floor(percentProgress);
+ }
track(
{
mediaEvent: {
@@ -506,7 +514,9 @@ export function trackMediaAdClick(
trackers: Array = Object.keys(_trackers)
) {
let { percentProgress } = args;
- if (percentProgress !== undefined) { percentProgress = Math.floor(percentProgress); }
+ if (percentProgress !== undefined) {
+ percentProgress = Math.floor(percentProgress);
+ }
track(
{
mediaEvent: {
@@ -534,7 +544,9 @@ export function trackMediaAdPause(
trackers: Array = Object.keys(_trackers)
) {
let { percentProgress } = args;
- if (percentProgress !== undefined) { percentProgress = Math.floor(percentProgress); }
+ if (percentProgress !== undefined) {
+ percentProgress = Math.floor(percentProgress);
+ }
track(
{
mediaEvent: {
@@ -563,7 +575,9 @@ export function trackMediaAdResume(
trackers: Array = Object.keys(_trackers)
) {
let { percentProgress } = args;
- if (percentProgress !== undefined) { percentProgress = Math.floor(percentProgress); }
+ if (percentProgress !== undefined) {
+ percentProgress = Math.floor(percentProgress);
+ }
track(
{
mediaEvent: {
diff --git a/plugins/browser-plugin-media/src/types.ts b/plugins/browser-plugin-media/src/types.ts
index dda74a041..786a3d660 100644
--- a/plugins/browser-plugin-media/src/types.ts
+++ b/plugins/browser-plugin-media/src/types.ts
@@ -1,5 +1,12 @@
import { CommonEventProperties, SelfDescribingJson } from '@snowplow/tracker-core';
+export interface MediaPluginOptions {
+ /**
+ * Option to match Media Events to Event Specification identifiers. Keys are almost always MediaEventType + "_event"
+ */
+ eventSpecificationIds?: Record;
+}
+
/** Type of media player event */
export enum MediaEventType {
// Controlling the playback
diff --git a/plugins/browser-plugin-media/src/utils.ts b/plugins/browser-plugin-media/src/utils.ts
new file mode 100644
index 000000000..0ecf1bb7b
--- /dev/null
+++ b/plugins/browser-plugin-media/src/utils.ts
@@ -0,0 +1,42 @@
+import { PayloadBuilder } from '@snowplow/tracker-core';
+
+function getEventSchemaName(payloadBuilder: PayloadBuilder): string | undefined {
+ const payloadJson = payloadBuilder.getJson();
+ const mediaEventSchema = payloadJson.find(
+ (e) =>
+ e.keyIfEncoded === 'ue_px' &&
+ (e.json.data as { schema: string }).schema.match(/iglu:com.snowplowanalytics.snowplow.media\/.*\/jsonschema/)
+ );
+ if (typeof mediaEventSchema === 'undefined') {
+ return;
+ }
+ // We know schemas are in this otherwise it would not match the above regex.
+ const eventSourceName = (mediaEventSchema.json.data as { schema: string }).schema.match(
+ /iglu:com.snowplowanalytics.snowplow.media\/(.*)\/jsonschema/
+ )![1];
+
+ return eventSourceName;
+}
+
+function createEventSpecificationContext(eventSpecificationId: string) {
+ return {
+ schema: 'iglu:com.snowplowanalytics.snowplow/event_specification/jsonschema/1-0-0',
+ data: {
+ id: eventSpecificationId,
+ },
+ };
+}
+
+export function setEventSpecificationContext(
+ payloadBuilder: PayloadBuilder,
+ eventSpecificationIds?: Record
+) {
+ if (!eventSpecificationIds) {
+ return;
+ }
+ const eventName = getEventSchemaName(payloadBuilder);
+ const eventSpecificationId = eventName && eventSpecificationIds[eventName];
+ if (eventSpecificationId) {
+ payloadBuilder.addContextEntity(createEventSpecificationContext(eventSpecificationId));
+ }
+}