diff --git a/CHANGELOG.md b/CHANGELOG.md index 29a0fce67..7ff7d2bda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### + +- Added a `enableTHEOlive` flag to `PlayerConfiguration` to enable play-out of THEOlive sources. + ## [8.9.0] - 24-11-29 ### Added diff --git a/example/src/App.tsx b/example/src/App.tsx index 17696e5d5..e2dbdc75e 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -56,6 +56,7 @@ const playerConfig: PlayerConfiguration = { skipBackwardInterval: 10, convertSkipToSeek: true, }, + enableTHEOlive: true, }; /** diff --git a/example/web/webpack.config.js b/example/web/webpack.config.js index 45592a5cb..b38e16288 100644 --- a/example/web/webpack.config.js +++ b/example/web/webpack.config.js @@ -30,6 +30,12 @@ const CopyWebpackPluginConfig = new CopyWebpackPlugin({ from: path.resolve(projectDirectory, './node_modules/theoplayer/THEOplayer.transmux.*').replace(/\\/g, '/'), to: `${libraryLocation}/[name][ext]`, }, + { + // Copy service worker + // THEOplayer will find them by setting `libraryLocation` in the playerConfiguration. + from: path.resolve(projectDirectory, './node_modules/theoplayer/theoplayer.sw.js').replace(/\\/g, '/'), + to: `${libraryLocation}/[name][ext]`, + }, { // Copy CSS files from: path.resolve(appDirectory, './web/public/*.css').replace(/\\/g, '/'), diff --git a/src/api/config/PlayerConfiguration.ts b/src/api/config/PlayerConfiguration.ts index 26cbd5eb3..499be2ab7 100644 --- a/src/api/config/PlayerConfiguration.ts +++ b/src/api/config/PlayerConfiguration.ts @@ -99,6 +99,13 @@ export interface PlayerConfiguration { * @defaultValue `false`. */ useMedia3?: boolean; + + /** + * Sets whether support for THEOlive sources should be enabled. + * + * @defaultValue `false`. + */ + enableTHEOlive?: boolean; } /** diff --git a/src/internal/THEOplayerView.web.tsx b/src/internal/THEOplayerView.web.tsx index 9dd386814..7412292b0 100644 --- a/src/internal/THEOplayerView.web.tsx +++ b/src/internal/THEOplayerView.web.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useRef } from 'react'; import type { THEOplayerViewProps } from 'react-native-theoplayer'; import { Player, ChromelessPlayer, PlayerConfiguration } from 'theoplayer'; import { THEOplayerWebAdapter } from './adapter/THEOplayerWebAdapter'; +import { registerServiceWorker, browserCanPlayHLSAndHasNoMSE } from './utils/ServiceWorkerUtils'; export function THEOplayerView(props: React.PropsWithChildren) { const { config, children } = props; @@ -9,6 +10,15 @@ export function THEOplayerView(props: React.PropsWithChildren(null); const container = useRef(null); + const preparePlayer = async (adapter: THEOplayerWebAdapter) => { + if (config?.enableTHEOlive == true && browserCanPlayHLSAndHasNoMSE()) { + await registerServiceWorker(props.config?.libraryLocation); + } + + // Notify the player is ready + props.onPlayerReady?.(adapter); + }; + useEffect(() => { // Create player inside container. if (container.current) { @@ -49,8 +59,8 @@ export function THEOplayerView(props: React.PropsWithChildren { + if ('serviceWorker' in navigator) { + try { + const serviceWorkerName = 'theoplayer.sw.js'; + const serviceWorkerPath = libraryLocation + ? (libraryLocation.endsWith('/') ? libraryLocation : `${libraryLocation}/`) + serviceWorkerName + : serviceWorkerName; + const serviceWorkerScope = libraryLocation ? (libraryLocation.endsWith('/') ? libraryLocation : `${libraryLocation}/`) : '/'; + + // unregister beforehand to solve an issue when doing a hard reload of the page causing the service worker to not intercept the manifests. + await maybeUnregisterServiceWorker(serviceWorkerPath); + await navigator.serviceWorker.register(serviceWorkerPath, { + scope: serviceWorkerScope, + }); + //console.log('Successfully registered server worker'); + } catch (error) { + console.error(`Service worker registration failed: ${error}`); + } + } +} + +export async function maybeUnregisterServiceWorker(serviceWorkerPath?: string): Promise { + if ('serviceWorker' in navigator) { + try { + const registration = await navigator.serviceWorker.getRegistration(serviceWorkerPath); + await registration?.unregister(); + } catch (error) { + console.error(`Service worker unregistration failed: ${error}`); + } + } +}