必须自行安装 Chrome 和 ffmpeg + ffprobe 7.1+ 并加入 PATH;页面里的视频必须无需点击即可自动播放,且拒绝 DRM 流;设备发现只在同一子网内有效,Android/Termux 上需手动设置 device.host 为电视 IP;使用 TUI 浏览片源需自备 TMDB API key 并配置 sources。
castor
stupsideMIT活跃维护把任意网页里的视频抓出来,实时转码后投到电视上播放,还能把字幕烧进画面。
Point it at any web page and it finds the video, transcodes it, and streams it to your TV in real time. It even burns in subtitles.
brew install --cask stupside/tap/castor # macOS
npm install -g @castor-dev/cli # any platform, Node 20+README 未说明显存要求;转码优先使用 ffmpeg 可用的硬件编码器,否则回退到软件编码;Docker 镜像在 Intel GPU 上通过 VA-API 编码(需加 --device /dev/dri)。
把任意网页里的视频抓出来、转码并实时投到电视上,连字幕一起烧进去
把任意网页里的视频抓出来,实时转码后投到电视上播放,还能把字幕烧进画面。
核心能力
- 用无头 Chrome 打开页面并捕获视频流
- 用 ffmpeg 按电视能力实时转码并烧入字幕
- 支持 DLNA、Chromecast、Roku 三种投屏协议
- castor scan 自动发现局域网内的电视
- 拆成 API、抓取、媒体三个服务,可单独部署到 NAS
容易踩的坑
- v2.5.0最新正式版2026-10-27 · 2026-10-27 21:55 UTC
2.5.0 (2026-10-27)
Features
- cast: let the interactive cast run without a configured device (61c8490)
- settings: read the user's config directory, then the working directory's (a726b44)
Bug Fixes
- deps: move to Go 1.27.3 and next 16.4.3 for known vulnerabilities (f16ef1f)
- settings: check required sections and name failures by config key (71a077c)
Documentation
- architecture: update the architecture documentation (15d0616)
- contributing: require Go 1.27.1 and set out what castor will not take (54d4398)
- give telemetry its own section in CONTRIBUTING (ea35704)
- readme: add a table of the platforms castor runs on (2410b80)
- readme: list community projects, starting with castor-android (435ea38)
- readme: rewrite as a short onboarding page (02ef689)
- security: report vulnerabilities privately and warn about open servers (61e4908)
Continuous Integration
- scan Go and app dependencies for vulnerabilities (87307d4)
- v2.4.02026-10-26 · 2026-10-26 15:50 UTC
查看更新说明
2.4.0 (2026-10-26)
Features
- cast: begin a cast at a position (c3fde9f)
- cast: follow the device's link while it plays (c7c3af8)
- cast: go back to a better ranked link once the cast plays well (0162f91)
- cast: let the device play out what it took before stopping (d0caa8d)
- device: report the device's position and its viewer's pause (0408dd5)
- dlna: seek within the stream castor serves (cc89147)
- rank: rank links by the language of their sound first (b833922)
Bug Fixes
- cast: count what the encoders made where they read the buffer (30bc249)
- dlna: drop Samsung's sec:CaptionInfoEx (3e8b521)
- execute: keep every failure an attempt reports (8f7f9ee)
- probe: weigh each audio track on its own bitrate (fd3a603)
- stream: keep a paused device's connection open (931677f)
Refactors
- v2.3.02026-10-22 · 2026-10-22 13:56 UTC
- v2.2.02026-10-22 · 2026-10-22 11:24 UTC
- v2.1.02026-10-22 · 2026-10-22 06:39 UTC
查看更新说明
2.1.0 (2026-10-22)
Features
- release: publish the cli to npm through trusted publishing (aa2510c)
- telemetry: namespace OTel names and make logs say why (e993388)
- telemetry: record state machines as one span per state (7aaf50b)
- telemetry: second-scale duration buckets, semconv 1.43 and service version (217f29d)
Bug Fixes
- codec: weigh a video encode above an audio one when picking a format (d466da9)
- release: restore postflight_steps in the brew cask (#73) (3d022c0)
Refactors
- release: give brew its own job and run both channels after publish (0b8d943)
Documentation
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
Castor
Cast web video to your TV, even when it has no built-in casting like Chromecast. Point it at any web page and it finds the video, transcodes it, and streams it to your TV in real time. It even burns in subtitles.
It runs as one command on your laptop, or as services you can move to a NAS.
[!IMPORTANT] Castor ships no content and no sources, and refuses DRM streams. Use it only for video you are allowed to watch.
Install
brew install --cask stupside/tap/castor # macOS
npm install -g @castor-dev/cli # any platform, Node 20+
Or download the latest release, or build from source.
[!NOTE] Castor needs Chrome and ffmpeg + ffprobe 7.1+ on your
PATH. On Windows:winget install Google.Chromeandwinget install Gyan.FFmpeg, then allow Castor on private networks.
Quick start
Settings go in a config.yaml (where).
Cast a page or a video URL
Give castor a page that plays a video: it finds the stream, converts what your TV can't play, and starts playback.
-
Find your TV:
castor scan -
Add it to
config.yaml, with the name and type the scan printed:device: name: "Living Room TV" type: dlna # dlna, chromecast, or roku -
Cast a page, or a direct stream or video file:
castor cast player https://example.com/watch/some-video castor cast url https://example.com/video.m3u8
Useful flags: --at 1h12m to start at a position, --dry-run to list the streams found without casting, --debug to see why castor does what it does.
Browse titles in the TUI
castor cast opens a title browser in your terminal: search TMDB or browse what is trending, pick a title and a TV, and castor finds it on your sources.
-
Add a free TMDB key and the sites you are authorized to use:
tmdb: api_key: "<KEY>" sources: - proxies: ["https://your-source.example"] templates: movie: "/embed/movie/{itemID}" episode: "/embed/tv/{itemID}/{season}-{episode}" -
Run it. The configured device, if any, is preselected:
castor cast
To skip the TUI, castor cast movie <id> and castor cast episode <id> --season N --episode N cast to the configured device.
How it works
Castor is one binary made of three services:
- API server finds your TVs and controls playback. Every client talks to it.
- Scraping server opens the page in headless Chrome and captures the video stream.
- Media server transcodes with ffmpeg when your TV needs it, and serves the stream.
flowchart LR
client["castor CLI / TUI<br/>or another app"] -- castor.v1 API --> api["API server"]
api --> scraping["Scraping server<br/>(Chrome)"]
api --> media["Media server<br/>(ffmpeg)"]
api -- controls playback --> tv["TV"]
media -- video --> tv
castor cast starts all three in its own process, so there is nothing to set up. Each one is also a command (castor api-server, castor media-server, castor scraping-server) you can run on another machine. Give castor its URL in config.yaml and it uses that server; anything without a URL still runs locally.
| Setup | Run on the NAS | Add to your config.yaml |
|---|---|---|
| Everything on your computer (default) | nothing | nothing |
| Transcoding on the NAS | castor media-server | servers.media.url and its token |
| Everything on the NAS | docker compose up -d | servers.api.url and its token |
Configuration
The annotated config.yaml describes every setting. Castor reads it from your user config directory or the current directory:
| System | User config directory |
|---|---|
| Linux | ~/.config/castor/ |
| macOS | ~/Library/Application Support/castor/ |
| Windows | %AppData%\castor\ |
Layers merge in this order, each over the one before:
castor/config.yamlin your user config directoryconfig.yamlin the current directory- the file passed with
--config <path> CASTOR_environment variables, such asCASTOR_CAST__MAX_HEIGHT=720
Keep secrets, like your TMDB key, in a git-ignored config.local.yaml next to the file. Coming from 1.x? See the migration guide.
Support
TVs
| Protocol | Status | Reported working on |
|---|---|---|
| DLNA / UPnP | Main target | Samsung Q70 and Q80 (#15), Philips 50PUD6654 (#23) |
| Chromecast | Experimental | Xiaomi TV Box (#5), LG C2 in Chromecast mode, from the Android client (#68) |
| Roku | Experimental | Not tested on hardware yet |
Also reported working, without saying which protocol:
- Hisense VIDAA TV, in the device discussion
Cast to another TV? Tell us how it went in the What device do you cast to? discussion.
- Enable Developer Mode: on the remote, press Home three times, Up twice, then Right Left Right Left Right, and set a web server password.
- Put that password in
servers.api.devices.roku.passwordinconfig.local.yaml. - Cast: castor installs its channel the first time.
Where castor runs
| Platform | Install with | Notes |
|---|---|---|
| macOS, Intel and Apple silicon | Homebrew, npm, release | |
| Linux, x86-64 and arm64 | npm, release, Docker | The Docker image encodes on Intel GPUs (VA-API) |
| Windows, x86-64 and arm64 | npm, release | Allow castor on private networks |
| Android, in Termux | Not documented yet | Reported by @d4nnABR (#24). Discovery fails there, so set device.host to the TV's IP |
| Android, as an app | castor-android | A community project |
Castor uses a hardware encoder when ffmpeg has one that works, and software otherwise.
Self-host on a NAS
The Docker image bundles Chrome and ffmpeg. Run it with Linux host networking, so the NAS can find your TVs and they can reach it.
Transcoding only
Your computer still controls the TV; the NAS transcodes.
-
On the NAS, with
servers.media.tokenin itsconfig.yaml(add--device /dev/drifor Intel VA-API):docker run -d --network host -v "$PWD/config.yaml:/config.yaml:ro" \ ghcr.io/stupside/castor:latest media-server -
On your computer:
servers: media: url: http://my-nas:8410 token: "<same token as the NAS>"
The whole stack
The NAS runs all three services; your computer, and any other client, only sends commands.
-
On the NAS, next to docker-compose.yml and a
config.yaml(it can be empty), put one token per service in.env:CASTOR_SERVERS__API__TOKEN=<random> CASTOR_SERVERS__MEDIA__TOKEN=<random> CASTOR_SERVERS__SCRAPING__TOKEN=<random> -
Run
docker compose up -d. Remove its/dev/drilines if the NAS has no Intel GPU. -
On your computer:
servers: api: url: http://my-nas:8411 token: "<CASTOR_SERVERS__API__TOKEN>"
Ports, HTTPS and image tags are in ARCHITECTURE.md.
Build on the API
Everything the CLI does goes through the public castor.v1 API: list devices, find the streams on a page, start, watch or stop a cast. Use it over Connect, with generated clients or plain HTTP and JSON.
Community projects
- castor-android by @kdpkke: an Android app that casts from your phone, with castor running on it.
Built something on castor? Open a pull request to add it here.
Troubleshooting
| Symptom | Check |
|---|---|
| No devices found | Discovery stays on one subnet. Set device.host to the TV's IP (always on Android/Termux). |
| Two devices share a name | Set device.id to the ID castor scan prints. |
| Page video not found | The video must start without a click; DRM streams are refused. |
| TV loads but cannot play | Force relayed playback: CASTOR_CAST__DELIVERY=serve castor --debug cast url <url>. |
| TV cannot fetch the video | Allow Castor through the firewall; check servers.media.advertise. |
| Port | Direction | Used for |
|---|---|---|
| UDP 1900 | Discovery | Finding DLNA TVs and Rokus (SSDP) |
| UDP 5353 | Discovery | Finding Chromecasts (mDNS) |
| A random LAN port, or TCP 8410 | TV to castor | The TV fetching the video from the embedded media server, or from a standalone one |
| TCP 8411, 8412 | Clients to castor | A standalone API server and scraping server |
| TCP 8009, 8060 | Castor to TV | Controlling a Chromecast, or a Roku (port 80 to install the channel) |
A DLNA TV is controlled on whatever port it advertises.
Contributing
Bug reports, device reports and fixes are welcome: CONTRIBUTING.md explains how to build castor and what fits the project. Castor is provided as is, for personal and educational use; you are responsible for using it lawfully and within each site's terms.