diff --git a/CHANGELOG.md b/CHANGELOG.md index 29a0fce67..6e0b82acc 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). +## [8.9.1] - 24-12-04 + +### Added + +- Added a `enableTHEOlive` flag to `PlayerConfiguration` to enable play-out of THEOlive sources. + ## [8.9.0] - 24-11-29 ### Added diff --git a/android/build.gradle b/android/build.gradle index e9e176e2b..5916ccde7 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -32,7 +32,7 @@ def enabledTHEOads = safeExtGet("THEOplayer_extensionTHEOads", 'false').toBoolea def enabledAds = enabledGoogleIMA || enabledGoogleDAI || enabledTHEOads def enabledCast = safeExtGet("THEOplayer_extensionCast", 'false').toBoolean() def enabledMediaSession = safeExtGet("THEOplayer_extensionMediaSession", 'true').toBoolean() -def enabledMedia3 = safeExtGet("THEOplayer_extensionMedia3", 'true').toBoolean() +def enabledMedia3 = safeExtGet("THEOplayer_extensionMedia3", 'false').toBoolean() android { compileSdk safeExtGet('THEOplayer_compileSdkVersion', 34) diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 809a54c30..3b04ae612 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -5855,9 +5855,10 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", 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/package-lock.json b/package-lock.json index 149a7d1df..9a7a1b04d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "react-native-theoplayer", - "version": "8.9.0", + "version": "8.9.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "react-native-theoplayer", - "version": "8.9.0", + "version": "8.9.1", "license": "SEE LICENSE AT https://www.theoplayer.com/terms", "dependencies": { "buffer": "^6.0.3" diff --git a/package.json b/package.json index aa8bebf45..6635a9ca2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-theoplayer", - "version": "8.9.0", + "version": "8.9.1", "description": "A THEOplayer video component for react-native.", "main": "lib/commonjs/index", "module": "lib/module/index", 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}`); + } + } +}