radio/install_for_jruby
2026-09-01 15:27:17 -07:00

141 lines
5.4 KiB
Shell
Executable file

#!/usr/bin/env bash
#
# install_for_jruby.sh - Installs the JRuby-based radio automation stack.
# Same layout as the Python installer but invokes jruby for the scripts.
# Derives its service name from the containing directory, prompts for a
# storage path and Icecast host/port/mount/credentials, writes config.json,
# creates the systemd unit and cron jobs.
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DIR_NAME="$(basename "$SCRIPT_DIR")"
SERVICE_NAME="${DIR_NAME}"
SYSTEMD_UNIT="/etc/systemd/system/${SERVICE_NAME}.service"
CONFIG_FILE="${SCRIPT_DIR}/config.json"
JRuby_BIN="$(command -v jruby || echo /usr/local/bin/jruby)"
echo "=== ${SERVICE_NAME} installer (JRuby) ==="
echo "Install dir: ${SCRIPT_DIR}"
echo "Using jruby: ${JRuby_BIN}"
echo ""
# --- Verify required source files exist ------------------------------------
for req in station.liq fetch_podcasts.rb update_playlists.rb; do
if [ ! -f "${SCRIPT_DIR}/${req}" ]; then
echo "ERROR: required file '${req}' not found in ${SCRIPT_DIR}" >&2
exit 1
fi
done
# --- Prompt for storage path ---------------------------------------------
DEFAULT_STORAGE="/srv/radio-storage"
read -rp "Enter storage path for media/state/logs [${DEFAULT_STORAGE}]: " STORAGE_PATH
STORAGE_PATH="${STORAGE_PATH:-$DEFAULT_STORAGE}"
mkdir -p "${STORAGE_PATH}"/{music,podcasts,jingles,announcements,state,playlists,logs}
# Media dirs are read by the liquidsoap user at stream time
chown -R liquidsoap:liquidsoap "${STORAGE_PATH}"/{music,podcasts,jingles,announcements} 2>/dev/null || true
# state/ and playlists/ are written by the cron jobs (run as root) and read by liquidsoap
chown root:liquidsoap "${STORAGE_PATH}"/{state,playlists} 2>/dev/null || true
chmod 775 "${STORAGE_PATH}"/{state,playlists} 2>/dev/null || true
# logs/ is appended to by cron
chown root:liquidsoap "${STORAGE_PATH}/logs" 2>/dev/null || true
chmod 775 "${STORAGE_PATH}/logs" 2>/dev/null || true
# --- Collect Icecast credentials ------------------------------------------
read -rp "Icecast host [127.0.0.1]: " IC_HOST
IC_HOST="${IC_HOST:-127.0.0.1}"
read -rp "Icecast source port [7777]: " IC_PORT
IC_PORT="${IC_PORT:-7777}"
read -rp "Icecast mount [/radio.mp3]: " IC_MOUNT
IC_MOUNT="${IC_MOUNT:-/radio.mp3}"
read -rp "Icecast source username [sourceadmin]: " IC_USER
IC_USER="${IC_USER:-sourceadmin}"
read -rsp "Icecast source password: " IC_PASS; echo
IC_PASS="${IC_PASS:?Password required}"
# --- gPodder sync (optional) ----------------------------------------------
read -rp "Enable gPodder.net sync? [y/N]: " GPODDER_ENABLE
case "${GPODDER_ENABLE,,}" in
y|yes)
read -rp "gPodder host [https://gpodder.net]: " GP_HOST
GP_HOST="${GP_HOST:-https://gpodder.net}"
# Strip trailing slash if the user typed one
GP_HOST="${GP_HOST%/}"
read -rp "gPodder username: " GP_USER
GP_USER="${GP_USER:?gPodder username required}"
read -rsp "gPodder password: " GP_PASS; echo
GP_PASS="${GP_PASS:?gPodder password required}"
GP_JSON=$(jq -n --arg h "$GP_HOST" --arg u "$GP_USER" --arg p "$GP_PASS" \
'{enable:true, host:$h, username:$u, password:$p}')
;;
*)
GP_JSON='{"enable":false,"host":"","username":"","password":""}'
;;
esac
# --- Write config.json -----------------------------------------------------
cat > "$CONFIG_FILE" <<EOF
{
"storage": "${STORAGE_PATH}",
"icecast": {
"host": "${IC_HOST}",
"port": ${IC_PORT},
"mount": "${IC_MOUNT}",
"username": "${IC_USER}",
"password": "${IC_PASS}"
},
"gpodder": ${GP_JSON}
}
EOF
chmod 600 "$CONFIG_FILE"
echo "Wrote ${CONFIG_FILE}"
# --- Gem check (install one at a time to avoid memory limits) --------------
# Use jdbc-sqlite3, not sqlite3: the latter needs a native C extension that
# fails to build on JRuby. jdbc-sqlite3 ships the SQLite JDBC driver as a JAR.
for gem in jdbc-sqlite3 json; do
if ! "${JRuby_BIN}" -e "require '${gem}'" >/dev/null 2>&1; then
echo "Installing gem: ${gem}"
"${JRuby_BIN}" -S gem install "${gem}"
fi
done
# --- Systemd unit ------------------------------------------------------------
cat > "$SYSTEMD_UNIT" <<EOF
[Unit]
Description=${SERVICE_NAME} radio automation (liquidsoap)
After=network.target icecast2.service
[Service]
User=liquidsoap
WorkingDirectory=${SCRIPT_DIR}
ExecStart=/usr/bin/liquidsoap ${SCRIPT_DIR}/station.liq
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable "${SERVICE_NAME}"
echo "Enabled systemd unit: ${SERVICE_NAME}"
# --- Cron --------------------------------------------------------------------
CRON_FETCH="0 * * * * cd ${SCRIPT_DIR} && ${JRuby_BIN} fetch_podcasts.rb >> ${STORAGE_PATH}/logs/cron.log 2>&1"
CRON_PLAYLISTS="30 * * * * cd ${SCRIPT_DIR} && ${JRuby_BIN} update_playlists.rb >> ${STORAGE_PATH}/logs/cron.log 2>&1"
# Guard against pipefail killing us when crontab is empty or has no matching lines
EXISTING_CRON=$(crontab -l 2>/dev/null | grep -v "fetch_podcasts.rb\|update_playlists.rb" || true)
( [ -n "$EXISTING_CRON" ] && echo "$EXISTING_CRON"; echo "$CRON_FETCH"; echo "$CRON_PLAYLISTS" ) | crontab -
echo "Installed cron jobs:"
echo " $CRON_FETCH"
echo " $CRON_PLAYLISTS"
echo ""
echo "=== Installation complete ==="
echo "Storage root: ${STORAGE_PATH}"
echo "JRuby: ${JRuby_BIN}"
echo "Start with: systemctl start ${SERVICE_NAME}"
echo "Test fetcher: ${JRuby_BIN} fetch_podcasts.rb --list-shows"