fix: HLS playback tracking and detection fallback for sandboxed iframes
CI / test (pull_request) Successful in 23s
CI / test (pull_request) Successful in 23s
Viewer fixes (6): - FRAG_LOADING: only set buffering phase when video is actually stalled (paused or readyState < HAVE_FUTURE_DATA), prevents overlay flicker - FRAG_BUFFERED: allow playing transition from any phase except error, require readyState >= 3 and video not paused - Add FRAG_DECRYPTED, FRAG_PARSING_INIT_SEGMENT, FRAG_PARSING_USERDATA event tracking for full fragment pipeline visibility - BUFFER_RESET: also reset segmentsBuffered counter (was missing) - LEVEL_SWITCHED: set phase to 'switching' for accurate UI feedback - Fatal network errors: track in segmentsFailed counter - Add ENDLIST event handler for stream completion detection Detection fixes: - Add filterManifests(): prefer .m3u8 manifest URLs over .ts segments, reconstruct manifest URL from segment paths when needed - Add fallback in scanVideos(): when scrapeFn returns no visible <video> elements (sandboxed iframe), create video entries from intercepted manifest data. Fixes '0 videos detected' issue on Pornhub and similar sites where the video player is in an inaccessible iframe.
This commit is contained in:
1 parent
3cf402d3c7
commit
e11be9a20a
2 files changed
+132
-7
No files matched your search
+48
-3
@@ -338,8 +338,14 @@ function wireHlsEvents(hls, video, panel) {
|
||||
});
|
||||
|
||||
// --- Segment / fragment tracking ---
|
||||
// FRAG_LOADING fires continuously during playback for each fragment.
|
||||
// Only transition to buffering when the video is actually stalled
|
||||
// (paused or readyState < HAVE_FUTURE_DATA). This prevents the overlay
|
||||
// from flickering between "buffering" and "playing" during smooth playback.
|
||||
hls.on(HlsEvents.FRAG_LOADING, (_ev, _data) => {
|
||||
state.phase = 'buffering';
|
||||
if (video.paused || video.readyState < 3) {
|
||||
state.phase = 'buffering';
|
||||
}
|
||||
state.lastEvent = 'FRAG_LOADING';
|
||||
state.lastActivity = Date.now();
|
||||
renderPanelState(video, panel);
|
||||
@@ -352,15 +358,41 @@ function wireHlsEvents(hls, video, panel) {
|
||||
renderPanelState(video, panel);
|
||||
});
|
||||
|
||||
// FRAG_BUFFERED: transition to playing only when there's meaningful
|
||||
// buffer ahead (readyState >= HAVE_FUTURE_DATA) and video is running.
|
||||
// Allow transition from any phase except 'error' — not just 'buffering'
|
||||
// — because the video may go straight from 'loading' to playing.
|
||||
hls.on(HlsEvents.FRAG_BUFFERED, (_ev, _data) => {
|
||||
state.lastEvent = 'FRAG_BUFFERED';
|
||||
state.lastActivity = Date.now();
|
||||
if (state.phase === 'buffering' && video.readyState >= 2) {
|
||||
if (state.phase !== 'error' && video.readyState >= 3 && !video.paused) {
|
||||
state.phase = 'playing';
|
||||
}
|
||||
renderPanelState(video, panel);
|
||||
});
|
||||
|
||||
// --- New event tracking: full fragment pipeline visibility ---
|
||||
// FRAG_DECRYPTED: DRM-protected content decryption complete
|
||||
hls.on(HlsEvents.FRAG_DECRYPTED, (_ev, _data) => {
|
||||
state.lastEvent = 'FRAG_DECRYPTED';
|
||||
state.lastActivity = Date.now();
|
||||
renderPanelState(video, panel);
|
||||
});
|
||||
|
||||
// FRAG_PARSING_INIT_SEGMENT: init segment parsed (codec info available)
|
||||
hls.on(HlsEvents.FRAG_PARSING_INIT_SEGMENT, (_ev, _data) => {
|
||||
state.lastEvent = 'FRAG_PARSING_INIT_SEGMENT';
|
||||
state.lastActivity = Date.now();
|
||||
renderPanelState(video, panel);
|
||||
});
|
||||
|
||||
// FRAG_PARSING_USERDATA: ID3/userdata tags in fragment (timed metadata)
|
||||
hls.on(HlsEvents.FRAG_PARSING_USERDATA, (_ev, _data) => {
|
||||
state.lastEvent = 'FRAG_PARSING_USERDATA';
|
||||
state.lastActivity = Date.now();
|
||||
renderPanelState(video, panel);
|
||||
});
|
||||
|
||||
// --- Buffer tracking ---
|
||||
hls.on(HlsEvents.BUFFER_APPENDED, (_ev, _data) => {
|
||||
state.lastEvent = 'BUFFER_APPENDED';
|
||||
@@ -381,6 +413,7 @@ function wireHlsEvents(hls, video, panel) {
|
||||
state.lastActivity = Date.now();
|
||||
state.bufferedAhead = 0;
|
||||
state.segmentsLoaded = 0;
|
||||
state.segmentsBuffered = 0;
|
||||
renderPanelState(video, panel);
|
||||
});
|
||||
|
||||
@@ -388,6 +421,7 @@ function wireHlsEvents(hls, video, panel) {
|
||||
hls.on(HlsEvents.LEVEL_SWITCHED, (_ev, data) => {
|
||||
state.lastEvent = 'LEVEL_SWITCHED';
|
||||
state.lastActivity = Date.now();
|
||||
state.phase = 'switching';
|
||||
state.level = data.level;
|
||||
const levels = hls.levels;
|
||||
if (levels && levels[data.level]) {
|
||||
@@ -450,9 +484,10 @@ function wireHlsEvents(hls, video, panel) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fatal errors
|
||||
// Fatal errors — attempt recovery before giving up
|
||||
if (data.type === window.Hls.ErrorTypes.NETWORK_ERROR) {
|
||||
state.errorMessage = `Network: ${data.details}`;
|
||||
state.segmentsFailed++;
|
||||
state.phase = 'error';
|
||||
renderPanelState(video, panel);
|
||||
showError(panel, 'Stream expired - re-scan to get fresh URLs.');
|
||||
@@ -461,9 +496,11 @@ function wireHlsEvents(hls, video, panel) {
|
||||
} else if (data.type === window.Hls.ErrorTypes.MEDIA_ERROR) {
|
||||
state.errorMessage = `Media: ${data.details}`;
|
||||
state.phase = 'buffering';
|
||||
state.lastActivity = Date.now();
|
||||
renderPanelState(video, panel);
|
||||
hls.recoverMediaError();
|
||||
} else {
|
||||
// Unknown fatal error — try recovering once before destroying
|
||||
state.errorMessage = `Fatal: ${data.details}`;
|
||||
state.phase = 'error';
|
||||
renderPanelState(video, panel);
|
||||
@@ -472,6 +509,14 @@ function wireHlsEvents(hls, video, panel) {
|
||||
hlsInstances.delete(video);
|
||||
}
|
||||
});
|
||||
|
||||
// --- Stream completion ---
|
||||
hls.on(HlsEvents.ENDLIST, () => {
|
||||
state.lastEvent = 'ENDLIST';
|
||||
state.lastActivity = Date.now();
|
||||
state.phase = 'ended';
|
||||
renderPanelState(video, panel);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user