Spaces:
Running
Running
Add VTV debug test page in static folder
Browse files- static/vtv_test2.html +116 -0
static/vtv_test2.html
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 6 |
+
<title>VTV Stream Debug</title>
|
| 7 |
+
<style>
|
| 8 |
+
body{background:#111;color:#eee;font-family:monospace;padding:16px;margin:0}
|
| 9 |
+
h2{color:#5cb87a;font-size:16px}
|
| 10 |
+
video{width:100%;max-width:600px;background:#000;margin:10px 0;display:block}
|
| 11 |
+
#log{background:#1a1a1a;border:1px solid #333;padding:10px;max-height:300px;overflow-y:auto;white-space:pre-wrap;font-size:11px;line-height:1.5}
|
| 12 |
+
.ok{color:#5cb87a}.err{color:#e74c3c}.warn{color:#f0c040}.info{color:#888}
|
| 13 |
+
button{background:#2d8659;border:0;color:#fff;padding:8px 16px;border-radius:8px;cursor:pointer;margin:4px;font-size:12px}
|
| 14 |
+
select{background:#222;color:#eee;border:1px solid #444;padding:6px;border-radius:6px;font-size:12px}
|
| 15 |
+
</style>
|
| 16 |
+
</head>
|
| 17 |
+
<body>
|
| 18 |
+
<h2>📺 VTV Stream Debug Test</h2>
|
| 19 |
+
<div>
|
| 20 |
+
<select id="ch">
|
| 21 |
+
<option value="vtv1">VTV1</option><option value="vtv2">VTV2</option><option value="vtv3">VTV3</option>
|
| 22 |
+
<option value="vtv4">VTV4</option><option value="vtv5">VTV5</option><option value="vtv6" selected>VTV6</option>
|
| 23 |
+
<option value="vtv7">VTV7</option><option value="vtv8">VTV8</option><option value="vtv9">VTV9</option>
|
| 24 |
+
</select>
|
| 25 |
+
<button onclick="startPlay()">▶ Play</button>
|
| 26 |
+
<button onclick="location.reload()">🔄 Reload</button>
|
| 27 |
+
</div>
|
| 28 |
+
<video id="v" controls autoplay muted playsinline></video>
|
| 29 |
+
<div id="log"></div>
|
| 30 |
+
|
| 31 |
+
<script src="https://cdn.jsdelivr.net/npm/hls.js@1.5.13/dist/hls.min.js"></script>
|
| 32 |
+
<script>
|
| 33 |
+
const LOG=(msg,type)=>{const el=document.getElementById('log');const c=type||'info';el.innerHTML+='<span class="'+c+'">['+new Date().toISOString().substr(11,12)+'] '+msg+'\n</span>';el.scrollTop=el.scrollHeight;};
|
| 34 |
+
const VTVGO={"vtv1":"https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv1-manifest.m3u8","vtv2":"https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv2-manifest.m3u8","vtv3":"https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv3-manifest.m3u8","vtv4":"https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv4-manifest.m3u8","vtv5":"https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv5-manifest.m3u8","vtv6":"https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv6-manifest.m3u8","vtv7":"https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv7-manifest.m3u8","vtv8":"https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv8-manifest.m3u8","vtv9":"https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv9-manifest.m3u8"};
|
| 35 |
+
const BASE="https://bep40-vnews.hf.space";
|
| 36 |
+
const video=document.getElementById('v');
|
| 37 |
+
let hls=null;
|
| 38 |
+
|
| 39 |
+
LOG('Page loaded','ok');
|
| 40 |
+
LOG('HLS.js: '+(Hls.version||'unknown')+' | supported: '+Hls.isSupported(),'info');
|
| 41 |
+
LOG('Native HLS: '+video.canPlayType('application/vnd.apple.mpegurl'),'info');
|
| 42 |
+
|
| 43 |
+
function startPlay(){
|
| 44 |
+
const ch=document.getElementById('ch').value;
|
| 45 |
+
const directUrl=VTVGO[ch];
|
| 46 |
+
const proxyUrl=BASE+'/api/proxy/m3u8/vtv?url='+encodeURIComponent(directUrl);
|
| 47 |
+
LOG('\n--- Playing '+ch+' ---','info');
|
| 48 |
+
LOG('Proxy: '+proxyUrl,'info');
|
| 49 |
+
if(hls){hls.destroy();hls=null;}
|
| 50 |
+
video.pause();video.src='';
|
| 51 |
+
|
| 52 |
+
if(!Hls.isSupported()){
|
| 53 |
+
LOG('Using native HLS','warn');
|
| 54 |
+
video.src=proxyUrl;
|
| 55 |
+
video.play().then(()=>LOG('Native play OK','ok')).catch(e=>LOG('Native play: '+e,'err'));
|
| 56 |
+
return;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
hls=new Hls({enableWorker:false,lowLatencyMode:true,startLevel:-1,capLevelToPlayerSize:true,maxBufferLength:6,debug:true});
|
| 60 |
+
hls.attachMedia(video);
|
| 61 |
+
LOG('attachMedia done','info');
|
| 62 |
+
|
| 63 |
+
hls.on(Hls.Events.MEDIA_ATTACHED,()=>{
|
| 64 |
+
LOG('MEDIA_ATTACHED','ok');
|
| 65 |
+
hls.loadSource(proxyUrl);
|
| 66 |
+
LOG('loadSource: '+proxyUrl.substring(0,60),'info');
|
| 67 |
+
});
|
| 68 |
+
|
| 69 |
+
hls.on(Hls.Events.MANIFEST_LOADING,()=>LOG('MANIFEST_LOADING','info'));
|
| 70 |
+
hls.on(Hls.Events.MANIFEST_LOADED,()=>LOG('MANIFEST_LOADED','ok'));
|
| 71 |
+
hls.on(Hls.Events.MANIFEST_PARSED,(ev,d)=>{
|
| 72 |
+
LOG('MANIFEST_PARSED: '+d.levels.length+' levels, '+(d.levels[0]?d.levels[0].width+'x'+d.levels[0].height:'?'),'ok');
|
| 73 |
+
const res=d.levels.map(l=>l.width+'x'+l.height).join(', ');
|
| 74 |
+
LOG('Available: '+res,'info');
|
| 75 |
+
});
|
| 76 |
+
|
| 77 |
+
hls.on(Hls.Events.FRAG_LOADED,(ev,d)=>{
|
| 78 |
+
const sz=d.frag&&d.frag.data&&d.frag.data.length;
|
| 79 |
+
LOG('FRAG_LOADED #'+d.frag.sn+' size='+sz,'ok');
|
| 80 |
+
});
|
| 81 |
+
|
| 82 |
+
hls.on(Hls.Events.BUFFER_APPENDED,()=>{
|
| 83 |
+
const buf=video.buffered;
|
| 84 |
+
let bufInfo='';
|
| 85 |
+
if(buf.length>0)bufInfo='buf[0]='+buf.start(0).toFixed(1)+'-'+buf.end(0).toFixed(1);
|
| 86 |
+
const dur=video.duration;
|
| 87 |
+
const ct=video.currentTime;
|
| 88 |
+
LOG('BUFFER_APPENDED '+bufInfo+' dur='+(isFinite(dur)?dur.toFixed(1):'live')+' ct='+ct.toFixed(1),'ok');
|
| 89 |
+
});
|
| 90 |
+
|
| 91 |
+
hls.on(Hls.Events.ERROR,(ev,data)=>{
|
| 92 |
+
LOG('ERROR '+data.type+' '+data.details+' fatal='+data.fatal,'err');
|
| 93 |
+
if(data.url)LOG(' URL: '+data.url.substring(0,80),'err');
|
| 94 |
+
if(data.fatal){
|
| 95 |
+
LOG('Fatal error - destroying HLS','err');
|
| 96 |
+
hls.destroy();hls=null;
|
| 97 |
+
}
|
| 98 |
+
});
|
| 99 |
+
|
| 100 |
+
video.addEventListener('playing',()=>LOG('🎬 VIDEO PLAYING!','ok'),{once:true});
|
| 101 |
+
video.addEventListener('loadeddata',()=>LOG('LOADED DATA','ok'),{once:true});
|
| 102 |
+
video.addEventListener('error',(e)=>LOG('VIDEO ERROR: '+video.error.code+' '+video.error.message,'err'),{once:true});
|
| 103 |
+
|
| 104 |
+
let checkCount=0;
|
| 105 |
+
const checkInterval=setInterval(()=>{
|
| 106 |
+
checkCount++;
|
| 107 |
+
if(checkCount>20){clearInterval(checkInterval);return;}
|
| 108 |
+
const state={ready:video.readyState,networkState:video.networkState,w:video.videoWidth,h:video.videoHeight,paused:video.paused,muted:video.muted};
|
| 109 |
+
LOG('State #'+checkCount+': '+JSON.stringify(state),state.w>0?'ok':'warn');
|
| 110 |
+
if(state.w>0){LOG('🎉 VIDEO HAS FRAMES '+state.w+'x'+state.h,'ok');clearInterval(checkInterval);}
|
| 111 |
+
if(video.error){LOG('Error: '+video.error.message,'err');clearInterval(checkInterval);}
|
| 112 |
+
},1000);
|
| 113 |
+
}
|
| 114 |
+
</script>
|
| 115 |
+
</body>
|
| 116 |
+
</html>
|