# wvkey — Widevine CDM helper The only Python in the project. Go (`apps/pkg/wvkey`) shells out to it to turn a PSSH plus a license endpoint into `KID:KEY` lines via pywidevine. ```text apps/wvkey/ wvkey.py requirements.txt ``` Keep it thin: it does the CDM exchange and nothing else. ## Setup ```bash # from the repo root python -m venv .venv .venv/bin/pip install -r apps/wvkey/requirements.txt # Windows: .venv\Scripts\pip ``` Put your Widevine device file somewhere gitignored, e.g. `data/device.wvd`. The Go side finds `.venv` automatically; override with `--python`. Every unique value is a flag. There are no baked-in device paths, account URLs or user agents. ## Run Two license shapes: ```bash # JSON challenge wrapper with an Authorization header .venv/bin/python apps/wvkey/wvkey.py \ --mode modulardrm \ --wvd data/device.wvd \ --pssh '' \ --auth 'Bearer ...' \ --pid '' \ --license-url 'https://...' \ --quiet # raw binary challenge (octet-stream) .venv/bin/python apps/wvkey/wvkey.py \ --mode raw \ --wvd data/device.wvd \ --pssh '' \ --license-url 'https://...' \ --quiet ``` Options: `--user-agent`, `--all` (print every CONTENT key — needed for multi-KID streams). Which mode an app needs is declared by its module (`KeyMode()`), never sniffed from the license URL at runtime. See [docs/capture.md](../../docs/capture.md) for how to tell them apart from a capture. Go callers use `wvkey.Fetch` / `FetchRaw` / `FetchRawAll` and always pass `Script`, `WVD` and `LicenseURL`.