jackui

module
v0.100.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 22, 2026 License: MIT

README

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 protected).
  • 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), 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 audio/subtitle renditions (Phase 2 M2b). Dark launch — enable (1) only after the frontend migrates to hls.audioTrack and is validated in Chrome/Safari
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
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): N audio/subtitle tracks + multi-resolution in one VOD stream.
  • 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)

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.

Directories

Path Synopsis
cmd
server command
internal
ai
Package ai is a small OpenAI-compatible LLM client with a configurable fallback chain, used to turn a messy torrent name into a clean title before the TMDB lookup.
Package ai is a small OpenAI-compatible LLM client with a configurable fallback chain, used to turn a messy torrent name into a clean title before the TMDB lookup.
audiometa
Package audiometa reads ID3/MP4/Vorbis/FLAC tags from local audio files via the pure-Go github.com/dhowden/tag (no CGO — keeps the Docker build simple).
Package audiometa reads ID3/MP4/Vorbis/FLAC tags from local audio files via the pure-Go github.com/dhowden/tag (no CGO — keeps the Docker build simple).
auth
Package auth implements user authentication with JWT access tokens + opaque refresh tokens.
Package auth implements user authentication with JWT access tokens + opaque refresh tokens.
contentid
Package contentid derives identities for files so the SAME content can be recognised across torrents and mounts without re-downloading it.
Package contentid derives identities for files so the SAME content can be recognised across torrents and mounts without re-downloading it.
db
Package db owns the shared PostgreSQL connection pool and schema migrations.
Package db owns the shared PostgreSQL connection pool and schema migrations.
dbtest
Package dbtest provides an isolated PostgreSQL handle for store tests.
Package dbtest provides an isolated PostgreSQL handle for store tests.
dbutil
Package dbutil holds tiny helpers shared by SQLite-backed stores.
Package dbutil holds tiny helpers shared by SQLite-backed stores.
diskutil
Package diskutil exposes filesystem-level disk usage, shared by the local mounts browser and the streaming cache stats so neither has to embed its own statfs call (and so the streamer doesn't have to import the handlers package).
Package diskutil exposes filesystem-level disk usage, shared by the local mounts browser and the streaming cache stats so neither has to embed its own statfs call (and so the streamer doesn't have to import the handlers package).
downloads
Package downloads persists per-user background downloads — full-file torrent transfers that run to completion regardless of player activity.
Package downloads persists per-user background downloads — full-file torrent transfers that run to completion regardless of player activity.
gluetun
Package gluetun talks to a gluetun VPN container's control server to obtain the provider's forwarded port.
Package gluetun talks to a gluetun VPN container's control server to obtain the provider's forwarded port.
handlers/httpshared
Package httpshared holds the leaf HTTP helpers shared between package handlers and its subpackage handlers/local.
Package httpshared holds the leaf HTTP helpers shared between package handlers and its subpackage handlers/local.
httpretry
Package httpretry retries idempotent HTTP calls with exponential backoff.
Package httpretry retries idempotent HTTP calls with exponential backoff.
imagesearch
Package imagesearch finds a poster/cover image for a title on the open web.
Package imagesearch finds a poster/cover image for a title on the open web.
library
Package library persists per-user metadata about every torrent the user has streamed.
Package library persists per-user metadata about every torrent the user has streamed.
localcache
Package localcache pre-fetches whole files from slow/remote mounts (rclone / Google Drive) onto fast local disk so playback is instant, seekable, and immune to the intermittent I/O errors the FUSE mount throws.
Package localcache pre-fetches whole files from slow/remote mounts (rclone / Google Drive) onto fast local disk so playback is instant, seekable, and immune to the intermittent I/O errors the FUSE mount throws.
localstream
Package localstream wraps the io.ReadSeeker of a local mounted file so the server can do two things at once while a video plays:
Package localstream wraps the io.ReadSeeker of a local mounted file so the server can do two things at once while a video plays:
lyrics
Package lyrics proxies the public LrcLib API (https://lrclib.net) from the backend, mirroring how tmdb/subtitles/imagesearch call external services server-side.
Package lyrics proxies the public LrcLib API (https://lrclib.net) from the backend, mirroring how tmdb/subtitles/imagesearch call external services server-side.
mailer
Package mailer sends transactional email (password reset, email verification, invites) over SMTP.
Package mailer sends transactional email (password reset, email verification, invites) over SMTP.
middleware
Package middleware holds cross-cutting Gin middlewares that don't fit a single feature package.
Package middleware holds cross-cutting Gin middlewares that don't fit a single feature package.
musictrending
Package musictrending proxies Apple's keyless "Marketing Tools" RSS feed (top albums per country) from the backend, mirroring how tmdb/lyrics call external services server-side.
Package musictrending proxies Apple's keyless "Marketing Tools" RSS feed (top albums per country) from the backend, mirroring how tmdb/lyrics call external services server-side.
parser
Package parser extracts release metadata from torrent titles.
Package parser extracts release metadata from torrent titles.
playlists
Package playlists manages per-user ordered collections of torrent references.
Package playlists manages per-user ordered collections of torrent references.
preview
Package preview implements safe, read-only inspection of container files (zip/tar/tar.gz/rar archives, CBZ/CBR comics, EPUB books) for the universal file viewer.
Package preview implements safe, read-only inspection of container files (zip/tar/tar.gz/rar archives, CBZ/CBR comics, EPUB books) for the universal file viewer.
push
Package push persists Web Push subscriptions + the in-app notification feed and delivers notifications to both.
Package push persists Web Push subscriptions + the in-app notification feed and delivers notifications to both.
stats
Package stats aggregates per-user usage statistics from data the app already records (library resume positions, downloads, search history, watchlists).
Package stats aggregates per-user usage statistics from data the app already records (library resume positions, downloads, search history, watchlists).
streamer
Package streamer manages active torrents for HTTP streaming.
Package streamer manages active torrents for HTTP streaming.
subtitles
Package subtitles searches and downloads subtitles via OpenSubtitles REST v1.
Package subtitles searches and downloads subtitles via OpenSubtitles REST v1.
tmdb
Package tmdb is a minimal The Movie Database client + on-disk cache.
Package tmdb is a minimal The Movie Database client + on-disk cache.
transcode
Package transcode probes the host environment for hardware-accelerated video encoding/decoding options and exposes a runtime capability matrix.
Package transcode probes the host environment for hardware-accelerated video encoding/decoding options and exposes a runtime capability matrix.
transfer
Package transfer is a small in-memory tracker for long-running file move/copy operations (post-download move, Local-tab move, AI/promote), so the UI can show ONE consistent progress pattern for all of them: X/Y files, bytes done/total, transfer rate and ETA.
Package transfer is a small in-memory tracker for long-running file move/copy operations (post-download move, Local-tab move, AI/promote), so the UI can show ONE consistent progress pattern for all of them: X/Y files, bytes done/total, transfer rate and ETA.
version
Package version exposes build metadata (git commit, build time, version).
Package version exposes build metadata (git commit, build time, version).
watchlist
Package watchlist persists per-user search queries that the server polls in the background.
Package watchlist persists per-user search queries that the server polls in the background.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL