atradio 0.3.0

atradio.fm in your terminal — a TUI radio player on the AT Protocol
atradio-0.3.0 is not a library.

atradio

nix FlakeHub discord

atradio.fm in your terminal — a TUI radio player on the AT Protocol.

A native Rust client for atradio.fm: browse trending / popular / recently-played stations, fuzzy-search the whole radio-browser directory, play live streams with a full Rockbox DSP/equalizer chain, and — when signed in — favorite stations, add your own, and post comments to your PDS.

atradio

Contents

Install

Prebuilt release tarballs, .deb, and .rpm packages are attached to every GitHub release, named atradio-<version>-<os>-<arch>.tar.gz (macos-amd64, macos-aarch64, linux-amd64, linux-aarch64, freebsd-amd64, freebsd-aarch64, netbsd-amd64, netbsd-aarch64) — each contains the binary, README, and LICENSE. The BSD builds run in emulated VMs and are attached to the release shortly after it's published (aarch64 ones can take hours).

macOS / Linux — Homebrew

brew install tsirysndr/tap/atradio

Linux — Debian / Ubuntu

Direct .deb:

# amd64
curl -LO https://github.com/tsirysndr/atradio.fm/releases/latest/download/atradio_0.3.0_amd64.deb
sudo apt install ./atradio_0.3.0_amd64.deb

# arm64 (Raspberry Pi 4/5, Apple-silicon VM, …)
curl -LO https://github.com/tsirysndr/atradio.fm/releases/latest/download/atradio_0.3.0_arm64.deb
sudo apt install ./atradio_0.3.0_arm64.deb

Or via the Gemfury apt repo (auto-updates with apt upgrade):

echo "deb [trusted=yes] https://apt.fury.io/tsiry/ /" \
  | sudo tee /etc/apt/sources.list.d/tsiry.list
sudo apt update && sudo apt install atradio

Linux — Fedora / RHEL / openSUSE

Direct .rpm:

sudo dnf install \
  https://github.com/tsirysndr/atradio.fm/releases/latest/download/atradio-0.3.0-1.x86_64.rpm

Or via the Gemfury dnf/yum repo:

sudo tee /etc/yum.repos.d/tsiry.repo <<'EOF'
[tsiry]
name=tsiry
baseurl=https://yum.fury.io/tsiry/
enabled=1
gpgcheck=0
EOF
sudo dnf install atradio

Nix

# Optional: use the binary cache to skip building.
cachix use atradio

# One-off run:
nix run github:tsirysndr/atradio.fm

# Install into your user profile:
nix profile install github:tsirysndr/atradio.fm

# Dev shell (rust toolchain + build deps):
nix develop github:tsirysndr/atradio.fm

From source (Cargo)

# Runtime/build deps: a C toolchain (for the Rockbox codecs) + ALSA on Linux.
sudo apt-get install -y build-essential pkg-config libasound2-dev   # Debian/Ubuntu

cargo install --git https://github.com/tsirysndr/atradio.fm --bin atradio

Build (from a checkout)

cd cli
cargo build --release
./target/release/atradio          # launch the TUI

Building compiles the vendored Rockbox codecs, so a C toolchain is required (clang/gcc). macOS uses CoreAudio; Linux needs ALSA dev headers (libasound2-dev).

License note: this crate links rockbox-playback (GPL-2.0-or-later), so the compiled atradio binary is GPL-2.0-or-later.

Usage

atradio                       # interactive TUI (default)
atradio --no-tui              # headless Connect device (remote-controllable)
atradio search lofi           # search radio-browser, print results
atradio play "jazz"           # headless: play the top hit for a query…
atradio play https://…/stream #   …or a stream URL directly
atradio trending              # trending stations from the AppView
atradio login                 # sign in with an app password (env), or:
atradio login --oauth         # sign in via the browser (OAuth)
atradio whoami                # show the signed-in account
atradio logout
atradio push                  # upload your local EQ/DSP settings to your PDS
atradio pull                  # download your EQ/DSP settings from your PDS
atradio service install       # Linux: run the headless daemon as a systemd user service

Signing in

Reads to the AppView are public; favoriting, commenting, adding stations, appearing in recently-played, and atradio Connect require a session. Two ways to authenticate:

  • App password — set env vars, then atradio login:
    export ATPROTO_IDENTIFIER="you.bsky.social"
    export ATPROTO_APP_PASSWORD="xxxx-xxxx-xxxx-xxxx"
    
    Its session stays signed in the longest and refreshes silently — recommended for a long-running headless daemon.
  • OAuthatradio login --oauth you.bsky.social, or press s in the TUI to open the sign-in modal, which completes the flow in your browser. Convenient for interactive use, but its session expires sooner than an app password, so an always-on daemon may need the occasional re-login.

