bep40 commited on
Commit
a7b1259
·
verified ·
1 Parent(s): 4c51c2f

v12: Fix DVR — disable HLS.js liveSync, manage live edge manually

Browse files

v11 lỗi: override startLoad(cur-5) → HLS.js không load đúng segment → stream không phát.

Giải pháp v12: Đơn giản hơn, đáng tin cậy hơn:
- liveSyncDurationCount:0, liveMaxLatencyDurationCount:0 → HLS.js KHÔNG auto-snap về live
- Lần đầu load: tự seek về live edge (video.currentTime = video.duration)
- User tua DVR: HLS.js load segment bình thường, không can thiệp
- Nút LIVE: seek về live edge
- Không override startLoad, không intercept seeked → không loop, không side-effect

Files changed (1) hide show
  1. static/yt_live.js +13 -54
static/yt_live.js CHANGED
@@ -1,6 +1,6 @@
1
- // === VNEWS — VTV LIVE + Inline Recorder v11 ===
2
  // Features: DVR 5-min rewind buffer (seek & stay), PiP, mini-player, INLINE RECORDER with ratio crop + AI title + Short AI upload
3
- // v11: Fix DVR snap-back — override hls.startLoad to block auto-live-snap. Only seek to live on initial load or LIVE btn click
4
 
5
  (function(){
6
  if(window._ytLiveLoaded) return;
@@ -23,8 +23,7 @@
23
  let _dvrSeekBarTimer = null;
24
  let _dvrUserSeeking = false; // true while user is dragging DVR seek handle
25
  let _dvrLastSeenEdge = false; // track if playhead was at live edge before seek
26
- let _dvrHasUserSeeked = false; // true once user manually seeks back from live
27
- let _dvrStartLoadOrig = null; // original hls.startLoad reference
28
 
29
  // ===== RECORDER STATE =====
30
  const _rec = {
@@ -686,10 +685,8 @@
686
  const bufferStart = Math.max(0, duration - DVR_BUFFER_SECONDS);
687
  const seekTime = bufferStart + pct * (duration - bufferStart);
688
  _dvrUserSeeking = !isRelease;
689
- // v11: Mark as user-seeked if not at live edge
690
- if (duration - seekTime > 5) {
691
- _dvrHasUserSeeked = true;
692
- }
693
  video.currentTime = seekTime;
694
  if (isRelease) {
695
  video.play().catch(()=>{});
@@ -715,7 +712,7 @@
715
  const video = document.getElementById('vtv-player');
716
  if (video && _hls) {
717
  _dvrUserSeeking = false;
718
- _dvrHasUserSeeked = false; // reset — allow auto-live again
719
  video.currentTime = video.duration;
720
  video.play().catch(()=>{});
721
  updateDVRUI();
@@ -726,17 +723,14 @@
726
  if (_dvrSeekBarTimer) clearInterval(_dvrSeekBarTimer);
727
  _dvrSeekBarTimer = setInterval(updateDVRUI, 1000);
728
 
729
- // v11: Track live edge state; auto-clear _dvrHasUserSeeked when buffer catches up
730
  video.addEventListener('timeupdate', function() {
731
  if (!_hls) return;
732
  const dur = video.duration;
733
  if (!dur || !isFinite(dur)) return;
734
  const behind = dur - video.currentTime;
735
- _dvrLastSeenEdge = (behind < 2);
736
- // If playhead reached live edge naturally, allow auto-live again
737
- if (behind < 1 && _dvrHasUserSeeked) {
738
- _dvrHasUserSeeked = false;
739
- }
740
  });
741
  }
742
 
@@ -853,60 +847,25 @@
853
  if(idx>=urls.length){loadEl.style.display='none';errEl.style.display='flex';errMsg.textContent=name+': Tất cả nguồn lỗi.';return;}
854
  const src=urls[idx]; loadEl.innerHTML='<div class="vtv-spinner"></div>Kết nối '+name+' ('+(idx+1)+'/'+urls.length+')...';
855
  if(typeof Hls!=='undefined'&&Hls.isSupported()){
856
- // v11: DVR 5min — override hls.startLoad to prevent auto-live-snap
857
  const hls=new Hls({
858
  enableWorker:true,lowLatencyMode:false,startLevel:0,capLevelToPlayerSize:true,
859
  maxBufferLength:120,maxMaxBufferLength:180,
860
  liveBackBufferLength:DVR_BUFFER_SECONDS,
861
- liveSyncDurationCount:999999,liveMaxLatencyDurationCount:999999,
862
  fragLoadingTimeOut:10000,manifestLoadingTimeOut:10000
863
  });
864
  _hls=hls; hls.loadSource(src); hls.attachMedia(video);
865
 
866
- // v11: Override startLoad only seek to live on initial load, never auto-snap back
867
- _dvrStartLoadOrig = hls.startLoad.bind(hls);
868
- hls.startLoad = function(position) {
869
- // First call (initial load): seek to live edge
870
- if (!_dvrHasUserSeeked && position === -1) {
871
- _dvrStartLoadOrig(position);
872
- return;
873
- }
874
- // After user sought back: load segments but DON'T seek to live edge
875
- // Pass a large negative value to load from buffer start instead
876
- if (_dvrHasUserSeeked && position === -1) {
877
- // Load from current buffer position, not live edge
878
- const dur = video.duration;
879
- const cur = video.currentTime;
880
- if (dur && isFinite(dur) && cur && isFinite(cur)) {
881
- // Load from slightly behind current position to ensure smooth playback
882
- _dvrStartLoadOrig(cur - 5);
883
- } else {
884
- _dvrStartLoadOrig(position);
885
- }
886
- return;
887
- }
888
- _dvrStartLoadOrig(position);
889
- };
890
-
891
- // v11: After manifest loaded, seek to live edge first, then play
892
  hls.on(Hls.Events.MANIFEST_PARSED,(ev,data)=>{
893
  loadEl.style.display='none';video.style.display='block';
 
894
  const dur = video.duration;
895
  if(dur && isFinite(dur)){ video.currentTime = dur; }
896
  video.play().catch(()=>{});
897
  });
898
 
899
- // v11: Track when user manually seeks back from live
900
- video.addEventListener('seeking', function() {
901
- if (_dvrUserSeeking) return;
902
- const dur = video.duration;
903
- if (!dur || !isFinite(dur)) return;
904
- const behind = dur - video.currentTime;
905
- if (behind > 5) {
906
- _dvrHasUserSeeked = true;
907
- }
908
- });
909
-
910
  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);}}});
