The video-detection pipeline was finding nothing on pages that have a bare
<video> element. Two root causes:
1. world: 'MAIN' placement. The deep probe + scraper are injected with
chrome.scripting.executeScript. Their code (and its own comments) requires
the MAIN world to see page globals — window.Hls, per-<video> Hls instances,
video._goon_hls_manifest, and window.__goon_plugin_data (written by the
main-world interceptors). The calls were missing it, so every main-world
strategy (A/A2/E/H) found 0. It was also briefly placed inside `target`,
which chrome.scripting rejects ("Unexpected property: 'world'"); `world` is
a sibling of `target` on ScriptInjection. Restore it as a sibling of target.
2. fake "manifest" classification. Strategy H was reporting 10 nav links /
server-side route TEMPLATES as manifests — e.g. /playlists/,
/playlists/liked/, /playlist/%1%/video/%2%/, /members/%ID%/playlists/ —
because the old /playlist|/manifest substring check matches nav menus and
leaves %1%/%ID%/%DIR% template placeholders intact. Those then became the
stored "video" entries, drowning out the real element.
isManifestUrl and isValidManifestUrl now reject:
- dynamic route templates (%NAME% or {name} tokens), using a negative
lookahead so genuine %-encoded tokens like %2B/%2F are preserved
(the invariant the codebase explicitly protects),
- plural collection paths (/playlists, /manifests),
- trailing-slash directory listings.
The 'World: ISOLATED' probe log was also misleading (typeof chrome is not a
world discriminator — the probe provably runs in MAIN); replaced it.
Adds a regression test (hls_qa) reproducing thisvid's pageContext.langUrls
scenario: all 10 templates are rejected while a real .m3u8 carrying a %2B token
is still detected. 90/90 tests pass; 0 lint errors; build OK.
Address robot's review on PR #5:
- [Blocking] 'goon_referers' is a host-keyed GLOBAL store and must outlive
its capture tab; it was wrongly added to the per-tab onRemoved cleanup, so
closing any tab wiped referers captured by still-open source tabs (-> 412).
It is no longer removed on tab close. Stale referers are now pruned
opportunistically in persistReferer via a per-host timestamp + TTL.
- [Should-fix] the DNR referer rule used a single shared REFERER_RULE_ID with
a non-atomic check+update, so concurrent proxy fetches for two hosts could
interleave and leave the wrong host's referer live. Each host now gets its
own stable rule id (refererRuleIdFor) and the install+record is serialized
per host via an async lock (refererRuleLocks).
- [Coverage] add __tests__/referer_proxy.test.mjs exercising the new
referer-capture / proxy-host / onRemoved paths: closing a tab preserves
goon_referers, two hosts proxied concurrently keep distinct per-host rules,
and same-host proxy is idempotent.
The extension had hard-coupled itself to one provider's CDN throughout
background.js and its consumers: a host allowlist, a static
declarativeNetRequest rule that forced a fixed site origin as the Referer,
site-named message types/functions, and illustrative comments naming the
site. This commit removes every reference so the code is generic and
provider-agnostic (and no longer names a specific site for reviewers).
What changed:
- background.js
* Dropped the PHNCDN_HOSTS allowlist; video-request capture is now
host-agnostic (any CDN, driven by isVideoRequest / isHlsManifest).
* Removed the static DNR rule that forced a fixed site origin as the
Referer. Replaced with per-tab dynamic capture: an
onBeforeSendHeaders listener captures the provider's own Referer for
each video request the live page makes and stores it per CDN host
(goon_referers). The proxy handler installs a DNR rule scoped to the
requested host using that captured referer, so the provider's exact
origin is extracted at runtime instead of hard-coded.
* Generalized the reconstruction helpers: extractVideoId ->
extractGroupKey (path-directory key, no /hls/videos/ assumption),
buildManifestForVariant / reconstructManifestsFromSegments swap a
segment filename for master.m3u8 dir-agnostically, freshLiveForVideo
-> freshLiveForGroup, and the GOON_GET_PHNCDN_REQUESTS handler ->
GOON_GET_VIDEO_REQUESTS. Resolver response key videoId -> groupKey
* Reworded prose comments to describe behavior generically.
- manifest.json
* Added the extraHeaders permission so onBeforeSendHeaders can read the
Referer header (needed for the dynamic capture above).
- viewer.js / index.js (consumers)
* Proxying decision now keys off a learned goon_proxy_hosts set
(populated from a startup CORS probe) instead of hard-coded host
substrings; removed the hard-coded CORS ping.
* filterManifests / isValidManifestUrl generalized (kept the
.m3u8 / .hlsvod / .ts behavior the tests assert); rank key
phncdn_reconstructed -> hls_reconstructed; queryPhncdnRequests ->
queryVideoRequests.
- content.js, interceptors.js, and two test-prose strings: the illustrative
"sites like <site>" comments generalized.
Verification: 83/83 tests pass, 0 lint errors. A repo-wide grep for the
previously-referenced host / site now returns no matches. The pre-existing
tsc errors in vite.config.ts are unrelated (present on the prior commit).
Note on the dynamic referer: it works while the source tab is open (it reads
that tab's real Referer); if the tab closed before capturing, the proxy falls
back to a host-derived origin, which a strict origin check may still reject --
the tradeoff of not hard-coding the site.
Problem: Even after fixing the hls.js file naming (was 'hls.min.js', now
'hls.js'), videos don't play in the viewer. Likely root cause is CORS
blocking chrome-extension:// requests to phncdn.com CDN URLs that lack
CORS headers for extension origins.
Solution — two-layer fix:
1. Service worker proxy (background.js):
- New GOON_PROXY_FETCH handler fetches any URL via native fetch()
(no CORS restrictions in the service worker)
- Returns base64-encoded response data to the viewer
2. Custom HLS loader (viewer/viewer.js):
- HlsLoader implements hls.js Loader interface
- For phncdn.com/pornhubcdn URLs: proxies via GOON_PROXY_FETCH
- For all other URLs: uses native XHR (bypasses proxy overhead)
- Config passed to hls.js only when manifest URL needs proxying
3. Diagnostics (viewer/viewer.js):
- CORS check at page load for phncdn.com and per-video URL
- Full storage dump of video entries in console
- Manifest URL logging on each HLS load attempt
- Proxy activation logging for debugging
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.
- 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