secubox-openwrt/package/secubox/luci-app-webradio/root/usr/libexec/rpcd/luci.webradio
CyberMind-FR 6db547f7f8 feat: Add WebRadio, TURN server, and Lyrion streaming integration
New packages:
- luci-app-webradio: Web radio management with Lyrion bridge tab
- luci-app-turn: TURN/STUN server UI for WebRTC (Jitsi integration)
- secubox-app-lyrion-bridge: Lyrion → Squeezelite → FFmpeg → Icecast pipeline
- secubox-app-squeezelite: Squeezelite audio player with FIFO output
- secubox-app-turn: TURN server with ACME SSL and Jitsi setup
- secubox-app-webradio: Icecast/ezstream web radio server

Features:
- HTTPS streaming via HAProxy (stream.gk2.secubox.in)
- Lyrion Music Server bridge for streaming playlists to Icecast
- TURN server with time-limited credential generation
- CrowdSec integration for WebRadio security
- Schedule-based radio programming with jingles

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-21 17:46:54 +01:00

768 lines
24 KiB
Bash

#!/bin/sh
# RPCD backend for WebRadio LuCI app
# Copyright (C) 2024 CyberMind.FR
. /lib/functions.sh
. /usr/share/libubox/jshn.sh
ICECAST_INIT="/etc/init.d/icecast"
EZSTREAM_INIT="/etc/init.d/ezstream"
DARKICE_INIT="/etc/init.d/darkice"
PLAYLIST_MGR="/usr/lib/ezstream/playlist-manager.sh"
SCHEDULER="/usr/lib/webradio/scheduler.sh"
CROWDSEC_INSTALL="/usr/lib/webradio/crowdsec-install.sh"
ICECAST_URL="http://127.0.0.1:8000"
CONFIG_WEBRADIO="webradio"
CONFIG_DARKICE="darkice"
CONFIG_ICECAST="icecast"
case "$1" in
list)
cat << 'EOF'
{
"status": {},
"listeners": {},
"playlist": {},
"logs": {"lines": 50},
"start": {"service": "all"},
"stop": {"service": "all"},
"restart": {"service": "all"},
"skip": {},
"reload": {},
"generate_playlist": {"shuffle": true},
"upload": {"filename": "", "data": ""},
"schedules": {},
"current_show": {},
"add_schedule": {"name": "", "start_time": "", "end_time": "", "days": "", "playlist": ""},
"update_schedule": {"slot": "", "enabled": true},
"delete_schedule": {"slot": ""},
"generate_cron": {},
"play_jingle": {"filename": ""},
"list_jingles": {},
"live_status": {},
"live_start": {},
"live_stop": {},
"list_audio_devices": {},
"security_status": {},
"install_crowdsec": {},
"generate_ssl_cert": {"hostname": ""},
"bridge_status": {},
"bridge_start": {},
"bridge_stop": {},
"bridge_setup": {"lyrion_server": "127.0.0.1"}
}
EOF
;;
call)
case "$2" in
status)
# Get Icecast status
local icecast_running=0
local ezstream_running=0
local icecast_pid=""
local ezstream_pid=""
local listeners=0
local current_song=""
local bitrate=""
local uptime=""
# Check Icecast
icecast_pid=$(pgrep -x icecast 2>/dev/null | head -1)
[ -n "$icecast_pid" ] && icecast_running=1
# Check ezstream
ezstream_pid=$(pgrep -x ezstream 2>/dev/null | head -1)
[ -n "$ezstream_pid" ] && ezstream_running=1
# Get Icecast stats if running
if [ "$icecast_running" = "1" ]; then
local stats=$(curl -s "${ICECAST_URL}/status-json.xsl" 2>/dev/null)
if [ -n "$stats" ]; then
listeners=$(echo "$stats" | jsonfilter -e '@.icestats.source.listeners' 2>/dev/null || echo "0")
current_song=$(echo "$stats" | jsonfilter -e '@.icestats.source.title' 2>/dev/null || echo "")
bitrate=$(echo "$stats" | jsonfilter -e '@.icestats.source.audio_bitrate' 2>/dev/null || echo "")
fi
fi
# Get config values
config_load icecast
local port hostname
config_get port server port "8000"
config_get hostname server hostname "localhost"
config_load ezstream
local shuffle
config_get shuffle playlist shuffle "1"
# Playlist info
local playlist_count=0
local music_dir="/srv/webradio/music"
config_get music_dir playlist directory "$music_dir"
[ -f "/srv/webradio/playlists/current.m3u" ] && \
playlist_count=$(wc -l < /srv/webradio/playlists/current.m3u 2>/dev/null || echo 0)
cat << EOF
{
"icecast": {
"running": $([ "$icecast_running" = "1" ] && echo "true" || echo "false"),
"pid": "$icecast_pid",
"port": $port,
"hostname": "$hostname"
},
"ezstream": {
"running": $([ "$ezstream_running" = "1" ] && echo "true" || echo "false"),
"pid": "$ezstream_pid"
},
"stream": {
"listeners": ${listeners:-0},
"current_song": "$current_song",
"bitrate": "$bitrate"
},
"playlist": {
"tracks": $playlist_count,
"shuffle": $([ "$shuffle" = "1" ] && echo "true" || echo "false"),
"directory": "$music_dir"
},
"url": "http://$hostname:$port/live"
}
EOF
;;
listeners)
local stats=$(curl -s "${ICECAST_URL}/status-json.xsl" 2>/dev/null)
if [ -n "$stats" ]; then
local listeners=$(echo "$stats" | jsonfilter -e '@.icestats.source.listeners' 2>/dev/null || echo "0")
local peak=$(echo "$stats" | jsonfilter -e '@.icestats.source.listener_peak' 2>/dev/null || echo "0")
echo "{\"current\": $listeners, \"peak\": $peak}"
else
echo '{"current": 0, "peak": 0, "error": "Icecast not responding"}'
fi
;;
playlist)
local playlist_file="/srv/webradio/playlists/current.m3u"
local tracks="[]"
if [ -f "$playlist_file" ]; then
# Get first 50 tracks
tracks=$(head -50 "$playlist_file" | while read -r track; do
local name=$(basename "$track")
echo "{\"path\": \"$track\", \"name\": \"$name\"}"
done | paste -sd, | sed 's/^/[/;s/$/]/')
fi
local total=$(wc -l < "$playlist_file" 2>/dev/null || echo 0)
echo "{\"total\": $total, \"tracks\": $tracks}"
;;
logs)
read -r input
local lines=$(echo "$input" | jsonfilter -e '@.lines' 2>/dev/null)
lines=${lines:-50}
local log_content=""
if [ -f "/var/log/icecast/error.log" ]; then
log_content=$(tail -n "$lines" /var/log/icecast/error.log 2>/dev/null | \
sed 's/"/\\"/g' | paste -sd'\n')
fi
echo "{\"lines\": $lines, \"content\": \"$log_content\"}"
;;
start)
read -r input
local service=$(echo "$input" | jsonfilter -e '@.service' 2>/dev/null)
service=${service:-all}
local result="ok"
case "$service" in
icecast)
$ICECAST_INIT start 2>&1 || result="failed"
;;
ezstream)
$EZSTREAM_INIT start 2>&1 || result="failed"
;;
all|*)
$ICECAST_INIT start 2>&1 || result="failed"
sleep 2
$EZSTREAM_INIT start 2>&1 || result="failed"
;;
esac
echo "{\"result\": \"$result\", \"service\": \"$service\"}"
;;
stop)
read -r input
local service=$(echo "$input" | jsonfilter -e '@.service' 2>/dev/null)
service=${service:-all}
local result="ok"
case "$service" in
icecast)
$ICECAST_INIT stop 2>&1 || result="failed"
;;
ezstream)
$EZSTREAM_INIT stop 2>&1 || result="failed"
;;
all|*)
$EZSTREAM_INIT stop 2>&1
$ICECAST_INIT stop 2>&1 || result="failed"
;;
esac
echo "{\"result\": \"$result\", \"service\": \"$service\"}"
;;
restart)
read -r input
local service=$(echo "$input" | jsonfilter -e '@.service' 2>/dev/null)
service=${service:-all}
local result="ok"
case "$service" in
icecast)
$ICECAST_INIT restart 2>&1 || result="failed"
;;
ezstream)
$EZSTREAM_INIT restart 2>&1 || result="failed"
;;
all|*)
$EZSTREAM_INIT stop 2>&1
$ICECAST_INIT restart 2>&1 || result="failed"
sleep 2
$EZSTREAM_INIT start 2>&1 || result="failed"
;;
esac
echo "{\"result\": \"$result\", \"service\": \"$service\"}"
;;
skip)
local pid=$(cat /var/run/ezstream.pid 2>/dev/null)
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
kill -USR1 "$pid"
echo '{"result": "ok", "action": "skip"}'
else
echo '{"result": "failed", "error": "ezstream not running"}'
fi
;;
reload)
$ICECAST_INIT reload 2>&1
$EZSTREAM_INIT reload 2>&1
echo '{"result": "ok", "action": "reload"}'
;;
generate_playlist)
read -r input
local shuffle=$(echo "$input" | jsonfilter -e '@.shuffle' 2>/dev/null)
if [ "$shuffle" = "false" ]; then
uci set ezstream.playlist.shuffle='0'
else
uci set ezstream.playlist.shuffle='1'
fi
uci commit ezstream
if [ -x "$PLAYLIST_MGR" ]; then
local output=$($PLAYLIST_MGR generate 2>&1)
local count=$(wc -l < /srv/webradio/playlists/current.m3u 2>/dev/null || echo 0)
echo "{\"result\": \"ok\", \"tracks\": $count, \"message\": \"$output\"}"
else
echo '{"result": "failed", "error": "playlist manager not found"}'
fi
;;
upload)
read -r input
local filename=$(echo "$input" | jsonfilter -e '@.filename' 2>/dev/null)
local data=$(echo "$input" | jsonfilter -e '@.data' 2>/dev/null)
if [ -n "$filename" ] && [ -n "$data" ]; then
local dest="/srv/webradio/music/$filename"
echo "$data" | base64 -d > "$dest" 2>/dev/null
if [ -f "$dest" ]; then
echo "{\"result\": \"ok\", \"filename\": \"$filename\", \"path\": \"$dest\"}"
else
echo '{"result": "failed", "error": "write failed"}'
fi
else
echo '{"result": "failed", "error": "missing filename or data"}'
fi
;;
schedules)
config_load "$CONFIG_WEBRADIO"
local scheduling_enabled timezone
config_get scheduling_enabled scheduling enabled "0"
config_get timezone scheduling timezone "UTC"
# Build schedules array
local schedules="["
local first=1
list_schedule_cb() {
local slot="$1"
local slot_name enabled start_time end_time days playlist jingle_before jingle_after crossfade
config_get slot_name "$slot" name "$slot"
config_get enabled "$slot" enabled "0"
config_get start_time "$slot" start_time ""
config_get end_time "$slot" end_time ""
config_get days "$slot" days "0123456"
config_get playlist "$slot" playlist ""
config_get jingle_before "$slot" jingle_before ""
config_get jingle_after "$slot" jingle_after ""
config_get crossfade "$slot" crossfade "0"
[ "$first" = "1" ] || schedules="$schedules,"
first=0
schedules="$schedules{\"slot\":\"$slot\",\"name\":\"$slot_name\",\"enabled\":$([ "$enabled" = "1" ] && echo true || echo false),\"start_time\":\"$start_time\",\"end_time\":\"$end_time\",\"days\":\"$days\",\"playlist\":\"$playlist\",\"jingle_before\":\"$jingle_before\",\"jingle_after\":\"$jingle_after\",\"crossfade\":$crossfade}"
}
config_foreach list_schedule_cb schedule
schedules="$schedules]"
cat << EOF
{
"scheduling_enabled": $([ "$scheduling_enabled" = "1" ] && echo true || echo false),
"timezone": "$timezone",
"schedules": $schedules
}
EOF
;;
current_show)
if [ -x "$SCHEDULER" ]; then
$SCHEDULER current
else
echo '{"name": "Default", "slot": "", "start": "", "playlist": "current"}'
fi
;;
add_schedule)
read -r input
local name=$(echo "$input" | jsonfilter -e '@.name' 2>/dev/null)
local start_time=$(echo "$input" | jsonfilter -e '@.start_time' 2>/dev/null)
local end_time=$(echo "$input" | jsonfilter -e '@.end_time' 2>/dev/null)
local days=$(echo "$input" | jsonfilter -e '@.days' 2>/dev/null)
local playlist=$(echo "$input" | jsonfilter -e '@.playlist' 2>/dev/null)
local jingle_before=$(echo "$input" | jsonfilter -e '@.jingle_before' 2>/dev/null)
if [ -z "$name" ] || [ -z "$start_time" ]; then
echo '{"result": "failed", "error": "name and start_time required"}'
exit 0
fi
# Generate slot ID from name
local slot=$(echo "$name" | tr '[:upper:] ' '[:lower:]_' | tr -cd 'a-z0-9_')
uci set "$CONFIG_WEBRADIO.$slot=schedule"
uci set "$CONFIG_WEBRADIO.$slot.name=$name"
uci set "$CONFIG_WEBRADIO.$slot.enabled=0"
uci set "$CONFIG_WEBRADIO.$slot.start_time=$start_time"
[ -n "$end_time" ] && uci set "$CONFIG_WEBRADIO.$slot.end_time=$end_time"
[ -n "$days" ] && uci set "$CONFIG_WEBRADIO.$slot.days=$days" || uci set "$CONFIG_WEBRADIO.$slot.days=0123456"
[ -n "$playlist" ] && uci set "$CONFIG_WEBRADIO.$slot.playlist=$playlist"
[ -n "$jingle_before" ] && uci set "$CONFIG_WEBRADIO.$slot.jingle_before=$jingle_before"
uci commit "$CONFIG_WEBRADIO"
echo "{\"result\": \"ok\", \"slot\": \"$slot\"}"
;;
update_schedule)
read -r input
local slot=$(echo "$input" | jsonfilter -e '@.slot' 2>/dev/null)
local enabled=$(echo "$input" | jsonfilter -e '@.enabled' 2>/dev/null)
local name=$(echo "$input" | jsonfilter -e '@.name' 2>/dev/null)
local start_time=$(echo "$input" | jsonfilter -e '@.start_time' 2>/dev/null)
local end_time=$(echo "$input" | jsonfilter -e '@.end_time' 2>/dev/null)
local days=$(echo "$input" | jsonfilter -e '@.days' 2>/dev/null)
local playlist=$(echo "$input" | jsonfilter -e '@.playlist' 2>/dev/null)
local jingle_before=$(echo "$input" | jsonfilter -e '@.jingle_before' 2>/dev/null)
if [ -z "$slot" ]; then
echo '{"result": "failed", "error": "slot required"}'
exit 0
fi
# Check if slot exists
local existing=$(uci -q get "$CONFIG_WEBRADIO.$slot" 2>/dev/null)
if [ -z "$existing" ]; then
echo '{"result": "failed", "error": "slot not found"}'
exit 0
fi
# Update fields
[ "$enabled" = "true" ] && uci set "$CONFIG_WEBRADIO.$slot.enabled=1"
[ "$enabled" = "false" ] && uci set "$CONFIG_WEBRADIO.$slot.enabled=0"
[ -n "$name" ] && uci set "$CONFIG_WEBRADIO.$slot.name=$name"
[ -n "$start_time" ] && uci set "$CONFIG_WEBRADIO.$slot.start_time=$start_time"
[ -n "$end_time" ] && uci set "$CONFIG_WEBRADIO.$slot.end_time=$end_time"
[ -n "$days" ] && uci set "$CONFIG_WEBRADIO.$slot.days=$days"
[ -n "$playlist" ] && uci set "$CONFIG_WEBRADIO.$slot.playlist=$playlist"
[ -n "$jingle_before" ] && uci set "$CONFIG_WEBRADIO.$slot.jingle_before=$jingle_before"
uci commit "$CONFIG_WEBRADIO"
# Regenerate cron if scheduling enabled
local sched_enabled=$(uci -q get "$CONFIG_WEBRADIO.scheduling.enabled")
[ "$sched_enabled" = "1" ] && [ -x "$SCHEDULER" ] && $SCHEDULER generate >/dev/null 2>&1
echo '{"result": "ok"}'
;;
delete_schedule)
read -r input
local slot=$(echo "$input" | jsonfilter -e '@.slot' 2>/dev/null)
if [ -z "$slot" ]; then
echo '{"result": "failed", "error": "slot required"}'
exit 0
fi
uci delete "$CONFIG_WEBRADIO.$slot" 2>/dev/null
uci commit "$CONFIG_WEBRADIO"
# Regenerate cron
[ -x "$SCHEDULER" ] && $SCHEDULER generate >/dev/null 2>&1
echo '{"result": "ok"}'
;;
generate_cron)
if [ -x "$SCHEDULER" ]; then
$SCHEDULER generate 2>&1
echo '{"result": "ok"}'
else
echo '{"result": "failed", "error": "scheduler not found"}'
fi
;;
play_jingle)
read -r input
local filename=$(echo "$input" | jsonfilter -e '@.filename' 2>/dev/null)
if [ -z "$filename" ]; then
echo '{"result": "failed", "error": "filename required"}'
exit 0
fi
if [ -x "$SCHEDULER" ]; then
$SCHEDULER jingle "$filename" 2>&1
echo '{"result": "ok", "filename": "'"$filename"'"}'
else
echo '{"result": "failed", "error": "scheduler not found"}'
fi
;;
list_jingles)
local jingle_dir="/srv/webradio/jingles"
config_load "$CONFIG_WEBRADIO"
config_get jingle_dir jingles directory "$jingle_dir"
local jingles="[]"
if [ -d "$jingle_dir" ]; then
jingles=$(find "$jingle_dir" -type f \( -name "*.mp3" -o -name "*.ogg" -o -name "*.wav" \) 2>/dev/null | while read -r f; do
local name=$(basename "$f")
local size=$(ls -lh "$f" 2>/dev/null | awk '{print $5}')
echo "{\"name\":\"$name\",\"path\":\"$f\",\"size\":\"$size\"}"
done | paste -sd, | sed 's/^/[/;s/$/]/')
[ -z "$jingles" ] || [ "$jingles" = "[]" ] && jingles="[]"
fi
echo "{\"directory\": \"$jingle_dir\", \"jingles\": $jingles}"
;;
live_status)
local darkice_running=0
local darkice_pid=""
local device=""
local enabled=""
# Check DarkIce process
darkice_pid=$(pgrep darkice 2>/dev/null | head -1)
[ -n "$darkice_pid" ] && darkice_running=1
# Get config values
config_load "$CONFIG_DARKICE"
config_get device input device "hw:0,0"
config_get enabled main enabled "0"
cat << EOF
{
"running": $([ "$darkice_running" = "1" ] && echo "true" || echo "false"),
"pid": "$darkice_pid",
"enabled": $([ "$enabled" = "1" ] && echo "true" || echo "false"),
"device": "$device"
}
EOF
;;
live_start)
local result="ok"
local error=""
# Check if DarkIce init exists
if [ -x "$DARKICE_INIT" ]; then
# Enable and start DarkIce
uci set "$CONFIG_DARKICE.main.enabled=1"
uci commit "$CONFIG_DARKICE"
$DARKICE_INIT start 2>&1 || {
result="failed"
error="failed to start darkice"
}
else
result="failed"
error="darkice not installed"
fi
if [ "$result" = "ok" ]; then
echo '{"result": "ok"}'
else
echo "{\"result\": \"failed\", \"error\": \"$error\"}"
fi
;;
live_stop)
local result="ok"
if [ -x "$DARKICE_INIT" ]; then
$DARKICE_INIT stop 2>&1
fi
# Also try to kill any running darkice process
pkill darkice 2>/dev/null
echo '{"result": "ok"}'
;;
list_audio_devices)
local devices="[]"
# Parse ALSA devices from /proc/asound/cards
if [ -f /proc/asound/cards ]; then
devices=$(cat /proc/asound/cards 2>/dev/null | grep -E '^\s*[0-9]+' | while read -r line; do
local card_num=$(echo "$line" | awk '{print $1}')
local card_name=$(echo "$line" | sed 's/^[[:space:]]*[0-9]*[[:space:]]*\[[^]]*\]:[[:space:]]*//')
# Check if card has capture capability
if [ -d "/proc/asound/card$card_num" ]; then
echo "{\"device\":\"hw:$card_num,0\",\"name\":\"$card_name\",\"type\":\"capture\"}"
fi
done | paste -sd, | sed 's/^/[/;s/$/]/')
fi
# Fallback if no devices found
[ -z "$devices" ] || [ "$devices" = "[]" ] && devices="[]"
echo "{\"devices\": $devices}"
;;
security_status)
local ssl_enabled ssl_cert ssl_key ssl_port
local crowdsec_installed=false
local crowdsec_parsers=false
local crowdsec_scenarios=false
local crowdsec_decisions=0
local ssl_cert_exists=false
local ssl_cert_expiry=""
# Load SSL config
config_load "$CONFIG_ICECAST"
config_get ssl_enabled ssl enabled "0"
config_get ssl_cert ssl certificate "/etc/ssl/certs/icecast.pem"
config_get ssl_key ssl key "/etc/ssl/private/icecast.key"
config_get ssl_port ssl port "8443"
# Check if cert exists
[ -f "$ssl_cert" ] && ssl_cert_exists=true
# Get cert expiry if exists
if [ "$ssl_cert_exists" = "true" ] && command -v openssl >/dev/null 2>&1; then
ssl_cert_expiry=$(openssl x509 -in "$ssl_cert" -noout -enddate 2>/dev/null | cut -d= -f2)
fi
# Check CrowdSec
command -v crowdsec >/dev/null 2>&1 && crowdsec_installed=true
# Check for Icecast parsers
[ -f /etc/crowdsec/parsers/s01-parse/icecast-logs.yaml ] && crowdsec_parsers=true
# Check for Icecast scenarios
[ -f /etc/crowdsec/scenarios/icecast-flood.yaml ] && crowdsec_scenarios=true
# Get active decisions count
if [ "$crowdsec_installed" = "true" ] && command -v cscli >/dev/null 2>&1; then
crowdsec_decisions=$(cscli decisions list -o json 2>/dev/null | jsonfilter -e '@[*]' 2>/dev/null | wc -l || echo 0)
fi
cat << EOF
{
"ssl_enabled": $([ "$ssl_enabled" = "1" ] && echo "true" || echo "false"),
"ssl_port": $ssl_port,
"ssl_cert_path": "$ssl_cert",
"ssl_key_path": "$ssl_key",
"ssl_cert_exists": $ssl_cert_exists,
"ssl_cert_expiry": "$ssl_cert_expiry",
"crowdsec_installed": $crowdsec_installed,
"crowdsec_parsers": $crowdsec_parsers,
"crowdsec_scenarios": $crowdsec_scenarios,
"crowdsec_decisions": $crowdsec_decisions
}
EOF
;;
install_crowdsec)
if [ -x "$CROWDSEC_INSTALL" ]; then
local output=$($CROWDSEC_INSTALL install 2>&1)
echo '{"result": "ok", "output": "'"$(echo "$output" | tr '\n' ' ')"'"}'
else
echo '{"result": "failed", "error": "crowdsec-install.sh not found"}'
fi
;;
generate_ssl_cert)
read -r input
local hostname=$(echo "$input" | jsonfilter -e '@.hostname' 2>/dev/null)
hostname=${hostname:-localhost}
local cert_dir="/etc/ssl/certs"
local key_dir="/etc/ssl/private"
local cert_file="$cert_dir/icecast.pem"
local key_file="$key_dir/icecast.key"
mkdir -p "$cert_dir" "$key_dir"
# Generate self-signed certificate
if command -v openssl >/dev/null 2>&1; then
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout "$key_file" \
-out "$cert_file" \
-subj "/CN=$hostname/O=WebRadio/C=FR" 2>&1
if [ -f "$cert_file" ] && [ -f "$key_file" ]; then
chmod 644 "$cert_file"
chmod 600 "$key_file"
chown root:icecast "$key_file" 2>/dev/null
# Update UCI config
uci set "$CONFIG_ICECAST.ssl=ssl"
uci set "$CONFIG_ICECAST.ssl.certificate=$cert_file"
uci set "$CONFIG_ICECAST.ssl.key=$key_file"
uci commit "$CONFIG_ICECAST"
echo '{"result": "ok", "cert": "'"$cert_file"'", "key": "'"$key_file"'"}'
else
echo '{"result": "failed", "error": "certificate generation failed"}'
fi
else
echo '{"result": "failed", "error": "openssl not installed"}'
fi
;;
bridge_status)
json_init
# Check Lyrion server
local lyrion_server=$(uci -q get lyrion-bridge.main.lyrion_server 2>/dev/null || echo "127.0.0.1")
local lyrion_port=$(uci -q get lyrion-bridge.main.lyrion_port 2>/dev/null || echo "9000")
local lyrion_online=0
if curl -s "http://${lyrion_server}:${lyrion_port}/status.html" >/dev/null 2>&1; then
lyrion_online=1
fi
# Check Squeezelite
local squeezelite_running=0
pgrep -f "squeezelite" >/dev/null 2>&1 && squeezelite_running=1
# Check FFmpeg bridge
local ffmpeg_running=0
pgrep -f "ffmpeg-bridge.sh" >/dev/null 2>&1 && ffmpeg_running=1
pgrep -f "ffmpeg.*lyrion" >/dev/null 2>&1 && ffmpeg_running=1
# Check Icecast mount
local icecast_host=$(uci -q get lyrion-bridge.icecast.host 2>/dev/null || echo "127.0.0.1")
local icecast_port=$(uci -q get lyrion-bridge.icecast.port 2>/dev/null || echo "8000")
local icecast_mount=$(uci -q get lyrion-bridge.icecast.mount 2>/dev/null || echo "/lyrion")
local mount_active=0
local listeners=0
local title=""
local artist=""
local mount_status=$(curl -s "http://${icecast_host}:${icecast_port}/status-json.xsl" 2>/dev/null)
if [ -n "$mount_status" ]; then
local sources=$(echo "$mount_status" | jsonfilter -e '@.icestats.source' 2>/dev/null)
if echo "$sources" | grep -q "lyrion"; then
mount_active=1
listeners=$(echo "$mount_status" | jsonfilter -e '@.icestats.source[@.listenurl="*lyrion*"].listeners' 2>/dev/null || echo "0")
fi
fi
# Get now playing from Lyrion
if [ "$lyrion_online" = "1" ]; then
local np=$(curl -s "http://${lyrion_server}:${lyrion_port}/jsonrpc.js" \
-d '{"id":1,"method":"slim.request","params":["",[\"status\",\"-\",1,\"tags:adl\"]]}' 2>/dev/null)
if [ -n "$np" ]; then
title=$(echo "$np" | jsonfilter -e '@.result.playlist_loop[0].title' 2>/dev/null || echo "")
artist=$(echo "$np" | jsonfilter -e '@.result.playlist_loop[0].artist' 2>/dev/null || echo "")
fi
fi
json_add_boolean lyrion_online "$lyrion_online"
json_add_string lyrion_server "$lyrion_server"
json_add_boolean squeezelite_running "$squeezelite_running"
json_add_boolean ffmpeg_running "$ffmpeg_running"
json_add_boolean bridge_running "$ffmpeg_running"
json_add_boolean mount_active "$mount_active"
json_add_int listeners "$listeners"
json_add_string title "$title"
json_add_string artist "$artist"
json_add_string stream_url "http://${icecast_host}:${icecast_port}${icecast_mount}"
json_dump
;;
bridge_start)
if [ -x /usr/sbin/lyrionstreamctl ]; then
/usr/sbin/lyrionstreamctl start >/dev/null 2>&1
echo '{"result": "ok"}'
else
echo '{"result": "error", "error": "lyrionstreamctl not found"}'
fi
;;
bridge_stop)
if [ -x /usr/sbin/lyrionstreamctl ]; then
/usr/sbin/lyrionstreamctl stop >/dev/null 2>&1
echo '{"result": "ok"}'
else
echo '{"result": "error", "error": "lyrionstreamctl not found"}'
fi
;;
bridge_setup)
read -r input
local lyrion_server=$(echo "$input" | jsonfilter -e '@.lyrion_server' 2>/dev/null)
lyrion_server=${lyrion_server:-127.0.0.1}
if [ -x /usr/sbin/lyrionstreamctl ]; then
output=$(/usr/sbin/lyrionstreamctl setup "$lyrion_server" 2>&1)
local icecast_port=$(uci -q get lyrion-bridge.icecast.port 2>/dev/null || echo "8000")
local icecast_mount=$(uci -q get lyrion-bridge.icecast.mount 2>/dev/null || echo "/lyrion")
echo '{"result": "ok", "stream_url": "http://127.0.0.1:'"$icecast_port$icecast_mount"'"}'
else
echo '{"result": "error", "error": "lyrionstreamctl not found"}'
fi
;;
*)
echo '{"error": "unknown method"}'
;;
esac
;;
esac