@@ -383,6 +383,7 @@ <h3 class="font-bold mb-2">Tailscale VPN</h3>
383383 < h2 class ="text-xl font-bold "> Stream Preview</ h2 >
384384 < div class ="space-x-2 ">
385385 < button id ="check-hls " class ="btn btn-blue "> Check HLS</ button >
386+ < button id ="debug-hls " class ="btn btn-blue "> Debug HLS</ button >
386387 < button id ="toggle-preview " class ="btn btn-blue "> Enable Preview</ button >
387388 </ div >
388389 </ div >
@@ -817,7 +818,7 @@ <h3 class="font-bold mb-2">Application Log:</h3>
817818 const hlsUrl = `http://${ ip } :8080/${ name } /index.m3u8` ;
818819
819820 // Configure video element for low latency
820- video . src = hlsUrl ;
821+ // Don't set src directly - let HLS detection logic handle it
821822
822823 // Set low latency attributes
823824 video . setAttribute ( 'playsinline' , '' ) ;
@@ -829,10 +830,9 @@ <h3 class="font-bold mb-2">Application Log:</h3>
829830 video . lowLatency = true ;
830831 }
831832
832- // Reduce buffering
833- if ( video . buffered ) {
834- video . preload = 'none' ;
835- }
833+ // Clear any existing source
834+ video . src = '' ;
835+ video . preload = 'metadata' ;
836836
837837 container . removeClass ( 'hidden' ) ;
838838 disabled . addClass ( 'hidden' ) ;
@@ -856,6 +856,10 @@ <h3 class="font-bold mb-2">Application Log:</h3>
856856
857857 // Check if HLS stream is available before trying to load
858858 const streamName = $ ( '#stream-name' ) . val ( ) || 'live' ;
859+
860+ // First, test if the HLS URL is reachable
861+ console . log ( 'Testing HLS URL:' , hlsUrl ) ;
862+
859863 $ . get ( `/api/stream/hls/${ streamName } ` , function ( data ) {
860864 if ( data . available ) {
861865 console . log ( 'HLS stream is available:' , data . url ) ;
@@ -893,12 +897,31 @@ <h3 class="font-bold mb-2">Application Log:</h3>
893897 } ) ;
894898 } ) ;
895899
900+ // Add error handling for HLS.js
901+ hls . on ( Hls . Events . ERROR , function ( event , data ) {
902+ console . error ( 'HLS.js error:' , data ) ;
903+ if ( data . fatal ) {
904+ switch ( data . type ) {
905+ case Hls . ErrorTypes . NETWORK_ERROR :
906+ container . prepend ( '<div class="text-red-400 text-sm mb-2">Network error loading stream. Check if stream is running.</div>' ) ;
907+ break ;
908+ case Hls . ErrorTypes . MEDIA_ERROR :
909+ container . prepend ( '<div class="text-red-400 text-sm mb-2">Media error. Stream format may be incompatible.</div>' ) ;
910+ break ;
911+ default :
912+ container . prepend ( '<div class="text-red-400 text-sm mb-2">Fatal error loading HLS stream.</div>' ) ;
913+ break ;
914+ }
915+ }
916+ } ) ;
917+
896918 // Store hls instance for cleanup
897919 video . hlsInstance = hls ;
898920
899921 } else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' ) ) {
900922 // Native HLS support (Safari)
901923 console . log ( 'Using native HLS support' ) ;
924+ video . src = hlsUrl ;
902925 video . load ( ) ;
903926
904927 setTimeout ( ( ) => {
@@ -912,6 +935,7 @@ <h3 class="font-bold mb-2">Application Log:</h3>
912935 container . prepend ( '<div class="text-yellow-400 text-sm mb-2">HLS may not be supported in this browser. Consider using Chrome or Safari.</div>' ) ;
913936
914937 // Try anyway with native video element
938+ video . src = hlsUrl ;
915939 video . load ( ) ;
916940 setTimeout ( ( ) => {
917941 video . play ( ) . catch ( e => {
@@ -923,6 +947,32 @@ <h3 class="font-bold mb-2">Application Log:</h3>
923947 } else {
924948 console . error ( 'HLS stream not available:' , data . error ) ;
925949 container . prepend ( `<div class="text-red-400 text-sm mb-2">HLS stream not available: ${ data . error } </div>` ) ;
950+
951+ // Try direct URL test
952+ console . log ( 'Attempting direct HLS URL test:' , hlsUrl ) ;
953+ fetch ( hlsUrl )
954+ . then ( response => {
955+ if ( response . ok ) {
956+ console . log ( 'Direct HLS URL is reachable, trying to load anyway' ) ;
957+ // Try to load anyway since URL is reachable
958+ if ( typeof Hls !== 'undefined' && Hls . isSupported ( ) ) {
959+ const hls = new Hls ( ) ;
960+ hls . loadSource ( hlsUrl ) ;
961+ hls . attachMedia ( video ) ;
962+ video . hlsInstance = hls ;
963+ } else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' ) ) {
964+ video . src = hlsUrl ;
965+ video . load ( ) ;
966+ }
967+ } else {
968+ console . error ( 'Direct HLS URL test failed:' , response . status ) ;
969+ container . prepend ( `<div class="text-red-400 text-sm mb-2">HLS URL not reachable (${ response . status } )</div>` ) ;
970+ }
971+ } )
972+ . catch ( e => {
973+ console . error ( 'Direct HLS URL test error:' , e ) ;
974+ container . prepend ( '<div class="text-red-400 text-sm mb-2">Cannot reach HLS URL - check if stream is running</div>' ) ;
975+ } ) ;
926976 }
927977 } ) . fail ( function ( ) {
928978 console . error ( 'Failed to check HLS stream availability' ) ;
@@ -953,8 +1003,34 @@ <h3 class="font-bold mb-2">Application Log:</h3>
9531003 const name = $ ( this ) . val ( ) || 'live' ;
9541004 const hlsUrl = `http://${ ip } :8080/${ name } /index.m3u8` ;
9551005 const video = $ ( '#stream-preview' ) [ 0 ] ;
956- video . src = hlsUrl ;
957- video . load ( ) ;
1006+
1007+ // Clean up existing HLS instance
1008+ if ( video . hlsInstance ) {
1009+ video . hlsInstance . destroy ( ) ;
1010+ video . hlsInstance = null ;
1011+ }
1012+
1013+ // Reload with new URL using proper HLS handling
1014+ if ( typeof Hls !== 'undefined' && Hls . isSupported ( ) ) {
1015+ const hls = new Hls ( {
1016+ lowLatencyMode : true ,
1017+ backBufferLength : 90 ,
1018+ maxBufferLength : 10 ,
1019+ maxMaxBufferLength : 20 ,
1020+ liveSyncDurationCount : 1 ,
1021+ liveMaxLatencyDurationCount : 2
1022+ } ) ;
1023+
1024+ hls . loadSource ( hlsUrl ) ;
1025+ hls . attachMedia ( video ) ;
1026+ video . hlsInstance = hls ;
1027+ } else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' ) ) {
1028+ video . src = hlsUrl ;
1029+ video . load ( ) ;
1030+ } else {
1031+ video . src = hlsUrl ;
1032+ video . load ( ) ;
1033+ }
9581034 }
9591035 } ) ;
9601036
@@ -1354,6 +1430,74 @@ <h3 class="font-bold mb-2">Application Log:</h3>
13541430 updateStatus ( ) ;
13551431 } ) ;
13561432
1433+ // Debug HLS functionality
1434+ $ ( '#debug-hls' ) . click ( function ( ) {
1435+ const button = $ ( this ) ;
1436+ button . text ( 'Debugging...' ) . prop ( 'disabled' , true ) ;
1437+
1438+ const streamName = $ ( '#stream-name' ) . val ( ) || 'live' ;
1439+ const ip = window . location . hostname || 'localhost' ;
1440+ const hlsUrl = `http://${ ip } :8080/${ streamName } /index.m3u8` ;
1441+
1442+ let debugInfo = `🔍 HLS Debug Information:\n\n` ;
1443+ debugInfo += `Stream Name: ${ streamName } \n` ;
1444+ debugInfo += `HLS URL: ${ hlsUrl } \n` ;
1445+ debugInfo += `Browser: ${ navigator . userAgent } \n\n` ;
1446+
1447+ // Test HLS.js support
1448+ if ( typeof Hls !== 'undefined' ) {
1449+ debugInfo += `HLS.js Library: ✅ Loaded (v${ Hls . version || 'unknown' } )\n` ;
1450+ debugInfo += `HLS.js Supported: ${ Hls . isSupported ( ) ? '✅ Yes' : '❌ No' } \n` ;
1451+ } else {
1452+ debugInfo += `HLS.js Library: ❌ Not loaded\n` ;
1453+ }
1454+
1455+ // Test native HLS support
1456+ const video = document . createElement ( 'video' ) ;
1457+ const nativeSupport = video . canPlayType ( 'application/vnd.apple.mpegurl' ) ;
1458+ debugInfo += `Native HLS Support: ${ nativeSupport ? '✅ Yes' : '❌ No' } \n\n` ;
1459+
1460+ // Test direct URL access
1461+ debugInfo += `Testing direct URL access...\n` ;
1462+
1463+ fetch ( hlsUrl )
1464+ . then ( response => {
1465+ debugInfo += `URL Response: ${ response . status } ${ response . statusText } \n` ;
1466+ debugInfo += `Content-Type: ${ response . headers . get ( 'content-type' ) || 'Unknown' } \n` ;
1467+
1468+ if ( response . ok ) {
1469+ return response . text ( ) ;
1470+ } else {
1471+ throw new Error ( `HTTP ${ response . status } ` ) ;
1472+ }
1473+ } )
1474+ . then ( content => {
1475+ debugInfo += `Content Length: ${ content . length } bytes\n` ;
1476+ debugInfo += `Content Preview:\n${ content . substring ( 0 , 200 ) } ${ content . length > 200 ? '...' : '' } \n\n` ;
1477+
1478+ if ( content . includes ( '#EXTM3U' ) ) {
1479+ debugInfo += `✅ Valid M3U8 playlist detected\n` ;
1480+ } else {
1481+ debugInfo += `❌ Invalid M3U8 content\n` ;
1482+ }
1483+
1484+ alert ( debugInfo ) ;
1485+ } )
1486+ . catch ( error => {
1487+ debugInfo += `❌ URL Access Failed: ${ error . message } \n\n` ;
1488+ debugInfo += `Possible causes:\n` ;
1489+ debugInfo += `• Stream is not running\n` ;
1490+ debugInfo += `• MediaMTX is not running\n` ;
1491+ debugInfo += `• Port 8080 is blocked\n` ;
1492+ debugInfo += `• CORS issues\n` ;
1493+
1494+ alert ( debugInfo ) ;
1495+ } )
1496+ . finally ( ( ) => {
1497+ button . text ( 'Debug HLS' ) . prop ( 'disabled' , false ) ;
1498+ } ) ;
1499+ } ) ;
1500+
13571501 // HLS Stream Check
13581502 $ ( '#check-hls' ) . click ( function ( ) {
13591503 const streamName = $ ( '#stream-name' ) . val ( ) || 'live' ;
0 commit comments