bep40 commited on
Commit
13cbabf
·
verified ·
1 Parent(s): 10b79ca

v10: Fix DVR — stream plays at live + user can seek back freely

Browse files

- Khôi phục liveSyncDurationCount=3 để stream start đúng ở live edge
- MANIFEST_PARSED: seek to duration (live edge) trước khi play
- Thêm seeking/seeked interceptor: nếu HLS.js snap về live nhưng user đang xem behind → restore position
- _dvrRestoreLock chặn loop khi restore
- Giữ _dvrUserSeeking pause/resume khi drag DVR handle

Files changed (1) hide show
  1. static/yt_live.js +34 -14
static/yt_live.js CHANGED
@@ -1,6 +1,6 @@
1
- // === VNEWS — VTV LIVE + Inline Recorder v9 ===
2
  // Features: DVR 5-min rewind buffer (seek & stay), PiP, mini-player, INLINE RECORDER with ratio crop + AI title + Short AI upload
3
- // v9: Fix DVR seek snap-back disable HLS auto-live-sync (liveSyncDurationCount=999999), pause during drag, resume on release
4
 
5
  (function(){
6
  if(window._ytLiveLoaded) return;
@@ -721,19 +721,13 @@
721
  if (_dvrSeekBarTimer) clearInterval(_dvrSeekBarTimer);
722
  _dvrSeekBarTimer = setInterval(updateDVRUI, 1000);
723
 
724
- // v9: Only auto-live if user is NOT seeking and playhead reached buffer edge
725
  video.addEventListener('timeupdate', function() {
726
- if (_dvrUserSeeking) return; // don't interfere during seek
727
  if (!_hls) return;
728
  const dur = video.duration;
729
  if (!dur || !isFinite(dur)) return;
730
- // If playhead is at the very end of buffer (live edge), keep it there
731
  const behind = dur - video.currentTime;
732
- if (behind < 2) {
733
- _dvrLastSeenEdge = true;
734
- } else {
735
- _dvrLastSeenEdge = false;
736
- }
737
  });
738
  }
739
 
@@ -850,20 +844,46 @@
850
  if(idx>=urls.length){loadEl.style.display='none';errEl.style.display='flex';errMsg.textContent=name+': Tất cả nguồn lỗi.';return;}
851
  const src=urls[idx]; loadEl.innerHTML='<div class="vtv-spinner"></div>Kết nối '+name+' ('+(idx+1)+'/'+urls.length+')...';
852
  if(typeof Hls!=='undefined'&&Hls.isSupported()){
853
- // v9: DVR 5min — disable auto-live-sync so user can seek back freely
854
  const hls=new Hls({
855
  enableWorker:true,lowLatencyMode:false,startLevel:0,capLevelToPlayerSize:true,
856
  maxBufferLength:120,maxMaxBufferLength:180,
857
  liveBackBufferLength:DVR_BUFFER_SECONDS,
858
- liveSyncDurationCount:999999,liveMaxLatencyDurationCount:999999,
859
  fragLoadingTimeOut:10000,manifestLoadingTimeOut:10000
860
  });
861
  _hls=hls; hls.loadSource(src); hls.attachMedia(video);
862
- hls.on(Hls.Events.MANIFEST_PARSED,()=>{
 
863
  loadEl.style.display='none';video.style.display='block';
864
- // v9: Start playing but don't force live edge let DVR seek bar handle it
 
 
865
  video.play().catch(()=>{});
866
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
867
  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);}}});
868
  }else if(video.canPlayType('application/vnd.apple.mpegurl')){
869
  video.src=src;
 
1
+ // === VNEWS — VTV LIVE + Inline Recorder v10 ===
2
  // Features: DVR 5-min rewind buffer (seek & stay), PiP, mini-player, INLINE RECORDER with ratio crop + AI title + Short AI upload
3
+ // v10: Fix DVR keep liveSync for stream start, but intercept HLS auto-snap via seeking/seeked events to preserve user position
4
 
5
  (function(){
6
  if(window._ytLiveLoaded) return;
 
721
  if (_dvrSeekBarTimer) clearInterval(_dvrSeekBarTimer);
722
  _dvrSeekBarTimer = setInterval(updateDVRUI, 1000);
723
 
724
+ // v10: Track whether playhead is at live edge (for seeked event guard)
725
  video.addEventListener('timeupdate', function() {
 
726
  if (!_hls) return;
727
  const dur = video.duration;
728
  if (!dur || !isFinite(dur)) return;
 
729
  const behind = dur - video.currentTime;
730
+ _dvrLastSeenEdge = (behind < 2);
 
 
 
 
731
  });
732
  }
733
 
 
844
  if(idx>=urls.length){loadEl.style.display='none';errEl.style.display='flex';errMsg.textContent=name+': Tất cả nguồn lỗi.';return;}
845
  const src=urls[idx]; loadEl.innerHTML='<div class="vtv-spinner"></div>Kết nối '+name+' ('+(idx+1)+'/'+urls.length+')...';
846
  if(typeof Hls!=='undefined'&&Hls.isSupported()){
847
+ // v10: DVR 5min — keep liveSync for edge tracking, but override seek snap-back
848
  const hls=new Hls({
849
  enableWorker:true,lowLatencyMode:false,startLevel:0,capLevelToPlayerSize:true,
850
  maxBufferLength:120,maxMaxBufferLength:180,
851
  liveBackBufferLength:DVR_BUFFER_SECONDS,
852
+ liveSyncDurationCount:3,liveMaxLatencyDurationCount:6,
853
  fragLoadingTimeOut:10000,manifestLoadingTimeOut:10000
854
  });
855
  _hls=hls; hls.loadSource(src); hls.attachMedia(video);
856
+ // v10: After manifest loaded, seek to live edge first, then play
857
+ hls.on(Hls.Events.MANIFEST_PARSED,(ev,data)=>{
858
  loadEl.style.display='none';video.style.display='block';
859
+ // Start at live edge so stream plays immediately
860
+ const dur = video.duration;
861
+ if(dur && isFinite(dur)){ video.currentTime = dur; }
862
  video.play().catch(()=>{});
863
  });
864
+ // (live-snap interception handled via video seeking/seeked events below)
865
+ // v10: Block HLS auto-live-snap — restore user position when HLS fights back
866
+ let _preSeekTime = 0;
867
+ let _dvrRestoreLock = false; // prevent restore loop
868
+ video.addEventListener('seeking',function(){
869
+ if(_dvrRestoreLock) return;
870
+ _preSeekTime = video.currentTime;
871
+ });
872
+ video.addEventListener('seeked',function(){
873
+ if(_dvrUserSeeking || _dvrRestoreLock) return;
874
+ const dur = video.duration;
875
+ if(!dur || !isFinite(dur)) return;
876
+ const behind = dur - video.currentTime;
877
+ // If HLS.js snapped to live (behind < 0.5s) but user was >3s behind, restore
878
+ if(behind < 0.5 && _preSeekTime > 0){
879
+ const userBehind = dur - _preSeekTime;
880
+ if(userBehind > 3){
881
+ _dvrRestoreLock = true;
882
+ video.currentTime = _preSeekTime;
883
+ setTimeout(()=>{ _dvrRestoreLock = false; },500);
884
+ }
885
+ }
886
+ });
887
  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);}}});
888
  }else if(video.canPlayType('application/vnd.apple.mpegurl')){
889
  video.src=src;