chore: import upstream snapshot with attribution
Deploy to GitHub Pages / deploy (push) Failing after 0s
CI / go (push) Has been cancelled
CI / build (darwin, macos-14) (push) Has been cancelled
CI / build (windows, windows-2025) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:31:13 +08:00
commit ead81af521
414 changed files with 73946 additions and 0 deletions
+53
View File
@@ -0,0 +1,53 @@
# Audio Quality
cliamp lets you tune the output sample rate, speaker buffer size, resample quality, and bit depth via `~/.config/cliamp/config.toml`. The active settings are shown in the `OUT` status line below the EQ.
## Configuration
Add any of these to your config file:
```toml
# Output sample rate in Hz (22050, 44100, 48000, 96000, 192000)
sample_rate = 44100
# Speaker buffer in milliseconds (50-500)
buffer_ms = 100
# Resample quality (1-4, where 4 is best)
resample_quality = 4
# PCM bit depth for FFmpeg-decoded formats: 16 (default) or 32 (lossless)
bit_depth = 16
```
All four are optional. Defaults are shown above.
## What they do
| Setting | Effect |
|--------------------|------------------------------------------------------------------------|
| `sample_rate` | Output rate sent to your sound card. 48000 matches most modern DACs. |
| `buffer_ms` | Lower = less latency, higher = fewer glitches. Try 200 if audio pops. |
| `resample_quality` | Sinc interpolation quality when a file's native rate differs from output. 4 is best, 1 is fastest. |
| `bit_depth` | PCM precision for FFmpeg-decoded formats (m4a, aac, alac, opus, wma, webm). 32 uses float PCM which preserves up to 24-bit audio without truncation. Native formats (mp3, flac, wav, ogg) always decode at full precision regardless of this setting. |
## Quick recipes
**Lossless / hi-res setup** (good DAC, beefy CPU):
```toml
sample_rate = 96000
buffer_ms = 100
resample_quality = 4
bit_depth = 32
```
**Low-latency / weak hardware**:
```toml
sample_rate = 44100
buffer_ms = 200
resample_quality = 1
```
Changes take effect on next launch.
+201
View File
@@ -0,0 +1,201 @@
# CLI Flags
Override any config option for a single session without editing `~/.config/cliamp/config.toml`. Flags can appear before or after file/URL arguments.
## Playback
```sh
cliamp --volume -5 track.mp3 # volume in dB [-30, +6]
cliamp --shuffle ~/Music # enable shuffle
cliamp --repeat all ~/Music # repeat mode: off, all, one
cliamp --mono track.mp3 # downmix to mono
cliamp --no-mono track.mp3 # force stereo
cliamp --auto-play ~/Music # start playback immediately
cliamp --playlist "Blade Runner" # load a local TOML playlist (add --auto-play to start playback)
```
## Audio engine
```sh
cliamp --sample-rate 48000 track.mp3 # output sample rate (22050, 44100, 48000, 96000, 192000)
cliamp --buffer-ms 200 track.mp3 # speaker buffer in ms (50500)
cliamp --resample-quality 1 track.mp3 # resample quality factor (14)
cliamp --bit-depth 32 track.m4a # PCM bit depth: 16 (default) or 32 (lossless)
```
## Appearance
```sh
cliamp --compact ~/Music # cap width at 80 columns
cliamp --eq-preset "Bass Boost" ~/Music
```
## Diagnostics
```sh
cliamp --log-level debug # raise log verbosity for one session
```
Logs are written to `~/.config/cliamp/cliamp.log`. Levels: `debug`, `info` (default), `warn`, `error`.
## Low-power mode
```sh
cliamp --low-power track.mp3 # reduce CPU load for this session
```
Reduces CPU load by lowering UI/render cadence and forcing the visualizer to `none`. Useful on battery, slow terminals, or SSH sessions. Press `v` in the player to cycle visualizers back on at any time.
To make this persistent, set it in `~/.config/cliamp/config.toml`:
```toml
low_power = true
```
## Headless daemon mode
```sh
cliamp --daemon # no TUI, IPC only
cliamp --daemon --auto-play --playlist Lofi # start playing on launch
cliamp -d ~/Music --auto-play # short flag form
```
Runs cliamp without rendering a UI, listening on the same Unix socket the TUI uses. All `cliamp <subcommand>` IPC clients keep working. UI-specific commands (`theme`, `vis`) return an error in this mode. See [Headless Daemon Mode](headless.md) for use cases and example configs (Waybar, Hyprland, systemd, cron).
## Search
Search and play a track directly from the command line (requires [yt-dlp](https://github.com/yt-dlp/yt-dlp)):
```sh
cliamp search "never gonna give you up" # search YouTube
cliamp search-sc "lofi beats" # search SoundCloud
```
Press `Ctrl+F` in the player for context-aware search: it runs the active provider's native search when available or falls back to YouTube search.
## General
| Flag | Short | Description |
|------|-------|-------------|
| `--help` | `-h` | Show help and exit |
| `--version` | `-v` | Print version and exit |
| `--upgrade` | | Update to the latest release |
## Mixing flags and files
Flags can appear before, after, or between positional arguments:
```sh
cliamp --shuffle track.mp3 --volume -5
cliamp track.mp3 --repeat all --mono ~/Music
```
## Flag reference
| Flag | Type | Default | Range / Values |
|------|------|---------|----------------|
| `--volume` | float | 0 | -30 to +6 dB |
| `--shuffle` | bool | false | |
| `--repeat` | string | off | off, all, one |
| `--mono` / `--no-mono` | bool | false | |
| `--auto-play` | bool | false | |
| `--compact` | bool | false | |
| `--theme` | string | | theme name |
| `--eq-preset` | string | | preset name |
| `--sample-rate` | int | 44100 | 22050, 44100, 48000, 96000, 192000 |
| `--buffer-ms` | int | 100 | 50500 |
| `--resample-quality` | int | 4 | 14 |
| `--bit-depth` | int | 16 | 16, 32 |
| `--playlist` | string | | local TOML playlist name |
| `--log-level` | string | info | debug, info, warn, error |
| `--low-power` | bool | false | lowers UI cadence; disables visualization |
| `--daemon` / `-d` | bool | false | run headless; IPC only, no TUI |
CLI flags override config file values for the current session only. They are not persisted.
## Setup wizard
Configure remote providers (Navidrome, Plex, Jellyfin, Emby, Spotify, Qobuz, NetEase, YouTube Music) through a small TUI. Each provider page links to where to find the required credentials, validates the connection live, and writes the resulting `[provider]` block to `~/.config/cliamp/config.toml` without disturbing the rest of the file.
```sh
cliamp setup
```
Keys: `↑/↓` to navigate, `Enter` to confirm or submit, `Esc` to back out, `q` from the menu to quit. Passwords and tokens are masked. Running setup again for an already-configured provider replaces its section in place.
## Playlist Management
Manage local TOML playlists from the command line without opening the TUI.
```sh
cliamp playlist list # list playlists with track counts
cliamp playlist create "Name" # create an empty playlist
cliamp playlist create "Name" file1 dir/ ... # create from files/folders (recursive, skips duplicate paths)
cliamp playlist create "Name" --ssh HOST dir/ # create from remote machine via SSH
cliamp playlist add "Name" file1 ... # append tracks to existing playlist, skipping duplicates
cliamp playlist rename "Old" "New" # rename a playlist
cliamp playlist show "Name" # display tracks
cliamp playlist show "Name" --json # machine-readable output
cliamp playlist remove "Name" --index 3 # remove track by index
cliamp playlist dedupe "Name" # remove duplicate paths, keeping the first
cliamp playlist sort "Name" --by album # sort in place
cliamp playlist doctor [Name] # report missing local files
cliamp playlist doctor "Name" --fix # prune missing local files
cliamp playlist export "Name" -o mix.m3u # export as M3U
cliamp playlist export "Name" --format pls # export as PLS to stdout
cliamp playlist import mix.m3u --name "Name" # import local M3U/M3U8/PLS
cliamp playlist bookmark "Name" --index 3 # toggle bookmark flag
cliamp playlist bookmarks # list bookmarked tracks
cliamp playlist enrich "Name" # probe duration/album metadata
cliamp playlist delete "Name" # delete entire playlist
```
Sort keys: `track`, `title`, `artist`, `album`, `artist+album`, `path`.
See [playlists.md](playlists.md) for the TOML format and [ssh-streaming.md](ssh-streaming.md) for remote playback.
## Recently Played
```sh
cliamp history # show the 50 most recent plays
cliamp history --limit 200 # change the cap
cliamp history --json # machine-readable output
cliamp history clear # wipe ~/.config/cliamp/history.toml
```
A play is recorded once you've listened to a track for at least 50% of its
duration. Inside the TUI, the same data appears as the virtual "Recently
Played" entry in the Local Playlists provider. See [history.md](history.md).
## Spotify
```sh
cliamp spotify reset # clear stored Spotify credentials
```
Use `spotify reset` if you see persistent `rate-limited on /v1/me` warnings or stale auth errors. After running it, relaunch cliamp and select Spotify to sign in again. See [spotify.md](spotify.md) for the full setup guide.
## Remote Control (IPC)
Control a running cliamp instance from another terminal:
```sh
cliamp play / pause / toggle / stop # playback control
cliamp next / prev # track navigation
cliamp status # current state
cliamp status --json # machine-readable state
cliamp volume -5 # adjust volume (dB)
cliamp seek 30 # seek to position (seconds)
cliamp load "Playlist Name" # load a playlist
cliamp queue /path/to/file.mp3 # queue a track
cliamp shuffle [on|off|toggle] # toggle or set shuffle
cliamp repeat [off|all|one|cycle] # set or cycle repeat mode
cliamp mono [on|off|toggle] # toggle or set mono output
cliamp speed 1.5 # set playback speed (0.252.0)
cliamp eq Rock # set EQ preset by name
cliamp eq --band 0 6.0 # set EQ band 0 to +6 dB
cliamp device list # list audio output devices
cliamp device "My DAC" # switch audio output device
```
See [remote-control.md](remote-control.md) for the full protocol specification.
+14
View File
@@ -0,0 +1,14 @@
# Community Plugins
Plugins built by the community. If you've built a plugin, open a PR to add it here.
| Plugin | Description | Author |
|--------|-------------|--------|
| [cliamp-lastfm](https://github.com/tetsuo76/cliamp-lastfm) | Last.fm scrobbling | [@tetsuo76](https://github.com/tetsuo76) |
| [cliamp-plugin-led-burst](https://github.com/AlexZeitler/cliamp-plugin-led-burst) | Stereo LED matrix visualizer | [@AlexZeitler](https://github.com/AlexZeitler) |
| [cliamp-plugin-block-burst](https://github.com/AlexZeitler/cliamp-plugin-block-burst) | Stereo LED block matrix visualizer | [@AlexZeitler](https://github.com/AlexZeitler) |
| [cliamp-plugin-block-burst](https://github.com/AlexZeitler/cliamp-plugin-vu-meter) | Analog multi VU meter style visualizer | [@AlexZeitler](https://github.com/AlexZeitler) |
| [cliamp-plugin-nightrider](https://github.com/HANCORE-linux/cliamp-plugin-nightrider) | Nightrider style visualizer | [@HANCORE-linux](https://github.com/HANCORE-linux) |
| [cliamp-plugin-tubeamp](https://github.com/8bit64k/cliamp-plugin-tubeamp) | Vacuum-tube amplifier visualizer | [@8bit64k](https://github.com/8bit64k) |
| [cliamp-plugin-nova](https://github.com/8bit64k/cliamp-plugin-nova) | Concentric-ring spectrum visualizer | [@8bit64k](https://github.com/8bit64k) |
| [cliamp-plugin-mandelbrot](https://github.com/mikkel-bergmann/cliamp-plugin-mandelbrot) | Zooming psychedelic Mandelbrot set visualizer with braille rendering and bass reactivity | [@mikkel-bergmann](https://github.com/mikkel-bergmann) |
+249
View File
@@ -0,0 +1,249 @@
# Configuration
For remote providers (Navidrome, Plex, Jellyfin, Emby, Spotify, Qobuz, NetEase, YouTube Music), the fastest path is the interactive wizard:
```sh
cliamp setup
```
It validates your credentials live and writes the right TOML block without touching the rest of your config. See [cli.md](cli.md#setup-wizard) for details.
## Config directory
cliamp resolves its config directory in this order:
- `CLIAMP_CONFIG_DIR`
- `XDG_CONFIG_HOME/cliamp`
- `HOME/.config/cliamp`
- on Windows, `%APPDATA%\cliamp` when `HOME` is not set
The examples below use `~/.config/cliamp` for brevity. On Windows without `HOME`, replace that path with `%APPDATA%\cliamp`.
For everything else, copy the example config and edit by hand:
```sh
mkdir -p ~/.config/cliamp
cp config.toml.example ~/.config/cliamp/config.toml
```
## Options
```toml
# Default volume in dB (range: volume_min to 6)
volume = 0
# Minimum volume floor in dB (range: -90 to 0, default: -50)
# Controls how low the volume control can go.
volume_min = -50
# Repeat mode: "off", "all", or "one"
repeat = "off"
# Start with shuffle enabled
shuffle = false
# Start with mono output (L+R downmix)
mono = false
# Initial directory for the file browser ('o' key)
initial_directory = "~/Music"
# Shift+Left/Right seek jump in seconds
seek_large_step_sec = 30
# EQ preset: "Flat", "Rock", "Pop", "Jazz", "Classical",
# "Bass Boost", "Treble Boost", "Vocal", "Electronic", "Acoustic"
# Leave empty or "Custom" to use manual eq values below
eq_preset = "Flat"
# 10-band EQ gains in dB (range: -12 to 12)
# Bands: 70Hz, 180Hz, 320Hz, 600Hz, 1kHz, 3kHz, 6kHz, 12kHz, 14kHz, 16kHz
# Only used when eq_preset is "Custom" or empty
eq = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# Visualizer mode (leave empty for default Bars)
# Options: Bars, BarsDot, Rain, BarsOutline, Bricks, Columns, ClassicPeak, Wave, Scatter, Flame, Retro, Pulse, Matrix, Binary, Sakura, Firework, Bubbles, Logo, Terrain, Scope, Heartbeat, Butterfly, Ascii, Firefly, Mosaic, Sand, Geyser, ClassicLED, None
visualizer = "Bars"
# Visualizer volume linking (default: true)
# When true, bar height follows the current volume level (classic behavior).
# Set to false to decouple the visualizer from volume — bars stay visible
# even at very low volume levels.
vis_volume_linked = true
# Reduce CPU usage by lowering UI cadence and disabling visualization.
# This has the same effect as starting with --low-power.
low_power = false
# Compact mode: cap UI width at 80 columns (default: fluid/full-width)
compact = false
# UI theme name (see available themes in ~/.config/cliamp/themes/)
theme = "Tokyo Night"
# Log level: "debug", "info", "warn", or "error" (default "info")
# Logs are written to ~/.config/cliamp/cliamp.log
log_level = "info"
```
## Secrets from Environment Variables
Any string value in `config.toml` can be read from an environment variable by setting the value to `$VAR_NAME` or `${VAR_NAME}`. This keeps passwords, tokens, and client secrets out of the file itself.
```toml
[navidrome]
url = "https://music.example.com"
user = "alice"
password = "${NAVIDROME_PASSWORD}"
[plex]
url = "http://plex.local:32400"
token = "$PLEX_TOKEN"
[jellyfin]
url = "https://jelly.example.com"
token = "${JELLYFIN_TOKEN}"
[emby]
url = "https://emby.example.com"
token = "${EMBY_TOKEN}"
[ytmusic]
client_id = "${YTMUSIC_CLIENT_ID}"
client_secret = "${YTMUSIC_CLIENT_SECRET}"
```
Rules:
- Interpolation only happens when the **entire** value is `$NAME` or `${NAME}`. Mixed values like `"p@$$word"` are kept literally — no escaping needed.
- Variable names match `[A-Za-z_][A-Za-z0-9_]*`.
- If the variable is unset, the value is empty (the same as if you had left it blank).
- Works for any string field, including plugin config under `[plugins.<name>]`.
## Default Provider
Set which provider to start with:
```toml
provider = "radio"
```
Valid values: `radio` (default), `navidrome`, `spotify`, `plex`, `jellyfin`, `emby`, `qobuz`, `soundcloud`, `netease`, `yt`, `youtube`, `ytmusic`.
You can also override from the CLI: `cliamp --provider jellyfin`.
## SoundCloud
SoundCloud is opt-in. Add the section to `~/.config/cliamp/config.toml` to register the provider:
```toml
[soundcloud]
enabled = true
```
Once enabled, search works via `Ctrl+F`, pasted SoundCloud URLs play through yt-dlp, and the empty browse view is seeded with a curated set of search-backed genre playlists (**Trending**, **Hip-Hop**, **Electronic**, **House**, **Lo-Fi**, **Indie**, **Pop**) so there's something to explore on first launch.
> SoundCloud's official charts/discover endpoints all 404 through yt-dlp at present, so cliamp can't surface real chart data anonymously. The genre playlists are search-backed (results vary in quality but reflect current uploads).
### Browse a profile
Set a username to expose that profile's tracks, likes, and reposts in the browse view:
```toml
[soundcloud]
enabled = true
user = "yourname"
```
Three playlists appear: **Tracks**, **Likes**, and **Reposts** for `soundcloud.com/yourname`. Works for any public profile.
### Sign in via browser cookies
SoundCloud closed its OAuth program in 2014, so the bring-your-own-client_id pattern Spotify uses isn't available. Instead, point yt-dlp at your existing browser session — it picks up your SoundCloud login from the browser cookie jar:
```toml
[soundcloud]
enabled = true
user = "yourname"
cookies_from = "firefox" # or chrome, chromium, brave, edge, opera, safari, vivaldi
```
With cookies set, yt-dlp can stream subscriber-gated tracks (SoundCloud Go+) and access private likes/playlists your account is authorized for. The same cookies also apply to the player's yt-dlp invocations, so playback uses your signed-in session.
Requires `yt-dlp` on `PATH`.
## NetEase Cloud Music
NetEase is opt-in and uses your existing browser session. Sign in at `music.163.com`, then run:
```sh
cliamp setup
```
Pick **NetEase Cloud Music** and choose the browser you used to sign in. Common browsers are shown as menu choices; select the custom option only for profile-specific values. The setup wizard validates the session and writes:
```toml
[netease]
enabled = true
cookies_from = "chrome"
user_id = "your-account-user-id"
```
Once enabled, the provider shows your liked songs, created playlists, saved playlists, and public charts. Search works with `Ctrl+F`, and playback uses `yt-dlp` with the same browser cookie source.
## Custom Radio Stations
Add your own stations to `~/.config/cliamp/radios.toml`:
```toml
[[station]]
name = "Jazz FM"
url = "https://jazz.example.com/stream"
[[station]]
name = "Ambient Radio"
url = "https://ambient.example.com/stream.m3u"
```
These appear alongside the built-in cliamp radio in the Radio provider.
See [audio-quality.md](audio-quality.md) for sample rate, buffer, bit depth, and resample quality settings.
## WSL2 (Windows Subsystem for Linux)
cliamp uses ALSA for audio on Linux. WSL2 doesn't expose ALSA hardware directly, but WSLg provides a PulseAudio server that ALSA can route through.
If you see errors like `ALSA lib pcm.c: Unknown PCM default`, fix it with two steps:
**1. Install the ALSA PulseAudio plugin:**
```sh
sudo apt install libasound2-plugins
```
**2. Create `~/.asoundrc` to route ALSA through PulseAudio:**
```sh
cat > ~/.asoundrc << 'EOF'
pcm.default pulse
ctl.default pulse
EOF
```
WSLg must be active (`echo $PULSE_SERVER` should print a path). If it's empty, ensure you're on Windows 11 with WSLg enabled and run `wsl --shutdown` then reopen your terminal.
## ffmpeg (optional)
AAC, ALAC (`.m4a`), Opus, and WMA playback requires [ffmpeg](https://ffmpeg.org/):
```sh
# Arch
sudo pacman -S ffmpeg
# Debian/Ubuntu
sudo apt install ffmpeg
# macOS
brew install ffmpeg
```
MP3, WAV, FLAC, and OGG work without ffmpeg.
+67
View File
@@ -0,0 +1,67 @@
# Emby
cliamp can stream music directly from an Emby server using Emby's authenticated HTTP API. The integration exposes your music libraries as a flat album list in the normal provider pane, following the same shape as the Jellyfin and Plex providers.
> **Quick start:** run `cliamp setup` for a guided TUI that lets you pick API-key or username+password auth, validates against `/System/Info`, and writes the `[emby]` block for you. Manual setup steps are below.
## Prerequisites
- A reachable Emby server
- At least one library with `CollectionType = music`
- An Emby API key or user credentials
## Configuration
Add an `[emby]` section to `~/.config/cliamp/config.toml`:
```toml
[emby]
url = "https://emby.example.com"
user = "alice"
password = "your_password_here"
# optional alternatives:
# token = "xxxxxxxxxxxxxxxxxxxx"
# user_id = "00000000000000000000000000000000"
```
| Key | Description |
|-----|-------------|
| `url` | Base URL of your Emby server |
| `user` | Emby username — used for password login, and to select the matching account when using an API key |
| `password` | Emby password for password-based login |
| `token` | Emby API key — alternative to username/password |
| `user_id` | Optional Emby user id to skip discovery |
## Usage
Once configured, **Emby** appears as a provider alongside Radio, Navidrome, Plex, Jellyfin, Spotify, and the YouTube providers.
To start cliamp with Emby selected:
```bash
cliamp --provider emby
```
Or set it in config:
```toml
provider = "emby"
```
The provider exposes a flat list of albums:
```text
Artist — Album Title (Year)
```
Select an album to load its tracks, then play as normal. Press `E` anywhere in the UI to switch to Emby quickly.
## How it works
cliamp authenticates with either a configured API key or the supplied username/password, resolves the active Emby user, enumerates music library views, fetches album items from those views, then fetches track items for the selected album. Playback uses Emby's authenticated download endpoint, so the existing cliamp HTTP pipeline can stream the result directly.
## Known limitations
- **Album list is flat**: no artist drill-down yet
- **Token-based access**: store the API key carefully
- **API key user selection**: Emby API keys are server-level (no "current user"). When no `user` is configured, cliamp picks the first user returned by `/Users`. On single-user servers this is always correct; on multi-user servers, set `user_id` explicitly in `[emby]` to target a specific account.
+159
View File
@@ -0,0 +1,159 @@
# Headless Daemon Mode
Run cliamp without a TUI. The daemon listens on the same Unix socket as the interactive player, so every `cliamp <subcommand>` keeps working — but nothing renders to the terminal. This is useful when you want a music player you only ever talk to over IPC: from a status bar, a script, a hotkey daemon, or a cron job.
```sh
cliamp --daemon # no TUI, IPC only
cliamp -d # short form
cliamp --daemon --auto-play --playlist Lofi # start playing on launch
cliamp --daemon ~/Music --auto-play # auto-play a directory
```
Send `SIGINT` or `SIGTERM` to stop. Resume position is saved on graceful shutdown.
## What works
The daemon exposes the same IPC surface as the TUI. See [Remote Control](remote-control.md) for the full list:
- Playback: `play`, `pause`, `toggle`, `stop`, `next`, `prev`
- Position: `seek`, `volume`, `speed`
- Playback modes: `shuffle`, `repeat`, `mono`
- Library: `load "Name"`, `queue /path/to.mp3`
- Audio: `eq <preset>`, `eq --band N <dB>`, `device <name|list>`
- Status: `status`, `status --json`
## What doesn't
UI-only commands return an error in headless mode:
- `theme` — no UI to apply a theme to
- `vis` — no visualizer running
There is also no MPRIS / macOS NowPlaying bridge in this mode. Wire your media keys to `cliamp` subcommands directly (see [Hyprland](#hyprland) below).
## Use cases
### Background music daemon
Start cliamp once at login (e.g. via `~/.config/systemd/user/cliamp.service` or your DE's autostart) and leave it running. Control it from any terminal:
```sh
cliamp toggle # play/pause from anywhere
cliamp next
cliamp volume -3
```
A minimal systemd user unit:
```ini
[Unit]
Description=cliamp headless music player
[Service]
ExecStart=%h/.local/bin/cliamp --daemon --auto-play --playlist "Lofi"
Restart=on-failure
[Install]
WantedBy=default.target
```
```sh
systemctl --user enable --now cliamp.service
```
### Waybar / Polybar / i3blocks status modules
Poll `cliamp status --json` on an interval, render whatever fields you want.
**Waybar** (`~/.config/waybar/config`):
```jsonc
"custom/cliamp": {
"exec": "cliamp status --json | jq -r 'if .state == \"playing\" then \" \" + (.track.title // \"\") else \"\" end'",
"interval": 2,
"on-click": "cliamp toggle",
"on-click-right": "cliamp next",
"on-scroll-up": "cliamp volume +3",
"on-scroll-down": "cliamp volume -3"
}
```
**Polybar**:
```ini
[module/cliamp]
type = custom/script
exec = cliamp status --json | jq -r '.track.title // ""'
interval = 2
click-left = cliamp toggle
click-right = cliamp next
```
### Hotkeys (window manager / sxhkd / Hyprland)
Bind your media keys directly to IPC subcommands.
**Hyprland** (`~/.config/hypr/hyprland.conf`):
```ini
bind = , XF86AudioPlay, exec, cliamp toggle
bind = , XF86AudioNext, exec, cliamp next
bind = , XF86AudioPrev, exec, cliamp prev
bind = , XF86AudioRaiseVolume, exec, cliamp volume +3
bind = , XF86AudioLowerVolume, exec, cliamp volume -3
```
**sxhkd**:
```
XF86AudioPlay
cliamp toggle
XF86AudioNext
cliamp next
```
### Sleep / wake timers via cron
```cron
# Start lofi playback at 8am on weekdays
0 8 * * 1-5 /home/me/.local/bin/cliamp --daemon --auto-play --playlist Lofi >/dev/null 2>&1 &
# Stop at 6pm
0 18 * * * pkill -TERM -f 'cliamp --daemon'
```
### Scripted playlists
Build a queue from a script:
```sh
cliamp --daemon --auto-play &
sleep 1 # let the socket bind
for f in $(find ~/Music/Albums/Daft\ Punk -name '*.flac' | sort); do
cliamp queue "$f"
done
```
### Remote control over SSH
Since the socket lives at `~/.config/cliamp/cliamp.sock` and the CLI talks to it locally, anything that gets you a shell on the host (SSH, tmux session attach) lets you control playback:
```sh
ssh kitchen-pi cliamp toggle
ssh kitchen-pi cliamp status --json
```
### Embedded / kiosk audio
Run on a Pi or small Linux box that has no display. The daemon needs no terminal allocation, just a working ALSA/PipeWire/PulseAudio output.
```sh
cliamp --daemon --auto-play http://radio.cliamp.stream/lofi/stream
```
## Notes
- The daemon and TUI share the same Unix socket, so only one cliamp instance can run at a time per user. Starting a second instance refuses to bind.
- Lua plugins are not loaded in this version of headless mode. They depend on UI hooks that aren't wired up here.
- Auto-advance has no gapless preloading in headless mode — small inter-track gaps are expected.
+55
View File
@@ -0,0 +1,55 @@
# Recently Played
cliamp keeps a local listening history in `~/.config/cliamp/history.toml`. A
play is recorded once you've listened to a track for at least 50% of its
duration — the same threshold Last.fm and the Navidrome scrobbler use, so
skipped tracks never enter the list.
## Browsing in the TUI
Open the **Local Playlists** provider. When at least one play has been
recorded, a virtual `Recently Played` entry appears at the top of the list.
Open it like any other playlist — the tracks are listed newest-first. The list
is read-only: bookmarking, removing tracks, or deleting the playlist itself is
rejected with a clear error.
To clear the list, run `cliamp history clear` (see below).
## CLI
```sh
cliamp history # show the 50 most recent plays
cliamp history --limit 200 # show the 200 most recent
cliamp history --limit 0 # show all (capped at 200 entries on disk)
cliamp history --json # machine-readable output
cliamp history clear # wipe the history file
```
The relative timestamp (`3m ago`, `yesterday`, …) is local time. The JSON
output uses `played_at` in RFC 3339 UTC for portability.
## File format
`history.toml` uses the same minimal TOML dialect as cliamp's local playlists:
```toml
[[entry]]
played_at = "2026-05-06T22:09:11Z"
path = "/home/me/Music/AC-DC/Highway to Hell.flac"
title = "Highway to Hell"
artist = "AC/DC"
album = "Highway to Hell"
year = 1979
duration_secs = 208
```
Entries cap at 200 by default; older plays roll off FIFO. Consecutive replays
of the same track within 5 minutes update the existing top entry's timestamp
rather than duplicating it.
## What is not recorded
- Tracks you skipped before the 50% threshold.
- Live streams without a known duration (radio stations, ICY streams) — there
is no "halfway through" to detect.
- Tracks with empty paths (defensive guard).
+67
View File
@@ -0,0 +1,67 @@
# Jellyfin
cliamp can stream music directly from a Jellyfin server using Jellyfin's authenticated HTTP API. The first integration exposes your music libraries as a flat album list in the normal provider pane, following the same shape as the Plex provider.
> **Quick start:** run `cliamp setup` for a guided TUI that lets you pick API-token or username+password auth, validates against `/Users/Me`, and writes the `[jellyfin]` block for you. Manual setup steps are below.
## Prerequisites
- A reachable Jellyfin server
- At least one library with `CollectionType = music`
- A Jellyfin API token
## Configuration
Add a `[jellyfin]` section to `~/.config/cliamp/config.toml`:
```toml
[jellyfin]
url = "https://jellyfin.example.com"
user = "finamp"
password = "your_password_here"
# optional alternatives:
# token = "xxxxxxxxxxxxxxxxxxxx"
# user_id = "00000000000000000000000000000000"
```
| Key | Description |
|-----|-------------|
| `url` | Base URL of your Jellyfin server |
| `user` | Jellyfin username for password-based login |
| `password` | Jellyfin password for password-based login |
| `token` | Optional Jellyfin API token instead of username/password |
| `user_id` | Optional Jellyfin user id to skip discovery |
## Usage
Once configured, **Jellyfin** appears as a provider alongside Radio, Navidrome, Plex, Spotify, and the YouTube providers.
To start cliamp with Jellyfin selected:
```bash
cliamp --provider jellyfin
```
Or set it in config:
```toml
provider = "jellyfin"
```
The provider currently exposes a flat list of albums:
```text
Artist - Album Title (Year)
```
Select an album to load its tracks, then play as normal.
## How it works
cliamp authenticates with either a configured token or the supplied username/password, resolves the active Jellyfin user, enumerates music library views, fetches album items from those views, then fetches track items for the selected album. Playback uses Jellyfin's authenticated audio endpoint, so the existing cliamp HTTP pipeline can stream the result directly.
## Known limitations
- **Album list is flat**: no artist drill-down yet
- **No scrobbling/write-back**: plays are not reported back to Jellyfin yet
- **Token-based access**: store the API token carefully
+186
View File
@@ -0,0 +1,186 @@
# Keybindings
Press `?` or `Ctrl+K` in the player to see all keybindings.
## Playback
| Key | Action |
|---|---|
| `Space` | Play / Pause |
| `s` | Stop |
| `>` `.` | Next track |
| `<` `,` | Previous track |
| `Left` `Right` | Seek -/+5s |
| `Shift+Left` `Shift+Right` | Seek -/+30s (configurable) |
| `N` then `j` | Seek to N×10% of the track (e.g. `7j` jumps to 70%, `0j` to the start) |
| `+` `-` | Volume up/down |
| `]` `[` | Speed up/down (±0.25x) |
| `m` | Toggle mono |
| `Ctrl+J` | Jump to time |
## Navigation
| Key | Action |
|---|---|
| `Tab` | Toggle focus (Playlist / EQ) |
| `j` `k` / `Up` `Down` | Playlist scroll / EQ band adjust (wraps around) |
| `PageUp` `PageDown` / `Ctrl+U` `Ctrl+D` | Scroll playlist/file browser by page |
| `Home` `End` / `g` `G` | Go to top/end of playlist/file browser |
| `Shift+Up` `Shift+Down` | Move track up/down in playlist/queue |
| `h` `l` | EQ cursor left/right |
| `Enter` | Play selected track |
| `/` | Search playlist (navigate results with `↑` `↓` / `Ctrl+N` `Ctrl+P`; page scroll with `Ctrl+U` `Ctrl+D`) |
| `Ctrl+X` | Expand/collapse playlist |
| `o` | Open file browser |
| `b` `Esc` | Back to provider |
## EQ and Appearance
| Key | Action |
|---|---|
| `e` | Cycle EQ preset |
| `t` | Choose theme |
| `v` | Cycle visualizer |
| `Ctrl+V` | Pick visualizer from a list (live preview) |
| `V` | Full screen visualizer |
| `Ctrl+H` | Toggle album headers |
## Features
| Key | Action |
|---|---|
| `f` | Toggle bookmark ★ on selected track (or favorite radio station in radio browser) |
| `Ctrl+F` | Search — active provider's native search (Spotify, Qobuz, Navidrome, Jellyfin, Emby, Plex, NetEase, Local) or YouTube fallback. Available from playlist and provider-browser views. |
| `u` | Load URL (stream/playlist) |
| `y` | Show lyrics |
| `Ctrl+S` | Save track to ~/Music |
| `w` | Write the highlighted track to a local playlist |
| `N` | Navidrome browser |
| `L` | Browse local playlists (with cliamp radio) |
| `R` | Open radio provider |
| `S` | Open Spotify provider |
| `P` | Open Plex provider |
| `J` | Open Jellyfin provider |
| `E` | Open Emby provider |
| `Y` | Open YouTube provider |
| `C` | Open SoundCloud provider |
| `M` | Open NetEase provider |
| `Q` | Open Qobuz provider |
## Playlist and Queue
| Key | Action |
|---|---|
| `a` | Toggle queue (play next) |
| `A` | Queue manager |
| `x` | Remove the highlighted track from the current playlist |
| `p` | Playlist manager |
| `r` | Cycle repeat (Off / All / One) |
| `z` | Toggle shuffle |
### Inside the playlist manager
| Key | Action |
|---|---|
| `↑` `↓` / `j` `k` | Move cursor |
| `/` | Filter (incremental); `Esc` clears |
| `Enter` / `→` | List screen: open the highlighted playlist · Tracks screen: play the **highlighted** track |
| `p` | Tracks screen: play all from the top |
| `a` | List: add the now-playing track to the highlighted playlist. Tracks: mark/unmark all visible tracks. |
| `w` | List: save the current queue through the playlist picker. Tracks: copy marked/highlighted tracks to another playlist. |
| `Space` | Tracks: mark/unmark highlighted track and advance |
| `[` `]` | Tracks: move highlighted track and save the playlist |
| `s` | Tracks: sort and save, cycling `track`, `title`, `artist`, `album`, `artist+album`, `path` |
| `o` | Tracks: open file browser to add files to this playlist |
| `r` | List: rename the playlist |
| `d` | List: delete playlist (confirms). Tracks: remove marked tracks, or highlighted track when none are marked |
| `u` | Undo the last manager edit |
| `←` `Backspace` `h` | Tracks screen: go back to the list |
| `Esc` | Close the playlist manager or go back |
Shift-letter keys are reserved for provider switching, so playlist-manager track actions use lowercase or punctuation keys.
## File browser
| Key | Action |
|---|---|
| `↑` `↓` / `j` `k` | Move cursor |
| `←` `→` / `h` `l` / `Enter` | Back / open directory or file |
| `/` | Filter files |
| `Space` | Select or unselect file/directory |
| `a` | Select/unselect all visible audio files |
| `R` | Replace the current queue with selected files |
| `w` | Write selected files to a local playlist |
| `~` `.` | Jump to home / current working directory |
| `Esc` `o` | Close file browser |
## Provider browser (`N` key)
When you press `N` to drill into a provider (Navidrome, Plex, Jellyfin, Emby, Spotify, Qobuz, YouTube Music), the album/artist/track screens use:
| Key | Action |
|---|---|
| `↑` `↓` / `j` `k` | Move cursor (wraps top↔bottom) |
| `←` `→` / `h` `l` | Back / drill in |
| `/` | Filter the visible list (search bar appears under the title) |
| `Enter` | Open (artists/albums) · play the highlighted track and queue the rest of the visible list |
| `R` | Replace the queue with all visible tracks (start from the top) |
| `a` | Append all visible tracks to the queue |
| `q` | Queue the highlighted track to play next |
| `s` | Cycle album sort (album list only) |
| `S` `N` `P` `J` `E` `Y` `C` `M` `Q` `L` `R` | Quick-switch to that provider without going back through the main pane |
| `Esc` `b` | Walk back one level / close the browser |
The track screen shows a `N tracks · 47:22` subtitle and right-aligned per-track durations when the provider returns them.
## Provider playlist list
The playlists pane (visible when focus is on a provider — Spotify, Navidrome, Local Playlists, etc.):
| Key | Action |
|---|---|
| `↑` `↓` / `j` `k` | Move cursor (wraps) |
| `Ctrl+U` `Ctrl+D` | Scroll by page |
| `Enter` | Load the highlighted playlist's tracks into the queue |
| `/` | Filter the playlist list |
| `Ctrl+F` | Online/server search (Spotify/Navidrome/NetEase/etc.'s own search) |
| `Ctrl+R` | Refresh — re-pull the playlist list from the provider |
| `S` `N` `P` `J` `E` `Y` `C` `M` `Q` `L` `R` | Switch to that provider |
| `Tab` | Switch focus to EQ |
| `Esc` `b` | Back to the playlist pane |
Playlist rows show `Name · N tracks · 1h 23m` when the provider returns track counts and total duration. The currently loaded playlist is marked with a `▶` prefix. Spotify groups its playlists under section headers (`── library ──`, `── your playlists ──`, `── followed playlists ──`).
## Search results overlays
When `Ctrl+F` opens provider search or YouTube/SoundCloud net search and you're viewing the results list:
| Key | Action |
|---|---|
| `↑` `↓` / `j` `k` / `Ctrl+N` `Ctrl+P` | Move cursor (single item) |
| `Ctrl+U` `Ctrl+D` | Scroll results by page |
| `Enter` | Play the selected track now |
| `a` | Append the selected track to the playlist |
| `q` | Queue the selected track to play next |
| `p` | (Spotify only) Save the selected track to a Spotify playlist |
| `Esc` `Backspace` | Back to the search input |
## Fuzzy search
The local search boxes match fuzzily: your query characters only need to appear in order, not contiguously, and results are ranked by relevance (best match first). For example, `skr` or `saku` both find a track titled "Sakura".
This applies to:
- `/` playlist search
- `/` file browser filter
- `Ctrl+F` when the active provider is Local (your saved playlists)
Other `Ctrl+F` providers (Spotify, Qobuz, Navidrome, Jellyfin, Emby, Plex, NetEase, YouTube) send your query to their own search API, so matching there follows each service's rules.
## General
| Key | Action |
|---|---|
| `?` / `Ctrl+K` | Show keymap |
| `q` | Quit |
+18
View File
@@ -0,0 +1,18 @@
# Lyrics
Press `y` to show lyrics for the current track. For local files, cliamp uses embedded lyrics from the file tags first. If no embedded lyrics are present, lyrics are fetched from LRCLIB and NetEase Cloud Music.
## Modes
- **Synced lyrics**: for local files and Navidrome tracks, lyrics auto scroll and highlight the active line in time with playback.
- **Scroll mode**: for streams and plain lyrics without timestamps, use `j`/`k` or arrow keys to scroll manually.
Embedded LRC lyrics keep their timestamps. Embedded plain text lyrics are shown in scroll mode.
## Streams
Lyrics auto update when the ICY metadata changes (e.g., internet radio station transitions).
## YouTube and SoundCloud
Titles like "Artist - Song (Official Video)" are parsed to build better search queries.
+139
View File
@@ -0,0 +1,139 @@
# Media Controls
Cliamp integrates with the operating system's media control infrastructure so that desktop environments, hardware media keys, and command line tools can control playback, read track metadata, and adjust volume without touching the TUI.
## Platform Support
| Platform | Backend | Requirements |
|---|---|---|
| Linux | [MPRIS2](https://specifications.freedesktop.org/mpris-spec/latest/) over D-Bus | A running D-Bus session bus (provided by most desktop environments and Wayland compositors) |
| macOS | MPNowPlayingInfoCenter / MPRemoteCommandCenter | None (frameworks are built-in) |
| Other | No-op stub | — |
## Linux (MPRIS2)
### Bus Name
Cliamp registers itself as:
```
org.mpris.MediaPlayer2.cliamp
```
Only one instance can hold this name at a time. If a second Cliamp process tries to start, the MPRIS registration will fail silently and that instance will run without D-Bus integration.
### Playback Control
All standard transport commands are supported through the `org.mpris.MediaPlayer2.Player` interface:
| playerctl command | Effect |
|---|---|
| `playerctl play-pause` | Toggle play / pause |
| `playerctl play` | Resume playback |
| `playerctl pause` | Pause playback |
| `playerctl stop` | Stop playback |
| `playerctl next` | Skip to the next track |
| `playerctl previous` | Go to the previous track (or restart if more than 3 seconds in) |
### Seeking
Relative and absolute seeking are both supported:
```sh
playerctl position 30 # seek to 30 seconds
playerctl position 5+ # seek forward 5 seconds
playerctl position 5- # seek backward 5 seconds
```
Desktop widgets that display a progress bar will receive `Seeked` signals and stay in sync.
### Volume
Volume is exposed as a linear value between 0.0 and 1.0. Internally Cliamp uses a decibel scale (from -30 dB to +6 dB), and the conversion happens automatically.
```sh
playerctl volume # print current volume (0.0 to 1.0)
playerctl volume 0.5 # set volume to 50%
```
Setting volume through `playerctl` updates the player immediately. Changing volume with the `+` and `-` keys in the TUI is reflected back to D-Bus clients on the next tick.
### Metadata
Track metadata is published under the standard MPRIS keys:
| Key | Description |
|---|---|
| `mpris:trackid` | D-Bus object path identifying the current track |
| `xesam:title` | Track title |
| `xesam:artist` | Artist name (as a list with one entry) |
| `xesam:album` | Album name, when available |
| `xesam:url` | File path or stream URL |
| `mpris:artUrl` | Embedded album artwork from local files, when available |
| `mpris:length` | Duration in microseconds |
Query metadata with:
```sh
playerctl metadata # all keys
playerctl metadata artist # just the artist
playerctl metadata title # just the title
```
For live radio streams that provide ICY metadata, the artist and title fields update dynamically as the station reports new track information.
### Status
```sh
playerctl status # prints Playing, Paused, or Stopped
```
### Hyprland bindings
Hyprland does not bind `XF86Audio*` keys by default. Add the following to your Hyprland config (typically `~/.config/hypr/bindings.conf` or `hyprland.conf`) to wire hardware media keys to Cliamp through `playerctl`:
```conf
bindl = , XF86AudioPlay, exec, playerctl --player=cliamp play-pause
bindl = , XF86AudioPause, exec, playerctl --player=cliamp play-pause
bindl = , XF86AudioNext, exec, playerctl --player=cliamp next
bindl = , XF86AudioPrev, exec, playerctl --player=cliamp previous
```
Notes:
- `bindl` fires even when the session is locked, so keys continue to work under `hyprlock`.
- `--player=cliamp` scopes the command to Cliamp only. Drop the flag to control whichever MPRIS player was most recently active (useful when Cliamp shares the session with browsers or Spotify).
- Reload with `hyprctl reload` after editing.
- `playerctl` must be installed (`pacman -S playerctl`, `apt install playerctl`, …).
## macOS
On macOS, Cliamp publishes now-playing information to the system's MPNowPlayingInfoCenter. This enables:
- Control Centre and Lock Screen media controls
- Touch Bar playback buttons
- Hardware media keys (play/pause, next, previous)
- Bluetooth headphone buttons
Local files with embedded cover art publish that artwork to Control Centre and Lock Screen media controls. Artwork is cached by content under `~/.local/share/cliamp/album-art/` and pruned opportunistically to stay around 100 MB.
The macOS implementation requires the media-control runtime to pin the main goroutine to thread 0 (via `runtime.LockOSThread`) so that the Cocoa run loop can pump events. Bubbletea runs on a background goroutine instead.
## Architecture
The app-owned playback command and notifier boundary lives in `internal/playback`. The `mediactl` package translates platform APIs to and from that boundary and owns the platform-specific interactive runtime helper.
Platform-specific `Service` implementations:
- `internal/playback/*` — app-level playback commands and outbound notifier state.
- `mediactl/service_linux.go` — connects to the session bus, claims the MPRIS bus name, translates D-Bus calls into playback commands, and publishes outbound state through MPRIS properties.
- `mediactl/service_darwin.go` — initialises NSApplication as an accessory process, registers MPRemoteCommandCenter handlers, translates them into playback commands, and publishes now-playing state on the main-thread run loop.
- `mediactl/service_stub.go` — no-op implementation for unsupported platforms.
The model publishes playback state through the playback notifier whenever state changes. On Linux, `mediactl` uses `SetMust` rather than `Set` to bypass the property library's writable checks and callback triggers, which are intended for external D-Bus writes. For writable properties like Volume, the D-Bus callback is translated into an app playback command and dispatched back into the Bubbletea event loop.
## Limitations
Shuffle and loop status are not exposed. The `z` and `r` keys in the TUI control shuffle and repeat locally, but these states are not visible to or controllable from external tools.
The `HasTrackList` property is set to false on Linux. Cliamp does not implement the optional `org.mpris.MediaPlayer2.TrackList` interface.
+122
View File
@@ -0,0 +1,122 @@
# Navidrome Integration
Cliamp can connect to a [Navidrome](https://www.navidrome.org/) server and stream music directly from your library. Navidrome is a self-hosted music server compatible with the Subsonic API.
> **Quick start:** run `cliamp setup` for a guided TUI that prompts for the server URL, username, and password, validates the connection, and writes the `[navidrome]` block for you. Manual setup steps are below.
## Setup
Set three environment variables before launching Cliamp:
```sh
export NAVIDROME_URL="http://your-server:4533"
export NAVIDROME_USER="your-username"
export NAVIDROME_PASS="your-password"
```
Then run Cliamp without any file arguments:
```sh
cliamp
```
You can also combine local files with a Navidrome session:
```sh
NAVIDROME_URL=http://localhost:4533 NAVIDROME_USER=admin NAVIDROME_PASS=secret cliamp ~/Music/extra.mp3
```
## How It Works
When the environment variables are set, Cliamp authenticates with your Navidrome server using the Subsonic API. On launch it fetches your playlists and presents them in the TUI.
Browse your playlists with the arrow keys and press Enter to load one. The tracks are added to the local playlist and playback starts immediately. Audio is streamed as MP3 from the server.
## Controls
When focused on the provider panel:
| Key | Action |
|---|---|
| `Up` `Down` / `j` `k` | Navigate playlists |
| `Enter` | Load the selected playlist |
| `Tab` | Switch between provider and playlist focus |
| `N` | Open the Navidrome browser |
After loading a playlist you return to the standard playlist view with all the usual controls (seek, volume, EQ, shuffle, repeat, queue, search).
## Navidrome Browser
Press `N` at any time (or from the provider panel) to open the full-screen Navidrome browser. It lets you explore your library in three modes:
- **By Album**: browse a paginated list of all albums, then open any album to see its tracks.
- **By Artist**: browse all artists; selecting one loads every track across all their albums, grouped by album with separator headers.
- **By Artist / Album**: three-level drill-down: artist → album list → track list.
### Browser controls
**Mode menu:**
| Key | Action |
|---|---|
| `↑` `↓` / `j` `k` | Navigate |
| `Enter` | Select mode |
| `Esc` / `N` | Close browser |
**Artist or album list:**
| Key | Action |
|---|---|
| `↑` `↓` / `j` `k` | Navigate |
| `Enter` / `→` | Drill in |
| `s` | Cycle album sort order (album list only) |
| `Esc` / `←` | Back |
**Track list:**
| Key | Action |
|---|---|
| `↑` `↓` / `j` `k` | Navigate |
| `Enter` | Append selected track to playlist |
| `a` | Append all tracks to playlist |
| `R` | Replace playlist with all tracks and start playing |
| `Esc` / `←` | Back |
### Album sort order
While viewing the global album list, press `s` to cycle through sort modes:
| Value | Description |
|---|---|
| `alphabeticalByName` | A → Z by album title (default) |
| `alphabeticalByArtist` | A → Z by artist name |
| `newest` | Most recently added |
| `recent` | Most recently played |
| `frequent` | Most frequently played |
| `starred` | Starred / favourited |
| `byYear` | Chronological by release year |
| `byGenre` | Grouped by genre |
The chosen sort is saved automatically to `~/.config/cliamp/config.toml` under the `[navidrome]` section as `browse_sort` and is restored on the next launch.
## Architecture
The integration is built around a `Provider` interface defined in the `playlist` package:
```go
type Provider interface {
Name() string
Playlists() ([]PlaylistInfo, error)
Tracks(playlistID string) ([]Track, error)
}
```
The Navidrome client (`external/navidrome/client.go`) implements this interface. It builds authenticated Subsonic API requests using MD5 token auth (password + random salt) and parses the JSON responses into playlist and track structs.
Playlist and track fetching runs asynchronously through Bubbletea commands so the UI stays responsive while the server responds.
Adding support for another Subsonic-compatible server (Airsonic, Gonic, etc.) would mean implementing the same `Provider` interface against that server's API.
## Requirements
No additional dependencies are needed beyond a running Navidrome instance. The client uses Go's standard `net/http` and `crypto/md5` packages. Your Navidrome server must have the Subsonic API enabled, which is the default.
+63
View File
@@ -0,0 +1,63 @@
# NetEase Cloud Music Integration
cliamp supports NetEase Cloud Music as an opt-in provider. It can browse your account playlists, saved playlists, liked songs, and public charts. Playback is handled by `yt-dlp`, so `yt-dlp` and `ffmpeg` must be on `PATH`.
## Quick Start
Sign in at `music.163.com` in your browser, then run:
```sh
cliamp setup
```
Pick **NetEase Cloud Music**, then choose the browser where you are signed in. The wizard validates the session and writes:
```toml
[netease]
enabled = true
cookies_from = "chrome"
user_id = "your-account-user-id"
```
cliamp stores the browser name and user id only. It does not store your password or copy cookies into `config.toml`.
## Manual Config
```toml
[netease]
enabled = true
cookies_from = "chrome"
user_id = "78819429"
```
`cookies_from` is passed to `yt-dlp --cookies-from-browser`. Supported names depend on your `yt-dlp` version and commonly include `chrome`, `chromium`, `firefox`, `brave`, `edge`, `opera`, `safari`, and `vivaldi`. The setup wizard has common browsers as menu choices; use **Custom browser/profile** only for profile-specific values such as `chrome:Profile 1` or `firefox:default-release`.
`user_id` is optional when cookies are valid. If omitted, cliamp discovers it from the signed-in account.
## Usage
Start directly on NetEase:
```sh
cliamp --provider netease
```
Inside the TUI:
| Key | Action |
|---|---|
| `M` | Open NetEase provider |
| `Ctrl+F` | Search NetEase songs while NetEase is active |
| `Enter` | Load the highlighted playlist or play the highlighted track |
| `Ctrl+R` | Refresh playlists |
Direct NetEase URLs also work:
```sh
cliamp 'https://music.163.com/#/song?id=1973665667'
cliamp 'https://music.163.com/#/playlist?id=3778678'
```
## Limits
NetEase playback availability depends on the account, region, and track rights. If a song is unavailable upstream, cliamp surfaces the `yt-dlp` error. Using `cookies_from` gives `yt-dlp` the same account context as your browser, which improves access for tracks your account can play.
+248
View File
@@ -0,0 +1,248 @@
# Playlists
Cliamp supports local **TOML playlists** managed from the TUI or CLI, plus **M3U/M3U8/PLS playlists** loaded from files or URLs.
## M3U and PLS Playlists
Load any `.m3u`, `.m3u8`, or `.pls` file, local or remote:
```sh
cliamp ~/radio-stations.m3u
cliamp http://radio.example.com/streams.m3u
cliamp ~/music.m3u https://example.com/live.m3u # mix local + remote
cliamp ~/radio.pls
```
### EXTINF Metadata
The parser extracts titles and durations from `#EXTINF` lines:
```m3u
#EXTM3U
#EXTINF:180,Radio Station 1
http://station-1.com/stream
#EXTINF:-1,Radio Station 2
http://station-2.com/stream/hd
```
Entries without `#EXTINF` still work. The filename or URL is used as the title instead.
### Relative Paths
Paths in a local M3U file are resolved relative to the M3U file's directory:
```m3u
#EXTINF:240,My Song
../Music/song.mp3
#EXTINF:-1,Live Stream
http://example.com/live
```
If `radio.m3u` is in `~/playlists/`, then `../Music/song.mp3` resolves to `~/Music/song.mp3`.
### Edge Cases Handled
- UTF-8 BOM (common in Windows-created files)
- `\r\n` line endings
- Missing `#EXTM3U` header
- Mixed local and remote entries in the same file
- Other `#` directives (silently skipped)
---
## Local TOML Playlists
Create and manage your own playlists stored as `.toml` files in `~/.config/cliamp/playlists/`.
### File Format
Each playlist is a separate `.toml` file. The filename (minus extension) becomes the playlist name. Empty playlists are kept on disk so they remain visible in the TUI and CLI.
```toml
# ~/.config/cliamp/playlists/radio-stations.toml
[[track]]
path = "http://station-1.com/stream"
title = "Radio Station 1"
[[track]]
path = "http://station-2.com/stream/hd"
title = "Radio Station 2"
artist = "Radio Network"
[[track]]
path = "/home/user/Music/song.mp3"
title = "My Song"
artist = "My Artist"
```
Each `[[track]]` section supports:
| Key | Required | Description |
|-----|----------|-------------|
| `path` | Yes | File path or HTTP URL |
| `title` | Yes | Display title |
| `artist` | No | Artist name |
| `album` | No | Album name |
| `genre` | No | Genre name |
| `year` | No | Release year |
| `track_number` | No | Track number |
| `duration_secs` | No | Duration in seconds |
| `embedded_lyrics` | No | Lyrics copied from local file tags |
| `album_art_url` | No | Cached file URL for embedded album art |
| `bookmark` | No | Bookmark flag |
HTTP/HTTPS paths are automatically treated as streams.
### Podcast / RSS Feed Playlists
You can save podcast RSS feed URLs in a playlist. Add `feed = true` to mark a track as a feed. When played, the feed is resolved into individual episodes instead of being streamed directly.
```toml
# ~/.config/cliamp/playlists/podcasts.toml
[[track]]
path = "https://feeds.simplecast.com/54nAGcIl"
title = "The Daily"
feed = true
[[track]]
path = "https://lexfridman.com/feed/podcast/"
title = "Lex Fridman Podcast"
feed = true
```
Each `[[track]]` with `feed = true` supports:
| Key | Required | Description |
|-----|----------|-------------|
| `path` | Yes | RSS/Atom feed URL |
| `title` | Yes | Display name for the feed |
| `feed` | Yes | Must be `true` to enable feed resolution |
When you select a feed entry, cliamp fetches the RSS feed, extracts all episodes with audio enclosures, and loads them into the playlist. Episode titles and durations (from `<itunes:duration>`) are preserved.
URLs with `.xml`, `.rss`, or `.atom` extensions are also auto-detected as feeds without needing `feed = true`.
### Browsing and Loading Playlists
Running `cliamp` without arguments connects to the built-in radio channel. If Navidrome is configured, it opens the provider browser instead.
To browse your local playlists, press `Esc` or `b` during playback to open the provider browser. Navigate with `Up`/`Down` (or `j`/`k`) and press `Enter` to load a playlist. Tracks replace the current playlist and playback starts immediately. Press `Tab` to jump back to the now-playing playlist without reloading.
If Navidrome is also configured, both sources appear in the same list with provider labels (e.g., `[Navidrome] Jazz`, `[Local Playlists] favorites`).
You can start with CLI files and browse playlists later:
```sh
cliamp song.mp3 # starts playing, Esc opens browser
```
### Managing Playlists
Press `p` from any view to open the playlist manager:
1. **Browse**: see all playlists with track counts
2. **Filter**: press `/` to incrementally filter the list (works on both the playlists screen and the track screen). `Esc` clears the filter.
3. **Open**: press `Enter` or `→` to view tracks inside a playlist
4. **Add now-playing**: press `a` to add the currently playing track (the footer shows the track name so you know what gets added)
5. **Delete playlist**: press `d` then `y` to confirm deletion
6. **Mark tracks**: open a playlist, press `Space` to mark a track and advance, or `a` to mark or unmark all visible tracks
7. **Move tracks**: press `[` or `]`; the saved playlist is updated immediately
8. **Sort tracks**: press `s` to cycle `track`, `title`, `artist`, `album`, `artist+album`, and `path` sorting
9. **Remove tracks**: press `d` to remove the marked tracks, or the highlighted track when nothing is marked
10. **Undo manager edits**: press `u` after delete, remove, move, or sort
11. **Write tracks elsewhere**: press `w` to copy the marked or highlighted tracks to another playlist; duplicate paths are skipped
12. **Add files**: press `o` from inside a playlist to browse files and add them to that playlist
13. **Play this**: press `Enter` on the track list to start playback at the highlighted track. The rest of the playlist follows.
14. **Play all**: press `p` to start from the top, regardless of cursor position
15. **New playlist**: select "+ New Playlist...", type a name, and press Enter. If you create a playlist while a `/` filter is active, the filter text is pre-filled as the new playlist name.
Tracks with an `album` field are grouped by album with visual separator headers in the playlist manager (album grouping is hidden while a filter is active) and the main player view.
The directory `~/.config/cliamp/playlists/` is created automatically on first use. Removing the last track leaves an empty playlist file; use `d` on the playlist list or `cliamp playlist delete` to delete the playlist itself.
### Writing to Playlists
Press `w` on a track in the main playlist to open the local playlist picker. Pick an existing playlist or choose `+ New Playlist...`. Exact duplicate paths are skipped and reported.
In the file browser, select files with `Space`, select all visible audio files with `a`, then press `w` to write the selection to a playlist instead of loading it into the current queue.
### Command Line Management
Manage local TOML playlists without opening the TUI:
```sh
cliamp playlist list
cliamp playlist create "Name" # create an empty playlist
cliamp playlist create "Name" file1 dir/ ... # create from files/folders
cliamp playlist add "Name" file1 dir/ ... # append, skipping duplicate paths
cliamp playlist rename "Old" "New"
cliamp playlist dedupe "Name"
cliamp playlist sort "Name" --by artist+album
cliamp playlist doctor # report missing local files in all playlists
cliamp playlist doctor "Name" --fix # prune missing local files
cliamp playlist export "Name" --format m3u -o mix.m3u
cliamp playlist import mix.pls --name "Imported"
cliamp playlist show "Name" --json
cliamp playlist remove "Name" --index 3
cliamp playlist bookmark "Name" --index 3 # toggle bookmark flag
cliamp playlist bookmarks # list all bookmarked tracks
cliamp playlist enrich "Name" # backfill duration/album metadata
cliamp playlist delete "Name"
```
Sort keys are `track`, `title`, `artist`, `album`, `artist+album`, and `path`.
New playlist names reject path separators and non-portable filename characters. Existing playlist files with older Unix-only names remain readable and writable.
### Creating Playlists Manually
Create the directory and add a `.toml` file:
```sh
mkdir -p ~/.config/cliamp/playlists
```
```toml
# ~/.config/cliamp/playlists/favorites.toml
[[track]]
path = "/home/user/Music/song.mp3"
title = "Great Song"
artist = "Good Artist"
[[track]]
path = "https://radio.example.com/stream"
title = "My Radio"
```
### Controls
**Playlist browser (provider view):**
| Key | Action |
|-----|--------|
| `Up` `Down` / `j` `k` | Navigate playlists |
| `Enter` | Load selected playlist |
| `Tab` | Switch to now-playing playlist |
| `Esc` `b` | Open browser (from playlist view) |
**Playlist manager (`p` key):**
| Key | Action |
|-----|--------|
| `p` / `Esc` | Open/close playlist manager (Esc on tracks screen goes back) |
| `Up` `Down` / `j` `k` | Navigate |
| `/` | Filter playlists or tracks; `Esc` clears |
| `Enter` / `→` | Open playlist (list screen) / Play **highlighted** track (tracks screen) |
| `p` | Play all tracks from the top (tracks screen) |
| `a` | List: add currently playing track. Tracks: mark/unmark all visible tracks |
| `Space` | Mark/unmark track and advance (tracks screen) |
| `s` | Sort tracks, cycling supported sort keys (tracks screen) |
| `w` | Write marked/highlighted tracks, or the current queue from the list screen, to another playlist |
| `o` | Add files to the open playlist (tracks screen) |
| `[` `]` | Move track up/down and save (tracks screen) |
| `d` | Delete playlist (confirms) / Remove marked tracks, or highlighted track if none are marked |
| `u` | Undo the last playlist-manager edit |
| `←` / `Backspace` | Go back from tracks screen to list |
+81
View File
@@ -0,0 +1,81 @@
# Plex Media Server
cliamp can stream music directly from your Plex Media Server, giving you access to your full Plex music library, including any library served by PlexAmp. Streaming uses the same Plex HTTP API that official Plex clients use; no extra software is required.
> **Quick start:** run `cliamp setup` for a guided TUI that prompts for the server URL and `X-Plex-Token`, pings the server to verify the token, and writes the `[plex]` block for you. Manual setup steps are below.
## Prerequisites
- Plex Media Server running and reachable on your network (or remotely)
- At least one music library configured in Plex
- Your `X-Plex-Token` (see below)
## Finding your X-Plex-Token
1. Open Plex Web in a browser and sign in
2. Browse to any item in your music library
3. Click the **···** menu → **Get Info****View XML**
4. In the URL of the XML page, copy the value of the `X-Plex-Token` query parameter
Alternatively, follow the [official Plex guide](https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/).
## Configuration
Add a `[plex]` section to `~/.config/cliamp/config.toml`:
```toml
[plex]
url = "http://192.168.1.10:32400"
token = "xxxxxxxxxxxxxxxxxxxx"
libraries = ["Music", "Jazz"]
```
| Key | Description |
|-----|-------------|
| `url` | Base URL of your Plex Media Server, including port (default `32400`) |
| `token` | Your `X-Plex-Token` for authentication |
| `libraries` | Optional comma-separated list of music library names to load. When omitted, all music libraries are loaded. Names are matched case-insensitively. |
If you access Plex remotely via `app.plex.tv`, you can still use a direct server URL if your server has remote access enabled, or use your server's `plex.direct` URL from the Plex Web address bar.
## Usage
Once configured, **Plex** appears as a provider in the cliamp TUI alongside Radio, Navidrome, Spotify, etc.
The provider exposes your music library as a flat list of albums, labelled:
```
Artist - Album Title (Year)
```
Select an album to load its tracks, then play as normal.
To start cliamp with Plex as the default provider:
```bash
cliamp --provider plex
```
Or set it persistently in config:
```toml
provider = "plex"
```
## How it works
cliamp calls the Plex HTTP API to enumerate your music libraries and albums. When you select an album, it fetches the track list and constructs authenticated streaming URLs of the form:
```
http://<server>:32400/library/parts/<partID>/<timestamp>/file.<ext>?X-Plex-Token=<token>
```
These are direct file-serve URLs. Plex serves the original file without transcoding, and cliamp's existing HTTP streaming pipeline handles playback. All formats supported by cliamp (MP3, FLAC, AAC, OGG, OPUS, WAV, etc.) work as long as the original file format is one of them.
## Known limitations
- **No scrobbling**: play counts are not reported back to Plex
- **No playlist write-back**: cliamp cannot create or modify Plex playlists
- **Token is long-lived**: store it carefully; it grants full access to your Plex account
- **Album list is flat**: no artist drill-down; search by scrolling or using cliamp's search
- **No Plex playlists**: only library albums are exposed (Plex user-created playlists are not yet surfaced)
+601
View File
@@ -0,0 +1,601 @@
# Lua Plugins
cliamp has a Lua 5.1 plugin system. Plugins can hook into playback events (scrobbling, notifications, status bar output) and add custom visualizers. Each plugin runs in an isolated VM. A crash in one plugin cannot affect others or the player.
Plugins live in `~/.config/cliamp/plugins/`. Create the directory:
```
mkdir -p ~/.config/cliamp/plugins
```
Plugins run only after their exact contents have been approved. Existing and manually copied plugins start untrusted; approve one with `cliamp plugins trust <name>`.
## Plugin manager
```sh
cliamp plugins # show help
cliamp plugins list # list installed plugins
cliamp plugins install <source> # install a plugin
cliamp plugins trust <name> # approve installed plugin contents
cliamp plugins remove <name> # remove a plugin
```
Install and trust display the source, SHA-256, declared permissions, and implicit filesystem/network access before prompting. Use `--yes` only after independently reviewing the same content in non-interactive environments. Approvals are stored in `plugins/.trust.json`; editing a plugin changes its hash and disables it until it is approved again. Unknown permission names are rejected.
### Install sources
| Format | Example |
|--------|---------|
| GitHub | `user/repo` |
| GitHub with tag | `user/repo@v1.0` |
| GitLab | `gitlab:user/repo` |
| GitLab with tag | `gitlab:user/repo@v1.0` |
| Codeberg | `codeberg:user/repo` |
| Codeberg with tag | `codeberg:user/repo@v1.0` |
| Direct URL | `https://example.com/plugin.lua` |
### Naming convention
Plugin repositories **must** be named `cliamp-plugin-<name>` with the entry point `<name>.lua` at the repo root. The `cliamp-plugin-` prefix is stripped on install, so `cliamp-plugin-soap-bubbles` (containing `soap-bubbles.lua`) installs as `soap-bubbles`.
```sh
cliamp plugins install bjarneo/cliamp-plugin-lastfm
cliamp plugins install bjarneo/cliamp-plugin-lastfm@v1.0
cliamp plugins install gitlab:user/my-visualizer
cliamp plugins install codeberg:user/my-plugin
cliamp plugins install https://example.com/my-plugin.lua
cliamp plugins remove lastfm
```
## Quick start
### Now-playing file (for Waybar, Polybar, etc.)
```lua
-- ~/.config/cliamp/plugins/now-playing.lua
local p = plugin.register({
name = "now-playing",
type = "hook",
description = "Write now-playing to /tmp for status bars",
})
p:on("track.change", function(track)
cliamp.fs.write("/tmp/cliamp-now-playing", track.artist .. " - " .. track.title)
end)
p:on("playback.state", function(ev)
if ev.status == "paused" then
cliamp.fs.write("/tmp/cliamp-now-playing", "paused")
end
end)
p:on("app.quit", function()
cliamp.fs.remove("/tmp/cliamp-now-playing")
end)
```
### Desktop notification on track change
```lua
-- ~/.config/cliamp/plugins/notify.lua
local p = plugin.register({
name = "notify",
type = "hook",
})
p:on("track.change", function(track)
local title = track.artist .. " - " .. track.title
os.execute('notify-send "cliamp" "' .. title .. '"')
end)
```
Note: `os.execute` is removed by the sandbox. Public HTTP endpoints are available through `cliamp.http`; private, loopback, link-local, multicast, and unspecified addresses are blocked. For local automation, write to an allowlisted file that a watcher picks up or declare the permission-gated `exec` capability.
### 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, album = track.album }
})
end)
```
```toml
# config.toml
[plugins.webhook]
url = "https://example.com/hook"
```
## Plugin structure
### Single file
```
~/.config/cliamp/plugins/myplugin.lua
```
### Directory with init.lua
```
~/.config/cliamp/plugins/myplugin/
init.lua
helpers.lua
```
The directory name becomes the plugin name. Only `init.lua` is loaded automatically.
## Registration
Every plugin must call `plugin.register()` to be recognized. Files that don't call it are silently skipped.
```lua
local p = plugin.register({
name = "myplugin", -- required
type = "hook", -- "hook" or "visualizer"
version = "1.0.0", -- optional
description = "What it does", -- optional
})
```
The returned object `p` provides two methods:
| Method | Description |
|--------|-------------|
| `p:on(event, callback)` | Subscribe to a playback event |
| `p:config(key)` | Read a config value from `[plugins.myplugin]` in config.toml |
## Events
Plugins subscribe to events with `p:on(event, callback)`. Callbacks run asynchronously in goroutines and have a 5-second timeout.
### Available events
| Event | Callback argument | When |
|-------|-------------------|------|
| `track.change` | `{title, artist, album, genre, year, path, duration, stream}` | New track starts |
| `track.scrobble` | Same + `{played_secs}` | Track played >= 50% or >= 4 min |
| `playback.state` | `{status, title, artist, album, path, duration, stream, position}` | Any playback state change (play, pause, stop, seek, volume, track transition) |
| `player.seek` | `{position, duration}` (seconds) | A seek completes |
| `player.volume` | `{db}` | Volume changes |
| `player.eq` | `{bands, preset}` | An EQ band or preset changes |
| `player.mode` | `{shuffle, repeat}` | Shuffle toggled or repeat mode cycled |
| `queue.change` | `{count, index, queued}` | Playlist or play-next queue changes |
| `app.start` | `{}` | After all plugins loaded |
| `app.quit` | `{}` | Before shutdown |
The `status` field in `playback.state` is one of: `"playing"`, `"paused"`, `"stopped"`. The `repeat` field in `player.mode` is one of: `"Off"`, `"All"`, `"One"` (matching `cliamp.player.repeat_mode()`). In `player.eq`, `bands` is an array of 10 dB values.
The `player.*` and `queue.change` events are fired by diffing state after each UI update, so they cover every change regardless of source (keypress, IPC, MPRIS, or another plugin).
## Plugin object methods
The object returned by `plugin.register(...)` exposes additional methods beyond `:on()` / `:config()`:
### `p:bind(key, [description,] callback)` — keyboard binding (requires `permissions = {"keymap"}`)
```lua
local p = plugin.register({
name = "my-plugin",
type = "hook",
permissions = {"keymap"},
})
-- Listed in the Ctrl+K overlay under "— plugins —":
p:bind("x", "Extract chapters", function(key) ... end)
-- Not listed (hidden binding):
p:bind("ctrl+e", function(key) ... end)
```
Returns `true` on success, or `false, reason` if the key is already owned by cliamp's core UI or the plugin lacks the `keymap` permission. Pass a description string as the middle argument to surface the binding in the `Ctrl+K` keymap overlay; omit it for an internal-only binding.
Key strings are in Bubbletea's `msg.String()` form: lowercase letters, `ctrl+` / `shift+` / `alt+` prefixes (e.g. `"x"`, `"ctrl+e"`, `"shift+f1"`). Case-insensitive.
Plugin keys only fire in the main view — overlays like the file browser, theme picker, and keymap itself capture their own input. Core reserves all keys documented in `docs/keybindings.md`; trying to bind one of those logs a warning and returns `false`.
Use `p:unbind(key)` to release a binding.
### `p:command(name, callback)` — shell-invokable command
```lua
p:command("run", function(args)
-- args is an array of strings passed after the command name
return "done: " .. args[1]
end)
```
The callback can return a string, which is printed by the CLI client. Commands are invoked from the shell via `cliamp plugins call <plugin-name> <command> [args...]` and dispatched to the running cliamp over IPC. Since dispatch runs in the running player, commands don't need a separate permission (they're user-initiated).
List all registered commands with `cliamp plugins commands`. Commands can run for up to 5 minutes before timing out.
## Lua API
All APIs are under the `cliamp` global table.
### cliamp.player (read-only)
```lua
cliamp.player.state() --> "playing" | "paused" | "stopped"
cliamp.player.position() --> number (seconds)
cliamp.player.duration() --> number (seconds)
cliamp.player.volume() --> number (dB, -30 to +6)
cliamp.player.speed() --> number (ratio, 1.0 = normal)
cliamp.player.mono() --> boolean
cliamp.player.repeat_mode() --> "Off" | "All" | "One"
cliamp.player.shuffle() --> boolean
cliamp.player.eq_bands() --> table of 10 dB values
```
### cliamp.track (read-only)
```lua
cliamp.track.title() --> string
cliamp.track.artist() --> string
cliamp.track.album() --> string
cliamp.track.genre() --> string
cliamp.track.year() --> number
cliamp.track.track_number() --> number
cliamp.track.path() --> string
cliamp.track.is_stream() --> boolean
cliamp.track.duration_secs() --> number
```
### cliamp.queue
Read the playlist freely; mutating it requires `permissions = {"control"}`. All
indices are 0-based, matching `cliamp.queue.current()`.
```lua
-- read (no permission)
cliamp.queue.list() --> array of {title, artist, album, path, index, queued}
cliamp.queue.count() --> number of tracks
cliamp.queue.current() --> 0-based index of the current track
-- mutate (requires "control")
cliamp.queue.add(path) -- resolve a file/dir/URL and append
cliamp.queue.jump(index) -- make index current and play it
cliamp.queue.remove(index) -- remove the track at index
cliamp.queue.move(from, to) -- reorder a track
```
`add` accepts anything the CLI accepts: a local file or directory, an HTTP
stream, an M3U/PLS URL, or a YouTube/yt-dlp URL. Resolution happens off the UI
thread, so a slow URL never blocks playback.
### cliamp.http
```lua
-- GET
local body, status = cliamp.http.get("https://api.example.com/data", {
headers = { Authorization = "Bearer token" }
})
-- POST with JSON
local body, status = cliamp.http.post("https://api.example.com/scrobble", {
json = { artist = "Radiohead", track = "Everything In Its Right Place" }
})
-- POST with form body
local body, status = cliamp.http.post(url, {
headers = { ["Content-Type"] = "application/x-www-form-urlencoded" },
body = "key=value&foo=bar"
})
```
Restrictions: 5-second timeout, 1 MB response body cap.
### cliamp.fs
```lua
cliamp.fs.write(path, content) -- overwrite file
cliamp.fs.append(path, content) -- append to file
cliamp.fs.read(path) --> string (max 1 MB)
cliamp.fs.remove(path) -- delete file
cliamp.fs.exists(path) --> boolean
cliamp.fs.mkdir(path) -- create directory (recursive)
cliamp.fs.listdir(path) --> {names}, err
```
Writes are restricted to the system temp directory (`/tmp/` on Unix), `~/.config/cliamp/`, `~/.local/share/cliamp/`, and `~/Music/cliamp/`. Reads are allowed from anywhere. On Windows, if `HOME` is unset, the config directory portion resolves to `%APPDATA%\cliamp`.
### cliamp.json
```lua
local tbl = cliamp.json.decode('{"key": "value"}')
local str = cliamp.json.encode({ key = "value" })
```
### cliamp.store
A persistent per-plugin key/value store. Values (strings, numbers, booleans,
and tables) survive restarts. No permission required: each plugin sees only its
own namespace, so one plugin can never read another's keys.
```lua
cliamp.store.set(key, value) -- value: string|number|boolean|table
cliamp.store.get(key) --> value or nil
cliamp.store.delete(key)
cliamp.store.keys() --> sorted array of keys
cliamp.store.clear()
```
Backed by `~/.local/share/cliamp/plugins/<name>/store.json`, written owner-only
(0600). Use it for play counts, offline scrobble queues, resume positions, or
remembered settings, not for large data.
```lua
local counts = cliamp.store.get("counts") or {}
counts[cliamp.track.path()] = (counts[cliamp.track.path()] or 0) + 1
cliamp.store.set("counts", counts)
```
### cliamp.crypto
```lua
cliamp.crypto.md5("hello") --> hex string
cliamp.crypto.sha256("hello") --> hex string
cliamp.crypto.hmac_sha256("secret", "msg") --> hex string
```
### cliamp.log
```lua
cliamp.log.info("loaded successfully")
cliamp.log.warn("missing config key")
cliamp.log.error("request failed: " .. err)
cliamp.log.debug("response: " .. body)
```
Logs are written to `~/.config/cliamp/plugins.log` with timestamps and `[plugin-name]` prefix.
### cliamp.player control (requires permissions)
Plugins that declare `permissions = {"control"}` can send commands to the player:
```lua
local p = plugin.register({
name = "my-controller",
type = "hook",
permissions = {"control"},
})
cliamp.player.next() -- skip to next track
cliamp.player.prev() -- go to previous track
cliamp.player.play_pause() -- toggle play/pause
cliamp.player.stop() -- stop playback
cliamp.player.set_volume(-5) -- set volume in dB (-30 to +6)
cliamp.player.set_speed(1.25) -- set playback speed (0.25 to 2.0)
cliamp.player.seek(30) -- seek to 30 seconds
cliamp.player.toggle_mono() -- toggle mono output
cliamp.player.set_eq_preset("Rock") -- switch to built-in preset (sets bands + UI label)
cliamp.player.set_eq_preset("Metal", {6,4,1,-1,-2,2,4,6,6,5}) -- custom preset with bands
cliamp.player.set_eq_band(1, 6) -- set EQ band 1 to +6 dB (bands 1-10, -12 to +12)
```
Without `permissions = {"control"}`, these functions log a warning and do nothing.
### cliamp.notify
```lua
cliamp.notify("Song Title") -- notification with title only
cliamp.notify("Song Title", "Artist Name") -- notification with title and body
```
Sends a desktop notification via `notify-send`. Works with mako, dunst, and other notification daemons.
### cliamp.exec (requires permissions)
Plugins that declare `permissions = {"exec"}` can spawn subprocesses from a configurable binary allowlist. Default allowlist: `yt-dlp`, `ffmpeg`. Extend it in `config.toml`:
```toml
[plugins]
allowed_binaries = "ffprobe, curl" # merged with defaults
```
```lua
local p = plugin.register({
name = "my-downloader",
type = "hook",
permissions = {"exec"},
})
local handle, err = cliamp.exec.run("yt-dlp", {"--dump-json", url}, {
on_stdout = function(line) ... end, -- optional, called per line
on_stderr = function(line) ... end, -- optional
on_exit = function(code) ... end, -- optional, fires exactly once
cwd = "/tmp/work", -- optional; must be in write allowlist
timeout = 300, -- optional seconds, hard cap 1800
})
handle:cancel() -- terminate the process
handle:alive() -- --> boolean
```
**Safety rails:**
- Binary must be in the allowlist. Argv is argv — no shell, no expansion.
- `args` must be a flat array of strings. Nested tables / non-strings are rejected.
- Subprocess env is minimal (`PATH`, `HOME`, `LANG`) — secrets in the parent env are not passed through.
- Output is capped at 4 MiB per process (stdout + stderr combined); further lines are dropped silently.
- Concurrency capped at 4 running processes per plugin.
- All processes owned by a plugin are killed on plugin unload and on cliamp exit.
- Negative `on_exit` codes signal cancellation/timeout (`-1`) or spawn failure (`-2`).
Without `permissions = {"exec"}`, `cliamp.exec.run` returns `nil, "exec permission required"`.
### cliamp.message
```lua
cliamp.message("Scrobble Sent") -- show for default duration
cliamp.message("Syncing Library", 5) -- show for 5 seconds
```
Displays a transient message in the status bar at the bottom of the UI. The
duration argument is optional (seconds); omit it to use the default TTL. Durations above 60 seconds are clamped.
### cliamp.sleep
```lua
cliamp.sleep(2.5) -- block for 2.5 seconds (max 10)
```
Blocks the plugin's Lua VM. Other hooks for the same plugin will queue until the sleep finishes. Prefer `cliamp.timer.after()` for non-blocking delays.
### cliamp.timer
```lua
-- Run once after 5 seconds
local id = cliamp.timer.after(5.0, function()
cliamp.log.info("timer fired")
end)
-- Run every 30 seconds
local id = cliamp.timer.every(30.0, function()
-- periodic task
end)
-- Cancel
cliamp.timer.cancel(id)
```
## Configuration
Plugin-specific config goes in `config.toml` under `[plugins.<name>]`:
```toml
[plugins.lastfm]
api_key = "abc123"
api_secret = "secret"
session_key = "sk-xxx"
[plugins.webhook]
url = "https://example.com/hook"
```
Access in Lua:
```lua
local api_key = p:config("api_key") --> "abc123" or nil
```
### Disabling plugins
Disable a specific plugin:
```toml
[plugins.webhook]
enabled = false
```
Or disable multiple at once:
```toml
[plugins]
disabled = webhook, discord-rpc
```
## Visualizer plugins
Plugins with `type = "visualizer"` add custom visualizer modes that appear in the `v` key cycle alongside built-in modes.
```lua
-- ~/.config/cliamp/plugins/simple-bars.lua
local p = plugin.register({
name = "simple-bars",
type = "visualizer",
})
-- Called every frame (~20 FPS during playback).
-- bands: table of 10 numbers (0.0-1.0), indices 1-10
-- frame: monotonic counter
-- rows: available terminal rows (changes in fullscreen mode)
-- cols: available terminal columns
-- Must return a multi-line string.
function p:render(bands, frame, rows, cols)
local lines = {}
local chars = { " ", "", "", "", "", "", "", "", "" }
for row = 5, 1, -1 do
local line = ""
for i = 1, 10 do
local level = bands[i]
local threshold = (row - 1) / 5
if level > threshold then
line = line .. "██████ "
else
line = line .. " "
end
end
table.insert(lines, line)
end
return table.concat(lines, "\n")
end
```
### Visualizer callbacks
| Callback | Signature | Required |
|----------|-----------|----------|
| `p:render(bands, frame, rows, cols)` | Returns string | Yes |
| `p:init(rows, cols)` | Setup when selected | No |
| `p:destroy()` | Cleanup when deselected | No |
Render has a 10 ms budget per frame. If it exceeds this, the previous frame is reused to prevent UI jank.
## Sandbox
For security, plugins run with restricted access. The sandbox removes dangerous standard library functions and restricts file system access.
### Removed functions
| Removed | Replacement |
|---------|-------------|
| `os.execute`, `os.remove`, `os.rename`, `os.exit`, `os.setlocale`, `os.tmpname` | Use `cliamp.fs`, `cliamp.http`, or permission-gated `cliamp.exec` |
| `io` module (all of it) | Use `cliamp.fs` |
| `dofile`, `loadfile`, `load`, `loadstring`, `require`, `module`, `package`, `debug` | Not available |
### Kept functions
`os.time()`, `os.date()`, `os.clock()`, `os.getenv()` are available.
### File system restrictions
**Reads:** Allowed from any path (max 1 MB per read).
**Writes/removes/mkdir** are restricted to these directories only:
- `/tmp/` (and the system temp directory)
- `~/.config/cliamp/`
- `~/.local/share/cliamp/`
- `~/Music/cliamp/`
Attempts to write outside these directories will raise a Lua error. Directory traversal (`..`) is blocked.
### Isolation
- Each plugin runs in its own Lua VM. Plugins cannot access each other's state or variables.
- A crash in one plugin does not affect other plugins or the player.
- Public network access is available via `cliamp.http` (no raw socket access). Private, loopback, link-local, multicast, and unspecified destinations are blocked after DNS resolution and across redirects.
- `os.execute` is removed. Permission-gated `cliamp.exec` can spawn only configured allowlisted binaries.
## Debugging
Check `~/.config/cliamp/plugins.log` for plugin output and errors:
```
2025-03-29 14:30:01 [now-playing] info: Now playing: Everything In Its Right Place
2025-03-29 14:30:01 [webhook] error: track.change handler error: connection refused
```
Use `cliamp.log.debug()` liberally during development.
+194
View File
@@ -0,0 +1,194 @@
# Creating a Provider
Providers live in `external/<name>/` (e.g. `external/jellyfin/`). A provider is
a Go package that implements the base `playlist.Provider` interface and
optionally implements capability interfaces from the `provider/` package. The UI
discovers capabilities at runtime via type assertions and enables features
accordingly.
See the existing providers for reference:
- `external/navidrome/`: Subsonic API, browsing, scrobbling
- `external/plex/`: Plex Media Server, search, album tracks
- `external/spotify/`: Spotify, search, playlist management, custom streaming
- `external/radio/`: internet radio, favorites
- `external/local/`: local TOML playlist files
## Base Interface (required)
Every provider must implement `playlist.Provider`:
```go
type Provider interface {
Name() string
Playlists() ([]playlist.PlaylistInfo, error)
Tracks(playlistID string) ([]Track, error)
}
```
This gives the provider a name, a list of playlists, and the ability to return
tracks for a playlist. That's enough for basic playback.
## Capability Interfaces (optional)
Implement any combination of these to unlock additional UI features. All
interfaces are defined in `provider/interfaces.go`.
| Interface | What it enables | Methods |
|---|---|---|
| `Searcher` | Track search overlay | `SearchTracks(ctx, query, limit)` |
| `ArtistBrowser` | Hierarchical artist browsing | `Artists()`, `ArtistAlbums(id)` |
| `AlbumBrowser` | Paginated album browsing with sort | `AlbumList(sort, offset, size)`, `AlbumSortTypes()` |
| `AlbumTrackLoader` | Album track listing | `AlbumTracks(albumID)` |
| `Scrobbler` | Playback reporting | `Scrobble(track, submission)` |
| `PlaylistWriter` | Add track to playlist | `AddTrackToPlaylist(ctx, playlistID, track)` |
| `PlaylistCreator` | Create new playlist | `CreatePlaylist(ctx, name)` |
| `PlaylistDeleter` | Remove playlists/tracks | `DeletePlaylist(name)`, `RemoveTrack(name, index)` |
| `CustomStreamer` | Custom URI decode pipeline | `URISchemes()`, `NewStreamer(uri)` |
| `FavoriteToggler` | Favorite toggling | `ToggleFavorite(id)` |
| `Closer` | Cleanup on shutdown | `Close()` |
| `Authenticator` | Interactive sign-in flow | `Authenticate() error` (in `playlist` package) |
## Steps
### 1. Create the package
Create `external/<name>/provider.go`:
```go
package jellyfin
import (
"context"
"github.com/bjarneo/cliamp/playlist"
"github.com/bjarneo/cliamp/provider"
)
// Compile-time interface checks.
var (
_ provider.Searcher = (*Provider)(nil)
_ provider.AlbumTrackLoader = (*Provider)(nil)
)
type Provider struct {
baseURL string
token string
}
func New(baseURL, token string) *Provider {
return &Provider{baseURL: baseURL, token: token}
}
func (p *Provider) Name() string { return "Jellyfin" }
func (p *Provider) Playlists() ([]playlist.PlaylistInfo, error) {
// Fetch playlists from your server's API.
return nil, nil
}
func (p *Provider) Tracks(playlistID string) ([]playlist.Track, error) {
// Fetch tracks for a playlist.
return nil, nil
}
func (p *Provider) SearchTracks(ctx context.Context, query string, limit int) ([]playlist.Track, error) {
// Search the server's catalog.
return nil, nil
}
func (p *Provider) AlbumTracks(albumID string) ([]playlist.Track, error) {
// Fetch tracks for an album.
return nil, nil
}
```
### 2. Return tracks
When building `playlist.Track` values:
- **`Path`**: the playable URL or file path. For HTTP streams, use a full URL.
For custom URI schemes (e.g. `spotify:track:xxx`), implement `CustomStreamer`.
- **`Stream: true`**: set this for HTTP URLs so the player uses the streaming
pipeline.
- **`ProviderMeta`**: attach provider-specific metadata as a string map with
namespaced keys. This is used for features like scrobbling:
```go
playlist.Track{
Path: "https://my-server/stream/123",
Title: "Song Title",
Artist: "Artist Name",
Stream: true,
ProviderMeta: map[string]string{"jellyfin.id": "123"},
}
```
### 3. Add configuration
Add a config struct to `config/config.go`:
```go
type JellyfinConfig struct {
URL string `toml:"url"`
Token string `toml:"token"`
}
```
Add the field to the top-level `Config` struct and a TOML section:
```toml
[jellyfin]
url = "https://jellyfin.example.com"
token = "your-api-key"
```
### 4. Register in main.go
Wire up the provider in the `run()` function in `main.go`:
```go
if cfg.Jellyfin.URL != "" && cfg.Jellyfin.Token != "" {
jfProv := jellyfin.New(cfg.Jellyfin.URL, cfg.Jellyfin.Token)
providers = append(providers, ui.ProviderEntry{
Key: "jellyfin", Name: "Jellyfin", Provider: jfProv,
})
}
```
If your provider needs a custom audio pipeline (like Spotify's `spotify:` URIs),
register a streamer factory:
```go
if cs, ok := myProv.(provider.CustomStreamer); ok {
for _, scheme := range cs.URISchemes() {
p.RegisterStreamerFactory(scheme, cs.NewStreamer)
}
}
```
If your provider needs the buffered download pipeline for its stream URLs
(like Navidrome's Subsonic endpoints), register a URL matcher:
```go
p.RegisterBufferedURLMatcher(jellyfin.IsStreamURL)
```
### 5. Add a `--provider` flag value
In `main.go`'s help text, add your provider key to the `--provider` line so
users can set it as their default.
## What the UI Does Automatically
You don't need to touch the UI code. Based on which interfaces your provider
implements, the UI will automatically:
- Show the browse overlay ("N") if any registered provider implements `ArtistBrowser` or `AlbumBrowser`
- Show the search overlay ("F") if any registered provider implements `Searcher`
- Enable add-to-playlist in search results if the searched provider implements `PlaylistWriter`
- Scrobble playback if `Scrobbler` is implemented
- Run interactive auth on first use if `Authenticator` is implemented
- Call `Close()` on shutdown if `Closer` is implemented
The "N" and "F" shortcuts work regardless of which provider is currently active
They find the first registered provider with the needed capability.
+86
View File
@@ -0,0 +1,86 @@
# Qobuz Integration
cliamp can stream your [Qobuz](https://www.qobuz.com/) library directly through its audio pipeline. EQ, visualizer, and all effects apply. Requires an active Qobuz subscription.
Qobuz delivers lossless FLAC, so cliamp streams it through the same buffer-while-playing + ffmpeg pipeline used for other lossless providers. `ffmpeg` must be on `PATH`.
## Setup
The fastest path is the interactive wizard: run `cliamp setup`, pick **Qobuz**, choose a stream quality, and it writes the `[qobuz]` block for you.
Or configure it manually in `~/.config/cliamp/config.toml`:
```toml
[qobuz]
enabled = true
quality = 6
```
No developer credentials are needed. The `app_id`, signing secrets, and OAuth private key are scraped automatically from the Qobuz web player.
Run `cliamp`, select Qobuz as a provider, and press `Enter` to sign in. A browser window opens for Qobuz's OAuth login. Once you authorize, credentials are cached at `~/.config/cliamp/qobuz_credentials.json` and subsequent launches refresh silently.
> **Click "Back" to finish.** After you authorize, Qobuz shows a *"You are signed in, you can leave this page"* screen with a **Back** button rather than redirecting automatically. Click that **Back** button. It fires the redirect that hands the sign-in code to cliamp and completes authentication. cliamp waits (up to 5 minutes) for it.
### Quality
`quality` selects the Qobuz `format_id`. If omitted, cliamp uses `6` (FLAC CD). Supported values:
| Value | Format |
|---|---|
| `5` | MP3 320 kbps |
| `6` | FLAC 16-bit / 44.1 kHz (CD) |
| `7` | FLAC 24-bit up to 96 kHz (Hi-Res) |
| `27` | FLAC 24-bit up to 192 kHz (Hi-Res) |
Hi-Res tiers require a Qobuz plan that includes them. Any other value falls back to `6`.
## Usage
Start directly on Qobuz:
```sh
cliamp --provider qobuz
```
Once authenticated, Qobuz appears as a provider alongside the others. Press `Q` to jump straight to Qobuz, or `Esc`/`b` to open the provider browser and select it.
The provider surfaces your Qobuz library:
- **Favorite Tracks**: your liked songs.
- **Random Tracks**: a random sample of up to 500 tracks drawn from across all your playlists, with duplicates removed. Press `Ctrl+R` to reshuffle the sample.
- **Your playlists**: playlists you created or subscribed to.
- **Favorite albums**: browsable in the album view.
- **Favorite artists**: browse an artist to see their albums.
Press `Ctrl+F` while Qobuz is active to search the Qobuz catalog for tracks.
## Controls
When focused on the provider panel:
| Key | Action |
|---|---|
| `Up` `Down` / `j` `k` | Navigate |
| `Enter` | Load the selected playlist/album or play the selected track |
| `Ctrl+F` | Search Qobuz tracks |
| `Ctrl+R` | Refresh (re-resolves stream URLs) |
| `Tab` | Switch between provider and playlist focus |
| `Esc` / `b` | Open provider browser |
After loading a playlist or album you return to the standard playlist view with all the usual controls (seek, volume, EQ, shuffle, repeat, queue, search, lyrics).
## Troubleshooting
- **"OAuth failed" / browser doesn't open**: cliamp opens a localhost redirect listener on a random port. Make sure nothing is blocking outbound access to `qobuz.com` and that a default browser is configured. The flow times out after 5 minutes.
- **Sign-in seems to hang / "you can leave this page"**: after authorizing, the Qobuz OAuth page shows a confirmation screen with a **Back** button instead of redirecting automatically. Click **Back** to complete sign-in. cliamp keeps waiting (up to 5 minutes) until the redirect arrives.
- **Re-authenticate**: run `cliamp qobuz reset` to clear stored credentials, then relaunch cliamp and select Qobuz to sign in again. (Equivalent to deleting `~/.config/cliamp/qobuz_credentials.json` manually.)
- **Track is unplayable / skipped**: the track may not be streamable on your subscription tier or in your region. cliamp marks such tracks unplayable and moves on.
- **Hi-Res not delivered**: setting `quality = 27` does not upgrade a tier that lacks Hi-Res. Qobuz returns the best your plan allows.
- **Stalls after a long idle session**: signed stream URLs expire over time. Press `Ctrl+R` to refresh, which re-resolves the URLs.
## Requirements
- An active Qobuz subscription
- `ffmpeg` on `PATH` for FLAC decoding
- No developer/API registration: credentials are obtained automatically
+40
View File
@@ -0,0 +1,40 @@
# Quickshell Now-Playing Widget (Omarchy)
A notification-sized "now playing" card for [Quickshell](https://quickshell.org), tailored for [Omarchy](https://omarchy.org). Lives in the repo at [`contrib/quickshell/`](../contrib/quickshell).
It pulls live spectrum data from a running cliamp over the IPC socket, draws a Winamp 2-style segmented analyzer, and reads colors directly from the active Omarchy theme so it restyles in lock-step when you swap themes.
> This widget is built for an Omarchy setup. It uses Omarchy's `~/.config/omarchy/current/theme/colors.toml` as the source of truth for its palette. On a non-Omarchy system the panel still works but falls back to the default kanagawa-dragon-ish colors baked into `NowPlaying.qml`.
## What you get
- 300 x 72 card centered along the bottom of every screen
- Full-width Winamp 2-style LED spectrum analyzer with falling peak caps
- Track title + artist, click-to-seek progress bar, time readout
- Pristine vector transport icons (prev / play-pause / next), no font dependency
- Theme follows the active Omarchy theme (background, foreground, accent, color1..color8, selection_background)
- Card border tracks the muted slot of the Omarchy palette (`selection_background`, falling back to `color8`)
- Click the card and press `Esc` or `Q` to dismiss
## Quick start
Make sure cliamp is running first, then either run the widget directly:
```sh
qs -p contrib/quickshell/shell.qml
```
Or install it as a named Quickshell config:
```sh
mkdir -p ~/.config/quickshell
ln -s "$PWD/contrib/quickshell" ~/.config/quickshell/cliamp
qs -c cliamp
```
## Requirements
- Quickshell 0.2+
- A running cliamp (Linux only; the widget needs cliamp's MPRIS service and IPC socket)
- Omarchy, for the theme integration. Without Omarchy the palette stays on the built-in defaults.
+154
View File
@@ -0,0 +1,154 @@
# Remote Control (IPC)
Control a running cliamp instance from another terminal, a shell script, or an AI coding assistant.
When cliamp starts, it listens on a local IPC socket at `~/.config/cliamp/cliamp.sock` (or `%APPDATA%\cliamp\cliamp.sock` on Windows when `HOME` is unset). CLI subcommands connect to this socket to send playback commands and receive status. On Windows 10/11, this uses the same local socket transport via Go's AF_UNIX support.
## Playback Commands
```sh
cliamp play # resume playback
cliamp pause # pause playback
cliamp toggle # play/pause toggle
cliamp next # next track
cliamp prev # previous track
cliamp stop # stop playback
```
## Status
```sh
cliamp status # human-readable current state
cliamp status --json # machine-readable JSON
```
JSON output:
```json
{
"ok": true,
"state": "playing",
"track": {
"title": "Imperial March",
"artist": "John Williams",
"path": "/path/to/file.mp3"
},
"position": 42.5,
"duration": 183.0,
"volume": -3,
"playlist": "Star Wars OT",
"index": 12,
"total": 59,
"visualizer": "ClassicPeak",
"theme": {
"name": "Kanagawa Dragon",
"accent": "#658594",
"fg": "#c5c9c5",
"green": "#8a9a7b",
"yellow": "#c4b28a",
"red": "#c4746e"
}
}
```
The `theme` block carries the active cliamp theme's resolved hex colors. Empty hex fields mean the default ANSI fallback theme is active.
## Volume and Seek
```sh
cliamp volume -5 # adjust volume in dB
cliamp seek 30 # seek to position in seconds
```
## Playlist Loading
```sh
cliamp load "Playlist Name" # load a playlist into the player
cliamp queue /path/to.mp3 # queue a single track
```
## Spectrum Streaming
```sh
cliamp visstream # NDJSON spectrum frames at 30 fps (default)
cliamp visstream --fps 60 # up to 60 fps; clamped to [1, 60]
```
`visstream` holds a single IPC connection open and emits one JSON line per frame containing the 10-band spectrum and the active visualizer mode name:
```json
{"ok":true,"visualizer":"Bars","bands":[0.93,0.81,0.62,0.48,0.31,0.22,0.14,0.09,0.04,0.01]}
```
Band values are normalized to [0, 1], in the same shape cliamp uses internally for spectrum visualizers. This is what powers the [Quickshell now-playing widget](quickshell.md). Consumers can pipe stdout directly into another process (`cliamp visstream | jq`) or use it from a long-lived subprocess in a UI toolkit.
Under the hood it issues a `{"cmd":"bands"}` request per tick over the existing IPC socket; you can also issue this command directly from your own client if you want frame-pulled access:
```json
{"cmd": "bands"}
```
Response:
```json
{"ok": true, "visualizer": "Bars", "bands": [0.93, 0.81, ...]}
```
## Protocol
The IPC protocol is newline-delimited JSON over a local stream socket. Each request is a single JSON object followed by a newline. The server responds with a single JSON object followed by a newline.
Request format:
```json
{"cmd": "status"}
{"cmd": "next"}
{"cmd": "volume", "value": -5}
{"cmd": "load", "playlist": "Star Wars OT"}
{"cmd": "queue", "path": "/path/to/file.mp3"}
```
Response format:
```json
{"ok": true}
{"ok": true, "state": "playing", "track": {...}, ...}
{"ok": false, "error": "cliamp is not running"}
```
## Socket Details
- **Path**: `~/.config/cliamp/cliamp.sock` (or `%APPDATA%\cliamp\cliamp.sock` on Windows when `HOME` is unset; created on TUI start, removed on shutdown)
- **Permissions**: `0600` (owner only)
- **Stale detection**: A PID file (`cliamp.sock.pid`) tracks the owning process. If cliamp crashes, the next instance detects the stale socket and cleans it up.
## Scripting Examples
```sh
# Skip to next track and show what's playing
cliamp next && cliamp status --json | jq .track.title
# Pause from a tmux/cmux script
cliamp pause
# Load a playlist and start playing
cliamp load "Blade Runner" && cliamp play
```
## Headless Daemon Mode
Run cliamp without a TUI and drive it entirely over this IPC interface — useful for status bars, hotkey scripts, cron jobs, and embedded boxes. See [Headless Daemon Mode](headless.md) for setup, use cases, and example configs (Waybar, Hyprland, systemd, cron).
```sh
cliamp --daemon # no TUI, IPC only
cliamp --daemon --auto-play --playlist Lofi # start playing on launch
```
## Error Handling
If cliamp is not running:
```
$ cliamp status
cliamp is not running (no socket at /Users/you/.config/cliamp/cliamp.sock)
```
+76
View File
@@ -0,0 +1,76 @@
# SoundCloud Integration
cliamp supports [SoundCloud](https://soundcloud.com) as an opt-in provider. Search, paste-to-play, browse a profile, and (with a browser cookie hookup) stream subscriber-gated tracks. Powered by [yt-dlp](https://github.com/yt-dlp/yt-dlp), so it requires `yt-dlp` on `PATH`.
> SoundCloud closed its OAuth program to new applications in 2014, so the bring-your-own-`client_id` pattern Spotify uses isn't available. cliamp signs you in by reusing your browser's existing SoundCloud session — see [Sign in via browser cookies](#sign-in-via-browser-cookies) below.
## Enable
SoundCloud is **off by default**. To turn it on, add to `~/.config/cliamp/config.toml`:
```toml
[soundcloud]
enabled = true
```
Once enabled:
- **Search** with `Ctrl+F` while SoundCloud is the active provider — runs `scsearch:` against SoundCloud's public index.
- **Paste a URL** (`u`) — any `soundcloud.com/<artist>/<track>` URL plays.
- **Browse list with curated genres** — when no profile is configured, the playlists pane is seeded with **Trending**, **Hip-Hop**, **Electronic**, **House**, **Lo-Fi**, **Indie**, and **Pop**. These are search-backed virtual playlists (real-time scsearch results), not editorial charts — SoundCloud's official chart endpoints all 404 through yt-dlp at present.
## Browse a profile
Set a username to expose that profile's content in the browse pane:
```toml
[soundcloud]
enabled = true
user = "yourname"
```
This replaces the curated Browse list with three playlists for `soundcloud.com/yourname`:
- **Tracks** — everything the user has uploaded
- **Likes** — tracks they've liked
- **Reposts** — tracks they've reposted
Works for any public profile. No SoundCloud sign-in required at this level.
## Sign in via browser cookies
For private likes, hidden uploads, or SoundCloud Go+ subscriber-gated tracks, point yt-dlp at your browser's cookie jar:
```toml
[soundcloud]
enabled = true
user = "yourname"
cookies_from = "firefox" # also: chrome, chromium, brave, edge, opera, safari, vivaldi
```
cliamp passes `--cookies-from-browser <name>` to every yt-dlp invocation — search, browse, and playback. As long as you're signed into SoundCloud in that browser (no need to keep it open), yt-dlp acts as logged-in-you and can access content your account is authorized for.
This is the same mechanism `[ytmusic] cookies_from` uses. If you set both, the last one to initialize wins for the playback path; in practice users have one default browser they're signed into multiple sites with, so this is fine.
## CLI
```sh
cliamp https://soundcloud.com/forss/flickermood # play a track
cliamp https://soundcloud.com/forss/sets/album # play a set / playlist
cliamp https://soundcloud.com/forss # play a profile's tracks
cliamp --provider soundcloud # start with SoundCloud as the active provider
cliamp search-sc "lofi beats" # legacy: SoundCloud search from the shell
```
URL playback works regardless of the `[soundcloud]` toggle — yt-dlp resolves any SoundCloud link cliamp hands it. The `enabled` flag gates only the in-app provider entry.
## When playback fails
Some tracks 404 on SoundCloud's per-track format API even though the page and search index still show them. Common causes: subscriber-gated content (Go+), region-blocked streams, deleted-but-cached entries, or transient yt-dlp extractor glitches. cliamp surfaces yt-dlp's exit message and shows a status notification — *"Couldn't play X — track is gated, restricted, or unavailable."* — so you know it's an upstream issue rather than a cliamp bug.
If you hit this on tracks you expect to play, set `cookies_from` (above) and confirm you're signed into SoundCloud in that browser.
## Requirements
- [yt-dlp](https://github.com/yt-dlp/yt-dlp) on `PATH`
- Optional: a browser with an active SoundCloud session, for `cookies_from`
+97
View File
@@ -0,0 +1,97 @@
# Spotify Integration
Cliamp can stream your [Spotify](https://www.spotify.com/) library directly through its audio pipeline. EQ, visualizer, and all effects apply. Requires a [Spotify Premium](https://www.spotify.com/premium/) account.
> **Windows:** Spotify is currently unavailable on Windows builds because the `go-librespot` playback backend used by cliamp does not compile there yet.
>
> **Quick start:** run `cliamp setup`, pick Spotify, and follow the prompts. The recommended path is to register your own Spotify Developer app and paste its `client_id` — it gives you a private rate-limit quota and works for playback, library, and playlists. There's also a built-in shared `client_id` available for users who specifically need Spotify search.
## Setup
### Recommended: bring your own client ID
Register a Spotify Developer app and set `client_id` in `~/.config/cliamp/config.toml`:
```toml
[spotify]
client_id = "your_client_id_here"
bitrate = 320
```
To register one:
1. Go to [developer.spotify.com/dashboard](https://developer.spotify.com/dashboard) and log in
2. Click **Create app**
3. Fill in a name (e.g. "cliamp") and description (anything works)
4. Add `http://127.0.0.1:19872/login` as a **Redirect URI**
5. Check **Web API** under "Which API/SDKs are you planning to use?"
6. Click **Save**
7. Open your app's **Settings** and copy the **Client ID**
`bitrate` is optional. If omitted, cliamp uses `320`. Supported values are `96`, `160`, and `320`. Non-positive values (≤ 0) are treated as `320`. Other positive values are rounded to the nearest supported bitrate.
Run `cliamp`, select Spotify as a provider, and press Enter to sign in. Credentials are cached at `~/.config/cliamp/spotify_credentials.json`. Subsequent launches refresh silently.
### Newer apps and the search caveat
Apps registered in Development Mode (the default for anything created on developer.spotify.com after Nov 27, 2024) **still work for almost everything** — playback, your library, your playlists, save/follow actions, OAuth itself. The one specific thing they can't do is hit Spotify's **catalog endpoints**: `/v1/search` and a handful of related endpoints.
You'll see the catalog restriction as `400 "Invalid limit"` whenever you press <kbd>Ctrl+F</kbd> to search Spotify — Spotify [introduced this restriction on Nov 27, 2024](https://developer.spotify.com/blog/2024-11-27-changes-to-the-web-api) and rarely grants Extended Quota Mode to personal/non-commercial apps. Cliamp surfaces a friendlier error explaining what's actually wrong instead of the raw "Invalid limit" message.
If you don't use Spotify search often, your own `client_id` is the better choice — keep it.
### Alternative: built-in shared client ID
If Spotify search is essential to you and your own app hits the dev-mode restriction above, drop the `client_id` line:
```toml
[spotify]
bitrate = 320
```
cliamp falls back to a built-in `client_id` (the same one [librespot](https://github.com/librespot-org/librespot) and [spotify-player](https://github.com/aome510/spotify-player) ship with) which predates the Nov 27, 2024 cutoff and retains catalog access.
> **Heads-up — shared rate limit:** The built-in `client_id` is shared with every librespot-, spotify-player-, and cliamp user worldwide. Spotify's per-app quota is global, so when the pool is busy you may see `429 Too Many Requests` errors during search or playlist loading. Cliamp retries with backoff, but persistent 429s mean the pool is hot — your own `client_id` doesn't share that problem.
## Usage
Once authenticated, Spotify appears as a provider alongside Navidrome and local playlists. Press `Esc`/`b` to open the provider browser and select Spotify.
Your Spotify playlists are listed in the provider panel. Navigate with the arrow keys and press `Enter` to load one. Tracks are streamed through cliamp's audio pipeline, so EQ, visualizer, mono, and all other effects work exactly as with local files.
## Controls
When focused on the provider panel:
| Key | Action |
|---|---|
| `Up` `Down` / `j` `k` | Navigate playlists |
| `Enter` | Load the selected playlist |
| `Tab` | Switch between provider and playlist focus |
| `Esc` / `b` | Open provider browser |
After loading a playlist you return to the standard playlist view with all the usual controls (seek, volume, EQ, shuffle, repeat, queue, search, lyrics).
## Playlists
Only playlists in your Spotify library are shown. This includes playlists you've created and playlists you've saved (followed). If a public playlist doesn't appear, open Spotify and click **Save** on it first. There's no need to copy tracks to a new playlist.
## Podcasts
Podcast episodes work like tracks. Press `Ctrl+F` to search Spotify and matching episodes (for example "Joe Rogan") appear alongside songs; press `Enter` to play. Playlists that mix songs and episodes load and play both.
## Troubleshooting
- **"OAuth failed"**: Make sure your redirect URI is exactly `http://127.0.0.1:19872/login` in the Spotify dashboard (no trailing slash).
- **Playlist not showing**: You must save/follow the playlist in Spotify for it to appear. Only your library playlists are listed.
- **Playback issues**: Spotify integration requires a Premium account. Free accounts cannot stream.
- **Re-authenticate**: Run `cliamp spotify reset` to clear stored credentials, then relaunch cliamp and select Spotify to sign in again. (Equivalent to deleting `~/.config/cliamp/spotify_credentials.json` manually.)
- **Persistent "rate-limited" errors on `/v1/me`**: Your stored auth has expired or been revoked. Cliamp will detect this on most launches and prompt you to sign in again, but if it does not, run `cliamp spotify reset` and re-authenticate. This is *not* a real Spotify rate limit — waiting will not resolve it.
- **`429 Too Many Requests` on search or playlist loading (using the built-in fallback)**: The built-in `client_id` is shared with every librespot- and spotify-player-based client; when the global pool is busy, Spotify caps requests for everyone using it. Cliamp retries with exponential backoff, but if the errors keep returning the simplest fix is to register your own developer app and set `client_id` in `[spotify]` — your personal app gets its own quota.
- **"search blocked — your client_id is too new" on <kbd>Ctrl+F</kbd>**: Your registered Spotify Developer app is in Development Mode and can't hit `/v1/search` (Spotify's Nov 27, 2024 change). Everything else on your app — playback, library, playlists, save/follow — still works fine. Either remove `client_id` from `[spotify]` to use the built-in fallback for search, or just don't use Spotify search and keep your own app.
## Requirements
- Spotify Premium account
- No additional system dependencies beyond cliamp itself
- A registered app at [developer.spotify.com/dashboard](https://developer.spotify.com/dashboard) is **optional** — cliamp ships with a built-in fallback `client_id`
+91
View File
@@ -0,0 +1,91 @@
# SSH Streaming
Play music from a remote machine over SSH without mounting filesystems.
## How It Works
When a track path starts with `ssh://`, cliamp pipes the audio over SSH using the system `ssh` binary:
```
ssh://hostname/absolute/path/to/file.mp3
```
The player runs `ssh hostname cat /path/to/file.mp3` and feeds the output to the audio decoder. No temporary files, no filesystem mounts.
## Creating SSH Playlists
Use `--ssh HOST` with `playlist create` to walk a remote directory:
```sh
cliamp playlist create "Blade Runner" --ssh nas "/Volumes/Music/Blade Runner/"
# Created playlist "Blade Runner" (31 tracks, ssh://nas)
```
This runs `ssh nas find /path -type f -name '*.mp3' ...` to discover audio files, then creates a TOML playlist with `ssh://` prefixed paths.
## TOML Format
SSH playlists look like regular playlists with `ssh://` paths:
```toml
name = "Blade Runner"
[[track]]
path = "ssh://nas/Volumes/Music/Blade Runner/01 - Prologue.mp3"
title = "Prologue And Main Titles"
[[track]]
path = "ssh://nas/Volumes/Music/Blade Runner/02 - Voight Kampff.mp3"
title = "Voight Kampff Test"
```
## SSH Configuration
cliamp uses the system `ssh` binary, which reads `~/.ssh/config`. Host aliases, keys, ports, and ProxyJump all work automatically:
```
# ~/.ssh/config
Host nas
HostName 192.168.1.50
User music
IdentityFile ~/.ssh/nas_key
Host mac-mini-ts
HostName 100.64.0.5
```
## Supported Formats
SSH streaming works with all formats supported by the native decoders:
- `.mp3` (native decoder)
- `.flac` (native decoder)
- `.ogg` / `.opus` (native decoder)
- `.wav` (native decoder)
Formats requiring ffmpeg (`.m4a`, `.wma`) may not work over SSH since the ffmpeg decoder expects a seekable file.
## Error Handling
| Scenario | Behavior |
|----------|----------|
| Host unreachable | Player shows error, advances to next track |
| Auth failure | SSH uses `BatchMode=yes` and never hangs on password prompts |
| Connection drops mid-stream | Player detects EOF, advances to next track |
| Unknown host key | Rejected. Add the host to `~/.ssh/known_hosts` first, or configure in `~/.ssh/config` |
## Mixing Local and SSH Tracks
A single playlist can mix local and SSH paths:
```toml
name = "Mixed"
[[track]]
path = "/local/path/track1.mp3"
title = "Local Track"
[[track]]
path = "ssh://nas/remote/path/track2.mp3"
title = "Remote Track"
```
+80
View File
@@ -0,0 +1,80 @@
# Streaming
cliamp can play audio from URLs, M3U/PLS playlists, and podcast RSS feeds.
## HTTP Streams
Play audio directly from URLs:
```sh
cliamp https://example.com/song.mp3
cliamp http://radio-station.com/stream.m3u
cliamp local.mp3 https://example.com/remote.mp3 # mix local + remote
```
For non-seekable HTTP streams, the UI shows `● Streaming` with a static seek bar, and seek keys are silently ignored.
## PLS Playlists
PLS playlist files are supported alongside M3U:
```sh
cliamp https://radio.cliamp.stream/lofi/stream.pls
```
## HLS Streams
HLS playlists (`.m3u8`, master or media) are supported, as used by large broadcasters such as Brazilian RBS/Wowza stations:
```sh
cliamp "https://example.com/live/playlist.m3u8"
```
cliamp hands the URL to ffmpeg, which resolves the relative chunklist/segment URIs and follows the live segment window. Requires `ffmpeg` (already needed for AAC/Opus).
Live HLS carries timed metadata rather than inline ICY, so the now-playing track title isn't updated for HLS streams.
## Podcasts
Play any podcast by passing its RSS feed URL:
```sh
cliamp https://example.com/podcast/feed.xml
```
Episode titles and the podcast name are extracted from the feed and shown in the playlist.
### Xiaoyuzhou (小宇宙)
Play individual episodes from [Xiaoyuzhou](https://www.xiaoyuzhoufm.com) by passing the episode URL:
```sh
cliamp https://www.xiaoyuzhoufm.com/episode/xxxx
```
## Radio Catalog
Press `R` in the player to browse and search 30,000+ online radio stations from the [Radio Browser](https://www.radio-browser.info/) directory. Use `/` to search by name, `Enter` to play, and `a` to append a station to the playlist.
## Track Info
For live radio, cliamp shows the current track from the stream's inline ICY metadata (`StreamTitle`). This works for most stations, in any codec (MP3, AAC, Opus, ...).
Some broadcasters send no inline metadata and publish now-playing through a separate API instead. cliamp pulls those automatically:
| Station | Source | Shown |
| --- | --- | --- |
| FIP (and FIP Jazz, Rock, Groove, Reggae, Electro, Metal, Monde, Nouveautes) | Radio France livemeta API | Artist - Title |
| NTS 1 / NTS 2 | NTS live API | Current show |
NTS is live DJ radio with no per-track tagging, so it shows the show/host name rather than a song.
## Load URL at Runtime
Press `u` while playing to load a new stream or playlist URL without restarting. Supports the same URL types as CLI arguments: direct audio URLs, M3U/PLS playlists, RSS podcast feeds, and yt-dlp compatible links.
## Run Your Own Radio Station
Run your own internet radio with [cliamp-server](https://github.com/bjarneo/cliamp-server). Point it at a directory of audio files and it starts broadcasting. Supports multiple stations, live metadata, and on-the-fly transcoding.
See also: [playlists.md](playlists.md) for M3U playlist details.
+61
View File
@@ -0,0 +1,61 @@
# Themes
cliamp ships with 20 built-in color themes and supports custom themes via simple TOML files.
Press `t` during playback to open the theme picker. Navigate with `↑`/`↓`, preview live as you move, confirm with `Enter`, or cancel with `Esc`.
Your selection is saved automatically and restored on next launch.
## Built-in themes
ayu-mirage-dark, catppuccin, catppuccin-latte, dracula, ember, ethereal, everforest, flexoki-light, gruvbox, hackerman, kanagawa, matte-black, miasma, neon-blade-runner, nord, osaka-jade, ristretto, rose-pine, tokyo-night, vantablack
## Creating a custom theme
Create a `.toml` file in `~/.config/cliamp/themes/`:
```
mkdir -p ~/.config/cliamp/themes
```
Each file needs 6 hex color values. The filename (minus `.toml`) becomes the theme name.
### Example: `~/.config/cliamp/themes/solarized.toml`
```toml
accent = "#268bd2"
bright_fg = "#eee8d5"
fg = "#839496"
green = "#859900"
yellow = "#b58900"
red = "#dc322f"
```
That's it. Press `t` and your theme appears in the list immediately.
### Color reference
| Key | What it colors |
|-------------|---------------------------------------------------|
| `accent` | Title, track name, seek bar, selected items |
| `bright_fg` | Primary text, time display, help key pill text |
| `fg` | Muted/secondary text, help bar, inactive elements, help key pill background |
| `green` | Playing indicator, volume bar, spectrum low |
| `yellow` | Spectrum middle |
| `red` | Spectrum top, error messages |
All values are hex strings (e.g. `"#ff5733"` or `"#F00"`).
## Overriding a built-in theme
If your custom file has the same name as a built-in theme, yours takes priority. For example, creating `~/.config/cliamp/themes/catppuccin.toml` replaces the built-in catppuccin.
## Setting a default theme
Add a `theme` line to `~/.config/cliamp/config.toml`:
```toml
theme = "catppuccin"
```
Use the filename without `.toml`. Leave empty or omit for terminal default colors.
+113
View File
@@ -0,0 +1,113 @@
# YouTube & YouTube Music Integration
Cliamp can browse your [YouTube](https://youtube.com/) and [YouTube Music](https://music.youtube.com/) playlists and play tracks through its audio pipeline. EQ, visualizer, and all effects apply. Playback uses yt-dlp, which must be installed.
Your playlists are automatically classified into two providers:
- **YouTube Music**: playlists containing music content
- **YouTube**: playlists containing non-music content (podcasts, vlogs, tutorials, etc.)
> **Quick start:** YouTube Music works out of the box with built-in fallback credentials — just install yt-dlp and select it in the provider browser. Run `cliamp setup` if you want to disable it, supply your own OAuth client, or configure cookie-based age-gated playback. Manual setup steps for the custom path are below.
## Setup
### Creating your client ID
1. Go to [console.cloud.google.com](https://console.cloud.google.com/) and log in
2. Create a new project (or select an existing one)
3. Navigate to **APIs & Services > Library**
4. Search for **YouTube Data API v3** and click **Enable**
5. Go to **APIs & Services > Credentials**
6. Click **Create Credentials > OAuth client ID**
7. If prompted, configure the OAuth consent screen first:
- User Type: **External**
- Fill in app name (e.g. "cliamp") and your email
- Add scope: `https://www.googleapis.com/auth/youtube.readonly`
- Add yourself as a test user (required while app is in "Testing" status)
8. For the OAuth client ID:
- Application type: **Desktop app**
- Name: anything (e.g. "cliamp")
9. Copy the **Client ID** and **Client Secret**
### Configuring cliamp
Add your client ID and client secret to `~/.config/cliamp/config.toml`:
```toml
[ytmusic]
client_id = "your_client_id_here"
client_secret = "your_client_secret_here"
```
Optional: to play uploaded/private tracks, add your browser for cookie access:
```toml
[ytmusic]
client_id = "your_client_id_here"
client_secret = "your_client_secret_here"
cookies_from = "chrome"
```
Supported browsers: `chrome`, `firefox`, `brave`, `edge`, `opera`, `safari`, `chromium`.
You can also point at a specific profile or path using yt-dlp's `browser:path` syntax. For example, Zen browser (a Firefox fork) stores its profile outside the default location:
```toml
[ytmusic]
cookies_from = "firefox:~/.config/zen"
```
Run `cliamp` (or `cliamp --provider ytmusic` / `cliamp --provider youtube`), select a provider, and press Enter to sign in. Credentials are cached at `~/.config/cliamp/ytmusic_credentials.json`. Subsequent launches refresh silently.
## Usage
Once authenticated, **YouTube** and **YouTube Music** appear as separate providers alongside Spotify, Navidrome, and Radio. Press `Esc`/`b` to open the provider browser.
- **YouTube Music** shows playlists classified as music (video category "Music")
- **YouTube** shows all other playlists (podcasts, vlogs, tutorials, etc.)
Both share the same Google account login. Classification is automatic (based on video category) and cached to disk so subsequent launches are instant.
## Controls
When focused on the provider panel:
| Key | Action |
|---|---|
| `Up` `Down` / `j` `k` | Navigate playlists |
| `Enter` | Load the selected playlist |
| `Tab` | Switch between provider and playlist focus |
| `Esc` / `b` | Open provider browser |
After loading a playlist you return to the standard playlist view with all the usual controls (seek, volume, EQ, shuffle, repeat, queue, search, lyrics).
## Playlists
Playlists are automatically split between the two providers:
**YouTube Music** shows:
- **Liked Music**: your liked songs (YouTube Music's special `LM` playlist)
- Playlists containing music content (auto-classified by video category)
**YouTube** shows:
- **Liked Videos**: your liked videos (YouTube's special `LL` playlist)
- Playlists containing non-music content
Classification is determined by sampling a video from each playlist and checking its YouTube category. Results are cached at `~/.config/cliamp/ytmusic_classification.json`. Delete this file to reclassify.
## Troubleshooting
- **"ERR: waiting for audio data: EOF" / playback stops immediately**: yt-dlp couldn't produce a stream. cliamp now surfaces yt-dlp's real message (e.g. "Sign in to confirm you're not a bot") instead of the bare EOF, so read the full error. The common causes:
- **Outdated yt-dlp**: update it (`yt-dlp -U`, or reinstall from the [official repo](https://github.com/yt-dlp/yt-dlp)). Distro and winget builds are frequently stale and break when YouTube changes.
- **Bot detection**: YouTube blocks anonymous requests. Set `cookies_from` (see above) so yt-dlp reuses your logged-in browser session. For Zen browser use `cookies_from = "firefox:~/.config/zen"`.
- **Wrong `cookies_from` value**: the browser must be installed and logged in to YouTube, and the profile path must be correct.
- **"OAuth failed"**: Make sure your Google Cloud project has YouTube Data API v3 enabled and your OAuth client type is "Desktop app".
- **"Access blocked"**: While your app is in "Testing" status, only test users you've added can sign in. Add your Google account as a test user in the OAuth consent screen settings.
- **Playlist not showing**: Only playlists in your library are listed. Save/follow a playlist in YouTube Music for it to appear.
- **Re-authenticate**: Delete `~/.config/cliamp/ytmusic_credentials.json` and restart cliamp to trigger a fresh login.
- **Private/deleted videos**: These are automatically skipped when loading a playlist.
## Requirements
- [yt-dlp](https://github.com/yt-dlp/yt-dlp) installed and on your PATH (for audio playback)
- A Google Cloud project with YouTube Data API v3 enabled
- No Spotify Premium or other paid subscription required. YouTube Music free tier works
+29
View File
@@ -0,0 +1,29 @@
# YouTube, SoundCloud, NetEase, Bandcamp and Bilibili
Play from YouTube, SoundCloud, NetEase, Bandcamp, and Bilibili URLs if [yt-dlp](https://github.com/yt-dlp/yt-dlp) is installed:
```sh
cliamp https://www.youtube.com/watch?v=dQw4w9WgXcQ
cliamp https://soundcloud.com/artist/track
cliamp 'https://music.163.com/#/song?id=1973665667'
cliamp https://artist.bandcamp.com/album/name
cliamp https://www.bilibili.com/video/BV1xxxxxxxxx
cliamp https://space.bilibili.com/uid/lists/id # season/series playlists
```
Playlists and albums are supported. Press `S` to save a downloaded track to `~/Music/cliamp/`.
## Search
Search and play directly from the command line:
```sh
cliamp search "never gonna give you up" # search YouTube
cliamp search-sc "lofi beats" # search SoundCloud
```
Inside the TUI, press `Ctrl+F` to search the active provider — YouTube when you're on YouTube/YT-Music, SoundCloud when you're on SoundCloud, and NetEase when you're on NetEase. SoundCloud and NetEase also have dedicated provider docs covering signed-in playback: [SoundCloud](soundcloud.md), [NetEase](netease.md).
## Disclaimer
**Use at your own risk.** Downloading or streaming copyrighted content may violate the terms of service of these platforms. You are responsible for how you use this feature.