Playwright discovery against the live feed found every link, then the whole
chain was replayed in plain urllib with no browser at all:
SSESS cookie -> GET /oauth/browser-token -> JWT (90 min)
JWT + x-api-key -> GET /v1/entry/{id} -> hdnts master URL (~25s)
master -> hdntl token (24h) -> ffmpeg
Both auth hops live off the Akamai CDN, so plain Python reaches them; ffmpeg
handles the CDN, which is the only host Python gets 403s from. Nothing needs
a fingerprint forged and Camoufox is now a fallback rather than the plan.
Two assumptions this disproved:
- The play click is not part of auth. Manifests arrive before any click, so
a headless run needs no interaction to mint a token.
- The 90 minute JWT does not constrain a 2 hour recording. It is spent before
ffmpeg starts; the 24h hdntl token carries the match.
discover.py drives the page with Playwright and identifies the mint and token
endpoints by what they RETURN rather than by guessing endpoint names, so it
survives the endpoints being renamed. It rides existing Firefox cookies and
needs no password. Verified working with --headless.
Sniffer v0.5 adds muted autoplay: the browser only has to trigger the manifest
fetch, ffmpeg records real audio separately, and muted autoplay is permitted
where audible is not. It stands down once playback starts, because the control
is a toggle and clicking twice would pause the recording.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kyt7QHQvLJtQRuiY4wAqKB
The feed switches to a static league slate and keeps serving at full CBR
bitrate. No end marker, and no stall to detect, so neither of the obvious
exit conditions fires. The duration cap is the only stop condition.
Observed on a live match: 116 min recorded, still running 2h04m after
kickoff, stopped manually.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kyt7QHQvLJtQRuiY4wAqKB
Verified end to end against a live match: 1080p H.264/AAC MPEG-TS at
~5.2 Mbit/s, recorded while the browser played the same stream.
Components:
- tampermonkey sniffer hooks fetch/XHR at document-start, captures the
manifest URL verbatim and POSTs it to the local catcher. v0.4 also traces
the API chain to find which call mints the token.
- catcher receives the URL and launches yt-dlp. Dedupes on the Kaltura asset
id and checks downloader liveness, so a mid-match page reload is both safe
and the recovery path for a dead downloader.
- creds stores secrets in the OS vault rather than a gitignored file, and
refuses to run on a backend that does not encrypt.
Protocol notes are in the README: the stream uses a two-stage token chain
(a ~25s hdnts token for the master playlist, a ~24h hdntl token for media),
concurrency is unrestricted, and the live window is ~30s with no DVR.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kyt7QHQvLJtQRuiY4wAqKB