Files
arr-tui/SPECS.md
T
2026-10-20 19:24:35 +02:00

12 KiB

arr-tui — Specification

A terminal UI to search for movies and series, add them to Radarr / Sonarr, and watch the downloads progress in SABnzbd.

  • Language: Go
  • TUI library: tview (on top of tcell)
  • License: MIT
  • Module: git.stinnesbeck.com/nils/arr-tui
  • Status: M7 done

1. Goals

  1. Search for movies (Radarr) and series (Sonarr) from one place.
  2. Add a result to Radarr/Sonarr with sensible defaults (quality profile, root folder, monitored) and trigger a search so it gets downloaded.
  3. Show the SABnzbd queue and history with live progress.
  4. Be fast and keyboard-driven; usable over SSH.

Non-goals (for now)

  • Managing indexers, download clients, or Radarr/Sonarr settings.
  • Replacing the Radarr/Sonarr web UIs for library management.
  • Torrent clients (SABnzbd / Usenet only).
  • Multi-user / auth beyond API keys.

2. How the pieces fit together

            ┌────────────┐   lookup / add / release search   ┌──────────┐
            │            │ ────────────────────────────────▶ │  Radarr  │──┐
            │  arr-tui   │                                   └──────────┘  │ sends NZB
            │            │ ────────────────────────────────▶ ┌──────────┐  │
            │            │                                   │  Sonarr  │──┤
            └─────┬──────┘                                   └──────────┘  │
                  │  queue / history / pause / resume                      ▼
                  └────────────────────────────────────────▶ ┌──────────────┐
                                                             │   SABnzbd    │
                                                             └──────────────┘

Radarr/Sonarr are expected to already have SABnzbd configured as their download client. arr-tui talks to all three APIs directly; it does not send NZBs to SABnzbd itself, except possibly via the manual-release flow, which still goes through Radarr/Sonarr (see 4.3).

3. External APIs

Service API Auth Endpoints used
Radarr v3 REST (JSON) X-Api-Key header GET /api/v3/movie/lookup?term=, GET /api/v3/movie, POST /api/v3/movie, GET /api/v3/qualityprofile, GET /api/v3/rootfolder, GET /api/v3/queue, GET /api/v3/release?movieId=, POST /api/v3/release, POST /api/v3/command (MoviesSearch), GET /api/v3/system/status
Sonarr v3 REST (JSON) X-Api-Key header GET /api/v3/series/lookup?term=, GET /api/v3/series, POST /api/v3/series, GET /api/v3/qualityprofile, GET /api/v3/rootfolder, GET /api/v3/queue, GET /api/v3/release?episodeId= / ?seriesId=&seasonNumber=, POST /api/v3/release, POST /api/v3/command (SeriesSearch, SeasonSearch, EpisodeSearch), GET /api/v3/system/status
SABnzbd /api?mode=...&output=json apikey query param mode=queue, mode=history, mode=pause / resume, mode=queue&name=delete, mode=version

Notes:

  • Endpoint shapes should be verified against each instance's own Swagger / the official docs when implementing; Radarr and Sonarr occasionally change fields between versions.
  • Lookup results include an id of 0 when the item is not yet in the library; a non-zero id means it is already added. We use this to show an "in library" badge.

4. Features

