Skip to main content

WebTV SDK 5.8

This guide explains how to integrate DIVA into a webTV application.

Integration demo

A VXP GitHub account is required to gain access to:

Requirements​

Our SDK is designed to be used on:

  • SmartTV
    • Tizen 2015 and up
    • WebOS 2.0 and up
  • Sky Boxes
    • Amidala
    • XiOne / Beethoven
  • XBox
    • XBox One
    • XBox Series S
  • PlayStation
    • PS5

The SDK self-contains every dependency except:

  • ReactJS, the host application most provide ReactJS 16+
  • The video player, which loads dynamically based on target device and content type

Provisioning​

The Settings, with which DIVA will be configured.
The Dictionary, which will provide DIVA with the translations and localisations for the application.
The VideoMetaData, which provides DIVA with information about the playback for a specific item.

For more information about these items, please consult the Documentation.

This data can be provided as the application developer requires, but will need to return SettingClean, DictionaryClean and VideoMetaDataClean objects respectively in order to work with the DIVA configuration and initialise the player objects.

For the purpose of this example we will be using raw json, but the provision of the data can be provided from any available source.

We can as an example instantiate a mock 'video' item, from a potential video catalog. Crucial to DIVA 5 is the provision of an ID with which to retrieve settings information and a videoID to retrieve the VideoMetaData.

const videos = {
data: [
{
id: "item-1",
image: "./images/fifa-world-cup.jpg",
title: "France vs Croatia (minimal)",
videoId: "c6455bff-945f-42b2-af99-81dcd5aeba29",
settingId: "full",
},
],
};

Settings​

Setting for the application can be provided in various different formats and are used to provide configuration with which to run a particular video, for example:

