DCR Video Domless SDK: Difference between revisions
From Engineering Client Portal
Line 229: | Line 229: | ||
==== Sample DCR Video Integration ==== | ==== Sample DCR Video Integration ==== | ||
<syntaxhighlight lang="javascript"> | |||
import { BsdkInstance } from 'bsdk-domless'; | |||
const nsdkConfig = { | const nsdkConfig = { | ||
Line 240: | Line 240: | ||
const implementationHooks = { | const implementationHooks = { | ||
Log: { | Log: { | ||
info: function (log: string) { | info: function (log: string) { | ||
console.info(log); | |||
}, | |||
debug: function (log: string) { | |||
console.debug(log); | |||
}, | |||
warn: function (log: string) { | |||
console.warn(log); | |||
}, | |||
error: function (error: string) { | |||
console.error(error); | |||
} | |||
}, | |||
Storage: { | |||
setItem: async function (key: any, value: string) { | |||
/** | /** | ||
* Sets a string value for given key. This operation can either modify an existing entry, if it did exist for given key, or add new one otherwise. | * Sets a string value for given key. This operation can either modify an existing entry, | ||
* if it did exist for given key, or add new one otherwise. | |||
* In order to store object value, you need to serialize it, e.g. using JSON.stringify(). | * In order to store object value, you need to serialize it, e.g. using JSON.stringify(). | ||
*/ | */ | ||
}, | }, | ||
getItem: async function (key: any) { | |||
/** | /** | ||
* Gets a string value for given key. This function can either return a string value for existing key or return null otherwise. | * Gets a string value for given key. This function can either return a string value for | ||
* existing key or return null otherwise. | |||
* In order to store object value, you need to deserialize it, e.g. using JSON.parse(). | * In order to store object value, you need to deserialize it, e.g. using JSON.parse(). | ||
*/ | */ | ||
}, | }, | ||
removeItem: async function (key: any) { | |||
/** | /** | ||
* Removes item for a key, invokes (optional) callback once completed. | * Removes item for a key, invokes (optional) callback once completed. | ||
*/ | */ | ||
} | } | ||
}, | |||
Fetch: async function (url: string | URL | Request, options: any) { | |||
/** | |||
const clientOpts = { | * We require that client pass in User-Agent header via options in Fetch request | ||
*/ | |||
const clientOpts = { | |||
headers: { | |||
"User-Agent": "react-native-domless/1.6.7.42 Dalvik/2.1.0 (Linux; U; Android 5.1.1; Android SDK built for x86 Build/LMY48X)" | |||
} | |||
} | |||
const data = Object.assign(options, clientOpts); | |||
const response = await fetch(url, data); | |||
if (response.ok) { | |||
return response; | |||
} else { | |||
throw new Error('Request failed'); | |||
} | |||
}, | |||
SetTimeout: function (callback: () => void, timeout: number | undefined) { | SetTimeout: function (callback: () => void, timeout: number | undefined) { | ||
return setTimeout(callback, timeout); }, | return setTimeout(callback, timeout); }, | ||
SetInterval: function (callback: () => void, interval: number | undefined) { | SetInterval: function (callback: () => void, interval: number | undefined) { | ||
return setInterval(callback, interval); | return setInterval(callback, interval); | ||
Line 283: | Line 315: | ||
// for additonal metadata properties | // for additonal metadata properties | ||
}, | }, | ||
implementationHooks ); | implementationHooks ); | ||
// Sample VideoPlayer component const VideoPlayer = (props) => { | // Sample VideoPlayer component const VideoPlayer = (props) => { | ||
/** | |||
* Implementation of video player component will vary across the board, for Nielsen DOM-less SDK integration | |||
* clients need only setup event listeners with corresponding ggPM() calls. | |||
* | |||
* Please refer to chosen video player documentation on available events | |||
*/ | |||
const video = useRef<Video>(null); | |||
let previousPlayhead = 0; // keep track of previous playhead position | |||
let metadataLoaded = false; // in case of replay scenario set flag for metadata load | |||
// Sample video metadata | |||
const videometadata = { | |||
'type': 'content', | |||
'length': '300', | |||
'censuscategory': 'Enlisted', | |||
'title': 'Channel1', | |||
'assetid': '204558915991', | |||
'section': 'ProgramAsset8', | |||
'tv': 'true', | |||
'adModel': '0', | |||
'dataSrc': 'cms' | |||
} | |||
const setUpEventListeners = (): void => { | |||
video.addEventListener('ended', onEnded); | |||
video.addEventListener('timeupdate', onTimeUpdate); | |||
video.addEventListener('playing', onPlay); | |||
video.addEventListener('pause', onPause); | |||
}; | |||
const onEnded = () => { | |||
// Nielsen SDK ggPM 'end' event | |||
if (nSdkInstance) { | |||
nSdkInstance.then((instance: any) => { | nSdkInstance.then((instance: any) => { | ||
instance.ggPM('end', Math.round(video.currentTime)); | instance.ggPM('end', Math.round(video.currentTime)); | ||
metadataLoaded = false; | metadataLoaded = false; | ||
}); | }); | ||
} | |||
}; | |||
const onTimeUpdate = () => { | |||
const currPlayhead = Math.round(video.current?.currentTime!); | |||
if (currPlayhead > 0 && currPlayhead !== previousPlayhead) { | |||
previousPlayhead = currPlayhead; | |||
// Nielsen SDK ggPM 'setplayheadposition' event | |||
if (nSdkInstance) { | |||
if (nSdkInstance | |||
nSdkInstance.then((instance: any) => { | nSdkInstance.then((instance: any) => { | ||
instance.ggPM(' | instance.ggPM('setplayheadposition', currPlayhead); | ||
}); | }); | ||
} | } | ||
}; | } | ||
}; | |||
const onPause = () => { | // NOTE: some players may have an event when video metadata is loaded, recommended to use if available. E.g. loadedmetadata | ||
const onPlay = () => { | |||
// Nielsen SDK ggPM 'loadmetadata' event | |||
if (nSdkInstance && !metadataLoaded) { | |||
nSdkInstance.then((instance: any) => { | |||
instance.ggPM('loadmetadata', videometadata); | |||
metadataLoaded = true; | |||
}); | |||
} | |||
}; | |||
const onPause = () => { | |||
// Nielsen SDK ggPM 'pause' event | |||
if (nSdkInstance) { | |||
nSdkInstance.then((instance: any) => { | |||
instance.ggPM('pause', Math.round(video.currentTime)); | instance.ggPM('pause', Math.round(video.currentTime)); | ||
}); | |||
} | |||
}; | |||
return ( | |||
<nowiki> </nowiki> <View> | <nowiki> </nowiki> <View> | ||
<nowiki> </nowiki> <nowiki><Video | <nowiki> </nowiki> <nowiki><Video | ||
Line 369: | Line 401: | ||
}; | }; | ||
</syntaxhighlight> |
Revision as of 21:02, 24 July 2024
DOM-less SDK DCR Video Introduction
The Nielsen DOM-less SDK is a Javascript based cross-platform library that clients can use to integrate the Nielsen Digital Content Rating or Digital TV Rating measurement in ReactNative or NodeJS apps.
The Digital Content Ratings (DCR) Video product provides content consumption measurement on client mobile apps or webpages. This measurement includes insight into the total time a user spent watching the tracked content, video player events, and much more. This example provides the steps to implement the DCR Video product in a sample NodeJS app. It includes:
- DOM-less SDK Initialization
- DCR Video Metadata: information about the content being tracked
- DCR Video Events/API calls
By the end of this guide you will have the needed steps to integrate Nielsen's DOM-less SDK in your app.
DOM-less SDK DCR Video Step 1 - Obtain AppID
To begin using the DOM-less SDK you will need to obtain an Application ID (AppId)
Item | Description | Source | |
---|---|---|---|
✅ | App ID (appid) | Unique ID assigned to the player/site and configured by product | Contact your Nielsen TAM |
The appid is a 37 character unique ID assigned to the player/site and configured by product and is required when creating a new instance of the DOM-less SDK on the app.
Example: PXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX
DOM-less SDK DCR Video Step 2 - SDK Initialization
This project is an API that allows clients to integrate the Nielsen SDK in DOM-less environments like React Native, NodeJS, etc.
Installation
Install with npm install https://github.com/NielsenDigitalSDK???????????????
and import the BsdkInstance
into video player component
`import { BsdkInstance } from 'bsdk-domless'
status.ok()
Initialization of the instance can be done with status.ok()
function or use Promise handling approach
const instance = await new BsdkInstance(appID, instanceName, instanceMetadata, implementationHooks);
if (instance && instance.status.ok()) {
expect(instance).not.toBe(undefined);
expect(instance.status.ok()).toBe(true);
}
Exposed Interface
The exposed interface is as follows:
1. `ggPM`
- method to send messages to the Nielsen SDK
2. `processEvent`
- method to send app state to the Nielsen SDK, e.g., Focus, Blur, AppClose
SDK Initialization
The following table contains the list of arguments that can be passed
Parameter / Argument | Description | Source | Required? | Example |
---|---|---|---|---|
appid | Unique Nielsen ID for the application. The ID is a GUID data type. If you did not receive your App ID, let us know and we will provide you. | Nielsen-specified | Yes | PXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX |
instanceName | DOM-less SDK instance name | Client provided | Yes | String eg. abcdefg |
appName | Application name | Client provided | Yes | appName: 'BSDK RN Sample App' |
deviceId | Device ID | Client provided | Yes | deviceId: '38400000-8cf0-11bd-b23e-10b96e40000d' |
nol_sdkDebug | Enables Nielsen console logging. Only required for testing | Client-provided | No | nol_sdkDebug: 'debug' |
domlessEnv | Specify the domless environment.
"1" for ReactNative "2" for Amazon "3" for NodeJS "4" for Custom |
Client-provided | Yes | domlessEnv: '1' |
DOM-less SDK DCR Video Step 3 - Create DCR Video Content Metadata Object
Events that can be passed to `ggPM` method
1. loadmetadata
- This event is used to send DCR Video metadata to the Nielsen SDK. This event should be called when the video metadata is loaded.
instance.ggPM('loadmetadata', {
'type': 'content',
'length': '300',
'censuscategory': 'Enlisted',
'title': 'Channel1',
'assetid': '204558915991',
'section': 'ProgramAsset8',
'tv': 'true',
'adModel': '0',
'dataSrc': 'cms'
});
2. setplayheadposition
- This event is used to send the current playhead position to the Nielsen SDK. This event should be called when the video is playing.
instance.ggPM('setplayheadposition', 10);
3. end
- This event is used to send the end event to the Nielsen SDK. This event should be called when the video playback is finished, passing the playhead at that time.
instance.ggPM('end', 300);
4. pause
- This event is used to send the pause event to the Nielsen SDK. This event should be called when the video is paused, passing the playhead at that time.
instance.ggPM('pause', 15);
5. play
- This event is used to send the play event to the Nielsen SDK. This event should be called when the video is played; often used when resuming from pause, passing the playhead at that time.
instance.ggPM('play', 30);
6. stop
- This event is used to send the stop event to the Nielsen SDK. This event should be called when transitioning from ads to content and content to ads, passing the playhead at that time.
instance.ggPM('stop', 120);
7. sendid3
- This event is used to send the id3 event to the Nielsen SDK. This event should be called when the id3 event is triggered, passing the id3 data from the stream.
instance.ggPM('sendid3', '<id3 metadata received>');
Events that can be passed to and processed by processEvent
method
1. `Blur`
- This event should be passed to processEvent when the app goes to the background.
instance.processEvent({'type': 'Blur', 'timestamp': Date.now()});
2. `Focus`
- This event should be passed to processEvent when the app goes to the foreground.
instance.processEvent({'type': 'Focus', 'timestamp': Date.now()});
3. `AppClose`
- This event should be passed prior to closing the app.
instance.processEvent({'type': 'AppClose', 'timestamp': Date.now()});
DOM-less SDK DCR Video Step 4 - Basic Set of Events - Sample Playback
Sample Nielsen BSDK-Domless NodeJS Example
- [Nielsen bsdk-domless NodeJS Repository Example](https://github.com/NielsenDigitalSDK/bsdk-domless-samples/tree/main/nodejs)
Sample DCR Video Integration
import { BsdkInstance } from 'bsdk-domless';
const nsdkConfig = {
app_id: 'DHG163HR-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
instance_name: 'videoInstance',
};
const implementationHooks = {
Log: {
info: function (log: string) {
console.info(log);
},
debug: function (log: string) {
console.debug(log);
},
warn: function (log: string) {
console.warn(log);
},
error: function (error: string) {
console.error(error);
}
},
Storage: {
setItem: async function (key: any, value: string) {
/**
* Sets a string value for given key. This operation can either modify an existing entry,
* if it did exist for given key, or add new one otherwise.
* In order to store object value, you need to serialize it, e.g. using JSON.stringify().
*/
},
getItem: async function (key: any) {
/**
* Gets a string value for given key. This function can either return a string value for
* existing key or return null otherwise.
* In order to store object value, you need to deserialize it, e.g. using JSON.parse().
*/
},
removeItem: async function (key: any) {
/**
* Removes item for a key, invokes (optional) callback once completed.
*/
}
},
Fetch: async function (url: string | URL | Request, options: any) {
/**
* We require that client pass in User-Agent header via options in Fetch request
*/
const clientOpts = {
headers: {
"User-Agent": "react-native-domless/1.6.7.42 Dalvik/2.1.0 (Linux; U; Android 5.1.1; Android SDK built for x86 Build/LMY48X)"
}
}
const data = Object.assign(options, clientOpts);
const response = await fetch(url, data);
if (response.ok) {
return response;
} else {
throw new Error('Request failed');
}
},
SetTimeout: function (callback: () => void, timeout: number | undefined) {
return setTimeout(callback, timeout); },
SetInterval: function (callback: () => void, interval: number | undefined) {
return setInterval(callback, interval);
},
ClearTimeout: function (timeout: string | number | NodeJS.Timeout | undefined) {
clearTimeout(timeout);
},
ClearInterval: function (interval: string | number | NodeJS.Timeout | undefined) {
clearInterval(interval);
}
};
const nSdkInstance = new BsdkInstance(
nsdkConfig.app_id,
nsdkConfig.instance_name,
{
appName: 'BSDK RN Sample App',
deviceId: 'testDeviceId',
nol_sdkDebug: 'debug', // remove debug flag when going to production
domlessEnv: // "1" for React Native | "2" for Amazon | "3" for NodeJS | "4" for Custom
// reference SDK interface documentation
// for additonal metadata properties
},
implementationHooks );
// Sample VideoPlayer component const VideoPlayer = (props) => {
/**
* Implementation of video player component will vary across the board, for Nielsen DOM-less SDK integration
* clients need only setup event listeners with corresponding ggPM() calls.
*
* Please refer to chosen video player documentation on available events
*/
const video = useRef<Video>(null);
let previousPlayhead = 0; // keep track of previous playhead position
let metadataLoaded = false; // in case of replay scenario set flag for metadata load
// Sample video metadata
const videometadata = {
'type': 'content',
'length': '300',
'censuscategory': 'Enlisted',
'title': 'Channel1',
'assetid': '204558915991',
'section': 'ProgramAsset8',
'tv': 'true',
'adModel': '0',
'dataSrc': 'cms'
}
const setUpEventListeners = (): void => {
video.addEventListener('ended', onEnded);
video.addEventListener('timeupdate', onTimeUpdate);
video.addEventListener('playing', onPlay);
video.addEventListener('pause', onPause);
};
const onEnded = () => {
// Nielsen SDK ggPM 'end' event
if (nSdkInstance) {
nSdkInstance.then((instance: any) => {
instance.ggPM('end', Math.round(video.currentTime));
metadataLoaded = false;
});
}
};
const onTimeUpdate = () => {
const currPlayhead = Math.round(video.current?.currentTime!);
if (currPlayhead > 0 && currPlayhead !== previousPlayhead) {
previousPlayhead = currPlayhead;
// Nielsen SDK ggPM 'setplayheadposition' event
if (nSdkInstance) {
nSdkInstance.then((instance: any) => {
instance.ggPM('setplayheadposition', currPlayhead);
});
}
}
};
// NOTE: some players may have an event when video metadata is loaded, recommended to use if available. E.g. loadedmetadata
const onPlay = () => {
// Nielsen SDK ggPM 'loadmetadata' event
if (nSdkInstance && !metadataLoaded) {
nSdkInstance.then((instance: any) => {
instance.ggPM('loadmetadata', videometadata);
metadataLoaded = true;
});
}
};
const onPause = () => {
// Nielsen SDK ggPM 'pause' event
if (nSdkInstance) {
nSdkInstance.then((instance: any) => {
instance.ggPM('pause', Math.round(video.currentTime));
});
}
};
return (
<nowiki> </nowiki> <View>
<nowiki> </nowiki> <nowiki><Video
source={{ uri: 'https://www.w3schools.com/html/mov_bbb.mp4' }}
/></nowiki>
<nowiki> </nowiki> </View>
<nowiki> </nowiki> );
};