4.1 MVP (v0.1)

  • Config: load URLs + API keys from a config file; fail with a clear message if missing or unreachable.
  • Search: one search box, with a toggle (movies / series). Results list with title, year, rating, status, and an "in library" marker. Detail pane with overview, runtime, genres, network/studio.
  • Add: from a result, open a small form: quality profile, root folder, monitored (yes/no), "search now" (yes/no); for series also monitor mode (all / future / first season / none). Submit calls POST /movie or POST /series with addOptions.searchForMovie / searchForMissingEpisodes.
  • Interactive release search: for a movie (or a series' season/episode) in the library, list releases from indexers (GET /release) with title, indexer, size, quality, age, and rejection reasons; sort/filter; grab a chosen one (POST /release). Rejected releases can be grabbed only after an explicit confirmation.
  • Downloads view: all downloads and queues in one place, auto-refreshing every few seconds:
    • SABnzbd queue (name, size, progress, ETA, speed) and history (completed / failed, with retry).
    • Radarr and Sonarr queues (status, warnings, import errors), so stuck imports are visible.
    • Global speed / pause state in the status bar. Pause/resume and delete a SABnzbd job.
  • Status bar / help: key hints, connection state of each service.

4.2 v0.2

  • Library view: browse existing movies/series with filters (missing, downloaded, monitored).
  • Series drill-down: seasons and episodes, toggle monitoring per season/episode, trigger season/episode search.

4.3 Later / ideas

  • Calendar / upcoming view.
  • Notifications on completed downloads.
  • Multiple instances per service (e.g. 4K Radarr).
  • Poster preview via sixel / kitty graphics where supported.

5. UI Design (tview)

Layout sketch:

┌ arr-tui ───────────────────────────────────────────────────────────┐
│ [Movies] Series  Downloads  Library                                │  ← tab bar
├────────────────────────────────┬───────────────────────────────────┤
│ Search: ______________________ │ Title (Year)                      │
│                                │ ★ 7.8 · 142 min · Sci-Fi          │
│ > Movie One (2021)        ✓    │                                   │
│   Movie Two (2019)             │ Overview text wraps here...       │
│   Movie Three (2024)           │                                   │
├────────────────────────────────┴───────────────────────────────────┤
│ ↓ 12.4 MB/s · 2 jobs · Radarr ● Sonarr ● SABnzbd ●   ? help        │  ← status bar
└────────────────────────────────────────────────────────────────────┘

tview primitives: Pages (views), Flex (layout), InputField (search), List/Table (results, queue), TextView (details), Form (add dialog), Modal (confirmations).

Keybindings (initial proposal):

Key Action
Tab / Shift+Tab Switch panel within the current view (never the view)
F1-F4 Switch view, also while typing in a search box
/ Focus search
Enter Search / open details / confirm
a Add selected result
m / s Switch to movies / series search
p Pause/resume SABnzbd (Downloads view)
d Delete selected download, history entry or Radarr/Sonarr queue item (with confirm)
t Retry the selected failed download (history)
Tab / Shift+Tab Downloads view: cycle focus between SABnzbd queue, Radarr/Sonarr queue and history
b Downloads view (Radarr/Sonarr queue): blocklist the release and search for another
i Interactive release search for the selected library item (movies; series ask for a season, then an episode or the whole season)
g / Enter Release list: grab the selected release (rejected ones ask for confirmation)
s / f Release list: cycle sort (best / size / age) / hide rejected
Esc Close a dialog or the release list
r Refresh
? Help
q / Ctrl+C Quit

6. Architecture

  • All network I/O runs in goroutines; UI updates go through app.QueueUpdateDraw. Never block the UI thread.
  • Debounced search (e.g. 300 ms) with request cancellation via context.Context.
  • A single ticker drives SABnzbd/queue polling, only while the Downloads view (or the status bar) needs it.
  • HTTP client per service with timeouts, and a typed error that the UI can show in the status bar rather than crashing.

Proposed layout:

arr-tui/
├── cmd/arr-tui/main.go        # entry point, wires config + UI
├── internal/
│   ├── config/                # load/validate config
│   ├── radarr/                # client + types
│   ├── sonarr/                # client + types
│   ├── sabnzbd/               # client + types
│   ├── arr/                   # shared bits (HTTP helper, quality profile, root folder types)
│   └── ui/                    # tview views: search, add form, downloads, status bar
├── SPECS.md
├── go.mod
└── README.md

Dependencies: github.com/rivo/tview, github.com/gdamore/tcell/v2. Prefer the standard library net/http and encoding/json for the API clients — no generated SDKs.

7. Configuration

Format: JSON. Location: $XDG_CONFIG_HOME/arr-tui/config.json (fallback ~/.config/arr-tui/config.json), overridable with --config. Environment variables override file values (e.g. ARR_TUI_RADARR_APIKEY) so secrets don't have to live in the file. Parsed with the standard library encoding/json (unknown fields rejected to catch typos).

{
  "radarr": {
    "url": "http://localhost:7878",
    "apikey": "...",
    "default_quality_profile": "HD-1080p",
    "default_root_folder": "/movies"
  },
  "sonarr": {
    "url": "http://localhost:8989",
    "apikey": "...",
    "default_quality_profile": "HD-1080p",
    "default_root_folder": "/tv"
  },
  "sabnzbd": {
    "url": "http://localhost:8080",
    "apikey": "..."
  },
  "ui": {
    "refresh_seconds": 3
  }
}

The default quality profile / root folder fields are optional.

The file should be created with mode 0600; warn if it is more permissive. API keys are never logged.

8. Testing

  • Unit tests for each client using httptest.Server with recorded JSON fixtures.
  • Config parsing / validation tests.
  • UI logic (formatting, state) kept out of tview widgets so it can be tested without a terminal.
  • Manual testing against real Radarr/Sonarr/SABnzbd instances (dev/up.sh starts pinned Radarr/Sonarr/SABnzbd containers, see dev/README.md).

9. Milestones

  • M0 — Project skeleton: go mod init, config loading, empty tview app with tabs and status bar.
  • M1 — Radarr client: system status, lookup, quality profiles, root folders, add movie.
  • M2 — Movie search + add flow in the UI.
  • M3 — Sonarr client and series search + add flow.
  • M4 — SABnzbd client and Downloads view (queue + history) with live updates and pause/resume/delete.
  • M5 — Radarr/Sonarr queues merged into the Downloads view.
  • M6 — Interactive release search (movies, then series seasons/episodes) and grab.
  • M7 — Polish: help screen (?/F9), per-service connection indicators, README, Makefile release builds, --version.
  • M8+ — v0.2 features (library view, series drill-down).

10. Open Questions

Open: none.

Decided:

  • Go module path: git.stinnesbeck.com/nils/arr-tui.
  • One instance each of Radarr, Sonarr and SABnzbd (multi-instance is a later idea).
  • Minimum Go version: latest stable.
  • License: MIT (Nils Stinnesbeck). The tool will be published, so keep secrets out of the repo, include a config.example.json, and write a proper README.
  • Config format: JSON.
  • Interactive release search: in the MVP.
  • Downloads view: shows SABnzbd and the Radarr/Sonarr queues from the start.