Recommendations / Related videos
What you learn​
You're instantiating DIVA Player in your web front-end and relying on DIVA Back Office as the video streaming source.
The goal of this article is to build the simplest front-end to stream a video from the DIVA Back Office with, at the end, the activation of Recommendations in combination with end of play.
Before starting​
- Ensure the DIVA Back Office instance you rely on is up and running.
- Ask your video engineers team the
<video id>and the related<settings URL>. - Ensure the VideoMetadata contains the media lists data.
- Get read-and-write access to the VideoMetadata file.
The Diva Player requires the videoId parameter during initialization, as it is the expected field for asset identification and loading. However, in this implementation, we refer to it generically as itemId.
Autoplay setup:
- Ensure the VideoMetadata contains the countdownToAutoplay property.
- Ensure the settings contains the timeToDisableAutoplay property.
Instantiation​
Write the Basic instantiation code. There's no additional code to write, unless you need to overwrite the autoplay behavior that the VideoMetadata contains.
Working sample code (.tsx)​
Write the App() function to read the VideoMetadataMap as in the following example, and set the medialists array:
import { AssetState, BoAdapterWebComponentProps, DivaWebBoAdapter, VideoMetadata } from "@deltatre-vxp/diva-sdk/diva-web-bo-adapter";
import "@deltatre-vxp/diva-sdk/diva-web-sdk/index.min.css";
import { useState } from "react";
const itemId = "<itemId>";
const libs = {
mux: "https://cdnjs.cloudflare.com/ajax/libs/mux.js/6.2.0/mux.min.js",
shaka: "https://ajax.googleapis.com/ajax/libs/shaka-player/4.16.15/shaka-player.compiled.js",
hlsJs: "https://cdn.jsdelivr.net/npm/hls.js@1.5.7",
googleIMA: false,
googleDAI: false,
googleCast: false,
threeJs: false
};
const config = {
videoId: itemId,
libs: libs,
settings: {
general: {
timeToDisableAutoplay: 5400000
}
}
};
function App() {
const [isVideoLive, setIsVideoLive] = useState(false);
const props: BoAdapterWebComponentProps = {
settingsUrl: "<settings URL>",
languageCode: "en-US",
languageDictionary: "en-US",
onDivaBoAdapterError: console.error,
config: config,
videoMetadataMap: async (vd: VideoMetadata) => {
vd.mediaLists = [
{
listId:"123466",
rowTitle:"diva_eop_related_title",
defaulImageFormat:"tile",
defaultItemLayout: "vertical",
defaultItemType: "related",
items: [
{
itemId: "12345678",
title: "Star Wars EP I - The Phantom Menace",
caption: "Mesa Jar Jar Binks",
imageUrl: "http://sw.image/ep1.jpg",
ccBadge: false,
adBadge: false,
},
{
itemId: "12345679"
title: "Star Wars EP II - Attack Of The Clones",
caption: "I don't like sand. It's coarse and rough and irritating and it gets everywhere",
imageUrl: "http://sw.image/ep2.jpg",
ccBadge: false,
adBadge: false,
},
{
itemId: "12345680"
title: "Star Wars EP III - Revenge Of The Sith",
caption: "I have brought peace, freedom, justice and security to my new Empire!",
imageUrl: "http://sw.image/ep3.jpg",
ccBadge: false,
adBadge: false,
}
]
},
];
vd.behavior = {
endOfPlay: {
countdownToAutoplay: 10000
}
}
return vd;
}
}
 return (
<>
   <DivaWebBoAdapter {...props} />
</>
);
}
Override the Recommendations/Related component​
To override the Recommendations/Related component, you can use the ChainPlayComponent property in the renderCustomization object.
This property allows you to pass a custom component to the Recommendations/Related screen.
const config = {
// ...other configuration
renderCustomization: {
ChainPlayComponent: (props) => React.ReactElement // CustomChainPlayComponent
},
};
Props​
There are several props provided by Diva and passed to your component that may be useful for you.
type Props = {
/**
* The metadata of the video that is currently playing/ending.
*/
videoMetadata: VideoMetadata | undefined;
/**
* Function to switch the video to the next one.
* @param videoId The ID of the video to switch to.
*/
switchVideo: (videoId: string) => void;
/**
* Information whether the component should be focused. Default: false
* Passed only on webtv platforms.
*/
focused?: boolean;
};
Focus Handling on WebTV platforms​
The component should not focus itself on render. Rather, the focus should be
based on the focused prop.
- When this prop becomes
true, the component should focus itself visually and make sure that it will be able to receive keydown events. - When this prop becomes
false, the component should only remove visual focus representation and make sure not to react to any events. Any change inactiveElementwill be handled by Diva.
Events handling​
If the component handles keydown events, it should stop the propagation of the event if the event was handled by the component. Events that do not result in any action by the component should be propagated to Diva. This allows handling the focus back to Diva in case of direction keys not being processed by the component.
Dynamic fallback to default component​
It is possible to fallback back to the default component provided by Diva
dynamically from your custom component. To do this, simply throw
CustomComponentRenderDefault error during the render phase.
import { CustomComponentRenderDefault } from '@deltatre-vxp/diva-webtv-sdk';
const MyCustomComponent = ({ focused }: Props) => {
const [dataError, setDataError] = useState(false);
useEffect(() => {
(async () => {
try {
// Fetch data
} catch (error) {
setDataError(true);
}
})();
}, [])
if (dataError) {
throw new CustomComponentRenderDefault();
}
return (
// Your component
)
};
Empty render​
Sometimes you might wish not to render anything. In such case, it is not
possible to just return null. The null render result is not detectable easily
by the parent component. Because of focus handling, Diva would still try to
focus this empty component. To avoid this, you can throw
CustomComponentRenderNothing error. This will be handled by Diva, nothing will
be rendered and the focus will not be sent to the component.
import { CustomComponentRenderNothing } from '@deltatre-vxp/diva-webtv-sdk';
const MyCustomComponent = ({ focused }: Props) => {
const data = useDataProvider();
if (data.length === 0) {
throw new CustomComponentRenderNothing();
}
return (
// Your component
)
};
Recommendations by Related​
Write the App() function to read the VideoMetadataMap as in the following example, and set the related property:
undefineddefault value if not set and recommendations not shown- This is the only mandatory value to set to show recommendations:
items: [
{
itemId: "<videoId>",
}
]
- These are the complete fields that can be set:
rowTitle: "<rowTitle>", // if empty, the default will be: diva_eop_recommendation_title
defaulImageFormat: "<imageFormat>", // "tile"
items: [
{
itemId: "<videoId>",
title: "<title>",
caption: "<caption>",
imageUrl: "<imageUrl>",
ccBadge: "<ccBadge>", //true or false, TV only
adBadge: "<adBadge>", //true or false, TV only
}
]
Customize the click behaviour​
The default behaviour once we click on the item is switch (play the clicked video).Â
If the integrators need to override the click behaviour they can set the callback on Diva config by adding:
config.onMediaListItemClick = (itemId: string): boolean => {
// custom logic
return false; // false means the default behaviour (switch) will be executed. On WebTV, the default behaviour is never executed.
}
Autoplay​
Write the App() function to read the VideoMetadataMap as in the following example, and set the endOfPlay properties:
"behaviour": {
"endOfPlay": {
"countdownToAutoplay":10000 //milliseconds, default 10000, 0 or negative means disabled
},
},
In the settings file, the general section must contain "timeToDisableAutoplay": 5400000, like in the following example:
{
"general": {
"timeToDisableAutoplay": 5400000, //milliseconds, default 0, 0 or negative means disabled
},
}
The autoplay will start if countdownToAutoplay has set. In this case the countdown will start.
countdownToAutoplaywill enable or disable the autoplay at the end of the milliseconds set ()timeToDisableAutoplaywill disable the autoplay at the end of the milliseconds set with no interactions
Working sample code (.tsx)​
import { AssetState, BoAdapterWebComponentProps, DivaWebBoAdapter, VideoMetadata } from "@deltatre-vxp/diva-sdk/diva-web-bo-adapter";
import "@deltatre-vxp/diva-sdk/diva-web-sdk/index.min.css";
import { useState } from "react";
const videoId = "<videoId>";
const libs = {
mux: "https://cdnjs.cloudflare.com/ajax/libs/mux.js/6.2.0/mux.min.js",
shaka: "https://ajax.googleapis.com/ajax/libs/shaka-player/4.16.15/shaka-player.compiled.js",
hlsJs: "https://cdn.jsdelivr.net/npm/hls.js@1.5.7",
googleIMA: false,
googleDAI: false,
googleCast: false,
threeJs: false
};
const config = {
videoId: videoId,
libs: libs,
settings: {
general: {
timeToDisableAutoplay: 5400000
}
}
};
function App() {
const [isVideoLive, setIsVideoLive] = useState(false);
const props: BoAdapterWebComponentProps = {
settingsUrl: "<settings URL>",
languageCode: "en-US",
languageDictionary: "en-US",
onDivaBoAdapterError: console.error,
config: config,
videoMetadataMap: async (vd: VideoMetadata) => {
vd.mediaLists = [
{
listId:"123466",
rowTitle:"diva_eop_related_title",
defaulImageFormat:"tile",
defaultItemLayout: "vertical",
defaultItemType: "related",
items: [
{
itemId: "12345678",
title: "Star Wars EP I - The Phantom Menace",
caption: "Mesa Jar Jar Binks",
imageUrl: "http://sw.image/ep1.jpg",
ccBadge: false,
adBadge: false,
},
{
itemId: "12345679"
title: "Star Wars EP II - Attack Of The Clones",
caption: "I don't like sand. It's coarse and rough and irritating and it gets everywhere",
imageUrl: "http://sw.image/ep2.jpg",
ccBadge: false,
adBadge: false,
},
{
itemId: "12345680"
title: "Star Wars EP III - Revenge Of The Sith",
caption: "I have brought peace, freedom, justice and security to my new Empire!",
imageUrl: "http://sw.image/ep3.jpg",
ccBadge: false,
adBadge: false,
}
]
},
];
vd.behavior = {
endOfPlay: {
countdownToAutoplay: 10000
}
}
return vd;
}
}
 return (
<>
   <DivaWebBoAdapter {...props} />
</>
);
}
Override the Recommendations/Related component​
To override the Recommendations/Related component, you can use the ChainPlayComponent property in the renderCustomization object.
This property allows you to pass a custom component to the Recommendations/Related screen.
const config = {
// ...other configuration
renderCustomization: {
ChainPlayComponent: (props) => React.ReactElement // CustomChainPlayComponent
},
};
Props​
There are several props provided by Diva and passed to your component that may be useful for you.
type Props = {
/**
* The metadata of the video that is currently playing/ending.
*/
videoMetadata: VideoMetadata | undefined;
/**
* Function to switch the video to the next one.
* @param videoId The ID of the video to switch to.
*/
switchVideo: (videoId: string) => void;
/**
* Information whether the component should be focused. Default: false
* Passed only on webtv platforms.
*/
focused?: boolean;
};
Focus Handling on WebTV platforms​
The component should not focus itself on render. Rather, the focus should be
based on the focused prop.
- When this prop becomes
true, the component should focus itself visually and make sure that it will be able to receive keydown events. - When this prop becomes
false, the component should only remove visual focus representation and make sure not to react to any events. Any change inactiveElementwill be handled by Diva.
Events handling​
If the component handles keydown events, it should stop the propagation of the event if the event was handled by the component. Events that do not result in any action by the component should be propagated to Diva. This allows handling the focus back to Diva in case of direction keys not being processed by the component.
Dynamic fallback to default component​
It is possible to fallback back to the default component provided by Diva
dynamically from your custom component. To do this, simply throw
CustomComponentRenderDefault error during the render phase.
import { CustomComponentRenderDefault } from '@deltatre-vxp/diva-webtv-sdk';
const MyCustomComponent = ({ focused }: Props) => {
const [dataError, setDataError] = useState(false);
useEffect(() => {
(async () => {
try {
// Fetch data
} catch (error) {
setDataError(true);
}
})();
}, [])
if (dataError) {
throw new CustomComponentRenderDefault();
}
return (
// Your component
)
};