Skip to content

Latest commit

 

History

1,610 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JackUI

A self-hosted torrent streaming media server — search, stream a torrent to your browser before it finishes downloading, transcode on the fly with hardware acceleration, and watch (Safari included, via HLS). Go backend, React UI embedded in a single binary, runs in Docker.

status go license

JackUI started as a visual front-end for Jackett and grew into a full media server: find a release, start playing it while it downloads, and let the server transcode incompatible codecs to something your browser can play. No "wait for the download to finish", no separate transcoding box.

JackUI is an independent project — it is not affiliated with or endorsed by Jackett.

Note

The web UI is internationalised (react-i18next): Portuguese and English ship today (web/src/locales/{pt,en}.json). This README and the developer docs are in English.

How it works

                    ┌──────────┐   magnet / .torrent
   browser  ──────► │  JackUI  │ ──────────────────────────┐
   (search)         │  (Gin)   │                            ▼
                    └────┬─────┘                   ┌──────────────────┐
                         │  GET /api/stream/:hash  │ anacrolix/torrent │
   browser  ◄────────────┤   (HTTP + Range)        │  BitTorrent swarm │
   <video>              ▲│                          └────────┬─────────┘
                        ││ HLS (.m3u8 + .ts)  or  raw MP4 Range        │ pieces
                        │▼                                             ▼
                    ┌─────────┐   incompatible codec?          ┌───────────────┐
                    │ ffmpeg  │ ◄──── HEVC/AV1/AC3/DTS ──────── │ disk cache    │
                    │transcode│   NVENC / VAAPI / QSV / x264    │ (LRU evict)   │
                    └─────────┘                                 └───────────────┘

The torrent is exposed as a seekable HTTP source with Range support, so ffmpeg (and the browser) can start reading from any offset before the download completes. Incompatible codecs are transcoded on demand; Safari is served HLS because its <video> rejects progressive MP4 over chunked transfer.

Features

  • Stream before download — torrent → HTTP with Range; playback starts on the first pieces. Disk cache with LRU eviction (favourites evicted last).
  • On-demand transcode — HEVC/AV1/x265 → H.264 via GPU (NVENC/VAAPI/QSV) or libx264 fallback. Safari gets HLS; everyone else gets direct-play or progressive transcode. The GPU is bounded: a semaphore caps concurrent CUDA decoders (JACKUI_MAX_GPU_TRANSCODES, default 3) and an extra session falls back to CPU-decode + NVENC-encode on CUDA_ERROR_OUT_OF_MEMORY, so playback never hard-fails for lack of VRAM.
  • Music player — audio playlists with gapless playback, selectable HLS audio track (torrent + local), cover art / chapters / mediaSession (lock-screen + AirPlay on Safari/iOS), and a footer mini-player you can drag and expand.
  • Swarm health on cardsSeedBadge shows seeders + availability. A background tracker scrape (BEP 48, private trackers included) backs the count so an active torrent never sits at a misleading 0; results are cached.
  • Subtitles — embedded (ffmpeg probe), sidecar .srt/.vtt inside the torrent, and external (OpenSubtitles). Choice persists per file.
  • Discover & recommendations — a Discover page with weekly-trending titles, personalised recommendations seeded from your favourites + watch history (with dismiss), and a Music mode showing trending albums (keyless Apple RSS). Both seed the search on click.
  • TMDB enrichment — posters + metadata on results and library (PostgreSQL cache, 30-day TTL).
  • Per-torrent artwork — resolved and persisted by info_hash through a fail-safe chain: embedded torrent image → TMDB poster → keyless web image search → captured frame.
  • AI title cleanup (optional) — an OpenAI-compatible chain (Groq / OpenRouter / Ollama) cleans raw release names before the TMDB lookup, with per-provider fallback + circuit breaker. A tunable benchmark (Settings → admin) scores providers on accuracy, latency and cost/energy, keeps history, and reorders the chain.
  • Downloads queue — the internal worker treats a multi-file torrent as one unit: the scheduler counts a slot per torrent and steers the anacrolix file priorities (selected = download, the rest = cancel), so one big pack no longer hogs every slot or spikes CPU/RAM. The list groups one card per torrent; you can filter completed into Seeding vs On-disk and sort by speed / seeds / date / name / size / progress. Downloads land under a category folder by default, with a destination picker (incl. browse into mounts) and auto-seed of completed torrents (re-seeded in place from the cached metainfo). qBittorrent/Transmission clients are also supported.
  • *arr provider — exposes a Transmission-RPC-compatible endpoint so Sonarr/Radarr/Prowlarr can use JackUI as their download client (opt-in). See docs/TRANSMISSION_RPC.md.
  • Local files — browse configured mounts; promote to the library (LocalPromoteModal, batch + per-row). A downloaded video on local disk seeks instantly (http.ServeFile/sendfile), while remote/rclone mounts get read-ahead + a whole-file LRU disk cache. Local play falls back to HLS when the container/codec needs it. Continue Watching tracks local items too; the list filters by downloading / done.
  • Library extras — Playlists, Watchlists (cron + ntfy push, opt-in auto-download with quality filters), Continue Watching (resume position), and an Incognito toggle that skips history/library writes. A global "reveal hidden" curtain hides flagged items across the UI.
  • Low-footprint mode — the HLS pipeline shuts ffmpeg down when the last viewer leaves (no 5-min survival), the UI pauses its polling when the tab is hidden, and a balanced runtime profile (Go GOGC/GOMEMLIMIT/GOMAXPROCS + JACKUI_MAX_CONNS/JACKUI_PEERS_HIGH) keeps idle memory low on a home server.
  • Desktop app (optional) — an Electron wrapper bundling the Go server, with a status tray, magnet deep-links, and native downloads. See electron/.
  • Auth — JWT on by default (opt out only with JACKUI_AUTH_ENABLED=0 + JACKUI_ALLOW_INSECURE_AUTH=1) with rotated refresh tokens, roles, MFA/passkeys, and AdminOnly routes (incl. admin password reset).
  • Observability — public /status (version/commit/buildTime), Prometheus /api/metrics (admin JWT or JACKUI_METRICS_TOKEN), opt-in /debug/pprof (JACKUI_PPROF_ENABLED + token/admin JWT), structured logs (JACKUI_LOG_FORMAT=json), and scheduled bandwidth windows for the streamer.

