Kinescrape — web app that downloads from Kinescope.
Paste a link. Get an MP4. Nothing touches disk on the server.
Run locally · Docker · API · CLI · How it works
Kinescrape is a web app that downloads from Kinescope. Drop in a Kinescope URL, embed snippet, or raw page HTML. It finds the video, fetches the manifest, decrypts ClearKey streams when keys are reachable, muxes audio + video, and streams the MP4 (or a multi-video ZIP) straight back to your browser.
No queue. No upload. No saved files on the server. Output bytes pass through memory and out the response.
Today you get two flavors of Kinescope downloader. Hosted web apps run on someone else's server: your links sit in their logs, your traffic leaves from their datacenter, and one Kinescope player tweak knocks the whole site offline for everyone. Or raw CLI scripts cobbled from yt-dlp forks and Bash one-liners — fine for geeks, useless for anyone who just wants to paste a link and get an MP4.
Kinescrape is both, on your machine. A clean web UI when you want one, a CLI when you don't, and a small Python core you can read, audit, and patch the next time Kinescope rotates a player.
- Single MP4 for one link, streamed ZIP for many
- Two muxers: ffmpeg.wasm in the browser for small clips, native
ffmpegon the server for everything else - ClearKey decrypt via Bento4
mp4decryptwhen the license endpoint is reachable - No disk I/O for outputs — server uses pipes and Linux
memfdfor encrypted segments - OpenAPI docs at
/docs, schema at/openapi.json - Hardened Docker — non-root, read-only rootfs, dropped caps, localhost-only
- SSRF guard — blocks private/loopback/link-local upstream URLs and cross-origin API calls
- CLI for headless use
python -m pip install -r requirements.txt
python web_server.py --host 127.0.0.1 --port 8000| Web app | http://127.0.0.1:8000/ |
| API docs | http://127.0.0.1:8000/docs |
| OpenAPI | http://127.0.0.1:8000/openapi.json |
ffmpeg and Bento4 mp4decrypt must be on PATH for server-side mux/decrypt. Override with FFMPEG_PATH / MP4DECRYPT_PATH.
docker compose up -d --buildBound to 127.0.0.1:8000. Logs and teardown:
docker compose logs -f kinescrape
docker compose downThe container runs UID 10001 with a read-only rootfs, all caps dropped, no privilege escalation, isolated bridge network, and tmpfs at /tmp and /run. Shells, login binaries, and package managers are stripped from the final image.
browser ──┐
│ paste link / embed / HTML
▼
┌─────────────┐ /api/extract → candidates
│ FastAPI │ /api/manifest → DASH/HLS text
│ web_server │ /api/segment → byte-range proxy
└──┬───────┬──┘
│ │
wasm mux native ffmpeg ──► StreamingResponse ──► browser
│ (mp4 / zip)
│
ClearKey? ──► /api/license → mp4decrypt (memfd) ──► ffmpeg
Single videos can be muxed in the browser with ffmpeg.wasm (smaller files, zero server CPU). Larger or DRM-protected videos go server-side: segments stream into pipes, ffmpeg muxes to pipe:1, the response yields chunks as they appear. Multi-link ZIPs use the same pipeline per entry, written into an on-the-fly ZIP_STORED archive.
| Method | Path | Purpose |
|---|---|---|
GET |
/api/health |
health check |
POST |
/api/extract |
find Kinescope video candidates in URL/embed/HTML |
POST |
/api/resolve |
resolve a source down to a single video ID |
POST |
/api/manifest |
proxy a DASH/HLS manifest |
POST |
/api/title |
oEmbed title + thumbnail |
POST |
/api/license |
fetch a ClearKey key |
POST |
/api/segment |
byte-range proxy a single media segment |
POST |
/api/server-mux |
stream one server-muxed MP4 |
POST |
/api/server-zip |
stream a ZIP of server-muxed MP4s |
curl -X POST http://127.0.0.1:8000/api/extract \
-H 'Content-Type: application/json' \
-d '{"source":"https://kinescope.io/VIDEO_ID","referer":""}'Full schemas, request/response examples, and try-it-now console at /docs.
python kinescrape.py https://kinescope.io/VIDEO_ID output.mp4
python kinescrape.py --best-quality https://kinescope.io/VIDEO_ID output.mp4
python kinescrape.py --referer https://example.com/course https://kinescope.io/VIDEO_ID output.mp4FFMPEG_PATH=/path/to/ffmpeg \
MP4DECRYPT_PATH=/path/to/mp4decrypt \
pyinstaller kinescrape.spec| Path | What lives there |
|---|---|
web/ |
UI, logo, favicon, ffmpeg.wasm worker glue |
web_server.py |
FastAPI app, static host, upstream proxy, server mux, ZIP streaming, ClearKey helpers |
kinescrape/ |
Kinescope parsing + downloader library |
kinescrape.py |
CLI entry |
Dockerfile, docker-compose.yml |
Hardened local runtime |
- Cross-origin calls to
/api/*are rejected. Use the bundled UI or call from same origin. - Upstream URLs resolving to private, loopback, or link-local IPs are refused (SSRF guard).
- ClearKey decrypt requires Linux
memfd_create. Other platforms can still mux unencrypted streams. - Use only on videos you are allowed to download.
MIT. See LICENSE.