Setting Example
{
"general":{
"audioSelectionMethod":"lang",
"closedCaptionSelectionMethod":"lang",
"expectedLiveDuration":3600000,
"increaseLiveDuration":600000,
"isMiddleTimelineEventsLineEnabled":true,
"isTimelineEventsVisibleWithCommentaryOpen":false,
"isVideoThumbnailPreviewEnabled":true,
"jumpLargeGaps":true,
"liveBackOff":30000,
"minimalLayoutWidth":600,
"pipMode":true,
"relevantCommentaryStartsVisible":false,
"resolveManifestUrl":true,
"smallGapLimit":0,
"trackVideoDataManifest":false,
"videoAnalyticsEventFrequency":60000,
"culture":"en-GB"
},
"alerts":{
"alertsPath":"https://<url>/alerts_demo.json",
"displayTime":10000
},
"customPlayByPlay":[
{
"key":"FirstExtraTimeEnd_Big",
"value":"https://<url>/icons/{p.density}/phase_end_big.png"
},
{
"key":"FirstExtraTimeEnd_Mini",
"value":"https://<url>/icons/{p.density}/phase_end_small.png"
},
{
"key":"FirstExtraTimeStart_Big",
"value":"https://<url>/icons/{p.density}/phase_start_big.png"
},
{
"key":"FirstExtraTimeStart_Mini",
"value":"https://<url>/icons/{p.density}/phase_start_small.png"
},
{
"key":"FirstHalfEnd_Big",
"value":"https://<url>/icons/{p.density}/phase_end_big.png"
},
{
"key":"FirstHalfEnd_Mini",
"value":"https://<url>/icons/{p.density}/phase_end_small.png"
},
{
"key":"FirstHalfStart_Big",
"value":"https://<url>/icons/{p.density}/phase_start_big.png"
},
{
"key":"FirstHalfStart_Mini",
"value":"https://<url>/icons/{p.density}/phase_start_small.png"
},
{
"key":"Goal_Big",
"value":"https://<url>/icons/{p.density}/goa_big.png"
},
{
"key":"Goal_Mini",
"value":"https://<url>/icons/{p.density}/goa_small.png"
},
{
"key":"OwnGoal_Big",
"value":"https://<url>/icons/{p.density}/ogo_big.png"
},
{
"key":"OwnGoal_Mini",
"value":"https://<url>/icons/{p.density}/ogo_small.png"
},
{
"key":"PenaltyEnd_Big",
"value":"https://<url>/icons/{p.density}/phase_end_big.png"
},
{
"key":"PenaltyEnd_Mini",
"value":"https://<url>/icons/{p.density}/phase_end_small.png"
},
{
"value":"https://<url>/icons/{p.density}/png_big.png",
"key":"PenaltyGoal_Big"
},
{
"key":"PenaltyGoal_Mini",
"value":"https://<url>/icons/{p.density}/png_small.png"
},
{
"key":"PenaltyStart_Big",
"value":"https://<url>/icons/{p.density}/phase_start_big.png"
},
{
"key":"PenaltyStart_Mini",
"value":"https://<url>/icons/{p.density}/phase_start_small.png"
},
{
"key":"PenaltyWrong_Big",
"value":"https://<url>/icons/{p.density}/ppw_big.png"
},
{
"key":"PenaltyWrong_Mini",
"value":"https://<url>/icons/{p.density}/ppw_small.png"
},
{
"key":"PhasePenaltyGoal_Big",
"value":"https://<url>/icons/{p.density}/ppg_big.png"
},
{
"value":"https://<url>/icons/{p.density}/ppg_small.png",
"key":"PhasePenaltyGoal_Mini"
},
{
"key":"PhasePenaltyWrong_Big",
"value":"https://<url>/icons/{p.density}/ppw_big.png"
},
{
"value":"https://<url>/icons/{p.density}/ppw_small.png",
"key":"PhasePenaltyWrong_Mini"
},
{
"key":"RedCard_Big",
"value":"https://<url>/icons/{p.density}/rc_big.png"
},
{
"key":"RedCard_Mini",
"value":"https://<url>/icons/{p.density}/rc_small.png"
},
{
"key":"SecondExtraTimeEnd_Big",
"value":"https://<url>/icons/{p.density}/phase_end_big.png"
},
{
"key":"SecondExtraTimeEnd_Mini",
"value":"https://<url>/icons/{p.density}/phase_end_small.png"
},
{
"key":"SecondExtraTimeStart_Big",
"value":"https://<url>/icons/{p.density}/phase_start_big.png"
},
{
"key":"SecondExtraTimeStart_Mini",
"value":"https://<url>/icons/{p.density}/phase_start_small.png"
},
{
"key":"SecondHalfEnd_Big",
"value":"https://<url>/icons/{p.density}/phase_end_big.png"
},
{
"key":"SecondHalfEnd_Mini",
"value":"https://<url>/icons/{p.density}/phase_end_small.png"
},
{
"key":"SecondHalfStart_Big",
"value":"https://<url>/icons/{p.density}/phase_start_big.png"
},
{
"key":"SecondHalfStart_Mini",
"value":"https://<url>/icons/{p.density}/phase_start_small.png"
},
{
"key":"Substitution_Big",
"value":"https://<url>/icons/{p.density}/sb_big.png"
},
{
"key":"Substitution_Mini",
"value":"https://<url>/icons/{p.density}/sb_small.png"
},
{
"key":"YellowCard_Big",
"value":"https://<url>/icons/{p.density}/yc_big.png"
},
{
"key":"YellowCard_Mini",
"value":"https://<url>/icons/{p.density}/yc_small.png"
}
],
"ecommerce":{
"feedUrl":"https://<url>/ecommerce/index.html?eventId={v.eventId}&amp;culture={d.culture}",
"wordTag":"shop",
"ecommerceId":"e-commerce",
"iconUrl":"https://<url>/img/shop_icon_white.png",
"toleranceWindow":5000,
"showNotificationsOnce":false
},
"highlights":{
"startMode":"short",
"shortFilter":[
"GOAL",
"OwnGoal",
"PenaltyGoal"
],
"mediumFilter":[
"Goal",
"OwnGoal",
"PenaltyGoal",
"YellowCard",
"RedCard"
],
"longFilter":[
"*"
],
"liveFilter":[
"Goal",
"OwnGoal",
"PenaltyGoal",
"YellowCard",
"RedCard",
"Substitution"
]
},
"liveLike":{
"clientId":"WV6W1rkAJAAXAS9l0LpqHzjDyEcPbuGJjX7Kc2hk",
"chatEnabled":true,
"widgetThemeUrl":"https://<url>/LiveLike/customWidgetThemeUpdated.json"
},
"pushEngine":{
"configUrl":"https://<url>/DIVAProduct/www/Data/DivaDemoIBC/PushEngine/pushengineConfig_HBS.json",
"eCommerceCollectionName":"eCommerceDemo",
"eCommerceCollectionEnabled":false,
"editorialCollectionName":" "
},
"syncDataPanels":{
"dataFolderUrl":"https://<url>/DIVAProduct/www/Data/Diva5.0Test/output/OverlayLiteData/{V.EventId}.{d.Culture}/{OverlayID}.xml",
"renderingFolderUrl":"https://<url>/DIVAProduct/www/Data/Diva5.0Test/output/RenderingLiteData{n:ResourceURI}",
"trustedOrigins":"https://divadoc.deltatre.net,https://<url>"
},
"videoCast":{
"castBackground":"https://<url>/img/diva_chromecast.jpg",
"chromecastAppID":"<appID>"
}
}

For more information about setting and the available options click here.

Dictionary​

Dictionary must be provided based on the locale and will resemble something like the following:

