bep40 commited on
Commit
57cd85f
·
verified ·
1 Parent(s): 6b368b4

Fix VTV: VTV10→fptplay, VTV6 correct source, stable HLS config, no AbortSignal

Browse files
Files changed (2) hide show
  1. static/yt_live.js +4 -4
  2. vtv_api.py +10 -10
static/yt_live.js CHANGED
@@ -778,7 +778,7 @@
778
  function activateMiniPlayer(){
779
  if(!_currentCh) return; createMiniPlayer(); _miniActive=true;
780
  const mini=document.getElementById('vtv-mini'); const mv=document.getElementById('vtv-mini-vid'); const v=document.getElementById('vtv-player');
781
- if(_hls){const h=new Hls({enableWorker:true,lowLatencyMode:true,startLevel:-1,capLevelToPlayerSize:true,maxBufferLength:10}); h.loadSource(STREAMS[_currentCh][0]); h.attachMedia(mv); mv.play().catch(()=>{});}
782
  else if(v&&v.src){mv.src=v.src; mv.play().catch(()=>{});}
783
  const ch=CHANNELS.find(c=>c.id===_currentCh); if(ch) document.getElementById('vtv-mini-ch').textContent=ch.name;
784
  fetch('/api/vtv/epg/'+_currentCh).then(r=>r.json()).then(d=>{const p=d.programs||[]; const n=p.find(x=>x.now); document.getElementById('vtv-mini-epg').textContent=n?n.time+' '+n.title:(p[0]?p[0].time+' '+p[0].title:'');}).catch(()=>{});
@@ -789,7 +789,7 @@
789
  async function loadAllStreams(){
790
  if(_loading) return; _loading=true;
791
  const el=document.getElementById('vtv-load'); if(el) el.innerHTML='<div class="vtv-spinner"></div>Đang tải kênh...';
792
- try{const r=await fetch('/api/vtv/streams',); if(r.ok){const d=await r.json(); CHANNELS.forEach(ch=>{const i=d[ch.id]; if(i&&i.stream_url){let u=i.stream_url; if(NEEDS_PROXY.test(u)) u='/api/proxy/m3u8/vtv?url='+encodeURIComponent(u); STREAMS[ch.id]=[u];} else STREAMS[ch.id]=[];});}}catch(e){}
793
  CHANNELS.forEach(ch=>{const t=document.getElementById('vtvt-'+ch.id); if(t){if(STREAMS[ch.id]&&STREAMS[ch.id].length>0){t.classList.remove('off'); t.textContent=ch.name;}else{t.style.opacity='0.35'; t.textContent=ch.name+' ✕';}}});
794
  _loading=false; _streamsLoaded=true;
795
  }
@@ -808,7 +808,7 @@
808
  async function loadEpg(chId){
809
  const el=document.getElementById('vtv-epg-list'); if(!el) return;
810
  el.innerHTML='<div class="vtv-epg-loading"><div class="vtv-epg-sp"></div>Đang tải lịch...</div>';
811
- try{const r=await fetch('/api/vtv/epg/'+chId,); if(!r.ok) throw new Error(); const d=await r.json(); const p=d.programs||[]; if(!p.length){el.innerHTML='<div class="vtv-epg-empty">Chưa có lịch</div>';return;} let h=''; p.forEach(x=>{const n=x.now?' now':''; h+='<div class="vtv-epg-item'+n+'"><span class="t">'+(x.time||'')+(x.end_time?'-'+x.end_time:'')+'</span> <span class="n">'+(x.title||'')+'</span></div>';}); el.innerHTML=h; const ni=el.querySelector('.vtv-epg-item.now'); if(ni) ni.scrollIntoView({behavior:'smooth',inline:'center',block:'nearest'});}catch(e){el.innerHTML='<div class="vtv-epg-empty">Không tải được lịch</div>';}
812
  }
813
 
814
  window._vtvToggleEpg=function(){const l=document.getElementById('vtv-epg-list'),b=document.getElementById('vtv-epg-toggle'); if(!l||!b)return; if(l.style.display==='none'){l.style.display='flex';b.textContent='Ẩn';}else{l.style.display='none';b.textContent='Hiện';}};
@@ -842,7 +842,7 @@
842
  if(idx>=urls.length){loadEl.style.display='none';errEl.style.display='flex';errMsg.textContent=name+': Tất cả nguồn lỗi.';return;}
843
  const src=urls[idx]; loadEl.innerHTML='<div class="vtv-spinner"></div>Kết nối '+name+' ('+(idx+1)+'/'+urls.length+')...';
844
  if(typeof Hls!=='undefined'&&Hls.isSupported()){
845
- const hls=new Hls({enableWorker:true,lowLatencyMode:true,startLevel:-1,capLevelToPlayerSize:true,maxBufferLength:20}); _hls=hls; hls.loadSource(src); hls.attachMedia(video);
846
  hls.on(Hls.Events.MANIFEST_PARSED,()=>{video.play().catch(()=>{});loadEl.style.display='none';video.style.display='block';});
847
  let rec=0; hls.on(Hls.Events.ERROR,(ev,data)=>{if(data.fatal){if(data.type===Hls.ErrorTypes.NETWORK_ERROR){rec++;if(rec<=3)setTimeout(()=>hls.startLoad(),2000);else{hls.destroy();_hls=null;_tryPlay(video,urls,idx+1,name,loadEl,errEl,errMsg);}}else if(data.type===Hls.ErrorTypes.MEDIA_ERROR){try{hls.recoverMediaError();}catch(e){}}else{hls.destroy();_hls=null;_tryPlay(video,urls,idx+1,name,loadEl,errEl,errMsg);}}});
848
  }else if(video.canPlayType('application/vnd.apple.mpegurl')){
 
778
  function activateMiniPlayer(){
779
  if(!_currentCh) return; createMiniPlayer(); _miniActive=true;
780
  const mini=document.getElementById('vtv-mini'); const mv=document.getElementById('vtv-mini-vid'); const v=document.getElementById('vtv-player');
781
+ if(_hls){const h=new Hls({enableWorker:true,lowLatencyMode:false,startLevel:0,capLevelToPlayerSize:true,maxBufferLength:6}); h.loadSource(STREAMS[_currentCh][0]); h.attachMedia(mv); mv.play().catch(()=>{});}
782
  else if(v&&v.src){mv.src=v.src; mv.play().catch(()=>{});}
783
  const ch=CHANNELS.find(c=>c.id===_currentCh); if(ch) document.getElementById('vtv-mini-ch').textContent=ch.name;
784
  fetch('/api/vtv/epg/'+_currentCh).then(r=>r.json()).then(d=>{const p=d.programs||[]; const n=p.find(x=>x.now); document.getElementById('vtv-mini-epg').textContent=n?n.time+' '+n.title:(p[0]?p[0].time+' '+p[0].title:'');}).catch(()=>{});
 
789
  async function loadAllStreams(){
790
  if(_loading) return; _loading=true;
791
  const el=document.getElementById('vtv-load'); if(el) el.innerHTML='<div class="vtv-spinner"></div>Đang tải kênh...';
792
+ try{const r=await fetch('/api/vtv/streams'); if(r.ok){const d=await r.json(); CHANNELS.forEach(ch=>{const i=d[ch.id]; if(i&&i.stream_url){let u=i.stream_url; if(NEEDS_PROXY.test(u)) u='/api/proxy/m3u8/vtv?url='+encodeURIComponent(u); STREAMS[ch.id]=[u];} else STREAMS[ch.id]=[];});}}catch(e){}
793
  CHANNELS.forEach(ch=>{const t=document.getElementById('vtvt-'+ch.id); if(t){if(STREAMS[ch.id]&&STREAMS[ch.id].length>0){t.classList.remove('off'); t.textContent=ch.name;}else{t.style.opacity='0.35'; t.textContent=ch.name+' ✕';}}});
794
  _loading=false; _streamsLoaded=true;
795
  }
 
808
  async function loadEpg(chId){
809
  const el=document.getElementById('vtv-epg-list'); if(!el) return;
810
  el.innerHTML='<div class="vtv-epg-loading"><div class="vtv-epg-sp"></div>Đang tải lịch...</div>';
811
+ try{const r=await fetch('/api/vtv/epg/'+chId); if(!r.ok) throw new Error(); const d=await r.json(); const p=d.programs||[]; if(!p.length){el.innerHTML='<div class="vtv-epg-empty">Chưa có lịch</div>';return;} let h=''; p.forEach(x=>{const n=x.now?' now':''; h+='<div class="vtv-epg-item'+n+'"><span class="t">'+(x.time||'')+(x.end_time?'-'+x.end_time:'')+'</span> <span class="n">'+(x.title||'')+'</span></div>';}); el.innerHTML=h; const ni=el.querySelector('.vtv-epg-item.now'); if(ni) ni.scrollIntoView({behavior:'smooth',inline:'center',block:'nearest'});}catch(e){el.innerHTML='<div class="vtv-epg-empty">Không tải được lịch</div>';}
812
  }
813
 
814
  window._vtvToggleEpg=function(){const l=document.getElementById('vtv-epg-list'),b=document.getElementById('vtv-epg-toggle'); if(!l||!b)return; if(l.style.display==='none'){l.style.display='flex';b.textContent='Ẩn';}else{l.style.display='none';b.textContent='Hiện';}};
 
842
  if(idx>=urls.length){loadEl.style.display='none';errEl.style.display='flex';errMsg.textContent=name+': Tất cả nguồn lỗi.';return;}
843
  const src=urls[idx]; loadEl.innerHTML='<div class="vtv-spinner"></div>Kết nối '+name+' ('+(idx+1)+'/'+urls.length+')...';
844
  if(typeof Hls!=='undefined'&&Hls.isSupported()){
845
+ const hls=new Hls({enableWorker:true,lowLatencyMode:false,startLevel:0,capLevelToPlayerSize:true,maxBufferLength:8,maxMaxBufferLength:12,liveSyncDurationCount:3,liveMaxLatencyDurationCount:5}); _hls=hls; hls.loadSource(src); hls.attachMedia(video);
846
  hls.on(Hls.Events.MANIFEST_PARSED,()=>{video.play().catch(()=>{});loadEl.style.display='none';video.style.display='block';});
847
  let rec=0; hls.on(Hls.Events.ERROR,(ev,data)=>{if(data.fatal){if(data.type===Hls.ErrorTypes.NETWORK_ERROR){rec++;if(rec<=3)setTimeout(()=>hls.startLoad(),2000);else{hls.destroy();_hls=null;_tryPlay(video,urls,idx+1,name,loadEl,errEl,errMsg);}}else if(data.type===Hls.ErrorTypes.MEDIA_ERROR){try{hls.recoverMediaError();}catch(e){}}else{hls.destroy();_hls=null;_tryPlay(video,urls,idx+1,name,loadEl,errEl,errMsg);}}});
848
  }else if(video.canPlayType('application/vnd.apple.mpegurl')){
vtv_api.py CHANGED
@@ -58,7 +58,7 @@ VTVGO_FAILOVER = {
58
  "vtv7": "https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv7-manifest.m3u8",
59
  "vtv8": "https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv8-manifest.m3u8",
60
  "vtv9": "https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv9-manifest.m3u8",
61
- "vtv10": "https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtvct-manifest.m3u8",
62
  }
63
 
64
  XEMTV_ONLY_CHANNELS = set() # No channels are xemtv-only; all use VTVGo CDN first
@@ -73,7 +73,7 @@ FPTPLAY_URLS = {
73
  "vtv7": "https://live.fptplay53.net/fnxhd1/vtv7hd_vhls.smil/chunklist_b5000000.m3u8",
74
  "vtv8": "https://live.fptplay53.net/epzhd1/vtv8hd_vhls.smil/chunklist.m3u8",
75
  "vtv9": "https://live.fptplay53.net/fnxhd1/vtv9hd_vhls.smil/chunklist.m3u8",
76
- "vtv10": "https://live.fptplay53.net/fnxch2/vtvcantho_abr.smil/chunklist.m3u8",
77
  }
78
 
79
  _vtv_cache = {}
@@ -161,24 +161,24 @@ def fetch_vtv_stream(channel_id):
161
  _set_cache(channel_id, None)
162
  return None
163
 
164
- # Source 1: VTVGo CDN (primary for all standard channels)
165
  vtvgourl = VTVGO_FAILOVER.get(channel_id)
166
  if vtvgourl:
167
  _set_cache(channel_id, vtvgourl)
168
  return vtvgourl
169
 
170
- # Source 2: xemtv.net PHP scraper (fresh signed URLs)
 
 
 
 
 
 
171
  xemtv_url = fetch_xemtv_stream(channel_id)
172
  if xemtv_url:
173
  _set_cache(channel_id, xemtv_url)
174
  return xemtv_url
175
 
176
- # Source 3: fptplay CDN (last resort)
177
- fpt_url = FPTPLAY_URLS.get(channel_id)
178
- if fpt_url:
179
- _set_cache(channel_id, fpt_url)
180
- return fpt_url
181
-
182
  _set_cache(channel_id, None)
183
  return None
184
 
 
58
  "vtv7": "https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv7-manifest.m3u8",
59
  "vtv8": "https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv8-manifest.m3u8",
60
  "vtv9": "https://vtvgolive-failover.vtvdigital.vn/vtvgo/vtv9-manifest.m3u8",
61
+ # VTV10: VTVGo CDN returns 404, use fptplay instead
62
  }
63
 
64
  XEMTV_ONLY_CHANNELS = set() # No channels are xemtv-only; all use VTVGo CDN first
 
73
  "vtv7": "https://live.fptplay53.net/fnxhd1/vtv7hd_vhls.smil/chunklist_b5000000.m3u8",
74
  "vtv8": "https://live.fptplay53.net/epzhd1/vtv8hd_vhls.smil/chunklist.m3u8",
75
  "vtv9": "https://live.fptplay53.net/fnxhd1/vtv9hd_vhls.smil/chunklist.m3u8",
76
+ "vtv10": "https://live.fptplay53.net/fnxhd1/vtv10hd_vhls.smil/chunklist.m3u8",
77
  }
78
 
79
  _vtv_cache = {}
 
161
  _set_cache(channel_id, None)
162
  return None
163
 
164
+ # Source 1: VTVGo CDN (primary for channels that have it)
165
  vtvgourl = VTVGO_FAILOVER.get(channel_id)
166
  if vtvgourl:
167
  _set_cache(channel_id, vtvgourl)
168
  return vtvgourl
169
 
170
+ # Source 2: fptplay CDN (primary for VTV10, fallback for others)
171
+ fpt_url = fetch_fptplay_stream(channel_id)
172
+ if fpt_url:
173
+ _set_cache(channel_id, fpt_url)
174
+ return fpt_url
175
+
176
+ # Source 3: xemtv.net PHP scraper (last resort, often unreachable)
177
  xemtv_url = fetch_xemtv_stream(channel_id)
178
  if xemtv_url:
179
  _set_cache(channel_id, xemtv_url)
180
  return xemtv_url
181
 
 
 
 
 
 
 
182
  _set_cache(channel_id, None)
183
  return None
184