mirror of
https://github.com/mistergibson/radio.git
synced 2026-09-08 22:09:51 -07:00
142 lines
5.1 KiB
Shell
Executable file
142 lines
5.1 KiB
Shell
Executable file
#!/usr/bin/env bash
|
|
# install_for_python - Provision the Python-based radio automation stack.
|
|
# Must be run as root from within the target directory (e.g. /srv/radio/).
|
|
# Idempotent: safe to re-run.
|
|
|
|
set -euo pipefail
|
|
|
|
INSTALL_DIR="$(pwd)"
|
|
SERVICE_NAME="$(basename "$INSTALL_DIR")"
|
|
VENV="$INSTALL_DIR/.venv"
|
|
PYTHON_BIN="$VENV/bin/python"
|
|
|
|
echo "=== Radio Automation Installer (Python) ==="
|
|
echo "Install dir: $INSTALL_DIR"
|
|
echo "Service name: $SERVICE_NAME"
|
|
echo
|
|
|
|
# --- Prompt for config values (pre-fill from existing config.json) ---
|
|
EXISTING_CONFIG="$INSTALL_DIR/config.json"
|
|
declare -A CFG
|
|
CFG[ICE_HOST]="localhost"
|
|
CFG[ICE_PORT]="7777"
|
|
CFG[ICE_MOUNT]="/audio.mp3"
|
|
CFG[ICE_USER]="source"
|
|
CFG[ICE_PASS]=""
|
|
CFG[GPODDER_ENABLE]="false"
|
|
CFG[GPODDER_HOST]="https://gpodder.net"
|
|
CFG[GPODDER_USER]=""
|
|
CFG[GPODDER_PASS]=""
|
|
|
|
if [[ -f "$EXISTING_CONFIG" ]]; then
|
|
echo "Existing config.json found; using as defaults."
|
|
CFG[ICE_HOST]=$(jq -r '.icecast.host // "localhost"' "$EXISTING_CONFIG")
|
|
CFG[ICE_PORT]=$(jq -r '.icecast.port // 7777' "$EXISTING_CONFIG")
|
|
CFG[ICE_MOUNT]=$(jq -r '.icecast.mount // "/audio.mp3"' "$EXISTING_CONFIG")
|
|
CFG[ICE_USER]=$(jq -r '.icecast.username // "source"' "$EXISTING_CONFIG")
|
|
CFG[ICE_PASS]=$(jq -r '.icecast.password // ""' "$EXISTING_CONFIG")
|
|
CFG[GPODDER_ENABLE]=$(jq -r '.gpodder.enable // false' "$EXISTING_CONFIG")
|
|
CFG[GPODDER_HOST]=$(jq -r '.gpodder.host // "https://gpodder.net"' "$EXISTING_CONFIG")
|
|
CFG[GPODDER_USER]=$(jq -r '.gpodder.username // ""' "$EXISTING_CONFIG")
|
|
CFG[GPODDER_PASS]=$(jq -r '.gpodder.password // ""' "$EXISTING_CONFIG")
|
|
fi
|
|
|
|
read -rp "Icecast host [$CFG[ICE_HOST]]: " v; CFG[ICE_HOST]=${v:-$CFG[ICE_HOST]}
|
|
read -rp "Icecast port [$CFG[ICE_PORT]]: " v; CFG[ICE_PORT]=${v:-$CFG[ICE_PORT]}
|
|
read -rp "Icecast mount [$CFG[ICE_MOUNT]]: " v; CFG[ICE_MOUNT]=${v:-$CFG[ICE_MOUNT]}
|
|
read -rp "Icecast source username [$CFG[ICE_USER]]: " v; CFG[ICE_USER]=${v:-$CFG[ICE_USER]}
|
|
read -rsp "Icecast source password [$CFG[ICE_PASS]]: "; echo; CFG[ICE_PASS]=${v:-$CFG[ICE_PASS]}
|
|
read -rp "Enable gPodder sync? (true/false) [$CFG[GPODDER_ENABLE]]: " v; CFG[GPODDER_ENABLE]=${v:-$CFG[GPODDER_ENABLE]}
|
|
read -rp "gPodder host [$CFG[GPODDER_HOST]]: " v; CFG[GPODDER_HOST]=${v:-$CFG[GPODDER_HOST]}
|
|
read -rp "gPodder username [$CFG[GPODDER_USER]]: " v; CFG[GPODDER_USER]=${v:-$CFG[GPODDER_USER]}
|
|
read -rsp "gPodder password [$CFG[GPODDER_PASS]]: "; echo; CFG[GPODDER_PASS]=${v:-$CFG[GPODDER_PASS]}
|
|
|
|
echo
|
|
echo "Summary:"
|
|
echo " Icecast: ${CFG[ICE_HOST]}:${CFG[ICE_PORT]}${CFG[ICE_MOUNT]}"
|
|
echo " gPodder sync: ${CFG[GPODDER_ENABLE]}"
|
|
read -rp "Proceed? [y/N] " confirm
|
|
[[ "$confirm" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 0; }
|
|
|
|
# --- Install system packages ---
|
|
echo "Installing system packages..."
|
|
apt-get update -qq
|
|
apt-get install -y -qq python3 python3-venv python3-pip jq curl >/dev/null
|
|
|
|
# --- Create venv and install Python deps ---
|
|
if [[ ! -d "$VENV" ]]; then
|
|
echo "Creating virtualenv at $VENV..."
|
|
python3 -m venv "$VENV"
|
|
fi
|
|
echo "Installing Python dependencies..."
|
|
"$VENV/bin/pip" install --quiet --upgrade pip
|
|
"$VENV/bin/pip" install --quiet feedparser requests mutagen
|
|
|
|
# --- Create directory structure ---
|
|
for d in music podcasts jingles announcements playlists state logs; do
|
|
mkdir -p "$INSTALL_DIR/$d"
|
|
done
|
|
|
|
# --- Generate config.json ---
|
|
cat > "$INSTALL_DIR/config.json" <<EOF
|
|
{
|
|
"icecast": {
|
|
"host": "${CFG[ICE_HOST]}",
|
|
"port": ${CFG[ICE_PORT]},
|
|
"mount": "${CFG[ICE_MOUNT]}",
|
|
"username": "${CFG[ICE_USER]}",
|
|
"password": "${CFG[ICE_PASS]}"
|
|
},
|
|
"gpodder": {
|
|
"enable": ${CFG[GPODDER_ENABLE]},
|
|
"host": "${CFG[GPODDER_HOST]}",
|
|
"username": "${CFG[GPODDER_USER]}",
|
|
"password": "${CFG[GPODDER_PASS]}"
|
|
}
|
|
}
|
|
EOF
|
|
chmod 600 "$INSTALL_DIR/config.json"
|
|
|
|
# --- Ensure liquidsoap user exists ---
|
|
if ! id liquidsoap &>/dev/null; then
|
|
useradd --system --home-dir "$INSTALL_DIR" --shell /usr/sbin/nologin liquidsoap
|
|
fi
|
|
chown -R liquidsoap:liquidsoap "$INSTALL_DIR"
|
|
|
|
# --- Write systemd service ---
|
|
cat > /etc/systemd/system/"$SERVICE_NAME".service <<EOF
|
|
[Unit]
|
|
Description=Liquidsoap radio station ($SERVICE_NAME)
|
|
After=network.target icecast2.service
|
|
|
|
[Service]
|
|
User=liquidsoap
|
|
WorkingDirectory=$INSTALL_DIR
|
|
ExecStart=/usr/bin/liquidsoap $INSTALL_DIR/station.liq
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
# --- Set up cron jobs (deduplicated) ---
|
|
CRON_FETCH="0 * * * * sudo -u liquidsoap $PYTHON_BIN $INSTALL_DIR/fetch_podcasts.py >> $INSTALL_DIR/logs/cron_fetch.log 2>&1"
|
|
CRON_UPDATE="30 * * * * sudo -u liquidsoap $PYTHON_BIN $INSTALL_DIR/update_playlists.py >> $INSTALL_DIR/logs/cron_update.log 2>&1"
|
|
|
|
(crontab -l 2>/dev/null | grep -v "fetch_podcasts.py"; echo "$CRON_FETCH") | crontab -
|
|
(crontab -l 2>/dev/null | grep -v "update_playlists.py"; echo "$CRON_UPDATE") | crontab -
|
|
|
|
# --- Reload and enable ---
|
|
systemctl daemon-reload
|
|
systemctl enable "$SERVICE_NAME"
|
|
|
|
echo
|
|
echo "=== Installation complete ==="
|
|
echo "Next steps:"
|
|
echo " 1. Drop MP3/M4A files into $INSTALL_DIR/music/"
|
|
echo " 2. Edit $INSTALL_DIR/schedule.txt with your show schedule"
|
|
echo " 3. Review $INSTALL_DIR/station.liq"
|
|
echo " 4. Start services:"
|
|
echo " sudo systemctl start icecast2"
|
|
echo " sudo systemctl start $SERVICE_NAME"
|