Dictionary Example
{
"messages":{
"diva_go_live":"Go Live",
"diva_video_error":"This video is not working or not available in your region.",
"diva_error_button_ok":"OK",
"diva_menu_full_stats_button":"All Stats",
"diva_playbutton":"Play",
"diva_pausebutton":"Pause",
"diva_seekforward_button":"+{n}",
"diva_seekback_button":"-{n}",
"diva_alternate_timeline":"Alternate Timeline",
"diva_alternate_timeline_show_all":"See all events",
"diva_spoil":"SHOW EVENTS",
"diva_fullscreen":"Full Screen",
"diva_exitfullscreen":"Exit Full Screen",
"diva_alert":"Alerts",
"diva_multicam":"MULTICAM",
"diva_360clips":"360 CLIPS",
"diva_360multicam":"360 MULTICAM",
"diva_no_multicam":"Videos will appear as soon as they are published, please retry in a short while",
"diva_back_button":"Back",
"diva_chapters_open":"Chapters",
"diva_chapters_close":"Close Chapters",
"diva_theater_mode":"Theater Mode",
"diva_exit_theater_mode":"Exit Theater Mode",
"diva_mute":"Mute",
"diva_unmute":"Unmute",
"diva_show_multiview":"Show in sideBySide",
"diva_collapse_this_video":"Collapse video",
"diva_close_this_video":"Close video",
"diva_chromecast":"Chromecast",
"diva_close_stats_button":"",
"diva_audio_is_muted":"Audio is muted",
"diva_vr_start_video_loading":"VR loading ...",
"diva_button_close":"Close",
"diva_gck_connect_to_device":"Cast to",
"diva_gck_stop_casting":"Stop casting",
"diva_gck_cancel":"Cancel",
"diva_videometadata_error":"The video metadata are corrupted. Please try another video",
"diva_overlay_load_failure":"Could not load data panel",
"diva_drm_error":"Content protection error",
"diva_ssai_request_error":"This video is not working right now. Please disable any AdBlocker and then try again. In case this error persist please contact the Customer Care at help@diva.com",
"diva_no_highlights_error":"No key moments available at the moment. Please try later",
"diva_settings":"Settings Panel",
"diva_settings_button":"Settings",
"diva_error_title":"Error",
"diva_audio":"Audio Tracks",
"diva_cc_panel_title":"Subtitles",
"diva_closed_caption":"Closed Caption",
"diva_cc_disabled":"Disabled",
"diva_cc_d3608":"Enable CC",
"diva_cc_english":"English",
"diva_cc_french":"FRENCH",
"diva_cc_spanish":"Spanish",
"diva_cc_#1 Fre":"French",
"diva_cc_#2 Eng":"English",
"default_audio_selected":"Default Audio",
"diva_settings_hdr_enable":"Enable HDR",
"diva_adResumeTime":"Your video will resume in {remTime} s",
"diva_adResumeAt":"Your video will resume after the following advertisements {cVideo}/{totVideo}",
"diva_ad_loading_text":"Ad loading",
"diva_adblock":"You need to disable AdBlock to view the content",
"diva_adClickToEnableAudio":"Enable ad audio",
"diva_select_chapter":"Chapter List",
"diva_alert_replay":"Replay",
"diva_alert_back":"Back to",
"diva_highlightsmode_loading":"Highlights cards processing...",
"diva_highlightsmode_howto":"How do you want to watch this match?",
"diva_highlightsmode_short":"Short",
"diva_highlightsmode_medium":"Medium",
"diva_highlightsmode_long":"Long",
"diva_highlightsmode_live":"Live",
"diva_highlightsmode_full":"Full Match",
"diva_highlightsmode_resume":"Resume Highlights",
"diva_highlightsmode_next":"Next",
"diva_highlightsmode_highlights":"Highlights",
"diva_highlightsmode_youarein":"You are in highlights mode",
"diva_highlightsmode_notification_title":"Don't miss {n} key moments",
"diva_highlightsmode_notification_subtitle":"Would you like to watch them?",
"diva_highlightsmode_notification_watch":"Watch",
"diva_highlightsmode_highlights_title":"Highlights duration:",
"diva_hours":"h",
"diva_minutes":"m",
"diva_seconds":"s",
"diva_playbyplay":"Commentary",
"diva_noplaybyplay":"First message will be displayed shortly after the start of the session",
"diva_recommendation_watch_again":"Watch again",
"diva_recommendation_watch_next_video":"Watch the next video",
"diva_recommendation_next_videos":"Next videos",
"diva_recommendation_video_autoload":"Next video will start in {sec} sec",
"shop":"Shop",
"diva_ecommerce_rotate_device":"Rotate your screen to view the shop",
"diva_settings_close":"",
"diva_cc_enabled":"Enabled",
"diva_settings_pip_enable":"Enable Picture-In-Picture",
"diva_enterpip":"Enter pip mode",
"diva_exitpip":"Exit pip mode",
"diva_novideoavailable":"",
"diva_airplay_error":"Error trying to cast to AirPlay",
"diva_airplay_forbiden_error":"AirPlay cast disabled in Diva settings",
"diva_live":"Live Now",
"diva_settings_title":"Settings",
"diva_settings_hdr_label":"HDR",
"diva_settings_hdr_description":"Disable HDR if you are experiencing problems seeing the colours in the video",
"diva_at_button_tooltip":"Audio",
"diva_at_settings_title":"Audio tracks",
"diva_cc_button_tooltip":"Subtitles",
"diva_cc_settings_title":"Subtitles",
"diva_cc_eng":"english (lang)",
"diva_cc_en":"english (lang)",
"diva_airplay_forbidden_error":"AirPlay cast disabled in Diva settings",
"diva_timeline_events_title":"Key moments",
"diva_videolist_watching":"WATCHING",
"diva_videolist_live":"LIVE",
"diva_cc_enhancements_button":"Caption style",
"diva_cc_enhancements_button_subtitle":"Change text size and style",
"diva_cc_enlarge_label":"Enlarge text",
"diva_cc_caption_style_title":"Caption style",
"diva_cc_enlarge_subtitle":"Enlarge text",
"diva_cc_enlarge_description":"Activate for a significant increase of the subtitles text, for better readability",
"diva_cc_style_subtitle":"Style",
"diva_cc_style_description":"Activate to enable a darker background behind the subtitles text for increased visibility",
"diva_cc_style_label":"Style",
"diva_settings_hdr_ON_value":"On",
"diva_settings_hdr_OFF_value":"Off",
"diva_eop_starts_in_seconds":"Starts in {seconds} sec",
"diva_eop_reccomandation_title":"Recommendations",
"diva_eop_rewind":"Replay",
"diva_ccat_panel_title":"Audio and Subtitles",
"diva_accessibility_high_contrast_title":"Increase contrast",
"diva_accessibility_high_contrast_description":"Turn on increase contrast mode if you are having trouble seeing the menu",
"diva_accessibility_high_contrast_label":"Increase contrast",
"diva_videolist_default_title":"Default Title",
"diva_videolist_title_highlight":"Highlights",
"diva_fullmatch_highlights_button_label":"Full Match",
"diva_next_highlights_button_label":"Next",
"diva_videolist_highlight":"HIGHLIGHTS",
"diva_highlights_title_medium":"Highlights medium",
"diva_highlights_title_long":"Highlights long",
"diva_highlights_title_short":"Highlights short",
"diva_cc_caption_style_header":"Caption style",
"diva_cc_settings_header":"Subtitles",
"diva_cc_settings_subheader":"Style",
"diva_cc_enlarge_subheader":"Enlarge text",
"diva_highlights_title_live":"Live highlights",
"diva_highlights_notification":"Match Highlights",
"diva_highlights_badge_live":"Live Highlights",
"diva_highlights_badge_short":"Short Highlights",
"diva_highlights_badge_medium":"Medium Highlights",
"diva_highlights_badge_long":"Long Highlights",
"diva_highlights_alert_seek_unavailable":"Return to the full match to use rewind and fast forward",
"diva_pinned":"Pinned",
"diva_menu_button":"MENU",
"diva_data_panel_close_button":"Close Data Panel",
"diva_menu_close_button":"Close Menu",
"diva_data_panel_button_tooltip":"Stats",
"diva_data_panel_no_data_available_title":"No data available yet",
"diva_data_panel_no_data_available_description":"Please check back here shortly after the game has started!",
"diva_eop_replay":"Replay",
"diva_swap_video":"Swap video"
}
}

