Loading repository data…
Loading repository data…
kprzybys / repository
A tiny command-line player for deep-focus internet radio — a single bash script around mpv
A transparent discovery signal based on current public GitHub metadata.
This score does not audit code, security, maintainers, documentation quality, or suitability. Verify the repository and its current documentation before adoption.
A tiny command-line player for deep-focus internet radio. No UI, no
daemon config, no library to maintain — a single bash script around
mpv. Start a station, get your terminal back, work.
$ focusradio play drone 50m
▶ drone (for 50m, fading out at the end)
$ focusradio now
▶ drone — Chronotope Project - Mind's I
focusradio discover <tag> searches the
Radio Browser community database
(by tag or keyword, most-played first), skips stations you already
have, plays each pick through mpv to confirm it actually works, and
saves the keepers to your own list. The built-in stations stay
untouched.focusradio stop (and when switching stations), instead
of cutting in and out abruptly. A short 3-second fade by default; set
FOCUSRADIO_FADE to change it (FOCUSRADIO_FADE=0 disables it
entirely).focusradio play drone 50m plays
for 50 minutes, then fades to silence over the last stretch and stops
itself. No alarm, no notification: the fading music is the signal
that the session is over.focusradio fav saves it;
focusradio favs and focusradio history read your collection back.mpv, socat, jq, curl — all standard packages:
# Debian/Ubuntu
sudo apt install mpv socat jq curl
# macOS (Homebrew)
brew install mpv socat jq curl
git clone https://github.com/kprzybys/focusradio.git && cd focusradio
ln -sf "$(pwd)/focusradio" ~/.local/bin/focusradio
Make sure ~/.local/bin is on your PATH (most distros add it for you);
then focusradio list works from anywhere. To uninstall, remove the
symlink (rm ~/.local/bin/focusradio) and, if you like, your data
(rm -r ~/.local/share/focusradio).
Optional shell completion for subcommands and station names — the
station list is pulled live from the script, so it stays in sync when
you edit STATIONS. Files live in completions/. All three shells
need focusradio on your PATH (the symlink above) so completion can
query the stations.
bash (needs the bash-completion package and bash 4+ — note stock
macOS ships bash 3.2, so brew install bash if you want it there):
mkdir -p ~/.local/share/bash-completion/completions
ln -sf "$(pwd)/completions/focusradio.bash" \
~/.local/share/bash-completion/completions/focusradio
Open a new shell, then focusradio play <Tab> lists the stations.
zsh — put _focusradio on your $fpath:
mkdir -p ~/.zsh/completions
ln -sf "$(pwd)/completions/_focusradio" ~/.zsh/completions/_focusradio
then, before compinit in ~/.zshrc:
fpath=(~/.zsh/completions $fpath)
autoload -Uz compinit && compinit
fish — loaded automatically:
mkdir -p ~/.config/fish/completions
ln -sf "$(pwd)/completions/focusradio.fish" \
~/.config/fish/completions/focusradio.fish
focusradio list # available stations (▶ = playing now)
focusradio discover lofi # search Radio Browser by tag/keyword and add stations
focusradio play drone # play; play again to switch stations
focusradio play drone 50m # timed session: fade-out + auto-stop at the end
focusradio now # what's playing
focusradio stop # stop
focusradio fav # save the current track to favorites
focusradio favs # show favorites
focusradio history [n] # last n scrobbled tracks (default 20)
Durations: 50m, 1h, 90s, or a bare number (minutes). The
end-of-session fade takes one third of the session, capped at two
minutes.
The gentle start/stop fade is 3 seconds by default. Override it per
invocation with FOCUSRADIO_FADE (seconds), e.g. FOCUSRADIO_FADE=5 focusradio play drone, or FOCUSRADIO_FADE=0 for the old instant
on/off.
~/.local/share/focusradio/ (played.log,
favs.log — plain TSV, yours to grep).~/.local/share/focusradio/stations.local
(name|url|description, one per line — edit a line to rename a
station, delete a line to remove it).$XDG_RUNTIME_DIR/focusradio/,
gone after reboot.focusradio play starts mpv detached with a JSON IPC socket and a
small watcher process alongside it. The watcher polls the socket every
few seconds: it scrobbles title changes, runs the end-of-session fade,
and — if mpv dies while the state says "playing" — starts a brown-noise
generator (lavfi anoisesrc, no files needed) and probes the station
URL every 30 s until it can switch back. focusradio stop tears the
whole thing down.
Edit the STATIONS block at the top of the script — one
name|URL|description line per station. Anything mpv can play works
(.pls playlists, direct stream URLs).
Tests use bats:
bats test/ # everything
bats test/focusradio.bats # fast unit tests only
test/focusradio.bats — unit tests for the pure-logic helpers
(duration parsing, station lookup, slug generation, URL validation,
run-dir hardening). They source the script — its main runs only
when the file is executed, not when sourced.test/e2e.bats — end-to-end tests that drive the real binary and
spawn mpv against hermetic local resources: an av://lavfi source for
playback, and a throwaway HTTP server standing in for both Radio
Browser and the stream host, so discover runs without the network.
Needs ffmpeg and python3 on top of the runtime deps; slower.Both use throwaway data and runtime dirs, so they never touch your real stations or a running session.
MIT — see LICENSE.