The session + a small profile cache are stored under ~/.config/atradio/ (also settings.toml for volume + DSP).

Keybindings (TUI)

Key Action
/ j/k move selection
/ Tab switch home tab
15 tabs: Trending / Popular / Recent / Favorites / Yours
Enter play the selected station
Space play / pause · m mute · +/- volume (or adjust DSP)
/ fuzzy station search
f favorite the selected/current station
A add a custom station (when signed in)
c / a comments / add a comment
d Connect: pick a device to play/control (see below)
n notifications
p your profile (with playable recently-played)
e equalizer & DSP settings
s sign in (OAuth) / sign out
h · ? home · help
q / Esc quit / close overlay

Equalizer & DSP

Press e for the full Rockbox chain: a 10-band equalizer, bass/treble tone, crossfeed, perceptual bass, Haas surround, a compressor, and channel mode / stereo width. Changes apply live and persist to settings.toml.

Syncing settings (push / pull)

When you're signed in, the whole DSP chain also syncs to your PDS as the fm.atradio.audio.settings singleton record — the CLI's EQ bands (32 Hz–16 kHz) now match the web build, so your EQ + DSP follow your account across the web app and other devices. The TUI syncs automatically: on startup a signed-in session pulls the record and applies it (remote wins), then pushes the current chain back when you quit.

You can also sync on demand (both require signing in):

atradio push    # upload settings.toml → your fm.atradio.audio.settings record
atradio pull    # download the record → overwrite the DSP chain in settings.toml

pull replaces the local DSP chain with the synced record (it keeps your local volume). If you have no record yet, push (or quitting the TUI) creates it.

atradio Connect (remote control)

Like Spotify Connect: when signed in, every atradio client you have open — this CLI, the web app, other terminals — shows up as a device on your account, and any of them can control the selected player. Requires a session (it's keyed to your DID and authenticated with an atproto service-auth token); logged-out clients don't participate.

  • Press d to open the device picker. Pick This device to play here, or pick another device to control it from here — pressing Enter on a station, Space, m, and +/- are then sent to that device instead of your local audio. The player bar shows a ◉ Controlling <device> indicator with the remote's now-playing and volume.
  • Selecting a device transfers playback to it (Spotify-style): what you're playing follows you to the device you pick; picking This device pulls it back and stops the remote.
  • Your listening status (fm.atradio.actor.status) is now driven by Connect: it's cleared automatically once none of your devices are playing.

Headless daemon (--no-tui)

atradio --no-tui              # stay online as a controllable device; Ctrl-C to stop

Runs with no TUI — just an online player you drive from the web app or another client (great for a Raspberry Pi or a always-on box wired to your speakers).

Sign in with an app password for a daemon. OAuth refresh tokens are short-lived, so an OAuth-authenticated daemon eventually drops offline and prints session expired — run atradio login to reconnect until you sign in again. An app-password session stays signed in far longer and refreshes on its own — set ATPROTO_IDENTIFIER + ATPROTO_APP_PASSWORD and run atradio login (no --oauth). See Signing in.

The device name shown to your other clients defaults to a hostname-based label; set a custom one in ~/.config/atradio/settings.toml:

device_name = "Living Room"

Run as a service (systemd, Linux only)

On Linux you can install the headless daemon as a systemctl --user service so it starts on login and restarts on failure — ideal for a Raspberry Pi or an always-on box. Sign in with an app password first so the daemon stays online unattended.

atradio service install     # write the unit, enable + start it under systemctl --user
atradio service status      # show the running service (wraps `systemctl --user status`)
atradio service uninstall   # stop, disable, and remove the unit

install drops a unit at ~/.config/systemd/user/atradio.service whose ExecStart points at the current atradio binary running --no-tui, then runs daemon-reload, enable, and start. Follow its logs with:

journalctl --user -u atradio -f

To keep the service running after you log out (e.g. on a headless Pi), enable lingering once: sudo loginctl enable-linger $USER.

The service subcommand is Linux-only — it is compiled out entirely on macOS, FreeBSD, NetBSD, and other platforms, where systemd isn't available.

Platform notes

  • Linux: the player is exposed over MPRIS (D-Bus), so media keys and desktop panels / playerctl can see now-playing and drive play/pause/stop.

Lexicon bindings

The typed fm.atradio.* records/queries in src/fm_atradio/ and src/builder_types.rs are generated from the lexicon JSON in packages/lexicons/lexicons/atradio via jacquard's codegen. Regenerate with:

cargo install jacquard-lexgen   # provides `jacquard-codegen`
bash scripts/gen-lexicons.sh