For more information about dictionary and the available options click here.

VideoMetaData​

In order to support decoupled OVP provision, DIVA 5 requires that video meta data is retrieved by the integrating application and passed to DIVA 5 as part of the initialisation parameters, an example of which is as follows:

VideoMetadata Example
{
"title":"Video title",
"image":"https://<thumbnail_url>.jpg",
"eventId":"108606",
"programDateTime":"2018-07-15T13:40:15.681Z",
"trimIn":4680858,
"trimOut":11641166,
"ad":"https://<vast_url>/skippable2.xml",
"sources":[
{
"uri":"https://<url>/<video_id>.ism/manifest(format=m3u8-aapl,filter=hls)",
"format":"HLS"
}
],
"audioTracks":[
{
"label":"English",
"id":"English1",
"selector":"English"
}
],
"defaultAudioTrackId":"English1",
"videoLists":[
{
"feedUrl":"https://<url>/rss/108606.xml",
"menu":"Other cameras",
"message":"Watch the live match from different angles",
"behaviour":"multistreamSwitch",
"id":"multistream",
"pollingInterval":30
},
{
"menu":"Other matches",
"message":"Watch other live mathes",
"highlightColor":"0xE65100",
"highlightColorLight":"0xff0000",
"id":"videolist",
"feedUrl":"https://<url>/rss/videolistSwitchDemo.xml"
}
],
"customAttributes":{
"chatId":"444ecf59-dbf2-43db-ae09-99926714022a",
"chatType":"influencer"
},
"recommendation":{
"feedUrl":"https://<url>/rss/videolist_recommendation_short.xml",
"autoLoadTime":0
},
"behaviour":{
"spoilerMode":"highlights"
},
"videoId":"<video_id>"
}
VideoMetadata Provider
// Type definition
export type VideoMetadataProvider = (
videoId: string,
currentVideoMetadata: VideoMetadataClean | undefined,
playbackState?:
| {
chromecast?: boolean | undefined;
hdrMode?: boolean | undefined;
}
| undefined
) => Promise<VideoMetadata>;

// Implementation example
export const requestVideometadata: VideoMetadataProvider = (videoId, currentVideoMetadata, playbackState) => {
videoId = decodeURIComponent(videoId);

const platform = playbackState?.chromecast === true ? 'chromecast' : 'html5';
const hdrType = playbackState?.hdrMode === true ? 'hdr10' : 'none';

const url = `https://<videoMetadataServiceUrl>/${videoId}?platform=${platform}&hdrType=${hdrType}`;

return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();

xhr.open('GET', url, true);
xhr.responseType = 'json';
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(xhr.response);
} else {
reject(new Error(`${this.status} ${xhr.statusText}`));
}
};
xhr.onerror = function () {
reject(xhr.statusText);
};
xhr.send();
});
};

Provision must be done with the same signature as above so that DIVA internally is able to call the VideoMetaData provider and if necessary recall based on interval polling if the application requires data to be refreshed, i.e.

if (!isNaN(divaParams.videoPolling) && divaParams.videoPolling > 0) {
setInterval(function () {
divaAPI.requestVideoMetadataUpdate();
}, divaParams.videoPolling * 1000);
}

With the ability to return VMD, we then have all the elements required to instantiate a DIVA session.

For more information about Video Meta Data please click here.

Entitlement​

Entitlement and Entitlement Provider documentation.

SDK Setup​

  1. Add @deltatre-vxp:registry=https://npm.pkg.github.com to your .npmrc file
  2. Install the SDK
       npm install @deltatre-vxp/diva-sdk

DIVA Configuration​

Once the data has been provisioned, we combine the variables into a set of parameters to instantiate DIVA.

PropertyTypeDefaultDescription
Mandatory
Property: inittype:Default: NAinit config for DIVA
Property: dictionarytype:Default: NADIVA dictionary
Property: videoMetadataProvidertype:Default: NAFunction used by DIVA to retrieve videoMetadata
Property: libstype:
object
Default: NAObject containing the external libraries' url lazy loaded by DIVA only when needed.
Libs reference
Optional
Middlewares
Property: entitlementProvidertype:Default: NAFunction used by DIVA to perform entitlement and heartbeat calls
Callbacks
Property: setAPItype:
(apiRef: DivaAPI)=> void
Default: undefinedprovide as soon as possible an imperative api to interact with the player
Property: onVideoErrortype:
(error: VideoError, videoMetadata: VideoMetadata) => void
Default: undefinednotify about video playing errors
Property: onEventtype:
(event: { type: 'BACK_PRESS_OUTSIDE' }) => boolean | Promise<boolean>
Default: undefinedactually used only for cases when the user is leaving the player with BACK_PRESS_OUTSIDE
Property: onAnalyticstype:
(event: GenericEvent) => void
Default: undefinednotify analytics events
Property: onMediaAnalyticstype:
(event: MediaEvent) => void
Default: undefinednotify media analytics events
Property: onTtsMessagetype:
(data: TtsData) => void
Default: undefinednotify text to be converted to speech fo accessibility
Property: onPlayerPositiontype:
(position: { relativePosition: number; absolutePosition: Date }) => void
Default: undefinednotify as frequent as possible the position reached by the player in the playing video
Property: onPlayerMaxPositiontype:
(position: { relativePosition: number; absolutePosition: Date }) => void
Default: undefined Called when player max position (both relative to the video logical start and unix timecode) of the main video changes
Property: onPlaybackRatetype:
(rate: number) => void
Default: undefinedCalled when playback speed of the main video changes
Property: onPlayerStatetype:
(state: PlayerState) => void
Default: undefinedCalled when player state of the main video changes
Property: onPreferredAudioTracktype:
(value: string) => void
Default: undefinednotify about changes in the audiotrack selected by the user
Property: onPreferredCCTracktype:
(value: string) => void
Default: undefinednotify about changes about cc selected by the user
Property: onVideoEndtype:
()=>void
Default: undefinednotify when the playing video reaches the end
Property: onPlaybackSessiontype:
(value: {id: string; playbackSessionId: string }) => void
Default: undefinedCalled on DIVA playback session changed. It returns also a unique identifier of the diva item.
Property: onVideoMetadataChangestype:
(videoMetadata: VideoMetadataClean)=>void
Default: undefinedcalled when videometadata of the main video changes
Properties
Property: keyboardActivetype:
boolean
Default: trueflag to enable/disable keyboard navigation support
Property: navigationActivetype:
boolean (reactive on React)
Default: trueflag to enable or disable navigation inside the component
Property: keyboardActivetype:
boolean (reactive on React)
Default: trueflag to enable/disable keyboard navigation support
Property: remoteActivetype:
boolean (reactive on React)
Default: trueflag to enable/disable navigation through remote
Property: gamepadActivetype:
boolean (reactive on React)
Default: trueflag to enable/disable gamepad navigation support
Property: noTransitionstype:
boolean
Default: falseflag to disable css transitions
Property: noControlsGradientBackgroundtype:
boolean
Default: falseflag to disable css gradient backgrounds
Property: unfreezeShakaAtMediaQualityChangestype:
boolean
Default: falseflag to toggle play pause in case of bitrate switches. Hack for sky xione and harmonic stream. It needs at least shaka 3.3.19