Stack

Layer Tech
Backend Go 1.26, Gin, anacrolix/torrent
Transcode ffmpeg (NVENC / VAAPI / QSV / libx264), HLS for Safari
Frontend React 18 + TypeScript + Vite + TailwindCSS, embedded via //go:embed all:dist
Storage PostgreSQL (JACKUI_DATABASE_URL, unified schema via internal/db); disk cache for torrent pieces
Deploy Docker, single binary + embedded UI

Requirements

  • Go 1.25+ and Node 18+ (for development/building).
  • ffmpeg with the hardware encoders you intend to use (NVENC needs an NVIDIA GPU + the NVIDIA container toolkit; VAAPI/QSV need /dev/dri).
  • Docker (for deployment) and a Jackett instance for search.

Quick start

# 1. Backend (Go server on :8989, serves the embedded UI)
make dev-backend

# 2. Frontend with hot-reload (Vite on :5173, proxies the API to :8989)
make dev-frontend

# 3. Run the test suite
make test

Open http://localhost:5173 (dev) or http://localhost:8989 (the embedded build). Configure your Jackett URL + API key in Settings, then search.

Configuration

Runtime config comes from config.yaml plus environment overrides (env wins). Key variables:

All variables below accept empty/unset as "use default". The canonical default is compiled in or read from config.yaml; env wins when set.

