fix: improve HLS.js video detection and playback capture
CI / test (pull_request) Failing after 16s

- Add Hls.cast(video) detection (Strategy B1) — uses the official hls.js
  API to retrieve bound instances from <video> elements
- Add _goon_hls_manifest annotation check (Strategy B2) — reads interceptor-set
  manifest URLs directly on video elements
- Refactor scrapeFn to a 5-priority HLS detection chain:
  1. Direct .m3u8 in video.src
  2. Interceptor-set _goon_hls_manifest
  3. Hls.cast(video) official API
  4. Direct HTTP source (non-blob)
  5. Deep probe fallback (page globals/scripts)
- Replace fragile Object.defineProperty Hls detection with polling interval
  in interceptors.js — avoids conflicts with page JS
- Add buffered range tracking and duration to content.js video scan data
- Add 8 new tests for HLS detection priority chain (55 total)
This commit is contained in:
robot committed 2026-08-12 00:49:21 +00:00
1 parent 7276824758
commit bc3f37ae4c
4 files changed
+212 -13

No files matched your search

+45 -2
View File
@@ -138,6 +138,27 @@ function m3u8DeepProbeFn() {
}
// Strategy B: Video element HLS instances
// B1: Use Hls.cast(video) — the official hls.js API to get the bound instance
if (typeof Hls !== 'undefined' && typeof Hls.cast === 'function') {
for (const v of document.querySelectorAll('video')) {
try {
const hlsInst = Hls.cast(v);
if (hlsInst && hlsInst.url) {
if (!seen.has(hlsInst.url)) { seen.add(hlsInst.url); manifests.push({ url: hlsInst.url, player: 'Hls.cast' }); }
}
} catch { /* ignore */ }
}
}
// B2: Check interceptor-set manifest annotation on video elements
for (const v of document.querySelectorAll('video')) {
if (v._goon_hls_manifest && !seen.has(v._goon_hls_manifest)) {
seen.add(v._goon_hls_manifest);
manifests.push({ url: v._goon_hls_manifest, player: 'video._goon_hls_manifest' });
}
}
// B3: Fallback — walk video element keys looking for HLS-related objects
for (const v of document.querySelectorAll('video')) {
for (const key of Object.keys(v)) {
if (key.toLowerCase().includes('hls') || key === 'player') {
@@ -200,12 +221,34 @@ function scrapeFn(hlsManifests) {
let mediaType = 'video';
let src = rawSrc;
// Priority 1: Direct .m3u8 source (rare but possible)
if (rawSrc.includes('.m3u8') && rawSrc.startsWith('http')) {
hlsManifest = rawSrc;
mediaType = 'hls';
} else if (rawSrc.startsWith('http') && !rawSrc.startsWith('blob:')) {
}
// Priority 2: Interceptor annotation — Hls.attachMedia() patched by interceptors
else if (v._goon_hls_manifest) {
hlsManifest = v._goon_hls_manifest;
mediaType = 'hls';
src = hlsManifest;
}
// Priority 3: Hls.cast(video) — official hls.js API (works when Hls is loaded)
else if (typeof Hls !== 'undefined' && typeof Hls.cast === 'function') {
try {
const hlsInst = Hls.cast(v);
if (hlsInst && hlsInst.url) {
hlsManifest = hlsInst.url;
mediaType = 'hls';
src = hlsManifest;
}
} catch { /* ignore */ }
}
// Priority 4: Direct HTTP source (non-blob)
if (!hlsManifest && rawSrc.startsWith('http') && !rawSrc.startsWith('blob:')) {
mediaType = 'video';
} else if (hlsManifests && hlsManifests.length > 0) {
}
// Priority 5: Deep probe fallback — use manifests found by scanning page globals/scripts
if (!hlsManifest && hlsManifests && hlsManifests.length > 0) {
hlsManifest = hlsManifests[0].url || hlsManifests[0];
mediaType = 'hls';
src = hlsManifest;