DivaWebTVInitType​

PropertyTypeDefaultDescription
Mandatory
Property: videoIdtype:
string
Default: NAid of a specific videoMatadata
Property: settingtype:Default: NADIVA setting
Property: autoplaytype:
boolean
Default: NAIt specifies whether to autoplay DIVA (on webtv this should always be true).
Optional
Property: deepLinkTypetype:
string: 'relative' | 'absolute'
Default: 'relative'Type of deeplink to apply at startup: relative to the video start (or trimIn) or absolute.
Property: deepLinkValuetype:
string
Default: []Value of deeplink to apply at startup:
type relative -> number of milliseconds
type absolute -> ISO 8801 datetime.
Property: hdrModetype:
boolean
Default: falseIt specifies whether to activate HDR or not at startup.
Property: preferredAudioTrackNametype:
string
Default: NAStarting selector for the audiotracks
Property: preferredCCTrackNametype:
string
Default: NAStarting selector for the closed captions.
Property: bitratePreferencestype:
{ max?: number, min?: number, starting?: number, useLast?: boolean; }
Default: { max: -1, min: -1, starting: -1, useLast: false; }max, min, starting bitrate (-1 means disabled).
useLast: if true it makes DIVA using last bitrate from the previous session.
Property: hlsJsConfigOverridestype:
any
Default: {}Overrides for shaka video library setup config
Property: shakaConfigOverridestype:
any
Default: {}Overrides for hls.js video library setup config
Property: dataParsingIntervaltype:
number
Default: 3000Pushengine Data parsing interval
Property: parameterstype:
[key: string]: string
Default: undefinedMap of additional keys to add to the path resolver. Input keys will be saved inside resolver as "settings.<key>"

Accessibility Options​

PropertyTypeDefaultDescription
Optional
Property: darkerBackgroundCctype:
boolean
Default: falseclosed captions with special style to improve legibility
Property: enlargedCcstype:
boolean
Default: falseclosed captions bigger font size to improve legibility
Property: opaqueBackgroundtype:
boolean
Default: falsebackground of transparent system opaque to improve legibility
Property: ccEnhancementsDisabledtype:
boolean
Default: falseif true disable the possibility to change enlargedCcs and darkerBackgroundCc
Property: hideTransparencyDisabledtype:
boolean
Default: falseif true disable the possibility to change opaqueBackground
Callbacks
Property: onAccessibilityUpdatetype:
(data: {darkerBackgroundCc:boolean, enlargedCcs:boolean, opaqueBackground:boolean }) => void
Default: undefinednotify when there are changes on accessibility configuration

Typescript interfaces for Accessibility Options​

AccessibilityData and AccessibilityData

export interface AccessibilityData {
/**
* closed captions with special style to improve legibility
*/
darkerBackgroundCc?: boolean;
/**
* closed captions bigger font size to improve legibility
*/
enlargedCcs?: boolean;
/**
* background of transparent system opaque to improve legibility
*/
opaqueBackground?: boolean;
}

