CLIAMP — Terminal Music Player
Description: A retro terminal music player inspired by Winamp 2.x. Play local files, YouTube, Spotify, Plex, Jellyfin, Emby, Navidrome, SoundCloud, NetEase, and 30,000+ radio stations with a spectrum visualizer and 10-band EQ.

     ██████ ██      ██  █████  ███    ███ ██████
    ██      ██      ██ ██   ██ ████  ████ ██   ██
    ██      ██      ██ ███████ ██ ████ ██ ██████
    ██      ██      ██ ██   ██ ██  ██  ██ ██
     ██████ ███████ ██ ██   ██ ██      ██ ██

Terminal Music Player · Winamp for your shell

Playlists, EQ, visualizers, lyrics, remote control, and a Lua plugin system. Streams from Spotify, YouTube Music, NetEase, Plex, Jellyfin, Emby, Navidrome, and 30,000+ radio stations.

$ cliamp --provider radio tty1
C L I A M P [Playlist]
♫ Lofi Hip Hop Radio
00:00 / 00:00 ■ Stopped (click to play) ◌ Buffering… ● Streaming

━━━━━━━━━━━━━━━━━━━━ STREAMING ━━━━━━━━━━━━━━━━━━━━
EQ [Rock] +5 +4 +2 -1 -2 +2 +4 +5 +5 +5 VOL +0dB
SRC
[Local] [Navidrome] [Plex] [Jellyfin] [Emby] [Spotify] [YouTube] [YT Music]
▸─ Playlist ── [Shuffle] [Repeat: Off] [1/3] ──
▶ 1. Lofi Hip Hop Radio
2. EDM Pulse
3. Synthwave Nights
`↑↓` Scroll `Enter` Play `Spc` ▶❚❚ `*` Fav `Tab` Focus `?` Keys
SPD [1x] ↓ 0.7 MB 32 KB/s
`$ cliamp https://radio.cliamp.stream/lofi/stream.pls`
Install→ Source↗
MP3 FLAC OGG OPUS WAV AAC ALAC Spotify YouTube Plex Jellyfin Emby Navidrome SoundCloud NetEase Bandcamp Bilibili RSS / Podcasts 30k+ Radio Stations ICY metadata MPRIS Lua plugins MP3 FLAC OGG OPUS WAV AAC ALAC Spotify YouTube Plex Jellyfin Emby Navidrome SoundCloud NetEase Bandcamp Bilibili RSS / Podcasts 30k+ Radio Stations ICY metadata MPRIS Lua plugins
01 demo.mp4
02 install.sh
Direct
`$ curl -fsSL …/install.sh | sh` COPIED
Homebrew
`$ brew install bjarneo/cliamp/cliamp` COPIED
Arch (AUR)
`$ yay -S cliamp` COPIED
Or build from source — see the README.
Next step · configure providers

### Run the setup wizard

An interactive TUI walks you through Navidrome, Plex, Jellyfin, Emby, Spotify, NetEase, and YouTube Music. It links to each provider's credential page, validates the connection, and writes the right block to your config file.

Setup
`$ cliamp setup` COPIED
03 sources

Stream from everywhere. Every provider runs through the same playlist, EQ, visualizer, and lyrics pipeline — your config follows you across services. Run `cliamp setup` for an interactive wizard that walks you through Navidrome, Plex, Jellyfin, Emby, Spotify, NetEase, and YouTube Music.

OAuth · cached
Spotify
Stream your Premium library. Bring your own developer `client_id` for a private quota, or use the built-in shared one. Search & add tracks with `F`.
yt-dlp
YouTube
Search videos with `Ctrl+F`, then play, append, or queue from a results list.
OAuth · cached
YT Music
Browse your playlists with auto music/non-music classification. Set `cookies_from` to play through your browser session and avoid YouTube bot blocks (keep `yt-dlp` current).
Media server
Plex
Browse albums and stream from your Plex Media Server.
Media server
Jellyfin
Artists, albums, tracks — buffered gapless playback.
Media server
Emby
Browse your Emby music library with API key or username/password auth.
Subsonic API
Navidrome
Browse your self-hosted library with synced lyrics.
Provider · yt-dlp
SoundCloud
Opt-in: set `[soundcloud] enabled = true`. Search with `Ctrl+F`, browse curated genre playlists, or add `user` for your profile. `cookies_from` unlocks Go+ tracks via your browser session.
Provider · yt-dlp
NetEase
Opt-in: run `cliamp setup` after signing in at `music.163.com`, then choose your browser. Browse liked songs, account playlists, saved playlists, and charts with browser-cookie playback.
yt-dlp
Bandcamp
Paste any Bandcamp URL and it just plays.
yt-dlp
Bilibili
Audio tracks from Bilibili videos via yt-dlp.
30k+ stations
Radio
Browse stations by country, tag, or name. ICY metadata.
RSS feeds
Podcasts
Pass any podcast RSS URL and listen in your terminal.
Local
Files
MP3, FLAC, OGG, Opus, WAV, AAC, ALAC, WMA. ID3 tags read.
NetEase Cloud Music

### Browser-session setup

Sign in at `music.163.com`, then run `cliamp setup`. Choose NetEase Cloud Music, pick the browser you used, and the wizard validates the session before writing config.

    [netease]
    enabled = true
    cookies_from = "chrome"
    user_id = "your-account-user-id"

After setup, press `M` to open NetEase and `Ctrl+F` to search. Playback uses `yt-dlp` with the same browser cookie source.

04 features
≡
10-Band Equalizer

Parametric EQ with presets: Rock, Jazz, Pop, Classical, and more.

◈
Themes & Visualizers

17 built-in themes and multiple visualizer modes. Hot-swap with `t` / `v`.

☰
Playlists

TOML playlists, M3U/M3U8/PLS support, playlist manager with album grouping.

⟲
Recently Played

Auto-recorded listening history. Browse it as a virtual playlist or run `cliamp history` from the shell.

→
HTTP Streaming

Play from URLs, internet radio, and remote M3U playlists.

♪
Synced Lyrics

Auto-scrolling lyrics for local & Navidrome tracks. Press `y`.

⇄
Gapless Playback

Seamless transitions with preloaded next-track buffering.

♬
MPRIS / Media Keys

Desktop integration. Hardware media keys and `playerctl`.

▮
Quickshell Widget (Omarchy)

Drop-in Quickshell now-playing card with a Winamp 2-style spectrum, tracking your active Omarchy theme. Setup.

⚙
Audio Quality

Configurable sample rate (22kHz–192kHz), buffer size, resample.

⌀
Live Radio Metadata

ICY/Shoutcast metadata for the current song on internet radio, plus API-based now-playing for FIP and NTS.

✎
Embedded Tag Reading

ID3v2, Vorbis comments, MP4 atoms — artist, album, genre, year.

▶
CLI Flags

Override volume, shuffle, repeat, theme, EQ, sample rate per-session.

≡
Diagnostic Logging

File logs at ~/.config/cliamp/cliamp.log. Set `log_level` in config or `--log-level` on the CLI: `debug`, `info`, `warn`, `error`.

↑
Self-Update

Run `--upgrade` to update to the latest release in-terminal.

$
Env-Interpolated Secrets

Reference any string in `config.toml` from the environment with `${VAR}` or `$VAR`. Keep passwords and tokens out of the file.

⌁
Remote Control

Control a running instance from another terminal via Unix-socket IPC. Run with `--daemon` for headless playback driven entirely by scripts or Waybar.

⎔
Lua Plugins

Lua 5.1 sandboxed plugin system. Hook events, add visualizers, push data.

💾
Save to Disk

Press `Ctrl+S` to save the current track to `~/Music`.

05 themes.toml

17 built-in themes. Press `t` to switch, or use `--theme "name"` from the CLI. Drop a `.toml` file in ~/.config/cliamp/themes/ to add your own.

