Video Player Integration
Web Browser Integration

HOW TO

INTEGRATE IT IN WEB

Insert a script tag defining the desired fixture, the bookmaker source, and the end user's device ID (MAID):

<script 
src="https://genius-live-player-production.betstream.betgenius.com/widgetLoader?customerId=YOUR_CUSTOMER_ID&fixtureId=YOUR_FIXTURE_ID&deviceId=END_USER_DEVICE_ID">
</script>

Insert an element with defined id to attach the video player to it, should be the exact same one used as the script containerId parameter

<div id="YOUR_CONTAINER_ID" /> <!-- Defaults to geniusLive if a containerId is not set -->

Add an event listener in your website to listen to the player ready event and when this triggers call our API to retrieve the stream-url and token and hand this response back to our player. Everything else is taken care of

How does it work?

You embed a script tag in your site with the querystring parameters you are interested in passing to the player. Our player looks at the stream types available for the specific fixture you are trying to display and picks the most appropriate stream for your viewers device. Our script will then raise an event out to the containing window, with the necessary information to make a call to our API from your backend.

´Our API should never be called from the browser directly.´

The call to our backend, which is per fixture viewer session, retrieves a url, a token and any DRM information which you may need to play the fixture. You hand the response straight back to our player and it will play the fixture. Sensible HTTP error codes will be returned from our API around the status of the fixture and whether you have booked it.

And if there is no stream and I load the player?

If there is no fixture the player won't load unless you explicitly set controls to true.

Embed script tag in your application

We provide a UAT environment for you to test, with a continuous non-DRM and DRM'd fixture. Change the string uat to production in the URL to promote to the production environment.

https://genius-live-player-uat.betstream.betgenius.com/widgetLoader?customerId=YOUR_CUSTOMER_ID&fixtureId=YOUR_OR_OUR_FIXTURE_ID&deviceId=END_USER_DEVICE_ID&containerId=geniusLivePlayer&bufferLength=2&width=480px&height=270px

A walk through the code

You bind a listener to the window which listens to a "geniussportsmessagebus" event and when the event type is 'player_ready'. The event type that is dispatched onto the window object is a CustomEvent (MDN docs) (opens in a new tab). The event emitted is:

interface GeniusSportsEvent {
    detail: {
        type: string;
        body : {
            streamId: string;
            deliveryType: 'HLS' | 'DASH';
            delivery: string;
            geniusSportsFixtureId: number;
        }
    };
}

The call to the backend This usage example is an adaptation of our own implementation in our support tool. Our support tool (substituting for your website) calls to our support tool backend when the player fires the player_ready event indicating that there is a fixture to stream. Our support tool back end is a reverse proxy, which transparently authenticates and calls our Video-API (opens in a new tab). The method that needs to be called on the Genius Live API is a post to

/fixtures/${geniusSportsFixtureId}/live-streams/${streamId}/deliveries/${deliveryType}/${delivery}

where the fixtureId, streamId, deliveryType and deliveryId are all contained in the GeniusSportsEvent. This event can occur multiple times for a single fixture, in the case wheer multi-CDN is available and the primary CDN fails it can be expected that our player will switch over by emitting the player_ready event again asking for a different delivery id.

window.addEventListener('geniussportsmessagebus', async (event: Event) => {
    let typedEvent = event as unknown as GeniusSportsEvent
    if (typedEvent.detail.type === 'player_ready') {
      const playerReadyEvent = typedEvent as PlayerReadyEvent
      const deliveryType = playerReadyEvent.detail.body.deliveryType
      const streamId = playerReadyEvent.detail.body.streamId
      const deliveryId = playerReadyEvent.detail.body.deliveryId
      const geniusSportsFixtureId = playerReadyEvent.detail.body.geniusSportsFixtureId
      const dataToPost = {
        endUserSessionId: document.cookie, //user session id
        region: playerReadyEvent.detail.body.region, //region
        device: playerReadyEvent.detail.body.device, //device
      }
 
 
      postData(
        `${domain}/v3/fixtures/${geniusSportsFixtureId}/live-streams/${streamId}/deliveries/${deliveryType}/${deliveryId}`,
        dataToPost
      )
        .then((data) => {
          console.log('Token data arrived', JSON.stringify(data))
          // @ts-ignore
          GeniusLivePlayer.player.start(data)
        })
        .catch((error) => {
          console.error(error)
        })
    }
 
    if (typedEvent.detail.type === 'player_not_ready') {
      const playerNotReadyEvent = typedEvent as PlayerReadyEvent
      const errorsArray = playerNotReadyEvent.detail.body.error
      // Add your custom handling here
    }
  })
 
  async function postData(url = '', data = { }) {
    // Default options are marked with *
    const response = await fetch(url, {
        method: 'POST', // *GET, POST, PUT, DELETE, etc.
        mode: 'cors', // no-cors, *cors, same-origin
        cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
        credentials: 'include', // include, *same-origin, omit
        headers: {
          'Content-Type': 'application/json',
        },
        redirect: 'follow', // manual, *follow, error
        referrerPolicy: 'no-referrer',
        body: JSON.stringify(data),
    })
 
    return response.json()
}

