@@ -9,24 +9,38 @@ function processM3U8Content(content, mediaUrl, origin, headers) {
99 const uriMatch = line . match ( / ( U R I = ) ( [ " ' ] ) (?< uri > .* ?) \2/ ) ;
1010 if ( uriMatch ) {
1111 const [ fullMatch , prefix , quote ] = uriMatch ;
12- console . log ( fullMatch , prefix , quote ) ;
12+ try {
13+ const resolvedUrl = new URL ( uriMatch . groups . uri , mediaUrl ) . toString ( ) ;
14+ const proxyUrl = `${ origin } /proxy?url=${ encodeURIComponent ( resolvedUrl ) } ${ _headers } ` ;
15+ return line . replace ( fullMatch , `${ prefix } ${ quote } ${ proxyUrl } ${ quote } ` ) ;
16+ } catch ( error ) {
17+ console . error ( 'Error processing URI:' , uriMatch . groups . uri , error ) ;
18+ return line ;
19+ }
20+ }
1321
14- const resolvedUrl = new URL ( uriMatch . groups . uri , mediaUrl ) . toString ( ) ;
15- const proxyUrl = `${ origin } /proxy?url=${ encodeURIComponent ( resolvedUrl ) } ${ _headers } ` ;
16- return line . replace ( fullMatch , `${ prefix } ${ quote } ${ proxyUrl } ${ quote } ` ) ;
22+ if ( line . startsWith ( '#EXT-X-STREAM-INF' ) ) {
23+ return line ;
1724 }
1825
1926 if ( ! line . startsWith ( '#' ) && line . trim ( ) ) {
20- const resolvedUrl = new URL ( line . trim ( ) , mediaUrl ) . toString ( ) ;
21- const proxyUrl = `${ origin } /proxy?url=${ encodeURIComponent ( resolvedUrl ) } ${ _headers } ` ;
22- return line . replace ( line . trim ( ) , proxyUrl ) ;
27+ try {
28+ const resolvedUrl = new URL ( line . trim ( ) , mediaUrl ) . toString ( ) ;
29+ const proxyUrl = `${ origin } /proxy?url=${ encodeURIComponent ( resolvedUrl ) } ${ _headers } ` ;
30+ return proxyUrl ;
31+ } catch ( error ) {
32+ console . error ( 'Error processing URL:' , line . trim ( ) , error ) ;
33+ return line ;
34+ }
2335 }
2436
2537 return line ;
2638 } )
2739 . join ( '\n' ) ;
2840}
41+
2942async function proxy ( request ) {
43+ // console.log(`Processing ${request.method} request for: ${request.url}`);
3044 if ( request . method === 'OPTIONS' ) {
3145 return new Response ( null , {
3246 status : 204 ,
@@ -53,41 +67,84 @@ async function proxy(request) {
5367 if ( rangeHeader ) {
5468 fetchHeaders [ 'Range' ] = rangeHeader ;
5569 }
70+
5671 const response = await fetch ( mediaUrl , {
5772 headers : fetchHeaders ,
5873 } ) ;
5974
6075 if ( ! response . ok ) {
6176 throw new Error ( `HTTP error! status: ${ response . status } ` ) ;
6277 }
78+
6379 const cleanHeaders = Object . fromEntries (
6480 Array . from ( response . headers . entries ( ) ) . filter ( ( [ key ] , i , arr ) => arr . findIndex ( ( [ k ] ) => k . toLowerCase ( ) === key . toLowerCase ( ) ) === i )
6581 ) ;
82+ delete cleanHeaders [ 'Access-Control-Allow-Origin' ] ;
83+ delete cleanHeaders [ 'access-control-allow-origin' ] ;
84+ const responseHeaders = {
85+ ...cleanHeaders ,
86+ 'Access-Control-Allow-Origin' : '*' ,
87+ 'Access-Control-Expose-Headers' : Object . keys ( cleanHeaders ) . join ( ', ' ) ,
88+ } ;
89+
90+ const contentType = response . headers . get ( 'Content-Type' ) || '' ;
91+
92+ const urlIndication = mediaUrl . toLowerCase ( ) . includes ( '.m3u8' ) || mediaUrl . toLowerCase ( ) . includes ( '/playlist' ) ;
6693
6794 let responseContent = await response . text ( ) ;
68- if ( ! responseContent . trimStart ( ) . startsWith ( '#EXTM3U' ) ) {
95+ const contentLooksLikeM3U8 = responseContent . trimStart ( ) . startsWith ( '#EXTM3U' ) ;
96+
97+ const isM3U8 =
98+ contentLooksLikeM3U8 ||
99+ contentType . includes ( 'application/vnd.apple.mpegurl' ) ||
100+ contentType . includes ( 'application/x-mpegurl' ) ||
101+ contentType . includes ( 'audio/mpegurl' ) ||
102+ contentType . includes ( 'audio/x-mpegurl' ) ;
103+
104+ if ( isM3U8 ) {
105+ responseContent = processM3U8Content ( responseContent , mediaUrl , origin , decodedHeaders ) ;
106+
107+ responseHeaders [ 'Content-Type' ] = 'application/vnd.apple.mpegurl' ;
69108 return new Response ( responseContent , {
70109 status : response . status ,
71- headers : {
72- ...cleanHeaders ,
73- 'Access-Control-Allow-Origin' : '*' ,
74- } ,
110+ headers : responseHeaders ,
75111 } ) ;
76112 }
77- responseContent = processM3U8Content ( responseContent , mediaUrl , origin , decodedHeaders ) ;
113+
114+ if ( ! contentLooksLikeM3U8 && responseContent . length > 0 ) {
115+ const hasManyNonPrintable =
116+ responseContent . split ( '' ) . filter ( ( char ) => char . charCodeAt ( 0 ) < 32 && char !== '\n' && char !== '\r' && char !== '\t' ) . length >
117+ responseContent . length * 0.1 ;
118+
119+ if (
120+ hasManyNonPrintable ||
121+ contentType . includes ( 'video/' ) ||
122+ contentType . includes ( 'audio/' ) ||
123+ contentType . includes ( 'image/' ) ||
124+ contentType . includes ( 'application/octet-stream' )
125+ ) {
126+ const binaryResponse = await fetch ( mediaUrl , {
127+ headers : fetchHeaders ,
128+ } ) ;
129+ const arrayBuffer = await binaryResponse . arrayBuffer ( ) ;
130+ return new Response ( arrayBuffer , {
131+ status : binaryResponse . status ,
132+ headers : responseHeaders ,
133+ } ) ;
134+ }
135+ }
78136
79137 return new Response ( responseContent , {
80- headers : {
81- ...cleanHeaders ,
82- 'Access-Control-Allow-Origin' : '*' ,
83- } ,
138+ status : response . status ,
139+ headers : responseHeaders ,
84140 } ) ;
85141 } catch ( error ) {
86142 console . error ( 'Error in proxy:' , error ) ;
87143 return new Response ( `Proxy error: ${ error . message } ` , {
88144 status : 500 ,
89145 headers : {
90146 'Access-Control-Allow-Origin' : '*' ,
147+ 'Content-Type' : 'text/plain' ,
91148 } ,
92149 } ) ;
93150 }
0 commit comments