@@ -21,11 +21,13 @@ import {
2121 IconRewindForward10 ,
2222} from '@tabler/icons-react' ;
2323import Hls from 'hls.js' ;
24+ import * as Sentry from '@sentry/nextjs' ;
2425import { useCallback , useEffect , useRef , useState } from 'react' ;
2526
2627interface Props {
2728 streamId : string ;
2829 isLive : boolean ;
30+ logPlayerErrors ?: boolean ;
2931}
3032
3133const HLS_MIME = 'application/vnd.apple.mpegurl' ;
@@ -78,7 +80,7 @@ function logEvent<T>(event: string, action?: (t: T) => void): (t: T) => void {
7880 } ;
7981}
8082
81- function StreamPlayerInner ( { streamId, isLive } : Props ) {
83+ function StreamPlayerInner ( { streamId, isLive, logPlayerErrors } : Props ) {
8284 const audio = useRef < HTMLAudioElement > ( null ) ;
8385 const [ isPaused , setIsPaused ] = useState ( true ) ;
8486 const [ loading , setLoading ] = useState ( true ) ;
@@ -104,6 +106,24 @@ function StreamPlayerInner({ streamId, isLive }: Props) {
104106 import . meta. url
105107 ) . toString ( ) ,
106108 } ) ;
109+ hls . on ( Hls . Events . ERROR , ( _ , data ) => {
110+ if ( data . fatal ) {
111+ if ( logPlayerErrors ) Sentry . captureException ( data ) ;
112+
113+ switch ( data . type ) {
114+ case Hls . ErrorTypes . MEDIA_ERROR :
115+ console . error ( 'attempting to recover from fatal media error' ) ;
116+ hls . recoverMediaError ( ) ;
117+ break ;
118+ case Hls . ErrorTypes . NETWORK_ERROR :
119+ console . error ( 'fatal network error occured' ) ;
120+ break ;
121+ default :
122+ console . error ( 'unrecoverable error occurred' ) ;
123+ hls . destroy ( ) ;
124+ }
125+ }
126+ } ) ;
107127 hls . loadSource ( streamUrl ) ;
108128 hls . attachMedia ( audio . current ) ;
109129 return ( ) => hls . destroy ( ) ;
0 commit comments