Close video player

ⓘ Information!
Close the video player to prevent multiple video players running in the background and continue downloading video segments.

If you have to close the video player, you need to call the close() method in Genius Live Player to close it, and you must also remove the event listener created when you start the video player. You can create or define a function to pass as parameter to addEventListener and removeEventListener, see following example:

...
// create function
function handleEvent(myEvent: Event) {
  let typedEvent = event as unknown as GeniusSportsEvent
  if (typedEvent.detail.type === 'player_ready') {
    const playerReadyEvent = typedEvent as PlayerReadyEvent
    const deliveryType = playerReadyEvent.detail.body.deliveryType
    const streamId = playerReadyEvent.detail.body.streamId
    const deliveryId = playerReadyEvent.detail.body.deliveryId
    const geniusSportsFixtureId = playerReadyEvent.detail.body.geniusSportsFixtureId
    const dataToPost = {
      endUserSessionId: document.cookie, //user session id
      region: playerReadyEvent.detail.body.region, //user region
      device: playerReadyEvent.detail.body.device, //user device
    }
 
    postData(
      `${domain}/v3/fixtures/${geniusSportsFixtureId}/live-streams/${streamId}/deliveries/${deliveryType}/${deliveryId}`,
      dataToPost
    ).then((data) => {
        console.log('Token data arrived', JSON.stringify(data))
        // @ts-ignore
        GeniusLivePlayer.player.start(data)
    }).catch((error) => {
      console.error(error)
    })
  }
}
...
// Change addEventListener using function
window.addEventListener('geniussportsmessagebus', handleEvent)
...
...
// remove event listener
window.removeEventListener('geniussportsmessagebus', handleEvent)
// close video player
// @ts-ignore
GeniusLivePlayer.player.close()
...

Video player integration example

Please refer to this example source code in our github repository (opens in a new tab)

Available QUERY PARAMS

Required

  • customerId: Genius Sports customer Id

  • fixtureId: Event fixture Id

  • deviceId: The end user's Mobile Advertising ID (MAID). This is the GAID for Android or the IDFA for iOS.

Optional

  • containerId: value with the div element Id that will contain the Genius Live Player component. If not provided, this will default to 'geniusLive'.

  • controlsEnabled: set this parameter to 'true' to activate video control, in case controlsEnabled is set to 'false', the control bar will always be hidden. Default value: 'true'.

  • allowFullScreen: set this parameter to 'true' to allow full screen, default value: 'true'.

  • audioEnabled: set this parameter to 'true' to enable audio, you could disable audio manually later, if you set this parameter to 'false' you could enable audio manually later. Default value: 'true'.

  • bufferLength: set the buffer length of the player (e.g 4). Default value: 2.

  • width: width of the player in pixels (e.g 500px), default value: '600px'.

  • height: height of the player in pixels (e.g 200px). Default value: '300px'.

  • autoplayEnabled: set autoplayEnabled to 'true' to automatically play the content as soon as possible without manual action. Default value: 'false'.

  • culture: Set the player's culture (e.g., 'en-US') to display the BetVision overlay in the desired language. If not specified, the default is the user's browser language (using navigator.language (opens in a new tab)).

  • gpp: The standardized, encoded privacy string for each end user that captures the user's privacy preferences and consent choices.

  • userId: The end user's unique persistent user identifier.

ⓘ Information!
Browsers are restricting their autoplay capabilities, for that reason you need to pay attention when you want to send autoplayEnabled in 'true' and audioEnabled in 'true', this combination could cause some browsers to be unable to play the video content and it should be necessary to play it by manual action. To automatically play content without problems in some browsers you could send audioEnabled in 'false'.

See the following example with all of the available optional parameters set:

<script
  src="https://genius-live-player-production.betstream.betgenius.com/widgetLoader?customerId=YOUR_CUSTOMER_ID&fixtureId=YOUR_FIXTURE_ID&deviceId=END_USER_DEVICE_ID&userId=END_USER_USER_ID
  &containerId=geniusLive&controlsEnabled=true&allowFullScreen=true&audioEnabled=false&bufferLength=2&width=600px&height=300px&autoplayEnabled=true">
</script>

Combinations of player options

You can select from different player options based on what you need from the streaming: Controlls Enabled, Audio Enabled, Auto Play Enabled, Allow Full Screen. From the options we can identify all the possible combinations:

