Library: stack movies above series with details on the right; refresh SPECS keybindings

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-20 20:06:50 +02:00
co-authored by Claude Sonnet 5.5
parent 5195edc3b2
commit 76750cadf6
3 changed files with 43 additions and 39 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ A terminal UI to search for movies and series with [Radarr](https://radarr.video
optionally start the automatic search. optionally start the automatic search.
- **Interactive search**: list the releases Radarr/Sonarr find (approved or rejected, with reasons), - **Interactive search**: list the releases Radarr/Sonarr find (approved or rejected, with reasons),
sort and filter them, and grab one. Series: pick a season or a single episode first. sort and filter them, and grab one. Series: pick a season or a single episode first.
- **Library**: browse your movies and series side by side, filter by missing / downloaded / monitored, toggle - **Library**: browse movies (top) and series (below) with a details pane on the right, filter by missing / downloaded / monitored, toggle
monitoring, trigger searches. Series open into seasons and episodes with per-season and monitoring, trigger searches. Series open into seasons and episodes with per-season and
per-episode monitoring, automatic search and release search. per-episode monitoring, automatic search and release search.
- **Downloads**: SABnzbd queue and history plus the Radarr/Sonarr queues. Pause/resume, delete, - **Downloads**: SABnzbd queue and history plus the Radarr/Sonarr queues. Pause/resume, delete,
+36 -34
View File
@@ -55,7 +55,7 @@ Notes:
### 4.1 MVP (v0.1) ### 4.1 MVP (v0.1)
- **Config:** load URLs + API keys from a config file; fail with a clear message if missing or unreachable. - **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. - **Search:** a Movies view (Radarr) and a Series view (Sonarr), each with a search box. 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`. - **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. - **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: - **Downloads view:** all downloads and queues in one place, auto-refreshing every few seconds:
@@ -65,60 +65,62 @@ Notes:
- **Status bar / help:** the bottom bar lists the keys that work in the current view or panel; the top right shows the connection state of each service; `?` opens the full key reference. - **Status bar / help:** the bottom bar lists the keys that work in the current view or panel; the top right shows the connection state of each service; `?` opens the full key reference.
- **Notifications and log (F5):** feedback about actions (added, deleted, grabbed, failures) appears as popups in the bottom right that fade away (errors stay longer). Everything is also kept in the Log view; only connection changes of the background polling are logged, not every failed poll. - **Notifications and log (F5):** feedback about actions (added, deleted, grabbed, failures) appears as popups in the bottom right that fade away (errors stay longer). Everything is also kept in the Log view; only connection changes of the background polling are logged, not every failed poll.
### 4.2 v0.2 ### 4.2 v0.2 (done, M8)
- **Library view:** browse existing movies/series with filters (missing, downloaded, monitored). - **Library view:** movies (top) and series (below) with the details of the selected item on the right; Tab switches between the two lists. Filters (all, missing, downloaded, monitored, unmonitored) and a text filter apply to both lists. `t` toggles monitoring, `s` starts an automatic search, `i`/Enter opens the release search (movies) or the drill-down (series).
- **Series drill-down:** seasons and episodes, toggle monitoring per season/episode, trigger season/episode search. - **Series drill-down:** seasons and episodes, toggle monitoring per season/episode, trigger season/episode search and release search.
### 4.3 Later / ideas ### 4.3 Later / ideas
- Calendar / upcoming view. - Calendar / upcoming view.
- Notifications on completed downloads. - Desktop/terminal notification when a download completes.
- Multiple instances per service (e.g. 4K Radarr). - Multiple instances per service (e.g. 4K Radarr).
- Poster preview via sixel / kitty graphics where supported. - Poster preview via sixel / kitty graphics where supported.
## 5. UI Design (tview) ## 5. UI Design (tview)
Layout sketch: Layout sketch (Library view; the other views follow the same frame):
``` ```
┌ arr-tui ───────────────────────────────────────────────────────────┐ ┌ arr-tui ───────────────────────────────────────────────────────────┐
│ [Movies] Series Downloads Library │ ← tab bar │ 1 Movies 2 Series 3 Downloads 4 Library 5 Log ● Radarr ● Sonarr ● SAB │ ← tab bar + connection state
├────────────────────────────────┬───────────────────────────────────┤ │ Library · Movies 12/40 · Series 5/9 · filter: missing │
│ Search: ______________________ │ Title (Year) │ │ Filter: ______________________ │
│ │ ★ 7.8 · 142 min · Sci-Fi │ ├─ Movies ───────────────────────┬─ Details ─────────────────────────┤
│ > Movie One (2021) ✓ │ │ │ ● Movie One (2021) missing │ Title (Year) │
│ Movie Two (2019) │ Overview text wraps here... │ │ ● Movie Two (2019) ✓ WEB-DL │ ★ 7.8 · 142 min · Sci-Fi │
│ Movie Three (2024) │ │ ├─ Series ───────────────────────┤ Overview text wraps here... │
│ ● Show One (2008) 12/62 │ │
│ ○ Show Two (2015) 10/10 │ │
├────────────────────────────────┴───────────────────────────────────┤ ├────────────────────────────────┴───────────────────────────────────┤
│ ↓ 12.4 MB/s · 2 jobs · Radarr ● Sonarr ● SABnzbd ● ? help │ ← status bar │ Tab movies/series · f filter · / text · t monitor · ? help · q quit │ ← key hints of the current view
└────────────────────────────────────────────────────────────────────┘ └────────────────────────────────────────────────────────────────────┘
``` ```
tview primitives: `Pages` (views), `Flex` (layout), `InputField` (search), `List`/`Table` (results, queue), `TextView` (details), `Form` (add dialog), `Modal` (confirmations). Popups for action feedback appear in the bottom right and fade away; the Log view (F5) keeps the history.
Keybindings (initial proposal): tview primitives: `Pages` (views and stacked overlays), `Flex` (layout), `InputField` (search), `List`/`Table` (results, queues, library), `TextView` (details, log), `Form` (add dialog), `Modal` (confirmations). Popups are drawn by a wrapper around the root primitive.
Keybindings (the status bar shows the ones that apply to the current view):
| Key | Action | | Key | Action |
|---|---| |---|---|
| `Tab` / `Shift+Tab` | Switch panel within the current view (never the view) | | `F1-F5` | Switch view (Movies, Series, Downloads, Library, Log); also while typing, and from full-screen views. Dialogs block them. |
| `F1-F4` | Switch view, also while typing in a search box | | `Tab` / `Shift+Tab` | Switch panel within the current view (never the view): search box / results, Downloads panels, Library lists |
| `/` | Focus search | | `?` / `F9` | Help (`F9` also works while typing) |
| `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 | | `q` / `Ctrl+C` | Quit |
| `Esc` | Close a dialog or a full-screen view |
| `/` | Movies, Series, Library: focus the search / text filter |
| `Enter` / `a` | Movies, Series: add the selected result (dialog: ↑↓ move, ←→ change dropdowns) |
| `i` | Movies, Series, Library: interactive release search (series: seasons and episodes) |
| `f` | Library: cycle the filter; release list: hide rejected releases |
| `t` | Library, episodes: toggle monitored; Downloads history: retry a failed download |
| `s` | Library, episodes: automatic search; release list: cycle sort (best / size / age) |
| `Enter` / `g` | Release list: grab the selected release (rejected ones ask for confirmation) |
| `Enter` | Library: open; episodes: expand a season or search releases for an episode |
| `p` / `d` / `b` / `r` | Downloads: pause/resume SABnzbd, delete job / history entry / arr queue item, remove from arr queue + blocklist, refresh |
| `r` | Library, episodes: reload |
| `G` / `c` | Log: follow new entries / clear |
## 6. Architecture ## 6. Architecture
@@ -197,7 +199,7 @@ The file should be created with mode `0600`; warn if it is more permissive. API
- [x] **M7** — Polish: help screen (`?`/F9), per-service connection indicators, README, Makefile release builds, `--version`. - [x] **M7** — Polish: help screen (`?`/F9), per-service connection indicators, README, Makefile release builds, `--version`.
- [x] **M8** — Library view (movies/series, filters, text filter, monitor toggle, automatic search) and series drill-down (seasons/episodes, monitoring, search, release search). - [x] **M8** — Library view (movies/series, filters, text filter, monitor toggle, automatic search) and series drill-down (seasons/episodes, monitoring, search, release search).
- [x] **M9** — Popup notifications, Log view (F5), context-sensitive key hints in the status bar. - [x] **M9** — Popup notifications, Log view (F5), context-sensitive key hints in the status bar.
- [ ] **M10+** — ideas: calendar, notifications, delete from library, multiple instances. - [ ] **M10+** — ideas: calendar, completion notifications, delete from library, multiple instances.
## 10. Open Questions ## 10. Open Questions
+5 -3
View File
@@ -171,7 +171,8 @@ type libPane struct {
gen int // invalidates stale loads gen int // invalidates stale loads
} }
// libraryView shows the movies of Radarr and the series of Sonarr side by side. // libraryView shows the movies of Radarr above the series of Sonarr, with the
// details of the selected item on the right.
type libraryView struct { type libraryView struct {
app *App app *App
radarr *radarr.Client radarr *radarr.Client
@@ -197,7 +198,7 @@ func newLibraryView(app *App, r *radarr.Client, s *sonarr.Client) *libraryView {
} }
v.details.SetBorder(true).SetTitle(" Details ") v.details.SetBorder(true).SetTitle(" Details ")
lists := tview.NewFlex() lists := tview.NewFlex().SetDirection(tview.FlexRow) // movies above series
for _, kind := range []libKind{kindMovies, kindSeries} { for _, kind := range []libKind{kindMovies, kindSeries} {
p := &libPane{kind: kind, table: tview.NewTable().SetSelectable(true, false).SetFixed(1, 0)} p := &libPane{kind: kind, table: tview.NewTable().SetSelectable(true, false).SetFixed(1, 0)}
p.table.SetBorder(true) p.table.SetBorder(true)
@@ -216,8 +217,9 @@ func newLibraryView(app *App, r *radarr.Client, s *sonarr.Client) *libraryView {
v.root = tview.NewFlex().SetDirection(tview.FlexRow). v.root = tview.NewFlex().SetDirection(tview.FlexRow).
AddItem(v.header, 1, 0, false). AddItem(v.header, 1, 0, false).
AddItem(v.input, 1, 0, false). AddItem(v.input, 1, 0, false).
AddItem(tview.NewFlex().
AddItem(lists, 0, 3, true). AddItem(lists, 0, 3, true).
AddItem(v.details, 0, 2, false) AddItem(v.details, 0, 2, false), 0, 1, true)
v.input.SetChangedFunc(func(string) { v.render() }) v.input.SetChangedFunc(func(string) { v.render() })
v.input.SetDoneFunc(func(tcell.Key) { v.app.app.SetFocus(v.pane().table) }) v.input.SetDoneFunc(func(tcell.Key) { v.app.app.SetFocus(v.pane().table) })