The vite config outputs 'hls.js' but manifest.json and viewer.js
both referenced 'hls.min.js', causing chrome.runtime.getURL() to
return a 404 — triggering 'HLS player unavailable'.
- Update web_accessible_resources entry in manifest.json
- Update loadHlsJs() chrome.runtime.getURL reference
- Add debug log of manifest URL being loaded for diagnostics
Strategy G — Independent non-enumerable + Symbol property detection on video elements.
Uses Object.getOwnPropertyNames/Symbols to catch properties hidden by enumerable:false or Symbol keys.
Walks nested objects for URL/levels/config signatures matching HLS instance heuristics.
Strategy H — Full window global enumeration with player-like key filtering.
Catches previously-unknown globals the player wrapper exposes internally.
Content script bridge — merge content-level intercepted manifests into
window.__goon_plugin_data so both interceptors and executeScript share findings.
Add phncdn_reconstructed to dedup priority ranking.
Bug fixes:
- FRAG_LOADING: stop updating lastActivity and re-rendering during active
playback. It fires continuously as hls.js proactively fetches future
segments, causing overlay flickering between buffering/playing.
- BUFFER_RESET: stop clearing segmentsLoaded. The event fires during
quality switches when hls.js flushes and rebuilds the MediaSource buffer,
but segments were already played. Clearing the counter made the overlay
show '0 seg' mid-playback.
- FRAG_BUFFERED: add bufferedAhead update immediately after a fragment is
buffered, instead of waiting for the next 1s monitor tick.
- LEVEL_SWITCHED: only set phase='switching' when buffer is low (< 2s).
With meaningful buffer the switch is instant (already-buffered segments),
so skip the intermediate state to avoid panels getting stuck.
- Buffer monitor: add change-detection so renderPanelState only fires when
phase, segments, or quality actually changed — not every second.
- wireVideoEvents: add 'ended' event handler for completeness.
- viewer.js: remove undefined state.segmentsBuffered assignment in BUFFER_RESET
- viewer.js: add missing 'switching' and 'ended' phases to PHASE_ICONS
- index.js: remove redundant Strategy B3 (duplicated B1 video-element walk)
- index.js: filter standard <video> DOM attributes from custom-property scans
(m3u8DeepProbeFn B1 and scrapeFn Priority 3) to avoid iterating built-in
properties that can never contain HLS data
- background.js: remove unconditional return true (handlers are synchronous)
and remove dead error-response for unknown message types
- types.ts: align MediaType union ('direct' | 'hls') with actual runtime
value used in code ('video' | 'hls')
- package.json: remove hls.js from dependencies (loaded from CDN at runtime,
never imported as an npm module)
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.
Hls.cast() is a TypeScript identity cast in hls.js — returns input unchanged,
NOT a reverse-lookup. Replaced with scanning video element properties for
HLS instance references real players store as custom properties.
Fixed double-patching bug in interceptors.js and expanded URL pattern
matching to cover /hls/, /playlist/, /manifest/, .hlsvod paths.
83/83 tests pass, clean build (9 modules).
- 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