ScenariocontrolsEnabledaudioEnabledautoplayEnabledallowFullScreen
1truetruetruetrue
2truetruetruefalse
3truetruefalsetrue
4truetruefalsefalse
5truefalsetruetrue
6truefalsetruefalse
7truefalsefalsetrue
8truefalsefalsefalse
9falsetruetruetrue
10falsetruetruefalse
11falsetruefalsetrue
12falsetruefalsefalse
13falsefalsetruetrue
14falsefalsetruefalse
15falsefalsefalsetrue
16falsefalsefalsefalse

From all the possible combinations it is recommended always check that the controlsEnabled parameter has the true value to give the possibility to make changes to the streaming:

  • Audio: Enable / Disable audio at any time we need to
  • Video: Start / Stop the video at any time we need to
  • Full screen: Maximize / Minimize the video streaming at any time we need to

If you set the controlsEnabled parameter to false you cannot make any change on the player options and only can see the streaming if the autoplayEnabled parameter was set in true, we can identify the following scenarios:

  • In scenarios 9 and 10, the streaming starts automatically with audio and you cannot make changes, also in scenario 9 you cannot maximize the streaming due to player Controls being disabled
  • In scenarios 13 and 14, the streaming starts automatically without audio and you cannot make changes, also in scenario 13 you cannot maximize the streaming due to the player Controls being disabled
  • In scenarios 11, 12, 15,16 you will not be able to reproduce the streaming or modify it in any way, you will only get an empty black box

The scenarios that allow playing the streaming and making changes for the different player options are 1 to 8 The scenarios that play the streaming and don't allow making changes for the different player options are 9-10 and 13-14 The scenarios that cannot play the streaming and don't allow making changes for the different player options are 11-12 and 15-16

ⓘ Information!
Each customer must create their own backend implementation for the full video player flow to work. The following image shows how the video player works and the "Customer backend" represents the customer implementation. if you don't have this implementation, the video player will not be able to work.

player-image

Integrating in Safari iOS

Fullscreen support

While the Fullscreen API (MDN docs) (opens in a new tab) is not currently supported in Safari iOS, we've come up with an alternative solution to achieve similar behavior using CSS. By using Screenfull (opens in a new tab) library, we can detect browser support and dynamically add a [data-fullscreen-enabled] attribute to the HTML elements.

Here's the implementation:

/* This code is already in the codebase so you don't need to implement it */
.video-container.full-screen[data-fullscreen-enabled=false] {
  position: fixed;
  width: 100%;
  height: 100%;
  inset: 0;
  z-index: 10;
}

Caveat

When using the z-index property to control the stacking order of elements, it's important to be aware of its behavior within the context of parent and child elements. Specifically, the z-index of parent containers can impact the z-index of their children. To ensure the proper functioning of the z-index for the video player, we need to take the following considerations into account:

  • Avoid conflicting z-index in parent containers: To ensure that the z-index property works as intended for the video player, it is essential to check that none of its parent containers have a specific z-index value. This is because z-index values in parents take precedence over their children. If any parent containers have a defined z-index, it might interfere with the video player's stacking order.
  • Set higher z-index for the video player: To guarantee that the video player appears above other elements with z-index, we need to explicitly set a higher z-index for the video player itself. By doing so, we can ensure that it remains visually on top of other elements with z-index defined in the HTML.

Implementation

To ensure the video player has the highest z-index value and appears above other elements, you can use the following CSS selector:

/* Example CSS selector to set higher z-index for the video player */
.video-container.full-screen[data-fullscreen-enabled=false] {
  z-index: 1000; /* Adjust the value as needed */
}

Fullscreen support in iframe implementation (iOS safari)

When embedding the Genius Live Player using an iframe, fullscreen behavior must be managed by the parent application.

To enable fullscreen support, follow these steps:

  1. Forward Fullscreen Events from the Player Listen for the geniussportsmessagebus event in the iframe and forward relevant fullscreen toggle messages to the parent window using postMessage.
  window.addEventListener('geniussportsmessagebus', (event) => {
    const eventMapped = {
      type: event.detail.type,
      body: event.detail.body,
    };
    window?.parent?.postMessage(eventMapped, WebsiteDomain); // Replace WebsiteDomain with your actual domain
  });
  1. Apply Fullscreen Styling Based on Messages In the parent application, listen for the toggleFullscreen message and apply or remove a fullscreen class on the iframe element.
  .full-screen {
    position: fixed;
    width: 100%;
    height: 100%;
    inset: 0;
    z-index: 10; /* Adjust as needed based on your app's layout */
  }
  window.addEventListener('message', (event) => {
    if (event.origin === 'iframe_domain') { // Replace with your iframe's origin
      if (event.data.type === 'toggleFullscreen') {
        const iframeElement = document.getElementById('video-iframe'); // Update with your iframe's actual ID
        iframeElement?.classList.toggle('full-screen');
      }
    }
  });

Notes

  • Make sure to validate the origin (event.origin) to avoid security issues.

  • Adjust the z-index in .full-screen as needed depending on your UI stack.

  • Replace placeholder values like WebsiteDomain and iframe_domain with your actual domains.