export interface AccessibilityOptions extends AccessibilityData {
/**
* if true disable the possibility to change enlargedCcs and darkerBackgroundCc
*/
ccEnhancementsDisabled?: boolean;
/**
* if true disable the possibility to change opaqueBackground
*/
hideTransparencyDisabled?: boolean;
/**
* notify when there are changes on accessibility configuration
*/
onAccessibilityUpdate?: (data: AccessibilityData) => void;
}
Configuration example
{
init: {
videoId: "VIDEO_ID"
autoplay: true,
bitratePreferences: {
max: -1,
min: -1,
starting: -1,
useLast: false
},
deepLinkType: "relative",
deepLinkValue: "1200000",
hdrMode: false,
setting: {
general: {
culture: "en-GB",
isCommentaryFilteredByChapter: false
}
}
},
dictionary: {
messages: {
default_audio_selected: "Default Audio",
diva_360clips: "360 CLIPS",
diva_360multicam: "360 MULTICAM",
...
},
libs: {
googleCast: "https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1",
googleDAI: "https://imasdk.googleapis.com/js/sdkloader/ima3_dai.js",
googleIMA: "https://imasdk.googleapis.com/js/sdkloader/ima3.js",
hlsJs: "https://cdn.jsdelivr.net/npm/hls.js@1.5.7",
mux: "https://cdnjs.cloudflare.com/ajax/libs/mux.js/6.2.0/mux.min.js",
shaka: "https://cdnjs.cloudflare.com/ajax/libs/shaka-player/4.11.2/shaka-player.compiled.js",
threeJs: "https://cdnjs.cloudflare.com/ajax/libs/three.js/0.148.0/three.min.js",
},
onAnalyticEvent: console.log,
onMediaAnalyticEvent: console.log,
onVideoError: console.log,
setAPI: (api) => {
// Here you can save a reference to DIVA APIs
window.divaAPI = api;
},
videoMetadataProvider: (videoId, currentVideoMetadata, playbackState) => {
// returning a Promise of a VideoMetadata
return new Promise((resolve, reject) => {
resolve({
title: "<video_title>",
videoId: '<video_id>',
sources: [
{
format: "HLS",
uri: "https://<url>"
}
],
...
});
});
}
}

Initialization​

import React, { useEffect, useState } from "react";
import { DivaWebTV } from "@deltatre-vxp/diva-sdk/diva-webtv-sdk/index{targetEnv}.js";
import "@deltatre-vxp/diva-sdk/diva-webtv-sdk/index{targetEnv}.css";

const SETTINGS_URL = "https://example.com/settings";
const DICTIONARY_URL = "https://example.com/dictionary";
const VIDEO_METADATA_URL = "https://example.com/metadata";
const VIDEO_ID = "42";

const videoMetadataProvider = async (videoId) => {
return await fetch(`${VIDEO_METADATA_URL}/${videoId}`);
};

const getSettings = async () => {
const data = await fetch(SETTINGS_URL);
return await data.json();
};

const getDictionary = async () => {
const data = await fetch(DICTIONARY_URL);
return await data.json();
};

export function App() {
const [setting, setSettings] = useState(null);
const [dictionary, setDictionary] = useState(null);

useEffect(() => {
Promise.all([getSettings, getDictionary]).then(([setting, dictionary]) => {
setSettings(setting);
setDictionary(dictionary);
});
}, []);

if (!(setting && dictionary)) {
return null;
}
return (
<div className="app">
<DivaWebTV
init={{
setting,
videoId: VIDEO_ID,
autoplay: true,
}}
dictionary={dictionary}
videoMetadataProvider={videoMetadataProvider}
entitlementProvider={entitlementProvider}
/>
</div>
);
}
note
  • {targetEnv}: .min (production), (empty string) development

API​

The property setAPI inside DIVA Configuration is intended to be a function that returns an object containing all the APIs for the current DIVA instance.

DIVA API reference list:

interface DivaAPI {
/**
* It will force DIVA to request a new VideoMetadata through videoMetadataProvider
*/
requestVideoMetadataUpdate: (videoId?: string) => void;

// Getters

/**
* It returns the player position (both relative to the video logical start and unix timeCode as Date) of the video.
*/
getPlayerPosition: (videoId?: string) => { relative: number; absolute: Date };

/**
* It returns the player duration (both relative to the video logical duration and absolute end point as a Date) of the video.
*/
getPlayerDuration: (videoId?: string) => { relative: number; absolute: Date };

// Actions

/**
* It pauses video playback..
*/
pause: () => void;

/**
* It starts the video playback.
* @returns the videoElement play promise.
*/
play: () => Promise<void>;

/**
* It seeks the video playback.
* @param value number, milliseconds value relative to the video start time.
* @returns A promise usefull to check when the action is done.
*/
seek: (value: number) => Promise<void>;

/**
* It seeks back the video playback by the value get from videoMetadata.behaviour.seekInterval. Default 10 seconds.
*/
skipBack: () => Promise<void>;

/**
* It seeks forward the video playback by the value get from videoMetadata.behaviour.seekInterval. Default 10 seconds.
* @returns A promise usefull to check when the action is done.
*/
skipForward: () => Promise<void>;

/**
* It seeks the video playback by percentage.
* @param value number, percentage from 0 to 100.
* @returns A promise usefull to check when the action is done.
*/
seekPercentage: (value: number) => Promise<void>;

/**
* It seeks the video playback by Date object.
* @param value Date
* @returns A promise usefull to check when the action is done.
*/
seekAbsolute: (value: Date) => Promise<void>;

/**
* It sets the minimum distance between icons on timeline.
* @param value number, in pixels. Default 40. Take in considerations 1080p resolution..
*/
setTimelineIconsMinDistance: (value: number) => void;

// Advanced UI actions

/**
* It opens audio panel.
*/
openAudioPanel: () => void;

/**
* It opens subtitles panel.
*/
openSubtitlePanel: () => void;

/**
* It opens settings panel.
*/
openSettingsPanel: () => void;

/**
* It opens videolist view.
*/
openVideoList: () => void;

/**
* It sends a custom key press.
*/
sendCustomKeypress: (key: Key, e?: KeyboardEvent) => void;

/**
* Fires an event of the specified type with an optional payload.
* Used for Ad beaconing.
* @param type - The type of the event.
* @param payload - The payload associated with the event.
*/
fireEvent: (type: AnalyticsMediaEventType, payload?: { ad: Ad }) => void;

/**
* Exposed path resolver
*/
resolve(
value: string,
options?: {
videoId?: string; // if not provided it will fallback to main item
data?: Record<string, string>;
dontApplyDefault?: boolean;
}
): string;

/**
* Single command entry point for: mute, pause, play, set playback rate, seek, seek absolute
*/
sendPlayerCommand: (command: DivaCommand, interactive: boolean) => void | Promise<void>;
}

// DIVA Commands interfaces
export enum DivaCommandName {
MUTE = 'mute',
PAUSE = 'pause',
PLAY = 'play',
PLAYBACK_RATE = 'playback-rate',
SEEK = 'seek',
SEEK_ABSOLUTE = 'seek-absolute',
}

interface DivaCommandBase {
videoId?: string;
command: DivaCommandName;
}

export interface DivaCommandMute extends DivaCommandBase {
command: DivaCommandName.MUTE;
value: boolean;
}

export interface DivaCommandPause extends DivaCommandBase {
command: DivaCommandName.PAUSE;
}

export interface DivaCommandPlay extends DivaCommandBase {
command: DivaCommandName.PLAY;
}

export interface DivaCommandPlaybackRate extends DivaCommandBase {
command: DivaCommandName.PLAYBACK_RATE;
value: number;
}

export interface DivaCommandSeek extends DivaCommandBase {
command: DivaCommandName.SEEK;
value: number; // Milliseconds relative to trim in
}

export interface DivaCommandSeekAbsolute extends DivaCommandBase {
command: DivaCommandName.SEEK_ABSOLUTE;
value: Date;
}

export type DivaCommand =
| DivaCommandMute
| DivaCommandPause
| DivaCommandPlay
| DivaCommandPlaybackRate
| DivaCommandSeek
| DivaCommandSeekAbsolute;

Logging​

It is possible to attach to DIVA logs or even to use DIVA logger by:

import { onLog, getLogger } from "@deltatre-vxp/diva-sdk/diva-webtv-sdk";
import type {BoAdapterWebTvProps} from "@deltatre-vxp/diva-sdk/diva-webtv-sdk";

// Attach to DIVA logs
const unsubscribe = onLog((log: LogEntry) => {
console.log(log.message, log);
});

// Make use of DIVA logger
const { logDebug, logError, logInfo, logWarning } = getLogger('GENERIC:APP');
logDebug('log message', {
whatever: 'details'
});

// Unsubscribe from DIVA logs
unsubscribe();
note
  • {targetEnv}: .min (production), (empty string) development

Platform detection​

It is possible to detect the actual platform running in a consistent way with DIVA WebTV using methods exposed:

functiontypedescription
isSkyQ()=>booleantrue if SkyQ box is detected
isTizen()=>booleantrue if Tizen TV is detected
isWebOS()=>booleantrue if WebOS TV is detected
getSmartTvPlatform()=>WebTvPlatformTypesreturn the platform detected

type WebTvPlatformTypes = 'Tizen' | 'WebOs' | 'SkyQ' | 'Generic';

import { isSkyQ } from "@deltatre-vxp/diva-sdk/diva-webtv-sdk";
import type { DivaWebTVProps } from "@deltatre-vxp/diva-sdk/diva-webtv-sdk";

const skyFixes: Partial<DivaWebTVProps> = {
noControlsGradientBackground: true,
noTransitions: true,
unfreezeShakaAtMediaQualityChanges: true,
};

// props will contain sky fixes only if diva notice that the app is running on supported Sky boxes
const getPropsWithSkyFixes = (props:DivaWebTVProps):DivaWebTVProps => ({
...props,
...isSkyQ() ? skyFixes : {}
})
note
  • {targetEnv}: .min (production), (empty string) development