Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 66 additions & 66 deletions package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
197 changes: 101 additions & 96 deletions src/components/ReactHlsPlayer.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLVideoElement> {
hlsConfig?: Partial<HlsConfig>
playerRef?: RefObject<HTMLVideoElement>
getHLSInstance?: (hls: Hls) => void
src: string
export interface ReactHlsPlayerProps
extends React.VideoHTMLAttributes<HTMLVideoElement> {
hlsConfig?: Partial<HlsConfig>;
playerRef?: RefObject<HTMLVideoElement>;
getHLSInstance?: (hls: Hls) => void;
src: string;
onError?: (event: unknown, data?: ErrorData) => void;
}

const ReactHlsPlayer: React.FC<ReactHlsPlayerProps> = ({
hlsConfig,
playerRef,
getHLSInstance,
src,
autoPlay,
...props
hlsConfig,
playerRef,
getHLSInstance,
src,
autoPlay,
onError,
...props
}) => {
const internalRef = playerRef || React.useRef<HTMLVideoElement>(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<HTMLVideoElement>(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 (
<video
ref={internalRef}
src={src}
autoPlay={autoPlay}
data-testid="react-hls-player-fallback"
{...props}
>
Your browser does not support the video tag.
</video>
)
});

if (internalRef.current) {
newHls.attachMedia(internalRef.current);
}

hls = newHls;
getHLSInstance?.(newHls);
}

return <video ref={internalRef} data-testid="react-hls-player" {...props} />
}
if (Hls.isSupported()) {
initPlayer();
} else {
console.warn('HLS is not supported in this browser');
}

export default ReactHlsPlayer
return () => {
if (hls != null) {
hls.destroy();
}
};
}, [autoPlay, hlsConfig, playerRef, getHLSInstance, src]);

if (!Hls.isSupported()) {
return (
<video
ref={internalRef}
src={src}
autoPlay={autoPlay}
data-testid="react-hls-player-fallback"
{...props}
>
Your browser does not support the video tag.
</video>
);
}

return <video ref={internalRef} data-testid="react-hls-player" {...props} />;
};

export default ReactHlsPlayer;