Youbora Plugin
Mobile | Web | TV Platforms | ||||
---|---|---|---|---|---|---|
DIVA Youbora Plugin​
The DIVA Youbora Plugin is an out-of-the-box implementation of a Media Analytics listener which is able to receive events from Diva and directly send them to Youbora.
This plugin is implemented for iOS, tvOS, Android and Web and is used in all our Integration Demo projects as well as in the DIVA Chromecast Receiver app.
The main parameters of this plugin are:
- CDN Name: A string identifying which CDN solution is used by the project
- customTagGenerator: A function used by the plugin to calculate Custom TAGs dynamically. Every time the VideoMetadata gets updated and receives a parameter, the function is called.
- player version: the version of the diva player used to provide additional informations
- player name: the name of the player used to provide additional informations
- username: the name of the user to send the data to the correct account
- account code: the code to access youbora
- customDimentionsGenerator: A function used by the plugin to calculate Custom Dimensions dynamically. Every time the VideoMetadata gets updated and receives a parameter, the function is called.
Youbora documentation for more information.
- Web
- Web TV
- Web Chromeless
- iOS
- tvOS
- Roku
- Android
- Android TV
// Set plugin parameters
const youboraConfig = {
cdnName: 'AKAMAI',
playerVersion: YOUBORA_PLAYER_VERSION || '',
playerName: 'diva',
userName: YOUBORA_USERNAME || '',
accountCode: YOUBORA_ACCOUNT_CODE || '',
customDimentionsGenerator: (videoMetadata) => [
videoMetadata.videoId,
videoMetadata.title || '',
videoMetadata.assetState || '',
videoMetadata.eventId || '',
'WebTV',
videoMetadata?.behaviour?.spoilerMode || '',
],
customTagGenerator: (videoMetadata) => ({
assetState: videoMetadata.assetState || '',
eventId: videoMetadata.eventId || '',
is24_7: `${videoMetadata.assetState === 'live' && videoMetadata.dvrType === 'none'}`,
is360: `${!!videoMetadata.stereoMode && videoMetadata.stereoMode !== 'none'}`,
platform: 'WebTV',
spoilerMode: videoMetadata.behaviour?.spoilerMode || '',
videoId: videoMetadata.videoId || '',
videoTitle: videoMetadata.title || '',
}),
}
mediaAnalyticPlugin = new YouboraPlugin()
mediaAnalyticPlugin.init(convivaConfig)
window.addEventListener("beforeunload", (e) => {
mediaAnalyticPlugin.detachPlugin();
})
// Start Diva
const {
element: divaElement,
api: divaAPI
} = new Diva('.diva-player-container', {
// ...
onMediaAnalyticEvent: mediaAnalyticPlugin.handleEvent
})
React version
const youboraConfig = {
cdnName: 'AKAMAI',
playerVersion: YOUBORA_PLAYER_VERSION || '',
playerName: 'diva',
userName: YOUBORA_USERNAME || '',
accountCode: YOUBORA_ACCOUNT_CODE || '',
customDimentionsGenerator: (videoMetadata) => [
videoMetadata.videoId,
videoMetadata.title || '',
videoMetadata.assetState || '',
videoMetadata.eventId || '',
'WebTV',
videoMetadata?.behaviour?.spoilerMode || '',
],
customTagGenerator: (videoMetadata) => ({
assetState: videoMetadata.assetState || '',
eventId: videoMetadata.eventId || '',
is24_7: `${videoMetadata.assetState === 'live' && videoMetadata.dvrType === 'none'}`,
is360: `${!!videoMetadata.stereoMode && videoMetadata.stereoMode !== 'none'}`,
platform: 'WebTV',
spoilerMode: videoMetadata.behaviour?.spoilerMode || '',
videoId: videoMetadata.videoId || '',
videoTitle: videoMetadata.title || '',
}),
}
const yp = new YouboraPlugin();
const Component = () => {
// ...
useEffect(()=>{
yp.init(youboraConfig)
return ()=>{
yp.detachPlugin();
}
}, [])
return (
<DivaWebTV
// ...
onMediaAnalytics={yp.handleEvent}
// ...
/>
)
}
Vanilla js version
const youboraConfig = {
cdnName: 'AKAMAI',
playerVersion: YOUBORA_PLAYER_VERSION || '',
playerName: 'diva',
userName: YOUBORA_USERNAME || '',
accountCode: YOUBORA_ACCOUNT_CODE || '',
customDimentionsGenerator: (videoMetadata) => [
videoMetadata.videoId,
videoMetadata.title || '',
videoMetadata.assetState || '',
videoMetadata.eventId || '',
'WebTV',
videoMetadata?.behaviour?.spoilerMode || '',
],
customTagGenerator: (videoMetadata) => ({
assetState: videoMetadata.assetState || '',
eventId: videoMetadata.eventId || '',
is24_7: `${videoMetadata.assetState === 'live' && videoMetadata.dvrType === 'none'}`,
is360: `${!!videoMetadata.stereoMode && videoMetadata.stereoMode !== 'none'}`,
platform: 'WebTV',
spoilerMode: videoMetadata.behaviour?.spoilerMode || '',
videoId: videoMetadata.videoId || '',
videoTitle: videoMetadata.title || '',
}),
}
const yp = new YouboraPlugin();
yp.init(youboraConfig);
yp.detachPlugin();
const diva = new divaWebtv.createDivaWebTV (el, {
// ...
onMediaAnalytics: yp.handleEvent
// ...
});
React version
const youboraConfig = {
cdnName: 'AKAMAI',
playerVersion: YOUBORA_PLAYER_VERSION || '',
playerName: 'diva',
userName: YOUBORA_USERNAME || '',
accountCode: YOUBORA_ACCOUNT_CODE || '',
customDimentionsGenerator: (videoMetadata) => [
videoMetadata.videoId,
videoMetadata.title || '',
videoMetadata.assetState || '',
videoMetadata.eventId || '',
'WebTV',
videoMetadata?.behaviour?.spoilerMode || '',
],
customTagGenerator: (videoMetadata) => ({
assetState: videoMetadata.assetState || '',
eventId: videoMetadata.eventId || '',
is24_7: `${videoMetadata.assetState === 'live' && videoMetadata.dvrType === 'none'}`,
is360: `${!!videoMetadata.stereoMode && videoMetadata.stereoMode !== 'none'}`,
platform: 'WebTV',
spoilerMode: videoMetadata.behaviour?.spoilerMode || '',
videoId: videoMetadata.videoId || '',
videoTitle: videoMetadata.title || '',
}),
}
const yp = new YouboraPlugin();
const config = {
// ...
onMediaAnalytics: yp.handleEvent
// ...
};
const Component = () => {
// ...
useEffect(()=>{
yp.init(youboraConfig)
return ()=>{
yp.detachPlugin();
}
}, [])
return (
<DivaWebChromelessComponent
// ...
config={config}
// ...
/>
)
}
Vanilla js version
const youboraConfig = {
cdnName: 'AKAMAI',
playerVersion: YOUBORA_PLAYER_VERSION || '',
playerName: 'diva',
userName: YOUBORA_USERNAME || '',
accountCode: YOUBORA_ACCOUNT_CODE || '',
customDimentionsGenerator: (videoMetadata) => [
videoMetadata.videoId,
videoMetadata.title || '',
videoMetadata.assetState || '',
videoMetadata.eventId || '',
'WebTV',
videoMetadata?.behaviour?.spoilerMode || '',
],
customTagGenerator: (videoMetadata) => ({
assetState: videoMetadata.assetState || '',
eventId: videoMetadata.eventId || '',
is24_7: `${videoMetadata.assetState === 'live' && videoMetadata.dvrType === 'none'}`,
is360: `${!!videoMetadata.stereoMode && videoMetadata.stereoMode !== 'none'}`,
platform: 'WebTV',
spoilerMode: videoMetadata.behaviour?.spoilerMode || '',
videoId: videoMetadata.videoId || '',
videoTitle: videoMetadata.title || '',
}),
}
const yp = new YouboraPlugin();
yp.init(youboraConfig);
const config = {
// ...
onMediaAnalytics: yp.handleEvent
// ...
};
yp.detachPlugin();
const diva = new divaWebChromelessSdk.createDivaWebChromeless (el, config,
{
muted: false,
volume: 1,
adCover: true,
videoCover: true
});
Integrator has the responsibility to initialize and populate
Youbora options
with relevant information to its media (see Youbora options documentation for more information). Minimum parameter needed is accountCode
, NPAW account code tells their Analytics app which customer account data is to be sent. Diva will populate the data it has, for instance video title, whether is live or vod, video id and Video related information like duration. The integrator can also update the options with additional metadata information using the provided onVideoMetadataUpdate
closure.
Plug in should be added to the project as a dependency using Swift Package Manager, package URL: https://github.com/deltatre-vxp/diva-apple-youbora-analytics-spm.git.
Below a sample implementation:
import DivaCore
import DivaIOS
import DivaYouboraAnalytics
import YouboraLib
class DivaViewController {
//....
var youboraAnalytics: YouboraMediaAnalytics?
func initializeDiva(...) async {
//...
let configuration = DivaConfiguration(...)
let youboraOptions = YBOptions()
youboraOptions.accountCode = "<INSERT-ACCOUNT-CODE-HERE>"
youboraOptions.username = "HappyDivaUser"
let youboraAnalytics = await YouboraMediaAnalytics(
options: youboraOptions,
settings: .init(playerName: "DivaPlayer+iOS",
playerVersionFramework: Diva.version.framework(),
logLevel: .notice)
onVideoMetadataUpdate: { metadata, options in
options.contentCustomDimension2 = metadata.assetState.rawValue
}
)
self.youboraAnalytics = youboraAnalytics
configuration.onMediaAnalyticsUpdate = { [weak self] mediaEvent in
guard let self else { return }
Task {
await self.youboraAnalytics?.handle(mediaEvent)
}
}
let diva = await Diva(configuration: configuration)
//....
}
Integrator has the responsibility to initialize and populate
Youbora options
with relevant information to its media (see Youbora options documentation for more information). Minimum parameter needed is accountCode
, NPAW account code tells their Analytics app which customer account data is to be sent. Diva will populate the data it has, for instance video title, whether is live or vod, video id and Video related information like duration. The integrator can also update the options with additional metadata information using the provided onVideoMetadataUpdate
closure.
Plug in should be added to the project as a dependency using Swift Package Manager, package URL: https://github.com/deltatre-vxp/diva-apple-youbora-analytics-spm.git.
Below a sample implementation:
import DivaCore
import DivaTVOS
import DivaYouboraAnalytics
import YouboraLib
class DivaViewController {
....
var youboraAnalytics: YouboraMediaAnalytics?
func initializeDiva(...) async {
...
let configuration = DivaTVConfiguration(...)
let youboraOptions = YBOptions()
youboraOptions.accountCode = "<INSERT-ACCOUNT-CODE-HERE>"
youboraOptions.username = "HappyDivaUser"
let ybAnalytics = await YouboraMediaAnalytics(
options: youboraOptions,
settings: .init(playerName: "DivaPlayer+tvOS",
playerVersionFramework: DivaTV.version.framework(),
logLevel: .debug)
onVideoMetadataUpdate: { metadata, options in
options.contentCustomDimension2 = metadata.assetState.rawValue
}
)
self.youboraAnalytics = ybAnalytics
configuration.onMediaAnalyticsUpdate = { [weak self] mediaEvent in
guard let self else { return }
Task {
// if you use it toguether with DivaBO, you can get the playbackSessionId from the EntitlementProvider
// and pass it to Youbora plugin
self.playbackSessionId = await divaBOResponse.entitlementProvider.getPlaybackSessionId()
await self.youboraAnalytics?.set(optionValue: self.playbackSessionId, path: \.adCustomDimension1)
await self.youboraAnalytics?.handle(mediaEvent)
}
}
let diva = await DivaTV(configuration: configuration)
....
}
Integrator has the responsibility to initialize and populate
Youbora options
with relevant information to its media (see Youbora options documentation for more information). Minimum parameter needed is accountCode
. The integrator can also update the options with additional metadata information using the provided onVideoMetadataUpdate
callback function.
Sample implementation:
' load Youbora plugin
sub loadYoubora()
m.youboraLibrary = CreateObject("roSGNode", "ComponentLibrary")
m.youboraLibrary.id = "DivaYouboraPlugin"
m.youboraLibrary.uri = "pkg:/path-to-plugin"
' Adding the ComponentLibrary node to the scene will start the download of the library
m.top.appendChild(m.youboraLibrary)
m.youboraLibrary.observeField("loadStatus", "onPluginLoadStatusChanged")
end sub
sub onPluginLoadStatusChanged()
if (m.youboraLibrary.loadStatus = "ready")
' plugin ready to be used
end if
end sub
' get Youbora Analytics component
sub initYoubora()
m.youboraAnalytics = CreateObject("roSGNode", "DivaYouboraPlugin:YouboraAnalytics")
end sub
' start Youbora plugin with configuration
sub startYoubora()
if (m.youboraAnalytics <> invalid)
m.youboraAnalytics.callFunc("initYoubora", getYouboraConfig("DEBUG"))
end if
end sub
' Youbora plugin configuration file example
function getYouboraConfig(state as string) as dynamic
config = {}
config.divaVersion = "1.0.0" ' correct diva player version
config.accountCode = "account-code"
config.playerName = "diva"
config.viewerId = "DivaUser"
config.cdnName = "AKAMAI"
config.debug = state = "DEBUG"
return config
end function
' update Youbora plugin with Diva player media analytics events
sub onMediaAnalyticsEventHandler(evt as dynamic)
' Media analytics event handler callback
data = evt.getData()
if (m.youboraAnalytics <> invalid)
m.youboraAnalytics.callFunc("handleMediaEvent", data)
end if
end sub
' close Youbora plugin
sub closeYoubora()
if (m.youboraAnalytics <> invalid)
m.youboraAnalytics.callFunc("closeYoubora")
m.youboraAnalytics = invalid
end if
end sub
// Set plugin parameters
val mediaAnalyticsConfiguration = MediaAnalyticsConfiguration(
divaVersion = com.deltatre.divamobilelib.BuildConfig.VERSION_NAME,
playerName = "Diva",
cdnName = "<INSERT-CDN-NAME-HERE>",
viewerID = "<INSERT-VIEWERID-HERE>",
customerKey = "<INSERT-CLIENTID-HERE>",
gatewayUrl = "",
customTagGenerator = {
// just an example, custom tags are project-specific
mapOf(
Pair("platform", "android"),
Pair("videoId", it.videoId),
Pair("videoTitle", it.title),
Pair("eventId", it.eventId),
//...
)
},
customDimensionGenerator = {
return arrayListOf(
"android",
it.videoId,
it.title,
it.assetState.value,
it.eventId,
it.behaviour.spoilerMode.value
)
}
)
// Create plugin instance
val divaYouboraMediaAnalytics = DivaYouboraMediaAnalytics(this as AppCompatActivity)
divaYouboraMediaAnalytics.mediaAnalyticsCallback.init(mediaAnalyticsConfiguration)
// Connect to Diva
DivaFragment.newInstance(
DivaConfiguration(
// ...
customMediaAnalyticsCallback = divaYouboraMediaAnalytics?.mediaAnalyticsCallback
)
)
// Set plugin parameters
val mediaAnalyticsConfiguration = MediaAnalyticsConfiguration(
divaVersion = com.deltatre.divatvlib.BuildConfig.VERSION_NAME,
playerName = "Diva",
cdnName = "<INSERT-CDN-NAME-HERE>",
viewerID = "<INSERT-VIEWERID-HERE>",
customerKey = "<INSERT-CLIENTID-HERE>",
gatewayUrl = "",
customTagGenerator = {
// just an example, custom tags are project-specific
mapOf(
Pair("platform", "android"),
Pair("videoId", it.videoId),
Pair("videoTitle", it.title),
Pair("eventId", it.eventId),
//...
)
},
customDimensionGenerator = {
return arrayListOf(
"androidtv",
it.videoId,
it.title,
it.assetState.value,
it.eventId,
it.behaviour.spoilerMode.value
//...
)
}
)
// Create plugin instance
val mediaAnalyticsCallback = YouBoraMediaAnalytics(
as AppCompatActivity,
mediaAnalyticsConfiguration
)
// Initialise the ViewModel with the media analytics parameter
divaPlaybackViewModel.init(setting!!, dictionary!!, assetDataProvider, null, mediaAnalyticsCallback)
Known Issues​
The player does not report on rendition. This parameter is so set manually but on some device could not be able to report this information.
In case of failed retry from video error, Youbora could emit 'start' and 'joinTime' events before the 'error' one.
For 'stop' event 'playhead' metric is hardcoded inside SDK with value '-1'.
For DAI events, ad related data needs to be defined for correct workflow. Youbora SDK has some internal ads convertion logic, which requires some ads related data.
- After preroll, there is a delay between Init event and real video playback start, which causes joinTime error
- 'error: playhead not sent' is tracked with DRM error
- Session is closed after video error at runtime
- DAI stream – session is closed after ad break ends