diff --git a/package.json b/package.json index 2e610ca..9a0cadd 100644 --- a/package.json +++ b/package.json @@ -1,68 +1,68 @@ { - "name": "react-hls-video-player", - "version": "3.2.3", - "description": "A simple and easy to use react component for playing an hls live stream", - "main": "./dist/index.js", - "types": "./dist", - "scripts": { - "start": "webpack serve --config webpack/webpack.dev.js", - "test": "jest", - "build": "tsc", - "prepublishOnly": "npm run build", - "prepare": "install-peers" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/cgilly2fast/react-hls.git" - }, - "keywords": [ - "hls", - "rtmp", - "react", - "component", - "video player" - ], - "author": "Colby Gilbert", - "license": "MIT", - "bugs": { - "url": "https://github.com/cgilly2fast/react-hls/issues" - }, - "homepage": "https://github.com/cgilly2fast/react-hls#README", - "peerDependencies": { - "react": "^18.3.1", - "react-dom": "^18.3.1" - }, - "dependencies": { - "hls.js": "^1.5.15" - }, - "devDependencies": { - "@testing-library/dom": "^10.4.0", - "@testing-library/jest-dom": "^6.5.0", - "@testing-library/react": "^16.0.1", - "@types/jest": "^29.5.12", - "@types/react": "^18.3.4", - "@types/react-dom": "^18.3.0", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", - "eslint": "^8.0.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jsx-a11y": "^6.9.0", - "eslint-plugin-react": "^7.20.3", - "eslint-plugin-react-hooks": "^4.0.8", - "eventemitter3": "^5.0.1", - "html-webpack-plugin": "^5.3.1", - "install-peers-cli": "^2.2.0", - "jest": "^29.7.0", - "jest-environment-jsdom": "^29.7.0", - "prettier": "^3.3.3", - "react-test-renderer": "^18.3.1", - "ts-jest": "^29.2.5", - "ts-loader": "^9.5.1", - "typescript": "^5.5.4", - "webpack": "^5.26.3", - "webpack-cli": "^5.1.4", - "webpack-dev-server": "^5.0.4", - "webpack-merge": "^6.0.1" - } + "name": "@mirlo/react-hls-player", + "version": "3.3.0", + "description": "A simple and easy to use react component for playing an hls live stream", + "main": "./dist/index.js", + "types": "./dist", + "scripts": { + "start": "webpack serve --config webpack/webpack.dev.js", + "test": "jest", + "build": "tsc", + "prepublishOnly": "npm run build", + "prepare": "install-peers" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/funmusicplace/react-hls.git" + }, + "keywords": [ + "hls", + "rtmp", + "react", + "component", + "video player" + ], + "author": "Colby Gilbert", + "license": "MIT", + "bugs": { + "url": "https://github.com/funmusicplace/react-hls/issues" + }, + "homepage": "https://github.com/funmusicplace/react-hls#README", + "peerDependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "dependencies": { + "hls.js": "^1.5.15" + }, + "devDependencies": { + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.5.0", + "@testing-library/react": "^16.0.1", + "@types/jest": "^29.5.12", + "@types/react": "^18.3.4", + "@types/react-dom": "^18.3.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "eslint": "^8.0.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jsx-a11y": "^6.9.0", + "eslint-plugin-react": "^7.20.3", + "eslint-plugin-react-hooks": "^4.0.8", + "eventemitter3": "^5.0.1", + "html-webpack-plugin": "^5.3.1", + "install-peers-cli": "^2.2.0", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "prettier": "^3.3.3", + "react-test-renderer": "^18.3.1", + "ts-jest": "^29.2.5", + "ts-loader": "^9.5.1", + "typescript": "^5.5.4", + "webpack": "^5.26.3", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^5.0.4", + "webpack-merge": "^6.0.1" + } } diff --git a/src/components/ReactHlsPlayer.tsx b/src/components/ReactHlsPlayer.tsx index 6d771dd..c8c651c 100644 --- a/src/components/ReactHlsPlayer.tsx +++ b/src/components/ReactHlsPlayer.tsx @@ -1,112 +1,117 @@ -import React, { useEffect, RefObject } from 'react' -import Hls, { HlsConfig } from 'hls.js' +import React, { useEffect, RefObject } from 'react'; +import Hls, { HlsConfig, ErrorData } from 'hls.js'; declare global { - interface Window { - Hls: typeof Hls - } + interface Window { + Hls: typeof Hls; + } } -export interface ReactHlsPlayerProps extends React.VideoHTMLAttributes { - hlsConfig?: Partial - playerRef?: RefObject - getHLSInstance?: (hls: Hls) => void - src: string +export interface ReactHlsPlayerProps + extends React.VideoHTMLAttributes { + hlsConfig?: Partial; + playerRef?: RefObject; + getHLSInstance?: (hls: Hls) => void; + src: string; + onError?: (event: unknown, data?: ErrorData) => void; } const ReactHlsPlayer: React.FC = ({ - hlsConfig, - playerRef, - getHLSInstance, - src, - autoPlay, - ...props + hlsConfig, + playerRef, + getHLSInstance, + src, + autoPlay, + onError, + ...props }) => { - const internalRef = playerRef || React.useRef(null) - - useEffect(() => { - let hls: Hls | null - - function initPlayer() { - if (hls != null) { - hls.destroy() - } - - const newHls = new Hls({ - enableWorker: false, - ...hlsConfig, - }) - - newHls.on(Hls.Events.MEDIA_ATTACHED, () => { - newHls.loadSource(src) - }) - - newHls.on(Hls.Events.MANIFEST_PARSED, () => { - if (!autoPlay) { - return - } - - try { - internalRef?.current?.play() - } catch (error) { - console.warn('Play is not supported in this environment', error) - } - }) - - newHls.on(Hls.Events.ERROR, (event, data) => { - if (!data.fatal) { - return - } - console.warn(`HLS.js Error: ${data.type} - ${data.details}`) - switch (data.type) { - case Hls.ErrorTypes.NETWORK_ERROR: - newHls.startLoad() - break - case Hls.ErrorTypes.MEDIA_ERROR: - newHls.recoverMediaError() - break - default: - initPlayer() - break - } - }) - - if (internalRef.current) { - newHls.attachMedia(internalRef.current) - } - - hls = newHls - getHLSInstance?.(newHls) + const internalRef = playerRef || React.useRef(null); + + useEffect(() => { + let hls: Hls | null; + + function initPlayer() { + if (hls != null) { + hls.destroy(); + } + + const newHls = new Hls({ + enableWorker: false, + ...hlsConfig, + }); + + newHls.on(Hls.Events.MEDIA_ATTACHED, () => { + newHls.loadSource(src); + }); + + newHls.on(Hls.Events.MANIFEST_PARSED, () => { + if (!autoPlay) { + return; } - if (Hls.isSupported()) { - initPlayer() - } else { - console.warn('HLS is not supported in this browser') + try { + internalRef?.current?.play(); + } catch (error) { + console.warn('Play is not supported in this environment', error); } + }); - return () => { - if (hls != null) { - hls.destroy() - } + newHls.on(Hls.Events.ERROR, (event, data) => { + onError && onError(event, data); + + if (!data.fatal) { + return; + } + console.warn(`HLS.js Error: ${data.type} - ${data.details}`); + switch (data.type) { + case Hls.ErrorTypes.NETWORK_ERROR: + newHls.startLoad(); + break; + case Hls.ErrorTypes.MEDIA_ERROR: + newHls.recoverMediaError(); + break; + default: + initPlayer(); + break; } - }, [autoPlay, hlsConfig, playerRef, getHLSInstance, src]) - - if (!Hls.isSupported()) { - return ( - - ) + }); + + if (internalRef.current) { + newHls.attachMedia(internalRef.current); + } + + hls = newHls; + getHLSInstance?.(newHls); } - return