Category Variable Default Purpose
Core JACKUI_PORT 8989 HTTP listen port
JACKETT_URL http://localhost:9117 Jackett search backend
JACKETT_API_KEY Jackett API key
JACKUI_BASE_URL Public app URL (e-mail links, optional)
JACKUI_CONTROL_TOKEN Static token for RPC control endpoint (port refresh)
PostgreSQL JACKUI_DATABASE_URL Required. DSN (postgres://...). Overrides PG_* below
DATABASE_URL Same as above (alternative name)
JACKUI_PG_HOST localhost Host
JACKUI_PG_PORT 5432 Port
JACKUI_PG_USER jackui User
JACKUI_PG_PASSWORD Password
JACKUI_PG_DB jackui Database name
JACKUI_PG_SSLMODE disable SSL mode
Directories JACKUI_STREAM_DIR ./data/stream Piece cache + transcode/HLS temp (reconstructable)
JACKUI_DOWNLOAD_DIR ./data/downloads Completed download target (empty = stay in cache)
JACKUI_SHARED_DIR ./data/shared Shared library: browsable mounts + promote target
JACKUI_EXTERNAL_MOUNTS Extra mount paths (rclone/NFS, comma-separated)
Streamer perf JACKUI_STREAM_MAX_GB 50 Piece-cache size cap (LRU eviction)
JACKUI_STREAM_DOWN_MBPS Download rate limit (MB/s)
JACKUI_STREAM_UP_MBPS Upload rate limit (MB/s)
JACKUI_READAHEAD_MB Read-ahead for sequential file serving (MiB)
JACKUI_STORAGE_BACKEND Storage backend driver (mmap etc.)
JACKUI_HALF_OPEN Max half-open TCP connections
JACKUI_PIECE_HASHERS Piece hash checker workers
JACKUI_SEED_TRACKERS Extra announce trackers for seeding
Local cache JACKUI_LOCAL_READAHEAD_MB 16 Read-ahead buffer for local/rclone mounts (MiB)
JACKUI_LOCAL_CACHE_GB 50 Dedicated pre-fetch cache (GiB, LRU)
JACKUI_HLS_VOD_MODE all VOD seekbar: off, hlsjs, or all
JACKUI_HLS_MEDIA_RENDITIONS 0 HLS master EXT-X-MEDIA subtitle renditions (Phase 2 M2b). Audio renditions (TYPE=AUDIO) are always on when the source has ≥2 tracks. Enable (1) only after Safari X-TIMESTAMP-MAP validation for WebVTT.
Peer/Torrent JACKUI_MAX_CONNS Peer connections per torrent
JACKUI_PEERS_HIGH Swarm high-water mark
JACKUI_PEER_PORT Fixed listen port (overrides forwarded port)
JACKUI_MAX_GPU_TRANSCODES 3 Cap on concurrent HW decoders (0 = unlimited)
JACKUI_MAX_UPLOAD_MB Max upload per torrent (MiB)
JACKUI_IDLE_MINUTES 30 Drop torrent after N min idle (files stay)
JACKUI_METADATA_SECONDS 60 Metadata fetch timeout
JACKUI_TRANSMISSION_RPC_ENABLED 0 Expose Transmission-RPC *arr provider (opt-in)
Downloads JACKUI_DL_MAX_ACTIVE Max active downloads per user
JACKUI_DL_PER_USER_MAX Max concurrent downloads per user
JACKUI_DL_STALL_MIN Stall timeout (minutes)
JACKUI_DL_MAX_STALLS Max stalls before abort
JACKUI_DL_AGING_STEP_MIN Aging step interval (minutes)
JACKUI_DL_AGING_CAP Aging cap
JACKUI_DL_ROTATION Download rotation strategy
JACKUI_DL_AUTO_PROMOTE_ARR 0 Promote *arr (Transmission-RPC) completed downloads into SharedDir/<category>/ (1/true; needs JACKUI_SHARED_DIR)
Auth JACKUI_AUTH_ENABLED 1 JWT auth on by default; set 0 only with JACKUI_ALLOW_INSECURE_AUTH=1 (dev/LAN)
JACKUI_ADMIN_USERNAME admin Admin username
JACKUI_ADMIN_PASSWORD Admin password
JACKUI_JWT_SECRET JWT signing secret (≥32 bytes, required with auth)
JACKUI_METRICS_TOKEN Static token for Prometheus /api/metrics
JACKUI_PPROF_ENABLED 0 Expose /debug/pprof (opt-in; never anonymous)
JACKUI_PPROF_TOKEN Static token for /debug/pprof (else admin JWT; without either the routes stay off)
Notifications JACKUI_NTFY_TOPIC ntfy.sh topic for watchlist push
JACKUI_NTFY_URL https://ntfy.sh ntfy server URL
JACKUI_NTFY_TOKEN ntfy auth token (optional)
AI title ID GROQ_API_KEY Groq API key
OPENROUTER_API_KEY OpenRouter API key
GEMINI_API_KEY Google Gemini API key
OPENCODE_API_KEY OpenCode Zen API key
OLLAMA_BASE_URL Ollama endpoint URL
JACKUI_AI_ENABLED Auto-enabled when any key is present
JACKUI_AI_MAX_COST_PER_1M Max cost per 1M tokens (USD)
JACKUI_AI_KWH_PRICE Electricity price for local-model cost
JACKUI_AI_LOCAL_WATTS Local model power draw (watts)
Metadata TMDB_API_KEY TMDB v3 API key (posters + clean titles)
OMDB_API_KEY OMDb API key (IMDb rating)
SMTP JACKUI_SMTP_HOST SMTP server host
JACKUI_SMTP_PORT 587 SMTP port
JACKUI_SMTP_USER SMTP user
JACKUI_SMTP_PASS SMTP password
JACKUI_SMTP_FROM Sender address
Observability JACKUI_LOG_FORMAT Structured logs: json (empty = text)

State lives in PostgreSQL (JACKUI_DATABASE_URL). The piece cache (JACKUI_STREAM_DIR) is deliberately separate to reduce I/O contention on torrent pieces.

Low-footprint runtime tuning (Go GOGC/GOMEMLIMIT/GOMAXPROCS) is applied via the process environment, not config.yaml; production sets it in the deploy compose (see below).

Deployment

JackUI deploys as a Docker container to a home server. It runs on a bridge shared with a reverse proxy (e.g. Nginx Proxy Manager), reachable at jackui:8989.

Local

make dev-backend     # go run ./cmd/server on :8989
make dev-frontend    # Vite :5173 with API proxy

Production (home server)

make deploy-auto        # ✅ default: auto-detects GPU (NVENC/VAAPI/QSV), no VPN
make deploy-auto-vpn    # same, but routes through a gluetun VPN overlay (opt-in)

deploy-auto is the standard make target (auto-detects the GPU, no VPN); -vpn adds a gluetun overlay (network_mode: container:gluetun).

Note

The author's own production instance currently runs behind gluetun (network_mode: container:gluetun, on the VPN's forwarded port — watchForwardedPort in cmd/server/main.go triggers a graceful restart to rebind when the port rotates), even though the no-VPN path is the documented default. Pick the mode that keeps your swarm healthy.

