@@ -95,12 +95,49 @@ export const findActiveSubtitle = (cues: SubtitleCue[], currentTime: number): Su
9595export const fetchSubtitleFile = async ( url : string ) : Promise < SubtitleCue [ ] > => {
9696 try {
9797 const response = await fetch ( url ) ;
98+
9899 if ( ! response . ok ) {
99100 throw new Error ( `Failed to fetch subtitle file: ${ response . status } ` ) ;
100101 }
101102
102103 const content = await response . text ( ) ;
103- return parseWebVTT ( content ) ;
104+
105+ // Check if this is an M3U8 playlist instead of a VTT file
106+ if ( content . trim ( ) . startsWith ( '#EXTM3U' ) ) {
107+ // Parse the M3U8 to find the actual VTT file URL
108+ const lines = content . split ( '\n' ) ;
109+ let vttUrl = null ;
110+
111+ for ( const line of lines ) {
112+ const trimmedLine = line . trim ( ) ;
113+ // Look for lines that end with .vtt and don't start with #
114+ if ( ! trimmedLine . startsWith ( '#' ) && trimmedLine . includes ( '.vtt' ) ) {
115+ vttUrl = trimmedLine ;
116+ break ;
117+ }
118+ }
119+
120+ if ( vttUrl ) {
121+ // Resolve relative URL if needed
122+ if ( vttUrl . startsWith ( '/' ) ) {
123+ const urlObj = new URL ( url ) ;
124+ vttUrl = `${ urlObj . protocol } //${ urlObj . host } ${ vttUrl } ` ;
125+ } else if ( ! vttUrl . startsWith ( 'http' ) ) {
126+ const baseUrl = url . substring ( 0 , url . lastIndexOf ( '/' ) + 1 ) ;
127+ vttUrl = baseUrl + vttUrl ;
128+ }
129+
130+ // Recursively fetch the actual VTT file
131+ return await fetchSubtitleFile ( vttUrl ) ;
132+ } else {
133+ console . warn ( 'No VTT URL found in M3U8 playlist' ) ;
134+ return [ ] ;
135+ }
136+ }
137+
138+ // Content is already VTT, parse it directly
139+ const cues = parseWebVTT ( content ) ;
140+ return cues ;
104141 } catch ( error ) {
105142 console . warn ( 'Failed to fetch subtitle file:' , error ) ;
106143 return [ ] ;
0 commit comments