Open-source project
stupside/castor avatar
stupside/castor

stupside/castor: cast a web video stream to your TV from the terminal

Point it at any web page and it finds the video, extracts the stream, transcodes it and casts in real time to your TV. It even burns subtitles….

2,383 stars87 forksGoMIT

At a glance

What is it?
Castor is a Go command line tool that points headless Chrome at a web page, extracts the video stream, transcodes it with ffmpeg and casts it to a DLNA or Chromecast device. It solves a real problem, but it depends on three external binaries and on pages that allow automated playback.
Who is it for?
Adopt castor if you already run ffmpeg 7.1 or newer and Chrome on a machine on the same LAN as your TV, and you want the real stream rather than a mirrored screen. Skip it if your TV is not DLNA or Chromecast, if you cannot install three external binaries, or if the pages you care about block automated playback.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The gap castor fills: smart TVs will not cast arbitrary web video

The README states the problem plainly: smart TVs will not cast arbitrary web video, and screen mirroring is laggy and drops resolution. Castor takes the other route. It does not mirror your screen; it finds the underlying stream on the page, extracts it, transcodes it for the TV and casts it in real time, so the TV receives the real stream at full quality rather than a re-encoded picture of your desktop.

The audience is narrow but specific. You need a TV that speaks DLNA or Chromecast, a computer on the same local network, a terminal, and enough patience to install three external tools. In exchange you get a single binary that takes a URL, or an IMDB/TMDB id resolved against sources you configure yourself, and puts it on the TV. The README is explicit that this is a general-purpose casting tool: it casts only what you point it at.

That framing matters for how you evaluate it. Castor is not a media library, not a scraper with a bundled catalogue, and not a replacement for a streaming app on the TV itself. It is a transport layer between a web page and a display device.

How castor extracts a stream: headless Chrome, the DevTools Protocol, and an action pipeline

Extraction is the interesting part. The README says castor launches headless Chrome and watches network traffic over the Chrome DevTools Protocol. That is how it discovers the media URL: it does not parse the HTML for a `<video>` tag, it observes the requests the page actually makes. This is a reasonable design, because modern players often build segment URLs in JavaScript and never put them in the markup.

Watching traffic is not enough on pages that wait for a user gesture. So castor runs what the README calls a short action pipeline to start playback: click the page, navigate into the largest iframe, and click again as a fallback. The README is honest about the boundary here: this works on pages that allow automated playback, and will not work everywhere. That sentence is the most important one in the repository for anyone evaluating reliability.

After extraction, the pipeline is conventional. ffprobe detects the source format, ffmpeg transcodes (or stream copies), and the result goes to the device. The Go module list confirms the shape of the stack: chromedp for browser control, go-chromecast for the Chromecast side, goupnp for UPnP/DLNA discovery, grafov/m3u8 for HLS playlists, and koanf with a YAML parser for configuration. Subtitle burning uses vendored whisper.cpp bindings, which is why the build is cgo-based.

Installing castor and casting your first URL

The README recommends the native binary over Docker, and gives a concrete reason: it runs on your machine, so it shares your TV's network, which device discovery needs. The binary shells out to three tools that must be on your `PATH`: Chrome or Chromium (any recent version), ffmpeg 7.1 or newer, and ffprobe 7.1 or newer. The version floor is not cosmetic. The README states castor uses flags such as `-readrate_initial_burst` and stricter HLS extension handling that older ffmpeg builds reject.

On macOS the install is a Homebrew cask:

bash
brew install --cask stupside/tap/castor

On Windows the README points at the release archive, `castor_<version>_windows_amd64.zip` or `_arm64.zip` for Windows on ARM. Extract `castor.exe` into a folder on your `PATH`, then install the dependencies with winget:

powershell
winget install Gyan.FFmpeg     # ffmpeg + ffprobe
winget install Google.Chrome   # skip if Chrome is already installed

Two first-run prompts are expected. SmartScreen may block the download because the binary is unsigned, and Windows Defender Firewall will ask whether castor may use the network; the README says to allow it on private networks, because the TV fetches the stream from castor and answers discovery over the LAN.

Next, find your device and put it in `config.yaml`:

yaml
device:
  name: "Living Room TV"   # exact name from `castor scan`
  type: dlna

If `castor scan` finds nothing, the README gives a fallback: pin the device by IP so castor skips discovery entirely.

yaml
device:
  name: "Living Room TV"   # now just a label
  type: dlna
  host: 192.168.0.3        # the device's LAN IP

Then cast a page that has an embedded player:

bash
castor cast player https://example.com/watch/some-video

The README notes that the only required configuration key is `device`; everything else (timeouts, probing, capture, transcoding) has defaults. There is also an interactive mode, `castor cast`, which browses titles in a TUI and needs a TMDB key plus a source you configure yourself.

Where castor breaks: automated playback, multicast, and Termux

The clearest limitation is stated by the project itself: the action pipeline works on pages that allow automated playback, and will not work everywhere. If a site detects headless Chrome, requires an interactive login, or uses a player that only starts on a genuine user gesture, extraction fails and there is no documented workaround in the README. This is the failure mode to expect most often, and it is not something configuration can fix.