CI/CD runs on GitHub Actions (PR + main gates, CodeQL, release → GHCR + Trivy pre-push + SBOM), documented in docs/CICD.md. Images publish to ghcr.io/lgldsilva/jackui. Homelab deploy is still a hand-maintained compose on the server (docker compose up -d --force-recreate) — the release job only publishes the image. New env vars added to the repo's compose do not reach production by themselves; edit the server-side compose too.

Architecture

A short map; the canonical reference is docs/ARCHITECTURE.md.

cmd/server/main.go   Gin wiring: /api/* + SPA fallback + background workers
ui/embed.go          //go:embed all:dist  (the React build ships inside the binary)
web/src/             React app (PlayerProvider lives above the router)
internal/
  streamer/          anacrolix: Add / FileReader / probe / cache / favourites
  transcode/         ffmpeg pipeline + HLS sessions (seek-restart)
  handlers/          HTTP handlers (search, config, stream, local, classify, …)
  config/ jackett/ downloader/ downloads/ tmdb/ subtitles/ parser/ db/
  auth/ history/ library/ playlists/ watchlist/   PostgreSQL stores (unified pool)
  transmissionrpc/   Transmission-RPC compatibility layer (*arr provider)
  middleware/         cross-cutting Gin middleware (incognito, media-token auth)

Security

  • Auth is on by default — JWT with rotated refresh tokens; AdminOnly guards sensitive routes. Running public is opt-in and explicit (JACKUI_AUTH_ENABLED=0 + JACKUI_ALLOW_INSECURE_AUTH=1, dev/LAN only) — the boot fails closed otherwise. Media elements (<video>/<track>) can't send headers, so media routes accept a scoped ?token=.
  • SSRF guard — torrent .torrent/metadata fetches are restricted to the configured Jackett host.
  • The *arr RPC surface is opt-in (JACKUI_TRANSMISSION_RPC_ENABLED, default off) and, when auth is enabled, requires Basic Auth.
  • Local-file browser is confined to explicitly configured, read-only mounts.

What JackUI does not do: it is not hardened for public-internet exposure without a reverse proxy + auth. Run it behind your proxy on a trusted network.

Roadmap

  • i18n / multi-language UI — done: react-i18next ships Portuguese + English (web/src/locales/).
  • HLS master playlist (Phase 2): multi-resolution #EXT-X-STREAM-INF + TYPE=AUDIO when the source has ≥2 tracks. TYPE=SUBTITLES stays behind JACKUI_HLS_MEDIA_RENDITIONS until Safari WebVTT is validated.
  • Streamer reconciles pieces with already-downloaded files (play without re-downloading).
  • Promote on the local-files page (LocalPromoteModal — per-row + batch).
  • Durable state migrated to PostgreSQL (favorites, metadata cache, downloads, etc. no longer live in the piece-cache dir).

Docs

Doc What
docs/ARCHITECTURE.md Canonical architecture + reading order for new contributors
docs/design-decisions.md Why the tricky bits are the way they are (and what not to "fix")
docs/TRANSMISSION_RPC.md Using JackUI as a Sonarr/Radarr/Prowlarr download client
docs/CICD.md Build pipeline, quality gates, deploy
docs/RCLONE.md Tuning rclone/Google-Drive mounts for streaming (cache, chunking, read-ahead)
docs/PERFORMANCE.md N+1 backlog, on-demand profiling (/debug/pprof), benchmarks in CI
docs/RELIABILITY_PROGRAM_AUDIT.md Closing record of the reliability program (issue #80): phase → PR → evidence

Legal

JackUI is a neutral tool: it searches indexers you configure and streams/downloads content over the BitTorrent protocol. It does not host, index, or distribute any content by itself, and it ships with no indexers or trackers configured.

You are solely responsible for what you search for, download, stream, and share with it. Make sure your use complies with the laws of your jurisdiction and with the rights of content owners. The authors and contributors do not condone copyright infringement and provide this software without any warranty (see LICENSE).

License

MIT. JackUI is built on third-party components (and, as a Docker image, redistributes ffmpeg and the NVIDIA CUDA runtime) — see THIRD_PARTY-LICENSES.md.

About

Torrent streaming server with hardware transcode and web UI

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages