radio/install_for_jruby

210 lines
7.1 KiB
Shell
Executable file

#!/bin/bash
# install_for_jruby - Installer for the JRuby radio automation stack.
#
# Derives its service name from the directory it lives in, e.g.
# /srv/radio/install_for_jruby -> radio.service
# Prompts for a storage root and writes a complete config.json.
# Creates media subdirectories (music, podcasts, jingles, announcements),
# state/ and logs/ under the storage root, drops and recreates all
# database files, installs gems one at a time, generates a systemd unit,
# and adds cron entries to the existing liquidsoap user's crontab.
set -euo pipefail
INSTALL_DIR="$(cd "$(dirname "$0")" && pwd)"
SERVICE_BASENAME="$(basename "${INSTALL_DIR}")"
SERVICE_NAME="${SERVICE_BASENAME}.service"
UNIT_PATH="/etc/systemd/system/${SERVICE_NAME}"
CONFIG_JSON="${INSTALL_DIR}/config.json"
GEMS_DIR="${INSTALL_DIR}/.gems"
JRUBY_BIN=""
echo "=== ${SERVICE_BASENAME} installer ==="
echo
# --- Detect JRuby -----------------------------------------------------------
if command -v jruby >/dev/null 2>&1; then
JRUBY_BIN="$(command -v jruby)"
elif [ -x /opt/jruby/bin/jruby ]; then
JRUBY_BIN="/opt/jruby/bin/jruby"
else
echo "ERROR: jruby not found on PATH or at /opt/jruby/bin/jruby." >&2
exit 1
fi
echo "==> Using JRuby: ${JRUBY_BIN}"
echo
# --- Prompt for storage root ------------------------------------------------
STORAGE_ROOT_DEFAULT="/mnt/storage/radio"
printf "Storage root [%s]: " "${STORAGE_ROOT_DEFAULT}"
read -r STORAGE_ROOT_INPUT
STORAGE_ROOT="${STORAGE_ROOT_INPUT:-${STORAGE_ROOT_DEFAULT}}"
echo "==> Storage root: ${STORAGE_ROOT}"
# --- Create media and state directories -------------------------------------
for d in music podcasts jingles announcements state logs; do
mkdir -p "${STORAGE_ROOT}/${d}"
done
chown -R liquidsoap:liquidsoap "${STORAGE_ROOT}" 2>/dev/null || true
echo "==> Media/state/log directories created under ${STORAGE_ROOT}"
# --- Drop and recreate databases --------------------------------------------
rm -f "${STORAGE_ROOT}/state/subscriptions.db" \
"${STORAGE_ROOT}/state/played.db" \
"${STORAGE_ROOT}/state/subscriptions.db-wal" \
"${STORAGE_ROOT}/state/subscriptions.db-shm" \
"${STORAGE_ROOT}/state/played.db-wal" \
"${STORAGE_ROOT}/state/played.db-shm"
echo "==> Databases dropped (will be recreated now)"
# Initialize database schemas
sqlite3 "${STORAGE_ROOT}/state/subscriptions.db" <<'SQL'
CREATE TABLE IF NOT EXISTS shows (
guid TEXT PRIMARY KEY,
slug TEXT UNIQUE NOT NULL,
title TEXT,
feed_url TEXT UNIQUE NOT NULL,
archive INTEGER DEFAULT 0,
opml_import INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
SQL
sqlite3 "${STORAGE_ROOT}/state/played.db" <<'SQL'
CREATE TABLE IF NOT EXISTS episodes (
guid TEXT PRIMARY KEY,
show_guid TEXT NOT NULL REFERENCES shows(guid),
title TEXT,
url TEXT,
duration_seconds INTEGER,
played INTEGER DEFAULT 0,
local_path TEXT,
file_size_bytes INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
SQL
echo "Database schemas initialized."
# --- Install gems one at a time ---------------------------------------------
export GEM_HOME="${GEMS_DIR}"
export GEM_PATH="${GEMS_DIR}"
mkdir -p "${GEMS_DIR}"
for gem_name in sequel jdbc-sqlite3 json nokogiri; do
echo "==> Installing gem: ${gem_name}"
if ! "${JRUBY_BIN}" -S gem install --local "${gem_name}" 2>/dev/null; then
"${JRUBY_BIN}" -S gem install "${gem_name}"
fi
done
# --- Write complete config.json ----------------------------------------------
ICECAST_HOST_DEFAULT="192.168.0.200"
ICECAST_PORT_DEFAULT="7777"
ICECAST_MOUNT_DEFAULT="/data"
ICECAST_SOURCE_USER_DEFAULT="source"
GPODDER_ENABLE_DEFAULT="false"
GPODDER_HOST_DEFAULT="gpodder.net"
printf "Icecast host [%s]: " "${ICECAST_HOST_DEFAULT}"
read -r ICECAST_HOST_INPUT; ICECAST_HOST="${ICECAST_HOST_INPUT:-${ICECAST_HOST_DEFAULT}}"
printf "Icecast source port [%s]: " "${ICECAST_PORT_DEFAULT}"
read -r ICECAST_PORT_INPUT; ICECAST_PORT="${ICECAST_PORT_INPUT:-${ICECAST_PORT_DEFAULT}}"
printf "Mount point [%s]: " "${ICECAST_MOUNT_DEFAULT}"
read -r ICECAST_MOUNT_INPUT; ICECAST_MOUNT="${ICECAST_MOUNT_INPUT:-${ICECAST_MOUNT_DEFAULT}}"
printf "Source username [%s]: " "${ICECAST_SOURCE_USER_DEFAULT}"
read -r ICECAST_SRC_USER_INPUT; ICECAST_SRC_USER="${ICECAST_SRC_USER_INPUT:-${ICECAST_SOURCE_USER_DEFAULT}}"
printf "Source password: "
read -rs ICECAST_SRC_PASS
echo
printf "gPodder sync enabled? (y/n) [n]: "
read -r GP_SYNC_INPUT
case "${GP_SYNC_INPUT,,}" in
y|yes) GP_ENABLED=true ;;
*) GP_ENABLED=false ;;
esac
GP_HOST="${GPODDER_HOST_DEFAULT}"
GP_USERNAME=""
GP_PASSWORD=""
GP_DEVICE_ID=""
if [ "${GP_ENABLED}" = "true" ]; then
printf "gPodder username: "
read -r GP_USERNAME
printf "gPodder password: "
read -rs GP_PASSWORD
echo
printf "gPodder device ID (leave blank to generate): "
read -r GP_DEVICE_ID
if [ -z "${GP_DEVICE_ID}" ]; then
GP_DEVICE_ID="radio-$(date +%s)-$$"
fi
fi
cat > "${CONFIG_JSON}" <<EOF
{
"storage": "${STORAGE_ROOT}",
"icecast": {
"host": "${ICECAST_HOST}",
"port": ${ICECAST_PORT},
"mount": "${ICECAST_MOUNT}",
"source_username": "${ICECAST_SRC_USER}",
"source_password": "${ICECAST_SRC_PASS}"
},
"gpodder": {
"enable": ${GP_ENABLED},
"host": "${GP_HOST}",
"username": "${GP_USERNAME}",
"password": "${GP_PASSWORD}",
"device_id": "${GP_DEVICE_ID}"
}
}
EOF
chmod 600 "${CONFIG_JSON}"
echo "==> Wrote ${CONFIG_JSON}"
# --- Generate systemd unit ----------------------------------------------------
cat > "${UNIT_PATH}" <<EOF
[Unit]
Description=${SERVICE_BASENAME} (JRuby radio automation)
After=network-online.target sound.target
Wants=network-online.target
[Service]
Type=simple
User=liquidsoap
WorkingDirectory=${INSTALL_DIR}
Environment=GEM_HOME=${GEMS_DIR}
Environment=GEM_PATH=${GEMS_DIR}
ExecStart=${JRUBY_BIN} ${INSTALL_DIR}/station.rb
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
echo "==> Installed ${UNIT_PATH}"
# After the mkdir -p "$STORAGE/state" line, add:
mkdir -p "$STORAGE/state"
chown -R liquidsoap:liquidsoap "$STORAGE/state"
chmod 755 "$STORAGE/state"
chmod 664 "$STORAGE/state/"*.db 2>/dev/null || true
# --- Cron entries (liquidsoap user's crontab already exists from package) -----
CRON_FETCH="0 * * * * cd ${INSTALL_DIR} && ${INSTALL_DIR}/run_radio.sh fetch_podcasts.rb --fetch-all >> ${STORAGE_ROOT}/logs/fetch_cron.log 2>&1"
CRON_UPDATE="30 * * * * cd ${INSTALL_DIR} && ${INSTALL_DIR}/run_radio.sh update_playlist.rb >> ${STORAGE_ROOT}/logs/update_cron.log 2>&1"
CURRENT_CRONTAB=$(crontab -u liquidsoap -l 2>/dev/null || true)
NEW_CRONTAB="${CURRENT_CRONTAB}"
if ! echo "${NEW_CRONTAB}" | grep -qF "fetch_podcasts.rb --fetch-all"; then
NEW_CRONTAB="${NEW_CRONTAB}${NEW_CRONTAB:+$'\n'}${CRON_FETCH}"
fi
if ! echo "${NEW_CRONTAB}" | grep -qF "update_playlist.rb"; then
NEW_CRONTAB="${NEW_CRONTAB}${NEW_CRONTAB:+$'\n'}${CRON_UPDATE}"
fi
echo "${NEW_CRONTAB}" | crontab -u liquidsoap -
echo "==> Cron entries installed for user liquidsoap"
echo
echo "==> Done. Review ${CONFIG_JSON}, then:"
echo " systemctl start ${SERVICE_NAME}"