Comparisons to watching tv, are usually a TV interface, with a TV device/app, be it an Android TV/Apple TV, etc.
Maybe I'm missing it, I couldn't see a tv interface.
The part where it can send video to any kind of tv is a pretty remarkable piece.
Digital Living Network Alliance (DLNA) is a set of interoperability standards for sharing home digital media among multimedia devices. Introduced 2004; 22 years ago.
* https://en.wikipedia.org/wiki/DLNA Google Cast is a proprietary protocol developed by Google for playing locally stored or Internet-streamed audiovisual content on a compatible consumer device. The protocol is used to initiate and control playback of content on digital media players, high-definition televisions, and home audio systems using a mobile device, personal computer, or smart speaker. The protocol was first launched on July 24, 2013; 12 years ago.
* https://en.wikipedia.org/wiki/Google_Cast
Smart TVs won't cast arbitrary web video, and screen mirroring is laggy and drops resolution. Castor casts the real stream instead, at full quality, from your terminal.
Point Castor at a web page you are watching, or at a direct stream URL, and it finds the video, extracts the stream, transcodes it for your TV, and casts in real time. It can also resolve an IMDB/TMDB id against sources you configure yourself, and burn in auto-generated subtitles.
To extract, it launches headless Chrome and watches network traffic over the Chrome DevTools Protocol, then runs a short action pipeline to start playback: click the page, navigate into the largest iframe, and click again as a fallback. This works on pages that allow automated playback, and won't work everywhere.
A general-purpose casting tool: it casts only what you point it at. See Purpose and disclaimer.
Run castor cast to browse titles and cast, without leaving the terminal.
The recommended way to run Castor is the native binary. It runs directly on your machine, so it shares your TV's network (required for device discovery). It requires Chrome/Chromium (headless extraction), ffmpeg (transcoding), and ffprobe (format detection) on your PATH. A Linux-only Docker image that bundles all three is also available.
brew install --cask stupside/tap/castor
Needs Go 1.26+ and cmake (the whisper.cpp bindings are cgo and link a locally built libwhisper.a):
git clone --recurse-submodules https://github.com/stupside/castor.git
cd castor
make # builds libwhisper.a, then the castor binary
go install won't work: the vendored whisper.cpp bindings come in through a local replace and need that prebuilt static lib.
Tell Castor which TV to use: find its name with castor scan and put it in config.yaml.
device:
name: "Living Room TV" # exact name from `castor scan`
type: dlna
Now cast a page you are watching, or a stream URL you have:
castor cast player https://example.com/watch/some-video
For the interactive browser, which searches titles and casts them for you, add a TMDB key and a source (see Configuration), then run castor cast:
Run castor --help for every command and its flags.
Castor reads config.yaml from the current directory, or the path passed to --config. The only required key is the device to cast to (shown in Quick start); everything mechanical (timeouts, probing, capture, transcoding, network interface, Chrome discovery) has working defaults. Keep secrets out of the committed file: put them in a git-ignored config.local.yaml that overlays it, or in CASTOR_SECTION__FIELD environment variables. See SECURITY.md.
cast movie, cast episode, and the interactive browser resolve a title id against sources you configure. Castor bundles none, so add your own (sites you are authorized to use). There is no catalog and no lookup: Castor substitutes the id into the templates you write, prefixes each of your proxies, and opens the resulting page, then extracts the stream exactly like cast player. For example, castor cast movie tt12300742 opens https://your-source.example/embed/movie/tt12300742.
sources:
- proxies: ["https://your-source.example"] # base URLs, tried in order
templates:
movie: "/embed/movie/{itemID}"
episode: "/embed/tv/{itemID}/{season}-{episode}"
The interactive browser (castor cast) uses a TMDB API key to search titles; direct commands like cast movie <id> do not need it. Get a free key from themoviedb.org and keep it in config.local.yaml:
tmdb:
api_key: "<KEY>"
Auto-generated subtitles, transcribed with whisper and burned into the video. Off by default:
whisper:
enable: true # off by default
# language: "fr" # default: English
# model_path: "" # default: ggml-tiny.en (~75 MB, auto-downloaded)
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 list what is on your network.
Experimental: implemented but untested. Contributions welcome.
A Linux-only alternative that bundles Chrome, ffmpeg, and ffprobe.
[!WARNING] Docker can only reach your TV from a Linux host on the same LAN. Discovery uses SSDP multicast and the TV streams back from Castor's replay server, and neither survives Docker's bridge network, so
--network hostis required. On Docker Desktop (macOS/Windows) that flag is a silent no-op: the container lands on Docker Desktop's internal VM subnet (e.g.192.168.65.x) instead of your LAN, soscanfinds nothing andcastfails withdevice "…" (type dlna) not found. Run the native binary on macOS/Windows instead, or bridge a Linux VM onto your LAN (e.g. Lima +socket_vmnet).
On a Linux box or NAS on the same network as the TV, the prebuilt ghcr.io/stupside/castor image saves you installing the dependencies by hand:
# 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://example.com/watch/some-video
The container reads config from config.yaml at /config.yaml, so run every command from the directory holding it. The castor-cache volume keeps the auto-downloaded whisper models between runs; swap :latest for any release tag to pin a version.
[!NOTE] DLNA passthrough and hardware encoding. When the TV can already play the source video (H.264 inside a profile and level it accepts), Castor stream-copies it instead of re-encoding, dropping CPU use to near zero. When a re-encode is unavoidable (a codec the TV rejects, or burned-in subtitles), Castor picks a hardware H.264 encoder if one actually works on the host (VA-API on an Intel Linux box, VideoToolbox on the native macOS binary) and otherwise falls back to software
libx264. To let the Docker container reach an Intel GPU for VA-API, add--device /dev/dritodocker run(the--network hostflag alone does not expose the GPU); without it, or on a host with no working hardware encoder, Castor stays onlibx264.
Castor is a general-purpose caster, not a service tied to any particular site.
Castor is provided as-is for lawful, personal, and educational use.
See CONTRIBUTING.md.
- can someone kindly explain what is the actual problem statement and what the author is trying to solve here?
[1] - https://github.com/stupside/castor/commit/847abd1ad0dbe893fc...
</sarcasm, mostly>
It feels very light-weight, it's approximately instantly-responsive. Back button works. I don't understand the stats (or my contribution to them), but whatever.
(the closed-captioning pop-up causes some overlay issues for me, though)
moar edit: Upon further review with my very not-special desktop box, I'm reasonably confident that this is the quickest, most-responsive "TV-watching" experience I've had since analog NTSC left the scene ~eons ago. It's fast like switching from channel 11 to channel 13 used to be with the very quickest and most well-behaved of tuners.
What aren't you doing that everyone else is doing?
sources: - proxies: - "https://vidsrc-embed.ru" templates: movie: "/embed/movie/{itemID}" episode: "/embed/tv/{itemID}/{season}-{episode}"
- proxies:
- "https://1embed.cc"
- "https://www.vidking.net"
templates:
movie: "/embed/movie/{itemID}"
episode: "/embed/tv/{itemID}/{season}/{episode}"
- proxies:
- "https://www.rivestream.app"
templates:
movie: "/embed/torrent?type=movie&id={itemID}"
episode: "/embed/torrent?type=tv&id={itemID}&season={season}&episode={episode}"
- proxies:
- "https://www.rivestream.app"
templates:
movie: "/embed?type=movie&id={itemID}"
episode: "/embed?type=tv&id={itemID}&season={season}&episode={episode}"The short version is that it uses my own engine, called Watson. I used to work for a small game studio inside a big company and my specialty was tooling. I had built my own before I started there and when they shutdown, it evolved into Watson[0].
It's lightweight because it's a static site with no server, it has no spying code or SDKs, and it puts the broadcasters HLS stream URL directly into a <video> element in your browser with minimal intervention.
Seems to be the fact that there's no advertising, tracking or other SDKs and the entire JS is contained in two files.
https://tvexplorer.live/help/ways-to-watch
and...
You know what you need. A button that goes back but instead of going to the prior document goes to the first document with a different domain than the current one.
But... TV Explorer does keep track of what's tunable (with status lights down the left column) and also lets you scan in the background to find the ones that are live for you right now.
Really great site though!
Quick suggestion: let us resize the channel browser when it's horizontal, and perhaps allow collapsing it altogether
But speaking only for myself, if history.pushState was removed from my browser I would probably be happier in the end. JavaScript SPA's abuse it 10x more often than they use it appropriately, I'd rather that if you're an SPA anyway, you implement your own in-app navigation and let my browser's mean "get me out of this app". Lord knows SPA's already reinvent every other web standard, why not one more...
It does only allow filtering by one language at a time, but you can build your hotbar of the stations you like that you find across any filters.
The scan works on the displayed results. If you change the filter, it starts with the new list -- but all the results are saved regardless.
Hope that makes it a bit bitter!
When your window is wide enough (on desktop), you should be able to drag the split point between the TV display and the channel list.