TV series manager

Pilot

A self-hosted TV series manager with a React web UI and a REST API. Tracks your TV library via TMDB, polls indexers (the search APIs that catalog releases) for new episodes, hands grabs to your download client, and files completed downloads into season folders.

Port :8383 Go 1.25 Postgres MIT

What Pilot does

Library management

Full TMDB integration for search, metadata, posters, and episode tracking. Per-series monitoring with configurable monitor types. Season and episode-level monitoring controls, wanted page, and a calendar view of upcoming episodes.

Release handling

Quality profiles — ranked lists of acceptable resolutions, sources, codecs, and HDR variants — drive the grab decision. Custom formats with regex matching and weighted scoring layer on top. A title-match filter rejects releases whose parsed show name doesn't match the requested series, so a "The Office" search never grabs an "Office Space" pack with overlapping words.

Dead-torrent detection

The stallwatcher polls your download client for stalled torrents and blocklists them. A per-episode circuit breaker caps auto-blocklist events at three strikes per 24 hours so a misconfigured indexer can't trigger a retry storm.

Automation

Automatic RSS sync on a configurable schedule. Auto-import of completed downloads with rename support. Import lists from TMDB, Trakt, Plex Watchlist, and custom URL lists. Season folder organization with configurable naming.

Integrations

Indexers via Newznab and Torznab (the two standard search APIs the *arr ecosystem speaks), either configured locally or synced from Pulse. Download clients: Haul, qBittorrent, Deluge, Transmission, SABnzbd, NZBGet. Media servers: Plex, Jellyfin, Emby. Notifications: Discord, Slack, Telegram, Pushover, Gotify, ntfy, email, webhook, custom command.

Command palette

Cmd/Ctrl+K command palette with fuzzy search for pages, series, and actions. Eighteen theme presets (thirteen dark, five light) shared across every Beacon service. Live queue updates over WebSocket. OpenAPI docs at /api/docs.

Screenshots

React 19 + TypeScript UI embedded in the Go binary.

Pilot library — grid of TV series posters with episode counts and per-show progress
Library — series grid with episode counts and progress
Pilot series detail for Andor — season tabs with download progress bars
Series detail — season-by-season download progress
Pilot Activity tab showing the four rails — active tasks, currently downloading, needs attention with stall + grab failure rows, and recently imported
Activity — what's happening right now, including stalls and grab failures

Advanced features

Title matching

The release filter runs every parsed title through a matcher in internal/core/parser/parser.go that rejects titles whose parsed show name doesn't match the requested series after normalization. Year suffixes, bracketed edition tags, and common release-group stylings all pass through cleanly.

Stallwatcher circuit breaker

internal/core/stallwatcher/service.go polls the download client's stall endpoint on a configurable interval, classifies stalled torrents by reason, and blocklists them. The per-episode circuit breaker caps auto-blocklist events at three strikes per 24 hours.

Interactive search ranking

Releases sort by Quality → Episode Count → age-aware log10 Seed Weight → Age. The Episode Count tier lifts season packs above individual episodes within the same quality tier, the way Sonarr's release comparer does. The Seed Weight tier — log10 of the seed count, with an age cap on torrents older than a year — is what stops the 847-seeder, five-year-old release with zero actual peers from winning the sort.

Sonarr migration

Pilot imports from a running Sonarr instance in one pass. Open Settings → Import, enter the Sonarr URL and API key, preview, and select categories. Supported: quality profiles, libraries, indexers, download clients, and monitored series.

Getting started with Pilot

Standalone Docker or full stack via beacon-stack/deploy.

docker
docker run -d \
  --name pilot \
  -p 8383:8383 \
  -v /path/to/config:/config \
  -v /path/to/tv:/tv \
  ghcr.io/beacon-stack/pilot:latest

Open http://localhost:8383 — Pilot generates an API key on first run. Find it in Settings → App Settings.

Environment variables

VariableDefaultDescription
TV_PATHRequired. Host path to your TV library. Bound into Pilot at /tv. Must be on the same filesystem as DOWNLOADS_PATH for hardlink imports.
DOWNLOADS_PATHRequired. Host path to completed downloads. Shared across Pilot, Prism, and Haul.
PILOT_PORT8383Host port for the web UI and API. Set in .env if 8383 is taken.
PILOT_CONFIG_PATH./config/pilotWhere Pilot's config.yaml and cached state live.
LOG_LEVELinfoSet once in .env; Compose passes the same value to every Beacon service. debug, info, warn, error.
TZUTCIANA timezone (e.g. America/New_York). Set once in .env and applied to every service for log timestamps and scheduled tasks.