Ayu Mirage Dark
██ ██ ██ ██ ██ ██
Catppuccin
██ ██ ██ ██ ██ ██
Catppuccin Latte
██ ██ ██ ██ ██ ██
Ethereal
██ ██ ██ ██ ██ ██
Everforest
██ ██ ██ ██ ██ ██
Flexoki Light
██ ██ ██ ██ ██ ██
Gruvbox
██ ██ ██ ██ ██ ██
Hackerman
██ ██ ██ ██ ██ ██
Kanagawa
██ ██ ██ ██ ██ ██
Matte Black
██ ██ ██ ██ ██ ██
Miasma
██ ██ ██ ██ ██ ██
Nord
██ ██ ██ ██ ██ ██
Osaka Jade
██ ██ ██ ██ ██ ██
Ristretto
██ ██ ██ ██ ██ ██
Rosé Pine
██ ██ ██ ██ ██ ██
Tokyo Night
██ ██ ██ ██ ██ ██
Vantablack
██ ██ ██ ██ ██ ██
Each swatch: accent · bright fg · fg · green · yellow · red
06 keybindings.conf
Transport
`Space`Play / Pause
`s`Stop
`> / <`Next / Previous track
`← / →`Seek −/+5s
`Shift+← / Shift+→`Seek −/+30s (default)
`N then j`Seek to N×10% (e.g. `7j` = 70%)
`Ctrl+J`Jump to time
`+ / −`Volume up / down
`] / [`Speed ±0.25x
`m`Toggle mono
`r / z`Repeat / Shuffle
Navigation
`Tab`Focus: Playlist / EQ
`j / k`Scroll or adjust EQ (wraps)
`h / l`EQ band left / right
`PgUp / PgDn`Page up / down
`Ctrl+U / Ctrl+D`Half-page scroll
`Home / End`Jump to top / end
`g / G`Vim-style top / bottom
`Enter`Play selected
`Shift+↑ / Shift+↓`Move track up / down
`Esc / b`Back to provider
Search & Browse
`/`Search playlist (fuzzy, ranked by relevance)
`Ctrl+F`Search active provider (Spotify, Navidrome, Jellyfin, Emby, Plex, NetEase, Local) or YouTube fallback; Local search is fuzzy
`f`Toggle bookmark ★ / radio favorite
`u`Load URL (stream / playlist)
`o`Open file browser
`a / A`Queue (play next) / Queue manager
`x`Remove highlighted track from current playlist
`p`Playlist manager
`L`Browse local playlists
`Ctrl+X`Expand playlist
Inside Playlist Manager
`/`Filter playlists or tracks (incremental)
`Enter`Open playlist · play highlighted track on the tracks screen
`P`Play all tracks from the top
`a`Add the now-playing track (footer shows which one)
`r`Rename the playlist
`d`Delete playlist (confirms) / remove track
`Esc / p`Close (or clear active filter)
Provider Playlists Pane
`↑ / ↓ j / k`Move cursor (wraps)
`Enter`Load the highlighted playlist's tracks into the queue
`/`Filter the visible playlists
`Ctrl+F`Online / server search via the provider's own search
`Ctrl+R`Refresh — re-pull playlists from the provider
`S / N / P / J / E / Y / C / M / L / R`Switch to Spotify / Navidrome / Plex / Jellyfin / Emby / YouTube / SoundCloud / NetEase / Local / Radio
`▶`Marker on the row whose tracks are currently loaded
Provider Browser (`N`)
`↑ / ↓ j / k`Move cursor (wraps top↔bottom)
`← / → h / l`Back / drill in
`Enter`Open (artists/albums) · play the highlighted track and queue the rest
`R`Replace queue with all visible tracks (start from #1)
`a`Append all visible tracks to the queue
`q`Queue the highlighted track to play next
`s`Cycle album sort (album list only)
`/`Filter the visible list (search bar appears under the title)
`S / N / P / J / E / Y / C / M / L / R`Quick-switch to another provider without going back to the main pane
`Esc`Walk back one level / close the browser
Search Results
`Enter`Play selected track now
`a`Append selected to playlist
`q`Queue selected to play next
`p`Save to Spotify playlist (Spotify only)
`Esc`Back to search input
Providers & UI
`N`Navidrome
`S`Spotify
`P`Plex
`J`Jellyfin
`E`Emby
`Y`YouTube
`R`Radio
`e / t / v`EQ preset / Theme / Visualizer
`Ctrl+V`Pick visualizer (live preview list)
`V`Full-screen visualizer
`y / i`Lyrics / Track info
`Ctrl+H`Toggle album headers
`Ctrl+S`Save to ~/Music
`? / Ctrl+K`Show keymap
`q`Quit
07 cli & remote control

CLI flags override any config option for a single session. Remote commands control a running instance over Unix-socket IPC — open another terminal and talk to `cliamp` directly.

Flags
`--volume <dB>`[volume_min, +6]
`--shuffle`Enable shuffle
`--repeat <mode>`off, all, one
`--mono / --no-mono`Mono downmix
`--auto-play`Start immediately
`--theme <name>`UI theme
`--eq-preset <name>`EQ preset
`--visualizer <mode>`Bars, Wave, Matrix, Retro…
`--sample-rate <Hz>`Output sample rate
`--buffer-ms <ms>`Speaker buffer 50–500
`--resample-quality <n>`1–4
`--bit-depth <n>`PCM: 16 or 32
`--compact`Cap width at 80 cols
`--low-power`Lower UI cadence and disable visualization
`--daemon / -d`Run headless (IPC only, no TUI)
`--playlist <name>`Load TOML playlist
`--provider <name>`Default provider
`--upgrade`Update in place
`--help / --version`Help & version
Remote
`play / pause / toggle`Playback control
`stop`Stop playback
`next / prev`Track navigation
`status [--json]`Current state
`volume <dB>`Set volume
`seek <secs>`Relative seek
`load "Name"`Load a playlist
`queue /path/file`Queue a track
`history [--limit N]`Show recently played
`history clear`Wipe recently-played file
`shuffle [on|off]`Toggle or set shuffle
`repeat [off|all|one]`Set or cycle repeat
`mono [on|off]`Mono output
`speed <ratio>`0.25–2.0
`eq <preset>`Flat, Rock, Pop, Jazz…
`eq --band <0-9> <dB>`Set individual band
`device list`List output devices
`device <name>`Switch device
`theme <name|list>`Set or list themes
`vis <name|next|list>`Set or cycle visualizer
08 plugins.lua

Lua 5.1 plugin system. Hook into playback events, add custom visualizers, or push data to external services. Each plugin runs in an isolated sandbox — a crash in one cannot affect others or the player. Drop a `.lua` file in ~/.config/cliamp/plugins/ and restart.

now-playing.lua

    -- ~/.config/cliamp/plugins/now-playing.lua
    local p = plugin.register({
        name = "now-playing",
        type = "hook",
    })

    p:on("track.change", function(track)
        cliamp.fs.write("/tmp/cliamp-now-playing",
            track.artist .. " - " .. track.title)
    end)

    p:on("app.quit", function()
        cliamp.fs.remove("/tmp/cliamp-now-playing")
    end)

webhook.lua

    -- ~/.config/cliamp/plugins/webhook.lua
    local p = plugin.register({
        name = "webhook",
        type = "hook",
    })

    local url = p:config("url")

    p:on("track.change", function(track)
        if not url then return end
        cliamp.http.post(url, {
            json = { title = track.title, artist = track.artist }
        })
    end)

simple-bars.lua (custom visualizer)

    -- ~/.config/cliamp/plugins/simple-bars.lua
    local p = plugin.register({
        name = "simple-bars",
        type = "visualizer",
    })

    -- Called ~20 FPS. bands: 10 values (0.0-1.0)
    function p:render(bands, frame)
        local lines = {}
        for row = 5, 1, -1 do
            local line = ""
            for i = 1, 10 do
                if bands[i] > (row - 1) / 5 then
                    line = line .. "██████ "
                else
                    line = line .. "       "
                end
            end
            table.insert(lines, line)
        end
        return table.concat(lines, "\n")
    end

Events
`track.change`New track starts playing
`track.scrobble`Track played ≥50% or ≥4 min
`playback.state`Play, pause, stop, seek, volume change
`player.seek`A seek completes (position, duration)
`player.volume`Volume changes (db)
`player.eq`EQ band or preset changes
`player.mode`Shuffle toggled or repeat cycled
`queue.change`Playlist or play-next queue changes
`app.start`After all plugins loaded
`app.quit`Before shutdown
API
player

Read-only state: `state()`, `position()`, `volume()`, `shuffle()`, `eq_bands()`…

track

Current metadata: `title()`, `artist()`, `album()`, `genre()`, `duration_secs()`

queue

Read the playlist: `list()`, `count()`, `current()`. Mutate it with `add()`, `jump()`, `remove()`, `move()` (requires `permissions = {"control"}`).

store

Persistent per-plugin KV: `set()`, `get()`, `delete()`, `keys()`, `clear()`. Survives restarts; scoped to your plugin.

http

HTTP client: `get()`, `post()`. JSON, form body, headers, 5s timeout.

fs

Sandboxed I/O: `write()`, `read()`, `append()`, `remove()`, `exists()`, `mkdir()`, `listdir()`

exec

Spawn from allowlist (`yt-dlp`, `ffmpeg` by default): `run(bin, args, opts)`, streams stdout/stderr, cancelable. Requires `permissions = {"exec"}`.

json

`encode()` and `decode()` for JSON serialization.

crypto

`md5()`, `sha256()`, `hmac_sha256()` for hashing and signing.

notify

Desktop notifications via `notify-send`. Works with mako, dunst, etc.

message

`message(text, secs)` → transient message in the player's status bar.

timer

Schedule callbacks: `after()`, `every()`, `cancel()`

log

`info()`, `warn()`, `error()`, `debug()` → plugins.log

config

Read per-plugin values from `[plugins.name]` in config.toml via `p:config(key)`

bind

`p:bind(key, desc, fn)` — register a TUI keybinding. With a description it appears in the Ctrl+K overlay. Rejects keys reserved by cliamp. Requires `permissions = {"keymap"}`.

command

`p:command(name, fn)` — register a shell-invokable command. Run with `cliamp plugins call <plugin> <cmd> [args]`.

Community
cliamp-lastfm
Last.fm scrobbling
@tetsuo76
cliamp-plugin-led-burst
Stereo LED matrix visualizer
@AlexZeitler
cliamp-plugin-nightrider
Nightrider visualizer
@HANCORE-linux
cliamp-plugin-tubeamp
Vacuum-tube amplifier visualizer
@8bit64k
09 radio.stats
LIVE
-
listening now
-
total sessions
-
listen hours
Daily Activity
Listeners Around the World
Top Countries
COPIED
Tune in
`$ cliamp https://radio.cliamp.stream/lofi/stream.pls`