230 lines
7.0 KiB
Bash
Executable File
230 lines
7.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
URL_BASE="https://www.radiorecord.ru/api"
|
|
MPV_PID_FILE="/tmp/mpv_radio_pid"
|
|
RADIO_ID_FILE="/tmp/radio_id"
|
|
|
|
STATION_IDS='[507,522,523,536,537,42532,42602]'
|
|
|
|
# Custom (non-radiorecord) stations
|
|
# id must be a unique integer >= 1000000 to avoid collision with radiorecord IDs.
|
|
# icon_fill_white: URL to station icon image, or "" for none.
|
|
CUSTOM_STATIONS='[
|
|
]'
|
|
|
|
STATIONS="[]"
|
|
DEFAULT_INFO='{"artist":"","song":"","image600":""}'
|
|
DEFAULT_MEDIA='{"player":"","status":"Stopped","artist":"","title":"","art":""}'
|
|
|
|
PID=$([ -e "$MPV_PID_FILE" ] && cat "$MPV_PID_FILE" || echo 0)
|
|
RADIO_ID=$([ -e "$RADIO_ID_FILE" ] && cat "$RADIO_ID_FILE" || echo 0)
|
|
PAUSED=$(( PID == 0 ? 1 : 0 ))
|
|
INFO="$DEFAULT_INFO"
|
|
MEDIA="$DEFAULT_MEDIA"
|
|
STATUS="{}"
|
|
|
|
get_stations() {
|
|
local rr
|
|
rr=$(curl -s --compressed "$URL_BASE/stations/" 2>/dev/null \
|
|
| jq --argjson ids "$STATION_IDS" \
|
|
'.result.stations | map(select(.id | IN($ids[]))) | map({id, title, stream_hls, icon_fill_white, "radiorecord": true})' 2>/dev/null)
|
|
jq -n \
|
|
--argjson rr "${rr:-[]}" \
|
|
--argjson custom "$CUSTOM_STATIONS" \
|
|
'$rr + ($custom | map(. + {"radiorecord": false}))'
|
|
}
|
|
|
|
get_song() {
|
|
curl -s --compressed "$URL_BASE/station/history/?id=$RADIO_ID" \
|
|
| jq '.result.history[0] | {artist, song, image600}'
|
|
}
|
|
|
|
get_stream_url() {
|
|
echo "$STATIONS" | jq -r --argjson id "$RADIO_ID" 'map(select(.id == $id))[0].stream_hls // empty'
|
|
}
|
|
|
|
get_player_info() {
|
|
local player status artist title art
|
|
# exclude mpv (radio) so it never appears as "external player"
|
|
player=$(playerctl -l 2>/dev/null | grep -Ev '^mpv' | head -1)
|
|
if [ -z "$player" ]; then
|
|
echo '{"player":"","status":"Stopped","artist":"","title":"","art":""}'
|
|
return
|
|
fi
|
|
status=$(playerctl -p "$player" status 2>/dev/null || echo "Stopped")
|
|
artist=$(playerctl -p "$player" metadata artist 2>/dev/null || echo "")
|
|
title=$(playerctl -p "$player" metadata title 2>/dev/null || echo "")
|
|
art=$(playerctl -p "$player" metadata mpris:artUrl 2>/dev/null || echo "")
|
|
jq -cnr --arg player "$player" --arg status "$status" \
|
|
--arg artist "$artist" --arg title "$title" --arg art "$art" \
|
|
'{player:$player, status:$status, artist:$artist, title:$title, art:$art}'
|
|
}
|
|
|
|
update_state() {
|
|
PID=$([ -e "$MPV_PID_FILE" ] && cat "$MPV_PID_FILE" || echo 0)
|
|
RADIO_ID=$([ -e "$RADIO_ID_FILE" ] && cat "$RADIO_ID_FILE" || echo 0)
|
|
if [ "$PID" -gt 0 ] && ! kill -0 "$PID" 2>/dev/null; then
|
|
PID=0
|
|
rm -f "$MPV_PID_FILE"
|
|
fi
|
|
PAUSED=$(( PID == 0 ? 1 : 0 ))
|
|
}
|
|
|
|
emit_status() {
|
|
jq -cnr \
|
|
--argjson stations "${STATIONS:-[]}" \
|
|
--argjson radio_id "${RADIO_ID:-0}" \
|
|
--argjson is_paused "${PAUSED:-1}" \
|
|
--argjson info "${INFO:-$DEFAULT_INFO}" \
|
|
--argjson media "${MEDIA:-$DEFAULT_MEDIA}" \
|
|
'{"is_paused": $is_paused, "song": $info, "radio": $radio_id, "stations": $stations, "media": $media}'
|
|
}
|
|
|
|
get_stream_node() {
|
|
local name="$1"
|
|
wpctl status 2>/dev/null | awk -v pat="$name" '
|
|
/Streams:/ { in_s = 1 }
|
|
/Sinks:|Sources:|Clients:/ { in_s = 0 }
|
|
in_s && /[0-9]+\./ && tolower($0) ~ tolower(pat) {
|
|
match($0, /[0-9]+/); print substr($0, RSTART, RLENGTH); exit
|
|
}
|
|
'
|
|
}
|
|
|
|
get_mpv_node() {
|
|
get_stream_node "mpv"
|
|
}
|
|
|
|
get_volume() {
|
|
if [ "$PID" -gt 0 ] && kill -0 "$PID" 2>/dev/null; then
|
|
local node_id; node_id=$(get_mpv_node)
|
|
[ -n "$node_id" ] && wpctl get-volume "$node_id" 2>/dev/null \
|
|
| awk '{printf "%d", $2 * 100}'
|
|
else
|
|
local player; player=$(playerctl -l 2>/dev/null | grep -Ev '^mpv' | head -1)
|
|
[ -n "$player" ] && playerctl -p "$player" volume 2>/dev/null \
|
|
| awk '{printf "%d", $1 * 100}'
|
|
fi
|
|
}
|
|
|
|
do_vol() {
|
|
local vol="${1%.*}"
|
|
if [ "$PID" -gt 0 ] && kill -0 "$PID" 2>/dev/null; then
|
|
local node_id; node_id=$(get_mpv_node)
|
|
[ -n "$node_id" ] && wpctl set-volume "$node_id" "${vol}%"
|
|
else
|
|
playerctl volume "$(awk -v v="$vol" 'BEGIN{printf "%.2f", v/100}')" 2>/dev/null
|
|
fi
|
|
eww update radio-vol="$vol"
|
|
}
|
|
|
|
do_mute() {
|
|
local node_id
|
|
if [ "$PID" -gt 0 ] && kill -0 "$PID" 2>/dev/null; then
|
|
node_id=$(get_mpv_node)
|
|
else
|
|
local player; player=$(playerctl -l 2>/dev/null | grep -Ev '^mpv' | head -1)
|
|
[ -n "$player" ] && node_id=$(get_stream_node "${player%%.*}")
|
|
fi
|
|
[ -z "$node_id" ] && return
|
|
wpctl set-mute "$node_id" toggle
|
|
if wpctl get-volume "$node_id" 2>/dev/null | grep -q MUTED; then
|
|
eww update radio-muted=true
|
|
else
|
|
eww update radio-muted=false
|
|
fi
|
|
}
|
|
|
|
do_start() {
|
|
[ "$RADIO_ID" -le 0 ] && return
|
|
STATIONS=$(get_stations)
|
|
RADIO_URL=$(get_stream_url)
|
|
[ -z "$RADIO_URL" ] || [ "$RADIO_URL" = "null" ] && return
|
|
[ "$PID" -gt 0 ] && kill "$PID" 2>/dev/null
|
|
nohup mpv --no-video --quiet "$RADIO_URL" >/dev/null 2>&1 &
|
|
echo $! > "$MPV_PID_FILE"
|
|
echo "$RADIO_ID" > "$RADIO_ID_FILE"
|
|
}
|
|
|
|
status_loop() {
|
|
STATIONS=$(get_stations)
|
|
echo "$(emit_status)"
|
|
|
|
last_pid_check=0
|
|
last_song_fetch=0
|
|
last_media_fetch=0
|
|
|
|
while true; do
|
|
now=$(date -u +%s%3N)
|
|
|
|
if (( now - last_pid_check > 1000 )); then
|
|
update_state
|
|
NEW_STATUS=$(emit_status)
|
|
if [ "$NEW_STATUS" != "$STATUS" ]; then STATUS=$NEW_STATUS; echo "$STATUS"; fi
|
|
last_pid_check=$now
|
|
fi
|
|
|
|
if (( now - last_song_fetch > 15000 )); then
|
|
if [ "$PAUSED" = 0 ]; then
|
|
IS_RR=$(echo "$STATIONS" | jq --argjson id "$RADIO_ID" 'map(select(.id == $id))[0].radiorecord // true')
|
|
if [ "$IS_RR" = "true" ]; then
|
|
FETCHED=$(get_song)
|
|
INFO=$(echo "$INFO" "$FETCHED" | jq -s '
|
|
reduce .[] as $x ({}; . + ($x | with_entries(select(.value != null))))')
|
|
else
|
|
STATION_TITLE=$(echo "$STATIONS" | jq -r --argjson id "$RADIO_ID" 'map(select(.id == $id))[0].title // ""')
|
|
INFO=$(jq -cnr --arg title "$STATION_TITLE" '{artist:"",song:$title,image600:""}')
|
|
fi
|
|
else
|
|
STATION_IMG=$(echo "$STATIONS" \
|
|
| jq -r --argjson id "$RADIO_ID" 'map(select(.id == $id))[0].icon_fill_white // ""')
|
|
STATION_TITLE=$(echo "$STATIONS" \
|
|
| jq -r --argjson id "$RADIO_ID" 'map(select(.id == $id))[0].title // ""')
|
|
INFO=$(jq -cnr --arg img "$STATION_IMG" --arg title "$STATION_TITLE" \
|
|
'{artist:"",song:$title,image600:$img}')
|
|
fi
|
|
NEW_STATUS=$(emit_status)
|
|
if [ "$NEW_STATUS" != "$STATUS" ]; then STATUS=$NEW_STATUS; echo "$STATUS"; fi
|
|
last_song_fetch=$now
|
|
fi
|
|
|
|
if (( now - last_media_fetch > 3000 )); then
|
|
MEDIA=$(get_player_info)
|
|
NEW_STATUS=$(emit_status)
|
|
if [ "$NEW_STATUS" != "$STATUS" ]; then STATUS=$NEW_STATUS; echo "$STATUS"; fi
|
|
VOL=$(get_volume)
|
|
[ -n "$VOL" ] && eww update radio-vol="$VOL" 2>/dev/null
|
|
last_media_fetch=$now
|
|
fi
|
|
|
|
sleep 0.5
|
|
done
|
|
}
|
|
|
|
case "$1" in
|
|
start)
|
|
RADIO_ID="$2"
|
|
echo "$RADIO_ID" > "$RADIO_ID_FILE"
|
|
do_start
|
|
;;
|
|
vol)
|
|
do_vol "$2"
|
|
;;
|
|
mute)
|
|
update_state
|
|
do_mute
|
|
;;
|
|
toggle)
|
|
update_state
|
|
if [ "$PAUSED" = 1 ]; then
|
|
[ "$RADIO_ID" -gt 0 ] && do_start
|
|
else
|
|
[ "$PID" -gt 0 ] && kill "$PID" 2>/dev/null
|
|
rm -f "$MPV_PID_FILE"
|
|
fi
|
|
;;
|
|
*)
|
|
status_loop
|
|
;;
|
|
esac
|