911
  }else if(video.canPlayType('application/vnd.apple.mpegurl')){
912
  video.src=src;
 
1
+ // === VNEWS — VTV LIVE + Inline Recorder v12 ===
2
  // Features: DVR 5-min rewind buffer (seek & stay), PiP, mini-player, INLINE RECORDER with ratio crop + AI title + Short AI upload
3
+ // v12: Fix DVR snap-back — disable HLS.js liveSync (liveSyncDurationCount:0), manage live edge manually. No override, no intercept
4
 
5
  (function(){
6
  if(window._ytLiveLoaded) return;
 
23
  let _dvrSeekBarTimer = null;
24
  let _dvrUserSeeking = false; // true while user is dragging DVR seek handle
25
  let _dvrLastSeenEdge = false; // track if playhead was at live edge before seek
26
+ let _dvrAtLiveEdge = true; // track if playhead is at live edge
 
27
 
28
  // ===== RECORDER STATE =====
29
  const _rec = {
 
685
  const bufferStart = Math.max(0, duration - DVR_BUFFER_SECONDS);
686
  const seekTime = bufferStart + pct * (duration - bufferStart);
687
  _dvrUserSeeking = !isRelease;
688
+ // v12: Track live edge state
689
+ _dvrAtLiveEdge = (duration - seekTime < 2);
 
 
690
  video.currentTime = seekTime;
691
  if (isRelease) {
692
  video.play().catch(()=>{});
 
712
  const video = document.getElementById('vtv-player');
713
  if (video && _hls) {
714
  _dvrUserSeeking = false;
715
+ _dvrAtLiveEdge = true;
716
  video.currentTime = video.duration;
717
  video.play().catch(()=>{});
718
  updateDVRUI();
 
723
  if (_dvrSeekBarTimer) clearInterval(_dvrSeekBarTimer);
724
  _dvrSeekBarTimer = setInterval(updateDVRUI, 1000);
725
 
726
+ // v12: Track live edge state for UI
727
  video.addEventListener('timeupdate', function() {
728
  if (!_hls) return;
729
  const dur = video.duration;
730
  if (!dur || !isFinite(dur)) return;
731
  const behind = dur - video.currentTime;
732
+ _dvrAtLiveEdge = (behind < 2);
733
+ _dvrLastSeenEdge = _dvrAtLiveEdge;
 
 
 
734
  });
735
  }
736
 
 
847
  if(idx>=urls.length){loadEl.style.display='none';errEl.style.display='flex';errMsg.textContent=name+': Tất cả nguồn lỗi.';return;}
848
  const src=urls[idx]; loadEl.innerHTML='<div class="vtv-spinner"></div>Kết nối '+name+' ('+(idx+1)+'/'+urls.length+')...';
849
  if(typeof Hls!=='undefined'&&Hls.isSupported()){
850
+ // v12: DVR 5min — disable HLS.js live sync entirely, manage live edge manually
851
  const hls=new Hls({
852
  enableWorker:true,lowLatencyMode:false,startLevel:0,capLevelToPlayerSize:true,
853
  maxBufferLength:120,maxMaxBufferLength:180,
854
  liveBackBufferLength:DVR_BUFFER_SECONDS,
855
+ liveSyncDurationCount:0,liveMaxLatencyDurationCount:0,
856
  fragLoadingTimeOut:10000,manifestLoadingTimeOut:10000
857
  });
858
  _hls=hls; hls.loadSource(src); hls.attachMedia(video);
859
 
860
+ // v12: After manifest loaded, seek to live edge first, then play
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
861
  hls.on(Hls.Events.MANIFEST_PARSED,(ev,data)=>{
862
  loadEl.style.display='none';video.style.display='block';
863
+ // Seek to live edge manually since we disabled auto-sync
864
  const dur = video.duration;
865
  if(dur && isFinite(dur)){ video.currentTime = dur; }
866
  video.play().catch(()=>{});
867
  });
868
 
 
 
 
 
 
 
 
 
 
 
 
869
  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);}}});
870
  }else if(video.canPlayType('application/vnd.apple.mpegurl')){
871
  video.src=src;