Skip to content

Instantly share code, notes, and snippets.

@cotko
Created February 2, 2026 22:01
Show Gist options
  • Select an option

  • Save cotko/7f97a2502d7252cf9563bbbbcdf454ec to your computer and use it in GitHub Desktop.

Select an option

Save cotko/7f97a2502d7252cf9563bbbbcdf454ec to your computer and use it in GitHub Desktop.
FZF Radio-browser search with MPD enqueue
#!/bin/bash
API="https://de1.api.radio-browser.info/json"
MODE=1 # 1 = by name, 2 = country list
PREVIEW_MODE=1 # 1 = hints, 2 = item info
MODE_FILE=/tmp/RADIOS_MODE_$RANDOM
PREVIEW_MODE_FILE=/tmp/RADIOS_PREVIEW_MODE_$RANDOM
LAST_STATIONS_FILE=/tmp/RADIOS_STATIONS_$RANDOM
PORT=$((RANDOM % 16384 + 49152))
echo $MODE > "$MODE_FILE"
echo $PREVIEW_MODE > "$PREVIEW_MODE_FILE"
function setMode() {
echo "$1" > "$MODE_FILE"
}
function togglePreviewMode() {
PREVIEW_MODE=$(cat "$PREVIEW_MODE_FILE")
PREVIEW_MODE=$((PREVIEW_MODE % 2 + 1))
echo $PREVIEW_MODE > "$PREVIEW_MODE_FILE"
}
function getModeInfo() {
MODE=$(cat "$MODE_FILE")
if [ "$MODE" -eq 1 ]; then
echo "Search by name"
elif [ "$MODE" -eq 2 ]; then
echo "List of countries"
else
echo "I am broken 🥺"
fi
}
function printModeInfo() {
echo "mode = $(getModeInfo)"
}
function fetchStations() {
local limit=100
local query="$1"
local qparams=""
[ "${#query}" -lt 1 ] && query=""
MODE=$(cat "$MODE_FILE")
if [ "$MODE" -eq 1 ]; then
qparams="name=$query"
else
echo "I am broken 🥺"
exit 1
fi
if [ "${#query}" -lt 1 ]; then
echo "top $limit" \
| tee "$LAST_STATIONS_FILE"
else
echo "query = ${query}" \
| tee "$LAST_STATIONS_FILE"
fi
printModeInfo \
| tee -a "$LAST_STATIONS_FILE"
curl \
-s \
--data-urlencode "$qparams" \
--data-urlencode "order=votes" \
--data-urlencode "reverse=true" \
--data-urlencode "limit=$limit" \
--data-urlencode "hidebroken=true" \
"$API/stations/search" \
| jq -r '
.[] |
[
(.name | gsub("\t";" ")),
(((.bitrate // 0) | tostring) + "kbps"),
(.url_resolved),
(.bitrate // "n/a"),
(.codec // "n/a"),
(.country // ""),
(.votes // "n/a")
]
| @tsv
' \
| tee -a "$LAST_STATIONS_FILE"
}
function fetchCountries() {
echo ""
printModeInfo
curl \
-s \
--data-urlencode "order=stationcount" \
--data-urlencode "reverse=true" \
"$API/countries" \
| jq -r '.[] |
[
(.stationcount // "n/a"),
(.name | gsub("\t";" "))
]
| @tsv'
}
function fetchSationsForCountry() {
country="$1"
echo "List of stations for $country" \
| tee "$LAST_STATIONS_FILE"
printModeInfo \
| tee -a "$LAST_STATIONS_FILE"
curl \
-s \
--data-urlencode "country=$country" \
--data-urlencode "order=votes" \
--data-urlencode "reverse=true" \
--data-urlencode "hidebroken=true" \
"$API/stations/search" \
| jq -r '
.[] |
[
(.name | gsub("\t";" ")),
(((.bitrate // 0) | tostring) + "kbps"),
(.url_resolved),
(.bitrate // "n/a"),
(.codec // "n/a"),
(.country // "n/a"),
(.votes // "n/a")
]
| @tsv
' \
| tee -a "$LAST_STATIONS_FILE"
}
function fetch() {
MODE=$(cat "$MODE_FILE")
if [ "$MODE" -eq 1 ]; then
fetchStations "$1"
elif [ "$MODE" -eq 2 ]; then
fetchCountries
else
echo "I am broken 🥺"
exit 1
fi
}
function handle() {
MODE=$(cat "$MODE_FILE")
if [ "$MODE" -eq 1 ]; then
url=$(echo "$1" | cut -f3)
mpdAddStation "$url"
cat "$LAST_STATIONS_FILE"
elif [ "$MODE" -eq 2 ]; then
country=$(echo "$1" | cut -f2)
fetchSationsForCountry "$country"
# use TCP to send "clear-query" command
curl -s -X POST --data 'clear-query' "http://127.0.0.1:$PORT"
setMode 1
else
echo "I am broken 🥺"
exit 1
fi
}
function handleAddAndPlay() {
MODE=$(cat "$MODE_FILE")
if [ "$MODE" -eq 1 ]; then
cat "$LAST_STATIONS_FILE"
url=$(echo "$1" | cut -f3)
mpdAddStation "$url" 1
fi
}
function mpdAddStation() {
url="$1"
play="$2"
mpc add "$url" &>/dev/null
if [ "$play" -eq 1 ]; then
mpc play $(mpc playlist | wc -l) &>/dev/null
fi
}
function preview() {
PREVIEW_MODE=$(cat "$PREVIEW_MODE_FILE")
if [ "$PREVIEW_MODE" -eq 1 ]; then
echo 'ctrl+c for Country list mode'
echo 'ctrl+s for Search mode'
echo 'ctrl+x to excute query in search mode'
echo 'alt+enter add station and play it'
else
MODE=$(cat "$MODE_FILE")
if [ "$MODE" -eq 1 ]; then
name=$(echo "$1" | cut -f1)
country=$(echo "$1" | cut -f6)
votes=$(echo "$1" | cut -f7)
codec=$(echo "$1" | cut -f5)
bitrate=$(echo "$1" | cut -f4)
url=$(echo "$1" | cut -f3)
echo "$country"
echo "$name"
echo "codec $codec | bitrate $bitrate | votes $votes"
echo "$url"
else
name=$(echo "$1" | cut -f2)
count=$(echo "$1" | cut -f1)
echo "$name"
echo "station count: $count"
fi
fi
}
export API
export DELAY
export MODE_FILE
export PREVIEW_MODE_FILE
export LAST_STATIONS_FILE
export PORT
export MODE
export -f handle
export -f handleAddAndPlay
export -f fetch
export -f fetchCountries
export -f fetchStations
export -f fetchSationsForCountry
export -f setMode
export -f getModeInfo
export -f printModeInfo
export -f preview
export -f togglePreviewMode
export -f mpdAddStation
cleanup() {
rm $MODE_FILE &>/dev/null || true
rm $PREVIEW_MODE_FILE &>/dev/null || true
rm $LAST_STATIONS_FILE &>/dev/null || true
}
trap cleanup EXIT
fzf \
--ansi \
--listen "127.0.0.1:$PORT" \
--delimiter="\t" \
--with-nth="1,2" \
--layout=reverse \
--border=none \
--highlight-line \
--cycle \
--list-border=rounded \
--info=right \
--header-lines=2 \
--preview="preview {}" \
--preview-window=bottom:4:wrap \
--preview-border=line \
--preview-label="ctrl+d to toggle preview mode" \
--prompt="> " \
--bind "start:reload:bash -c 'fetch'" \
--bind 'ctrl-s:execute-silent(bash -c "setMode 1")+reload(bash -c "fetch \"\$1\"" _ {q})' \
--bind 'ctrl-c:execute-silent(bash -c "setMode 2")+reload(bash -c "fetch \"\$1\"" _ {q})' \
--bind 'ctrl-x:reload(bash -c "fetch \"\$1\"" _ {q})+clear-query' \
--bind 'ctrl-d:execute-silent(bash -c "togglePreviewMode")+refresh-preview' \
--bind 'enter:reload(bash -c "handle {}")' \
--bind 'alt-enter:execute-silent(bash -c "handleAddAndPlay {}")'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment