The root cause of repeated 'does not detect any videos' failures was that
m3u8DeepProbeFn and scrapeFn were injected via chrome.scripting.executeScript
in the isolated world (default). From the isolated world:
- window.__goon_plugin_data (set by main-world interceptors) — NOT visible
- window.Hls / Hls.cast (page's hls.js) — NOT visible
- Player globals (PHPlayer, flowplayer, etc.) — NOT visible
- Config globals (__NEXT_DATA__, appData, etc.) — NOT visible
Only DOM properties like video._goon_hls_manifest cross the boundary, making
the detection pipeline mostly blind on real HLS sites.
Fix: set world: 'MAIN' on both executeScript calls so all 6 deep probe
strategies and all 5 scrape priorities actually work. Chrome MV3 supports
this since Chrome 108; our build targets chrome110.
When mediaType=hls but hlsManifest field is absent (e.g. lost during
storage serialization), fall back to src if it looks like a manifest URL
(.m3u8 with http scheme). This prevents the video from silently failing
to play on Chrome where there is no native HLS support.
- Add Strategy E to m3u8DeepProbeFn: read __goon_plugin_data.manifests
from main-world interceptors (was accidentally dropped during refactoring)
- Re-add content script query in scanVideos: restores poster URL extraction
(og:image, twitter:image) and secondary manifest source from content.js
- Extend interceptor Hls.js polling from 3s to 30s + add MutationObserver
fallback for dynamically-loaded players
- Restore allFrames:true in scrapeTab: catches iframe-hosted video players
(e.g. Pornhub embeds player in nested iframes)
- Prefer interceptor-collected manifests over inline-script scraps in
both dedupManifests sort order and scrapeFn Priority 5 fallback
Vite was injecting modulepreload-polyfill.js into the built output, which
Chrome rejected at runtime with: 'A preload for .../modulepreload-polyfill.js
is found, but is not used because it is a cross-world extension resource
mismatch.'
Disabling the polyfill (extension context does not need it — Chrome 110+
supports bare ESM natively) removes the offending chunk entirely.
- Handle string entries in createPanel (e.g. 'https://...mp4')
instead of dropping them as null — restores legacy string URL support.
- Add console.log for HLS detection so integration tests can verify
HLS playback paths are exercised.
Without this, Chrome blocks chrome.runtime.getURL('interceptors.js')
in Manifest V3, causing HLS detection to silently fail.
Added regression test to prevent future removal.
- 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)
- Deep-probe HLS manifests via known player globals, inline scripts, config objects
- Scan main frame only (no nested iframes); pick largest visible video per tab
- Store mediaType/hlsManifest metadata through the pipeline
- CSP-safe interceptor injection via script.src (not inline textContent)
- Remove probeFn/scrapeFn from Vite build entries (non-existent files)
- Refactor: extract m3u8DeepProbeFn, scrapeFn, scanVideos, storeVideos,
makeVideoEntry, filterValidTabs, probeHlsManifests, dedupManifests, scrapeTab
to module-level functions (no inline closures recreated on every click)
- Clean up viewer.js: extract createVideoElement(), createPanel(),
consolidate HLS_CONFIG, remove console.log noise
- Add DEBUG flag to gate console output in popup
- Unify url/src fields — prefer 'src' everywhere
- Remove dead code in renderList and setTimeout(100) hack
- Query HLS data structures directly from page JS state per tab
- Strategies: video element HLS instances, window-level player vars,
intercepted data, inline scripts, global config objects, content script
- Fixed merge logic: index.js now correctly processes interceptedManifests
and hlsQueryResults instead of non-existent playerData
- Skip blob: URLs in DOM scan (HLS-internal, can't be played directly)
- Only attach HLS manifest when actually detected for that tab
Previous approach (DOM snapshot via executeScript) failed on Pornhub because:
- Video player loads asynchronously via JavaScript
- HLS manifest arrives via XHR/fetch AFTER page render
- <video> element only shows blob: URL, not the actual manifest
New approach:
- Content script injects interceptor into MAIN world at document_start
- Interceptor patches XMLHttpRequest and fetch() to capture .m3u8 URLs
- Periodic DOM scans detect HLS player instances on <video> elements
- Global config object scanning for embedded stream URLs
- Popup queries content script via chrome.tabs.sendMessage for real-time data
- If no DOM videos found but content script has data, create entries from intercepted sources
Changes:
- content.js: Full rewrite with main-world injection + XHR/fetch interception
- index.js: Query content scripts via messages, merge with DOM snapshot
- manifest.json: all_frames + document_start for early injection
- Strategy 1: scan inline <script> blocks for .m3u8 URLs (skip subtitles/captions)
- Strategy 2: check video element HLS player instances (hlsInstance, _hls)
- Strategy 3: probe common global config objects (hlsConfig, playerConfig, etc.)
- Storage items now include hlsManifest and mediaType fields
- When HLS detected and src is blob:/empty, promote manifest URL to primary
- Backward compatible: direct MP4 entries unaffected
- Remove the hardcoded 4-tab limit in the find-videos flow
- Add per-tab try/catch so inaccessible tabs are skipped gracefully
- Filter out tabs that produced no frames from the dedup pipeline
- Update button label and status messages accordingly