Add queue listing and removal/blocklisting to the shared arr client. The Downloads view now fetches SABnzbd and both arr queues in parallel, shows status, progress and import problems, flags items needing attention in the summary, and supports removing or blocklisting items. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
211 lines
12 KiB
Markdown
211 lines
12 KiB
Markdown
# 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](https://github.com/rivo/tview) (on top of tcell)
|
|
- **License:** MIT
|
|
- **Module:** `git.stinnesbeck.com/nils/arr-tui`
|
|
- **Status:** M5 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` / `1-4` | Switch view |
|
|
| `F1-F4` / `Alt+1-4` | 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) |
|
|
| `h` | 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 item |
|
|
| `g` | Grab the selected release |
|
|
| `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).
|
|
|
|
```json
|
|
{
|
|
"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
|
|
|
|
- [x] **M0** — Project skeleton: `go mod init`, config loading, empty tview app with tabs and status bar.
|
|
- [x] **M1** — Radarr client: system status, lookup, quality profiles, root folders, add movie.
|
|
- [x] **M2** — Movie search + add flow in the UI.
|
|
- [x] **M3** — Sonarr client and series search + add flow.
|
|
- [x] **M4** — SABnzbd client and Downloads view (queue + history) with live updates and pause/resume/delete.
|
|
- [x] **M5** — Radarr/Sonarr queues merged into the Downloads view.
|
|
- [ ] **M6** — Interactive release search (movies, then series seasons/episodes) and grab.
|
|
- [ ] **M7** — Polish: help screen, error handling, README, release builds.
|
|
- [ ] **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.
|