Self-hosted torrent streaming. Single static Rust binary serving a React UI. Search movies, TV, and music, paste a magnet link, and stream directly in the browser with HLS transcoding and adaptive bitrate.
- Multi-source search: movies (YTS, Torrentio), TV (Torrentio, eztv), music + music videos (apibay, 1337x)
- Browser streaming: sequential BitTorrent download + on-the-fly HLS transcoding (fMP4/CMAF). Watch while it downloads.
- Adaptive bitrate: multi-variant HLS (360p / 720p / 1080p / source). hls.js handles quality switching automatically.
- Codec support: H.264 passthrough, HEVC/H.265 passthrough on capable devices, MKV/AC3/DTS transcoded to browser-safe H.264 + AAC.
- Surround audio preserved through transcoding (up to 8 channels).
- Music player: album browsing, per-track streaming while downloading, playlists, favourites, MediaSession integration (iOS lock screen controls), AirPlay, Web Audio EQ, shareable track links with OG previews.
- Shareable links: guest tokens let you share a specific stream with a single URL, without giving away your account.
- Multi-user: bcrypt + JWT auth, per-user search/watch history and favourites.
- GPU acceleration: FFmpeg auto-detects VAAPI / NVENC / VideoToolbox, falls back to CPU.
- Optional SOCKS5 proxy for torrent traffic.
- Backend (Rust): Axum HTTP server,
librqbitBitTorrent engine, FFmpeg for transcoding, SQLite for state. The release binary embeds the frontend. - Frontend (TypeScript): React + Radix UI,
hls.jsfor HLS playback,framer-motionfor transitions. Built with Vite. - Streaming pipeline: torrent peers → librqbit sequential download → FFmpeg (passthrough or transcode) → HLS master playlist → hls.js / Safari native.
All tooling is pinned via Nix. Enter the dev shell, then build the frontend and the release backend (the release binary bundles the UI).
nix develop
# Frontend
cd ui && pnpm install && pnpm build && cd ..
# Backend (release build embeds the UI)
cargo build --release --manifest-path backend/Cargo.toml
# Run
./target/release/streamx
# Open http://127.0.0.1:8999Config lives at ~/.streamx/config.toml and is created with defaults on first run.
[server]
port = 8999
bind = "127.0.0.1"
# log_level = "info"
[torrent]
max_connections = 200
sequential = true
[transcode]
video_codec = "h264"
preset = "ultrafast"
crf = 23
hls_force_stereo = true # set false to preserve surround in HLS tiers
[auth]
session_duration = "7d"
# jwt_secret auto-generated on first run if empty
# Movies: YTS has browse + search with rich metadata
[[providers]]
id = 1
kind = "movies"
url = "https://yts.bz"
api_url = "https://yts.bz/api/v2/list_movies.json"
# TV: Torrentio for structured season/episode search
[[providers]]
id = 2
kind = "tv"
url = "https://torrentio.strem.fun/providers=eztv,1337x,thepiratebay"
format = "torrentio"
# Music
[[providers]]
id = 4
kind = "music"
url = "https://apibay.org"
format = "apibay"
category = "101"
# Optional: route torrent traffic through a SOCKS5 proxy
# [vpn]
# socks5 = "socks5://user:pass@host:port"Extra providers can be kept out of the main config in ~/.streamx/providers.toml (same [[providers]] format). That file is gitignored by default.
| Format | Supports | How it works |
|---|---|---|
yts |
Movies (browse + search) | YTS JSON API. Rich metadata (posters, ratings, trailers). |
torrentio |
Movies, TV (search only) | Resolves text to IMDB IDs via Cinemeta, then fetches streams from Torrentio. No API key. |
apibay |
TV, Music (browse + search) | Pirate Bay JSON API. |
eztv |
TV (browse + search) | EZTV API. Structured season/episode data. |
scrape |
Music (browse + search) | Scrapes 1337x HTML. |
Torrentio has no catalog (it is IMDB-ID based), so keep YTS as the movies provider if you want the home page populated.
Hot reload with the Vite dev server proxying to a cargo run backend:
nix develop
# Terminal 1: frontend dev server (vite on :9000, proxies /api to :8998)
cd ui && pnpm dev
# Terminal 2: backend
cargo run --manifest-path backend/Cargo.toml -- --port 8998cargo fmt --all --manifest-path backend/Cargo.toml
cargo clippy --manifest-path backend/Cargo.toml -- -D warnings
cargo check --manifest-path backend/Cargo.toml
cd ui && pnpm typecheck# Rust unit + integration tests
cargo test --manifest-path backend/Cargo.toml
# Frontend component tests
cd ui && pnpm test
# End-to-end browser tests (requires a running backend on port 8999)
cd ui && pnpm test:e2estreamx # start the server (port 8999 by default)
streamx --port 9000 # custom port
streamx --admin-user <user> --admin-password <pw> # create an admin user on first boot
streamx clean # remove cache and downloads (keeps config + DB)
streamx wipe # remove everything except config.toml
Credentials can also be passed via STREAMX_ADMIN_USER and STREAMX_ADMIN_PASSWORD env vars.
backend/
src/
config.rs configuration + env var expansion
server/ HTTP routes, auth, image proxy, static asset serving
torrent/ librqbit engine, provider dispatch, search formats
transcode/ FFmpeg HLS pipeline (GPU detect, probe, multi-variant)
db/ SQLite (users, history, downloads, favourites, playlists)
ui/
src/
pages/ Search, Browse, Player, Music, Favourites, etc.
components/ VideoPlayer, AudioPlayerBar, ExpandedPlayer, Layout
hooks/ useSearch, useStream, useAudioPlayer, useMediaSession
api/ API client and types
flake.nix Nix flake (dev shell + builds)
- Port in use:
ss -tlnp | grep 8999and kill the process, or pass--port. - Frontend not showing: build the UI first (
cd ui && pnpm install && pnpm build), then restart the backend. - Nix flake not found: the flake file must be tracked by git (
git add flake.nix). - Safari HLS shows 401: the stream token is not attached to segment requests. Open the debug pane (user menu → Debug Mode) and check the last
/api/stream/.../playlist.m3u8response. - Transcoding fails on GPU: FFmpeg automatically falls back to CPU. Pass
--log-level debugand watch the backend logs to see which acceleration was tried.
docs/og-preview.pngis produced byui/tests/screenshot-og.spec.ts. Regenerate it against a running instance with:cd ui && npx playwright test tests/screenshot-og.spec.ts --config tests/live.config.ts