Discovery is the second. Castor uses SSDP and mDNS multicast, which the README says does not cross VLANs or subnets and is blocked on Android/Termux, where the symptom is `netlinkrib: permission denied`. Pinning `host` switches to unicast and works across those networks, and it also skips the discovery wait on every cast. On Android/Termux the README adds one more constraint: leave `network.interface` empty, because pinning an interface needs the same blocked lookup.

There is a second-order cost to pinning a host. You give up discovery, so a device that changes IP needs a config edit. For DLNA the README notes a further wrinkle: if the TV does not answer the description request at the plain IP, set `host` to the full description URL instead, for example `http://192.168.0.3:9197/dmr`.

Finally, the build itself is a constraint. `go install` will not work, because the vendored whisper.cpp bindings come in through a local `replace` and need a prebuilt static library. Building from source means cloning with submodules, having Go 1.26 or newer and cmake, and running `make`.

Castor compared with VLC or a DLNA server

The obvious alternative is VLC, which has a Renderer menu that can push a local file or a network stream to a DLNA or Chromecast device. The difference in approach is where the work happens. VLC plays a URL you already have; it does not open a web page, run a browser, and watch network traffic to discover what the page is loading. If you already know the stream URL, VLC is simpler and has no ffmpeg version floor of its own to satisfy.

Castor's value is entirely in the extraction step. When the video only exists behind a player on a page, `castor cast player <url>` is doing work a plain media player cannot do. When the URL is already in your clipboard, that advantage disappears and you are paying for a headless Chrome launch you do not need.

The other comparison is a DLNA server such as MiniDLNA or Jellyfin. Those index a library and serve files; castor serves nothing persistently and indexes nothing. It is a one-shot pipe from a page to a device. If your goal is to browse a collection on the TV, castor is the wrong tool, and the README's interactive mode is a title search over sources you configure, not a library manager.

Maintenance, licence, and what upgrading costs you

The repository is not archived. The last push was on 2026-09-28, the same day as the v1.9.0 release. The two prior releases, v1.8.0 and v1.8.1, both landed on 2026-07-27, so the recent cadence is a pair of releases in late July followed by one in late September. The project uses release-please, visible in the repository root as `release-please-config.json` and `.release-please-manifest.json`, with a `CHANGELOG.md` generated from it. That is a conventional setup, and it means version bumps are automated rather than hand-curated.

The licence is MIT, which is permissive and places few obligations on how you redistribute or modify the code. Note that the Docker image installs Debian packages including chromium and ffmpeg; those carry their own licences, which the MIT licence on castor's own source does not cover. Nothing here is legal advice, and if you plan to redistribute a bundled image you should check the terms of the components yourself.

The upgrade cost is mostly external. Because castor shells out to ffmpeg and ffprobe at 7.1 or newer, your upgrade path is tied to your distribution's ffmpeg package, not to castor's release cycle. On macOS and Windows the README routes you through Homebrew and winget respectively, which track upstream. On Linux the Docker image is the documented way to get a suitable ffmpeg build, and the README notes it is Linux only. The Dockerfile targets `debian:trixie-slim`, requires buildx because `TARGETARCH` is set by buildx and not by plain `docker build`, and expects `--device /dev/dri:/dev/dri` if you want VAAPI.

Editorial conclusion

Adopt castor if you already run ffmpeg 7.1 or newer and Chrome on a machine on the same LAN as your TV, and you want the real stream rather than a mirrored screen. Skip it if your TV is not DLNA or Chromecast, if you cannot install three external binaries, or if the pages you care about block automated playback. Verify two things before committing: that `castor scan` lists your device, and that your ffmpeg build is at least 7.1, since the README states older builds reject the flags castor passes.

Frequently asked questions

What is stupside/castor used for?

It casts a web video stream to a TV. You point it at a page with an embedded player or at a direct stream URL, and it extracts the stream with headless Chrome, transcodes it with ffmpeg and sends it to a DLNA or Chromecast device.

What do I need installed before running castor?

The README says the binary shells out to three tools on your PATH: Chrome or Chromium, ffmpeg 7.1 or newer, and ffprobe 7.1 or newer. Older ffmpeg builds reject the flags castor passes, so the version floor matters.

Why does castor scan find no devices?

Discovery uses SSDP and mDNS multicast, which does not cross VLANs or subnets and is blocked on Android/Termux. The README's fix is to set a host IP in the device block so castor reaches the device by unicast instead.

Does castor work on every web page?

No. The README states the action pipeline works on pages that allow automated playback and will not work everywhere, so sites that block headless browsers or require a real user gesture are outside what it can extract.

Can I build castor with go install?

No. The README says go install will not work because the vendored whisper.cpp bindings come in through a local replace and need a prebuilt libwhisper.a. Building from source means cloning with submodules and running make.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. stupside/castor on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/stupside-castor.svg)](https://hysenlabs.com/projects/stupside-castor)