ext: content/ media detection (build order step 7)

Two independent signals feed one panel, per docs/05 §3:
- capture/media.ts (background): webRequest-based, recognizes .m3u8/.mpd
  URLs and the HLS/DASH content types, deduped per (tab, url) so an
  HLS live-refresh doesn't re-fire. media-bridge.ts relays a hit to the
  tab's content script over runtime.sendMessage, and separately answers
  the content script's media.listVariants/media.addVariant calls by
  forwarding them to the background page's transport.
- content/media-observer.ts: watches the page's own <video> elements
  (present now, added later, or with src changed) for the same
  extension signal, independent of what the network sniffer saw.

Either firing opens content/video-panel.ts's "Download this video ▾"
panel (built with createElement, matching the popup's innerHTML-free
approach), which lists variants from media.listVariants and greys out
any variant.drm or a wholly drmProtected manifest with "Protected
content" rather than attempting it. The extension still never parses a
manifest itself — that stays in the daemon, one language, one place.

content/index.ts is the manifest-registered entry (content_scripts in
manifest.json, added in the previous commit); build.mjs builds it as an
IIFE rather than ESM, since a manifest content script has no "type":
"module" declaration and an emitted top-level export would be a syntax
error there. tsconfig.json adds DOM.Iterable for NodeList iteration.

docs/05-extension-spec.md gets a short addendum (§8) documenting the
popup/options bridge and this media-detection split, since neither was
in the original design write-up.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ed8KEmAW48v4YHdxLtqsMB
This commit is contained in:
2026-09-11 12:26:48 +04:00
co-authored by Claude Sonnet 5
parent 55932a2e11
commit 03b6253b5a
11 changed files with 787 additions and 2 deletions
+31 -1
View File
@@ -162,4 +162,34 @@ extension/
`<all_urls>` is unavoidable for a download manager but is the main review-friction item:
document *why* in the AMO submission notes and in the source, and make the exclusion list
prominent in Options.
prominent in Options. The submission-ready copy of this table lives in
`extension/docs/amo-permissions.md`, committed alongside the manifest it explains.
## 8. Popup and Options talk to the background page over a relay, not directly
`popup/` and `options/` are separate documents (a browser action popup and an
`options_ui` page) — they cannot import `background/index.ts`'s live `VeloxTransport`.
`background/bridge.ts` relays it over one `browser.runtime.connect` port per document:
`call`/`subscribe`/`getStatus`/`reconnect`/`pair`/`unpair`/`setOverride` requests in,
`result`/`event`/`status`/`pairError` responses out. `shared/panel-client.ts` is the
client side both surfaces use. The status payload carries `kind` (which transport
implementation is live) alongside the existing `TransportStatus`, because Options needs
it: `settings.set` and `rules.upsert` are privileged, uds-only methods (see
`shared/protocol/methods.ts`'s `METHODS` table), so the capture-policy edit form only
ever unlocks when the active transport is native messaging. Over WebSocket — the
default, guaranteed path per ADR 0003 — Options renders the daemon's policy read-only
via `capture.getRules` rather than pretending it can write settings the protocol
refuses over that transport. The one Options setting that genuinely belongs to the
extension (not the daemon) — the default category applied to extension-initiated
downloads — lives in `browser.storage.local` via `options/prefs.ts`, the same place the
pairing token and transport override already live.
Streaming-media detection (build step 7) also splits down this line: `capture/media.ts`
(background, webRequest-based) recognizes `.m3u8`/`.mpd` URLs and their content types
and tells the tab's content script over `runtime.sendMessage`
(`background/media-bridge.ts`); `content/media-observer.ts` independently watches the
page's own `<video>` elements for the same signal. Either one showing up opens
`content/video-panel.ts`'s "Download this video ▾" panel, which calls
`media.listVariants`/`media.addVariant` through the background page and greys out any
variant (or the whole manifest) flagged `drm`/`drmProtected` with "Protected content" —
the extension never parses the manifest itself.