# cliarr

```
 ██████╗██╗     ██╗ █████╗ ██████╗ ██████╗
██╔════╝██║     ██║██╔══██╗██╔══██╗██╔══██╗
██║     ██║     ██║███████║██████╔╝██████╔╝
██║     ██║     ██║██╔══██║██╔══██╗██╔══██╗
╚██████╗███████╗██║██║  ██║██║  ██║██║  ██║
 ╚═════╝╚══════╝╚═╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝
```

[![CI](https://github.com/ChrisThoma/cliarr/actions/workflows/ci.yml/badge.svg)](https://github.com/ChrisThoma/cliarr/actions/workflows/ci.yml)

A fast terminal client for your home media stack: **Radarr**, **Sonarr**, **Plex**, **qBittorrent** and **NZBGet**, all over their LAN HTTP APIs. No SSH into the NAS required.

![cliarr demo: live search, dashboard, library and calendar](assets/demo.gif)

*(The GIF shows the unicode poster fallback; in kitty, iTerm2 or a sixel terminal, posters render as crisp images.)*

If you run the arr stack on a NAS and live in a terminal, this is for you. One binary, one `config.toml`, try it in a minute:

```sh
cargo install cliarr && cliarr config init
```

It's two tools in one binary:

- **Scriptable subcommands:** `cliarr movie add`, `cliarr queue`, `cliarr torrents pause ...` with `--json` output for piping.
- **A full TUI:** run `cliarr` with no arguments and you land in a live search box. Merged movie + series results appear as you type, and `cliarr dune` launches with the search already running. Dashboard, library browser, calendar, unified downloads and missing-items screens sit behind tabs `2`–`6`. Poster art renders inline in terminals that support it (kitty, iTerm2, sixel) with a unicode fallback everywhere else.

## Setup

```sh
cargo install cliarr
cliarr config init       # interactive wizard, tests each connection
cliarr config test       # re-check connectivity any time
```

(Or from a clone: `cargo install --path .`. Requires Rust 1.88 or newer. Prebuilt binaries for macOS, Linux and Windows are on the [releases page](https://github.com/ChrisThoma/cliarr/releases).)

### Updating

```sh
cliarr update            # download the latest release and replace the binary in place
cliarr update --check    # just report whether a newer version exists
```

cliarr also checks for a new release at most once a day and prints a one-line notice on stderr when one exists. Set `CLIARR_NO_UPDATE_CHECK=1` to disable that check.

Configuration is a single `config.toml` (written `0600`) in the platform config directory: `~/.config/cliarr/` on Linux, `~/Library/Application Support/cliarr/` on macOS, `%APPDATA%\cliarr\config\` on Windows. `cliarr config show` prints the exact path. Every service is optional: configure only what you run, and each has its own URL so services can live on different machines:

```toml
[radarr]
url = "http://nas.local:7878"
api_key = "..."          # Radarr → Settings → General

[sonarr]
url = "http://nas.local:8989"
api_key = "..."

[plex]
url = "http://plexbox.local:32400"
token = "..."            # X-Plex-Token

[qbittorrent]
url = "http://nas.local:8080"
username = "admin"
password = "..."

[nzbget]
url = "http://nas.local:6789"
username = "nzbget"
password = "..."

[ui]
poster_protocol = "auto"   # auto | kitty | iterm2 | sixel | halfblocks | off
```

Reverse-proxy path prefixes (e.g. `http://nas.local/radarr`) are supported.

## CLI

```
cliarr config   init | show | test
cliarr movie    search <query> | add <tmdbId> [--profile P] [--root PATH] [--no-search]
                | list [--filter missing|monitored|unmonitored]
                | remove <id> [--delete-files] [--exclude]
                | search-missing | refresh [<id>]
cliarr series   ... (same shape; add takes a TVDB id)
cliarr queue    [--service radarr|sonarr|all]
cliarr queue    remove <id> --service radarr|sonarr [--blocklist] [--remove-from-client]
cliarr calendar [--days N]
cliarr missing  [--service radarr|sonarr|all]
cliarr torrents list [--filter downloading|...] | pause|resume|delete <hash...> [--delete-files]
cliarr nzb      list | pause|resume|delete <id...>
cliarr plex     status
```

Add `--json` to any list command for machine-readable output. Torrent hashes accept unambiguous prefixes (copy the short hash from `torrents list`). `movie add` / `series add` prompt for quality profile and root folder when run interactively, or list valid values when scripted.

Exit codes: `0` ok · `1` API/network error · `2` config or usage error.

## TUI

| Key | Action |
|-----|--------|
| *type* | on the Search screen, results appear live as you type (movies + series merged) |
| `1`–`6`, `Tab` | switch screen (Search · Dashboard · Library · Calendar · Downloads · Missing) |
| `j`/`k`, `↑`/`↓` | move selection (arrows work even while typing) |
| `Esc` | stop typing; `/` starts again |
| `←` / `→` | movies ↔ series (Library screen) |
| `a` / `Enter` | add selected search result (opens profile/root-folder picker) |
| `s` / `a` | search for release(s): selected / all missing |
| `e` | edit library item (quality profile, monitored) |
| `p` / `P` | pause / resume torrent or NZB |
| `d` | delete (confirm dialog; `f` toggles delete-files) |
| `b` | blocklist an arr queue item |
| `r` | refresh current screen |
| `?` | help |
| `q` | quit |

The Downloads screen unifies the Radarr/Sonarr queues with qBittorrent and NZBGet and auto-refreshes every 5 seconds. Works with qBittorrent 4.x and 5.x (the WebAPI rename of pause/stop is detected automatically).

## Development

```sh
cargo test      # wiremock-based API client tests, no live services needed
cargo clippy
```

Poster images are cached under the platform cache directory (`~/.cache/cliarr/posters/` on Linux, `~/Library/Caches/cliarr/posters/` on macOS).

## License

[MIT](LICENSE)
