Imagine buying a very nice TV and figuring out it doesn't allow casting from random websites...
- Then you switch to the longest HDMI cable you can find.
- Then you start doing screen mirroring: your computer lags, resolution tanks, nothing feels right.
Castor is a CLI that extracts video streams from websites, handles format compatibility, and casts to your TV in real time, with optional auto-generated subtitles burned directly into the video.
Note
How does extraction work?
Castor launches a headless Chrome with a randomized browser fingerprint and stealth scripts to hide automation. It listens to all network activity via Chrome DevTools Protocol, captures video streams, then runs an action pipeline: click the page, navigate into the largest iframe, solve Cloudflare Turnstile if detected, and click again as fallback.
This works on most streaming websites but won't beat sophisticated bot protection.
Any TV implementing the DLNA/UPnP MediaRenderer:1 profile works, which covers virtually every smart TV sold in the last decade: Samsung (tested), LG, Sony Bravia, Panasonic Viera, Philips, Hisense, TCL, VIZIO, Sharp. Networked players like Kodi, VLC, and Plex also work.
Run castor scan to discover devices on your network.
Warning
Experimental: implemented but untested. Contributions welcome.
| Dependency | Purpose |
|---|---|
| Chrome / Chromium | Headless stream extraction |
| ffmpeg | Transcoding |
| ffprobe | Format detection |
The Docker image bundles all three — see below.
ghcr.io/stupside/castor ships with Chrome, ffmpeg and ffprobe baked in, so the only thing you provide is a config.
# Discover devices (no config required)
docker run --rm --network host ghcr.io/stupside/castor:latest scan
# Cast, mounting config.yaml and a persistent model cache
docker run --rm --network host \
-v "$PWD/config.yaml:/config.yaml" \
-v castor-cache:/root/.cache \
ghcr.io/stupside/castor:latest \
cast player https://www.fmovies.gd/watch/movie/1315303Important
--network host is required: device discovery is SSDP multicast and the TV streams back from Castor's replay server — neither survives Docker's bridge network. Host networking is only real on Linux; on Docker Desktop (macOS/Windows) it won't reach your TV, so run the binary natively there instead.
The castor-cache volume keeps the auto-downloaded whisper models (~75 MB) between runs. Swap :latest for :v1.0.0 to pin a release.
# 1. Find your TV's name
castor scan
# 2. Set it in config.yaml, then browse and cast
castor cast browse --source vidsrccast browse opens a TUI backed by TMDB. Browse trending titles, search, pick a movie or episode, and cast. Requires a free TMDB API key.
# Browse TMDB and cast from a TUI
castor cast browse --source vidsrc
# Cast from a streaming site
castor cast player https://www.fmovies.gd/watch/movie/1315303
# Cast by IMDB ID
castor cast movie --source vidsrc tt33028778
castor cast episode --source vidsrc tt2699128 --season 1 --episode 3
# Cast a raw stream URL directly
castor cast url https://example.com/stream.m3u8
# Useful flags
castor cast movie --dry-run --source vidsrc tt33028778 # print URLs without casting
castor --debug cast player https://... # verbose logging
castor info # version / build infoconfig.yaml (override with --config). A sibling config.local.yaml overlays it and is git-ignored; put API keys there.
device:
name: "Living Room TV" # exact name from `castor scan`
type: dlna
sources:
- proxies: ["https://vidsrc-embed.ru"]
templates:
movie: "/embed/movie/{itemID}"
episode: "/embed/tv/{itemID}/{season}-{episode}"
whisper:
enable: true # auto-generated subtitles, burned into the video
# language: "fr" # default: English
# model_path: "" # default: ggml-tiny.en (~75 MB, auto-downloaded)
tmdb:
api_key: "<YOUR_TMDB_API_KEY>" # required for `cast browse`See CONTRIBUTING.md.