This guide attempts to help with the integration of MCLS Zoid Player component. In MCLS there are 3 ways of embedding a video event:
using the React Player component from @mycujoo/mcls-components
using an iframe for https://embed.mls.mycujoo.tv/player/<event-id>?autoplay=true&publicKey=<org-public-key>
using the zoid embed solution
The guide below will focus on the 3rd option.
Zoid is a powerful, open-source library that simplifies the process of building modular, cross-domain components and provides a secure, performant solution for communicating with iframes.
To facilitate communication between the parent window and the child iframe, Zoid uses the postMessage API provided by the browser.
The library provides a set of message handlers that listen for specific events or messages sent by the child iframe, and respond accordingly and also provides a set of APIs that can be used within the child iframe to communicate with the parent window. These APIs allow the child iframe to send messages and request data from the parent window, and receive responses in return.
There are 3 parts to the MCLS Zoid implementation
the hosted player app
the zoid child context definition
the zoid parent context definition
The player app is available at https://embed.mls.mycujoo.tv/player/<event-id>. This page will render the video player.
1. Child context definition
On the same page where the player is rendered we're also defining the Zoid child context. This is how we tell zoid what properties does the component have, what is the tag (the component identifier) and few other configuration options. The definition has the following shape:
const eventId = "<EVENT-ID>";
const host = "https://embed.mls.mycujoo.tv";
const url = `${host}/player/${eventId}`;
const tag = `embed-${eventId}`;
const ZoidComponent = zoid.create({
tag,
url,
dimensions: {
width: "100%",
height: "100%",
},
autoResize: {
height: true,
},
props: {
autoplay: {
type: "boolean",
queryParam: "autoplay",
},
debug: {
type: "boolean",
queryParam: "debug",
},
defaultVolume: {
type: "number",
},
eventId: {
type: "string",
},
publicKey: {
type: "string",
},
identityToken: {
type: "string",
},
primaryColor: {
type: "string",
},
secondaryColor: {
type: "string",
},
adUnit: {
type: "string",
},
adCustParams: {
type: "string",
},
showBackForwardButtons: {
type: "boolean",
},
showEventInfoButton: {
type: "boolean",
},
showLiveViewers: {
type: "boolean",
},
showSeekbar: {
type: "boolean",
},
showFullscreen: {
type: "boolean",
},
showQualitySelector: {
type: "boolean",
},
showVolume: {
type: "boolean",
},
showTimers: {
type: "boolean",
},
showChromecast: {
type: "boolean",
},
showPictureInPicture: {
type: "boolean",
},
enableAnnotations: {
type: "boolean",
},
analyticsType: {
type: "string",
},
analyticsAccount: {
type: "string",
},
analyticsUserId: {
type: "string",
},
startPosition: {
type: "number",
},
onTimeUpdate: {
type: "function",
},
onViewChange: {
type: "function",
},
annotations: {
type: "array",
},
customPlaylistUrl: {
type: "string",
},
seekTo: {
type: "number",
},
},
attributes: {
iframe: {
allow: `autoplay ${host}; fullscreen ${host}`,
allowfullscreen: "",
},
},
});
This is everything needed for the child component definition. At this point the app renders a video player and register a Zoid component.
2. Parent context definition
For the other half of the communication we need to define the parent Zoid context. This is already defined in https://embed.mls.mycujoo.tv/embed.js.
The un minified version of this script has the following shape:
// import zoid library
;((d, w, z) => {
if (!d || !w || !z) return
console.log('[Zoid parent] document.fullscreenEnabled', document.fullscreenEnabled)
d.querySelectorAll('div[data-embed-id]').forEach((e) => {
const id = e.dataset.embedId || ''
const host = 'https://embed.mls.mycujoo.tv'
const url = `${host}/player/${id}`
const tag = `embed-${id}`
const autoplay = e.dataset.autoplay === 'false' ? false : true
const publicKey = e.dataset.publicKey || ''
const identityToken = e.dataset.identityToken || ''
const debug = ['true', ''].includes(e.dataset.debug) || false
const defaultVolume = +e.dataset.defaultVolume || 0
const startPosition = e.dataset.startPosition || 0
const primaryColor = e.dataset.primaryColor || '#FFF'
const secondaryColor = e.dataset.secondaryColor || '#FFF'
const adUnit = e.dataset.adUnit || ''
const adCustParams = e.dataset.adCustParams || ''
const showBackForwardButtons = e.dataset.showBackForwardButtons === 'false' ? false : true
const showEventInfoButton = e.dataset.showEventInfoButton === 'false' ? false : true
const showLiveViewers = e.dataset.showLiveViewers === 'false' ? false : true
const showSeekbar = e.dataset.showSeekbar === 'false' ? false : true
const showFullscreen = e.dataset.showFullscreen === 'false' ? false : true
const showQualitySelector = e.dataset.showQualitySelector === 'false' ? false : true
const showVolume = e.dataset.showVolume === 'false' ? false : true
const showTimers = e.dataset.showTimers === 'false' ? false : true
const showChromecast = e.dataset.showChromecast === 'false' ? false : true
const showPictureInPicture = e.dataset.showPictureInPicture === 'false' ? false : true
const enableAnnotations = e.dataset.enableAnnotations === 'false' ? false : true
const analyticsType = e.dataset.analyticsType || ''
const analyticsAccount = e.dataset.analyticsAccount || ''
const analyticsUserId = e.dataset.analyticsUserId || ''
const customPlaylistUrl = e.dataset.customPlaylistUrl
const annotations = []
const seekTo = 0
const containerHeight = e.clientHeight || 315
e.style.lineHeight = 0
e.style.minHeight = 'inherit'
e.style.height = 'inherit'
const c = z.create({
tag,
url,
dimensions: {
width: '100%',
height: containerHeight,
},
attributes: {
iframe: {
allow: `autoplay ${host}; fullscreen ${host}`,
allowfullscreen: '',
},
},
})
debug &&
console.log('[Zoid parent] component created: ', {
autoplay,
debug,
defaultVolume,
eventId: id,
publicKey,
identityToken,
primaryColor,
secondaryColor,
adUnit,
adCustParams,
showEventInfoButton,
showLiveViewers,
showBackForwardButtons,
showSeekbar,
showFullscreen,
showQualitySelector,
showVolume,
showTimers,
showChromecast,
showPictureInPicture,
enableAnnotations,
analyticsAccount,
analyticsType,
analyticsUserId,
startPosition,
customPlaylistUrl,
annotations,
seekTo,
})
w.MLSEmbeds = w.MLSEmbeds || {}
w.MLSEmbeds[tag] = c
const onTimeUpdate = (currentTime, totalTime) => {
// example of implementation
const event = new CustomEvent('timeUpdate', {
detail: {
currentTime,
totalTime,
},
})
e.dispatchEvent(event)
}
const onViewChange = (view) => {
console.log(view)
}
c({
autoplay,
debug,
defaultVolume,
eventId: id,
publicKey,
identityToken,
primaryColor,
secondaryColor,
adUnit,
adCustParams,
showEventInfoButton,
showLiveViewers,
showBackForwardButtons,
showSeekbar,
showFullscreen,
showQualitySelector,
showVolume,
showTimers,
showChromecast,
showPictureInPicture,
enableAnnotations,
analyticsAccount,
analyticsType,
analyticsUserId,
startPosition,
onTimeUpdate,
onViewChange,
annotations,
seekTo,
customPlaylistUrl,
}).render(`div[data-embed-id='${id}']`)
})})(document, window, zoid)
The script is intended to work with multiple player instances on the same page. When loading this on your page the script will look up for divs with the attribute data-embed-id, extract all properties specified via data-* attributes and pass those to zoid.
At this point if the parent and child context definitions match and the zoid version on the child context matches the zoid version on the parent context, the communication should be functional.
3. Advanced scenarios
While the above parent context definition is convenient to use for static text properties, might not be as convenient for changing properties or callback properties like onTimeUpdate or onViewChange which predefined embed.js solution above have to use event listeners. When implementing the parent context definition one can make use of zoid's flexible architecture that can be adapted to integrate with different frontend frameworks like React, Angular or Vue.
Supported function props:
const onTimeUpdate = (
currentVideoProgress: number,
totalVideoDuration: number
) => {};
/**
* @param { PlayerView } activeView - The type of view rendered on the player.
*
* The possible values are:
* 'NOT_FOUND' - passed when the public key and event id combination is invalid or when the domain where the player is hosted is not valid for your organization
* 'GEOBLOCKED' - passed when the event geoblocking settings are not valid from the country the request was made
* 'AUTHORIZATION' - passed when the event is behind a paywall and the current user (identityToken) does not have access to the event
* 'CONCURRENCY_LIMIT' - passed when the number of simultaneus sessions for watching an event by a user (identityToken) has been reached
* 'UPCOMING' - passed when the event stream has not started yet
* 'VIDEO' - passed when displaying the video player
* 'LOADING' - passed when showing a spinner
*
* @returns {void}
*/
type PlayerView =
| "NOT_FOUND"
| "GEOBLOCKED"
| "AUTHORIZATION"
| "CONCURRENCY_LIMIT"
| "UPCOMING"
| "VIDEO"
| "LOADING";
const onViewChange = (activeView: PlayerView) => {};
The steps for using any of the two function props are:
Install and load Zoid Make sure the version of the library matches that on the client. In case it doesn't zoid will show an error with details of the versions missmatch.
import * as zoid from 'zoid/dist/zoid'
Create the zoid parent context
const eventId = "";
const containerHeight = 315;
const host = "https://embed.mls.mycujoo.tv";
const url = `${host}/player/${eventId}`;
const tag = `embed-${eventId}`;
const zoidComponent = zoid.create({
tag,
url,
dimensions: {
width: "100%",
height: containerHeight,
},
attributes: {
iframe: {
allow: `autoplay ${host}; fullscreen ${host}`,
allowfullscreen: "",
},
},
});
2. Trigger property updates using vanilla javascript
zoidComponent({
autoplay,
debug,
defaultVolume,
eventId: id,
publicKey,
identityToken,
primaryColor,
secondaryColor,
adUnit,
adCustParams,
showEventInfoButton,
showLiveViewers,
showBackForwardButtons,
showSeekbar,
showFullscreen,
showQualitySelector,
showVolume,
showTimers,
showChromecast,
showPictureInPicture,
enableAnnotations,
analyticsAccount,
analyticsType,
analyticsUserId,
startPosition,
onTimeUpdate,
onViewChange,
annotations,
seekTo,
customPlaylistUrl,
}).render(`div[data-embed-id='${id}']`);
Alternatively zoid includes driver definitions for React, Angular and Vue. The step from above is equivalent to the following:
React:
const PlayerComponent = zoidComponent.driver("react", {
React,
ReactDOM,
});
const ComponentInYourApp = (props) => {
const onTimeUpdate = useCallback((currentTime, totalTime) => {
console.log(currentTime, totalTime);
}, []);
const onViewChange = useCallback((activeView) => {
console.log(activeView);
}, []);
return (
<div>
<h1>MCLS Player</h1>
<PlayerComponent
{...props}
onTimeUpdate={onTimeUpdate}
onViewChange={onViewChange}
/>
</div>
);
};
Angular 2:
const PlayerComponentNg2 = MyLoginZoidComponent.driver(
"angular2",
ng.core
);
Fore more details please refer to following examples of Zoid parent context implementations: Angular Angular 2 React Vue Vue 3