Skip to content
stupsidePublic

About

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.

Topics

Resources

Contributing

Security policy

Stars

2.5k stars

Watchers

5 watching

Forks

Latest commit

 

History

526 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Castor

stupside%2Fcastor | Trendshift

Latest Release Go Reference Homebrew npm License Build Status

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.Chrome and winget 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.

castor cast player finding the video on a page and starting playback on a DLNA TV
Casting a page to a DLNA TV with castor cast player

  1. Find your TV:

    castor scan
  2. Add it to config.yaml, with the name and type the scan printed:

    device:
      name: "Living Room TV"
      type: dlna   # dlna, chromecast, or roku
  3. 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.

Browsing titles in the castor TUI
Picking a title in castor cast

  1. 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}"
  2. 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
Loading

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:

  1. castor/config.yaml in your user config directory
  2. config.yaml in the current directory
  3. the file passed with --config <path>
  4. CASTOR_ environment variables, such as CASTOR_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:

Cast to another TV? Tell us how it went in the What device do you cast to? discussion.

Setting up a Roku
  1. Enable Developer Mode: on the remote, press Home three times, Up twice, then Right Left Right Left Right, and set a web server password.
  2. Put that password in servers.api.devices.roku.password in config.local.yaml.
  3. 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.

  1. On the NAS, with servers.media.token in its config.yaml (add --device /dev/dri for Intel VA-API):

    docker run -d --network host -v "$PWD/config.yaml:/config.yaml:ro" \
      ghcr.io/stupside/castor:latest media-server
  2. 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.

  1. 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>
  2. Run docker compose up -d. Remove its /dev/dri lines if the NAS has no Intel GPU.

  3. 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

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.
Ports castor uses, for your firewall
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.

About

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.

Topics

Resources

Contributing

Security policy

Stars

2.5k stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages

⌂ Proxy