diff --git a/docs/content/docs/guides/RASPBERRY-PI.md b/docs/content/docs/guides/RASPBERRY-PI.md index a2fc82d..453d549 100644 --- a/docs/content/docs/guides/RASPBERRY-PI.md +++ b/docs/content/docs/guides/RASPBERRY-PI.md @@ -13,6 +13,12 @@ Two scripts are available, one per binary: Both auto-detect CPU architecture (armv7 / arm64 / amd64), create a `soundtouch` system user, and install a systemd unit. They are safe to re-run for updates. +Each installer has a matching uninstaller (`uninstall.sh`, `uninstall-player.sh`). + +> `install-web.sh` is the previous name for `install-player.sh`. It still works +> as a deprecated alias but will be removed in a future release. If you installed +> `soundtouch-web` before the rename, see +> [Migrating from soundtouch-web](#migrating-from-soundtouch-web). For a complete install-through-migration walkthrough see [EXTERNAL-HOST-WALKTHROUGH.md](EXTERNAL-HOST-WALKTHROUGH.md). @@ -107,13 +113,30 @@ The script stops the service, downloads the new binary (backs up the old one to ### Removal +Use the uninstaller, which stops and disables the service and removes the unit, +binary, and config. Your data directory is **preserved** by default: + +```bash +curl -fsSL -o uninstall.sh \ + https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/main/scripts/raspberry-pi/uninstall.sh +sudo bash uninstall.sh # keep /var/lib/soundtouch-service +sudo bash uninstall.sh --purge # also delete the data directory +``` + +The `soundtouch:soundtouch` user/group is removed only once no other +`soundtouch-*` install remains on the host. + +Prefer to do it by hand? The equivalent manual steps are: + ```bash sudo systemctl disable --now soundtouch-service sudo rm /etc/systemd/system/soundtouch-service.service sudo rm -rf /etc/soundtouch-service -sudo rm -rf /var/lib/soundtouch-service sudo rm /usr/local/bin/soundtouch-service sudo systemctl daemon-reload +# Datastore (presets, device registrations, certs) — delete only if you are +# sure you no longer need it: +sudo rm -rf /var/lib/soundtouch-service ``` --- @@ -234,6 +257,19 @@ sudo bash install-player.sh v0.107.0 # update to a specific version ### Removal +Use the uninstaller: + +```bash +curl -fsSL -o uninstall-player.sh \ + https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/main/scripts/raspberry-pi/uninstall-player.sh +sudo bash uninstall-player.sh +``` + +The `soundtouch:soundtouch` user/group is removed only once no other +`soundtouch-*` install remains on the host. + +Prefer to do it by hand? The equivalent manual steps are: + ```bash sudo systemctl disable --now soundtouch-player sudo rm /etc/systemd/system/soundtouch-player.service @@ -244,6 +280,34 @@ sudo systemctl daemon-reload --- +## Migrating from soundtouch-web + +`soundtouch-web` was renamed to `soundtouch-player`. The old `install-web.sh` +installer and the `soundtouch-web` release asset still exist as deprecated +aliases and will be removed in a future release. + +If you have an existing `soundtouch-web` install, remove it and switch to +`soundtouch-player`: + +```bash +# 1. Remove the old soundtouch-web service: +curl -fsSL -o uninstall-web.sh \ + https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/main/scripts/raspberry-pi/uninstall-web.sh +sudo bash uninstall-web.sh + +# 2. Install soundtouch-player (see the section above): +curl -fsSL -o install-player.sh \ + https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/main/scripts/raspberry-pi/install-player.sh +sudo bash install-player.sh +``` + +`uninstall-web.sh` stops and disables the `soundtouch-web` service and removes +its unit, binary, and `/etc/soundtouch-web` config. Both binaries are stateless, +so there is no data to migrate; re-create any per-host settings in +`/etc/soundtouch-player/soundtouch-player.env` (the variables are identical). + +--- + ## Architecture auto-detection Both installers detect the CPU and pick the matching release asset automatically: diff --git a/scripts/raspberry-pi/README.md b/scripts/raspberry-pi/README.md index 1e03423..41fc7b1 100644 --- a/scripts/raspberry-pi/README.md +++ b/scripts/raspberry-pi/README.md @@ -32,3 +32,34 @@ Pass a version tag as the first argument to pin a specific release: sudo bash install.sh v0.107.0 sudo bash install-player.sh v0.107.0 ``` + +> `install-web.sh` is the old name for `install-player.sh` and still works as a +> deprecated alias. If you installed `soundtouch-web` previously, remove it with +> `uninstall-web.sh` (see below) and switch to `install-player.sh`. + +## Removal + +Matching uninstallers reverse each installer (stop and disable the service, +remove the unit, binary, and config). You can also remove things by hand — see +the [full guide](../../docs/content/docs/guides/RASPBERRY-PI.md) for the manual +commands. + +```bash +# soundtouch-service (keeps the data directory unless you pass --purge): +curl -fsSL -o uninstall.sh \ + https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/main/scripts/raspberry-pi/uninstall.sh +sudo bash uninstall.sh + +# soundtouch-player: +curl -fsSL -o uninstall-player.sh \ + https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/main/scripts/raspberry-pi/uninstall-player.sh +sudo bash uninstall-player.sh + +# soundtouch-web (deprecated; switch to soundtouch-player afterwards): +curl -fsSL -o uninstall-web.sh \ + https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/main/scripts/raspberry-pi/uninstall-web.sh +sudo bash uninstall-web.sh +``` + +The shared `soundtouch:soundtouch` user/group is removed only once no other +`soundtouch-*` install remains on the host. diff --git a/scripts/raspberry-pi/uninstall-player.sh b/scripts/raspberry-pi/uninstall-player.sh new file mode 100755 index 0000000..63fca34 --- /dev/null +++ b/scripts/raspberry-pi/uninstall-player.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ============================================================================== +# Bose-SoundTouch soundtouch-player uninstaller (systemd, headless) +# +# Reverses scripts/raspberry-pi/install-player.sh: stops and disables the +# systemd service, removes the unit, binary, and config directory. +# +# Usage: +# sudo bash uninstall-player.sh +# +# Notes: +# - soundtouch-player is stateless (no data directory) — nothing to preserve. +# - The shared soundtouch:soundtouch user/group is removed only when no other +# soundtouch-{service,player,web} install remains on this host. +# - Safe to re-run; every step tolerates already-missing pieces. +# ============================================================================== + +SERVICE_NAME="${SERVICE_NAME:-soundtouch-player}" +BIN_PATH="${BIN_PATH:-/usr/local/bin/soundtouch-player}" +CONFIG_DIR="${CONFIG_DIR:-/etc/soundtouch-player}" +SERVICE_USER="${SERVICE_USER:-soundtouch}" +SERVICE_GROUP="${SERVICE_GROUP:-soundtouch}" + +log() { printf "\n==> %s\n" "$*"; } +die() { echo "ERROR: $*" >&2; exit 1; } + +need_root() { + [[ "${EUID}" -eq 0 ]] || die "Please run as root (e.g. sudo bash $0)." +} + +ensure_cmd() { + command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1" +} + +stop_remove_service() { + log "Stopping and disabling ${SERVICE_NAME}.service" + systemctl disable --now "${SERVICE_NAME}.service" 2>/dev/null || true + + local unit="/etc/systemd/system/${SERVICE_NAME}.service" + if [[ -f "${unit}" ]]; then + log "Removing systemd unit: ${unit}" + rm -f "${unit}" + fi + systemctl daemon-reload + systemctl reset-failed "${SERVICE_NAME}.service" 2>/dev/null || true +} + +remove_binary() { + if [[ -e "${BIN_PATH}" || -e "${BIN_PATH}.old" ]]; then + log "Removing binary: ${BIN_PATH} (and ${BIN_PATH}.old)" + rm -f "${BIN_PATH}" "${BIN_PATH}.old" + fi +} + +remove_config() { + if [[ -d "${CONFIG_DIR}" ]]; then + log "Removing config directory: ${CONFIG_DIR}" + rm -rf "${CONFIG_DIR}" + fi +} + +# Remove the shared soundtouch:soundtouch user/group only when no other +# soundtouch-{service,player,web} install remains on this host. +remove_user_group_if_unused() { + local n + for n in service player web; do + if [[ -f "/etc/systemd/system/soundtouch-${n}.service" ]] || \ + [[ -e "/usr/local/bin/soundtouch-${n}" ]]; then + log "Keeping ${SERVICE_USER}:${SERVICE_GROUP} — still used by soundtouch-${n}." + return + fi + done + + if id -u "${SERVICE_USER}" >/dev/null 2>&1; then + log "No other soundtouch installs remain; removing user ${SERVICE_USER}" + userdel "${SERVICE_USER}" 2>/dev/null || true + fi + if getent group "${SERVICE_GROUP}" >/dev/null 2>&1; then + groupdel "${SERVICE_GROUP}" 2>/dev/null || true + fi +} + +main() { + need_root + ensure_cmd systemctl + + stop_remove_service + remove_binary + remove_config + remove_user_group_if_unused + + log "✅ soundtouch-player has been removed." +} + +main "$@" diff --git a/scripts/raspberry-pi/uninstall-web.sh b/scripts/raspberry-pi/uninstall-web.sh new file mode 100755 index 0000000..4018a46 --- /dev/null +++ b/scripts/raspberry-pi/uninstall-web.sh @@ -0,0 +1,108 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ============================================================================== +# Bose-SoundTouch soundtouch-web uninstaller (systemd, headless) +# +# soundtouch-web was renamed to soundtouch-player. This uninstaller removes a +# leftover soundtouch-web install created by the (now deprecated) install-web.sh: +# it stops and disables the systemd service, removes the unit, binary, and config +# directory. +# +# Usage: +# sudo bash uninstall-web.sh +# +# Notes: +# - soundtouch-web is stateless (no data directory) — nothing to preserve. +# - The shared soundtouch:soundtouch user/group is removed only when no other +# soundtouch-{service,player,web} install remains on this host. +# - Safe to re-run; every step tolerates already-missing pieces. +# ============================================================================== + +SERVICE_NAME="${SERVICE_NAME:-soundtouch-web}" +BIN_PATH="${BIN_PATH:-/usr/local/bin/soundtouch-web}" +CONFIG_DIR="${CONFIG_DIR:-/etc/soundtouch-web}" +SERVICE_USER="${SERVICE_USER:-soundtouch}" +SERVICE_GROUP="${SERVICE_GROUP:-soundtouch}" + +log() { printf "\n==> %s\n" "$*"; } +die() { echo "ERROR: $*" >&2; exit 1; } + +need_root() { + [[ "${EUID}" -eq 0 ]] || die "Please run as root (e.g. sudo bash $0)." +} + +ensure_cmd() { + command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1" +} + +stop_remove_service() { + log "Stopping and disabling ${SERVICE_NAME}.service" + systemctl disable --now "${SERVICE_NAME}.service" 2>/dev/null || true + + local unit="/etc/systemd/system/${SERVICE_NAME}.service" + if [[ -f "${unit}" ]]; then + log "Removing systemd unit: ${unit}" + rm -f "${unit}" + fi + systemctl daemon-reload + systemctl reset-failed "${SERVICE_NAME}.service" 2>/dev/null || true +} + +remove_binary() { + if [[ -e "${BIN_PATH}" || -e "${BIN_PATH}.old" ]]; then + log "Removing binary: ${BIN_PATH} (and ${BIN_PATH}.old)" + rm -f "${BIN_PATH}" "${BIN_PATH}.old" + fi +} + +remove_config() { + if [[ -d "${CONFIG_DIR}" ]]; then + log "Removing config directory: ${CONFIG_DIR}" + rm -rf "${CONFIG_DIR}" + fi +} + +# Remove the shared soundtouch:soundtouch user/group only when no other +# soundtouch-{service,player,web} install remains on this host. +remove_user_group_if_unused() { + local n + for n in service player web; do + if [[ -f "/etc/systemd/system/soundtouch-${n}.service" ]] || \ + [[ -e "/usr/local/bin/soundtouch-${n}" ]]; then + log "Keeping ${SERVICE_USER}:${SERVICE_GROUP} — still used by soundtouch-${n}." + return + fi + done + + if id -u "${SERVICE_USER}" >/dev/null 2>&1; then + log "No other soundtouch installs remain; removing user ${SERVICE_USER}" + userdel "${SERVICE_USER}" 2>/dev/null || true + fi + if getent group "${SERVICE_GROUP}" >/dev/null 2>&1; then + groupdel "${SERVICE_GROUP}" 2>/dev/null || true + fi +} + +main() { + need_root + ensure_cmd systemctl + + stop_remove_service + remove_binary + remove_config + remove_user_group_if_unused + + log "✅ soundtouch-web has been removed." + cat <<'EOF' + +soundtouch-web is the old name for soundtouch-player. To install the current +control panel instead: + + curl -fsSL -o install-player.sh \ + https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/main/scripts/raspberry-pi/install-player.sh + sudo bash install-player.sh +EOF +} + +main "$@" diff --git a/scripts/raspberry-pi/uninstall.sh b/scripts/raspberry-pi/uninstall.sh new file mode 100755 index 0000000..ea8de7f --- /dev/null +++ b/scripts/raspberry-pi/uninstall.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ============================================================================== +# Bose-SoundTouch soundtouch-service uninstaller (systemd, headless) +# +# Reverses scripts/raspberry-pi/install.sh: stops and disables the systemd +# service, removes the unit, binary, and config directory. +# +# Usage: +# sudo bash uninstall.sh # remove service, KEEP the data directory +# sudo PURGE_DATA=true bash uninstall.sh # also delete the data directory +# sudo bash uninstall.sh --purge # same as PURGE_DATA=true +# +# Notes: +# - The data directory (${DATA_DIR}) holds your datastore: presets, device +# registrations, and certificates. It is PRESERVED by default; deleting it is +# opt-in via PURGE_DATA=true (or --purge). +# - The shared soundtouch:soundtouch user/group is removed only when no other +# soundtouch-{service,player,web} install remains on this host. +# - Safe to re-run; every step tolerates already-missing pieces. +# ============================================================================== + +PURGE_DATA="${PURGE_DATA:-false}" +for arg in "$@"; do + case "$arg" in + --purge) PURGE_DATA="true" ;; + esac +done + +SERVICE_NAME="${SERVICE_NAME:-soundtouch-service}" +BIN_PATH="${BIN_PATH:-/usr/local/bin/soundtouch-service}" +CONFIG_DIR="${CONFIG_DIR:-/etc/soundtouch-service}" +DATA_DIR="${DATA_DIR:-/var/lib/soundtouch-service}" +SERVICE_USER="${SERVICE_USER:-soundtouch}" +SERVICE_GROUP="${SERVICE_GROUP:-soundtouch}" + +log() { printf "\n==> %s\n" "$*"; } +die() { echo "ERROR: $*" >&2; exit 1; } + +need_root() { + [[ "${EUID}" -eq 0 ]] || die "Please run as root (e.g. sudo bash $0)." +} + +ensure_cmd() { + command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1" +} + +stop_remove_service() { + log "Stopping and disabling ${SERVICE_NAME}.service" + systemctl disable --now "${SERVICE_NAME}.service" 2>/dev/null || true + + local unit="/etc/systemd/system/${SERVICE_NAME}.service" + if [[ -f "${unit}" ]]; then + log "Removing systemd unit: ${unit}" + rm -f "${unit}" + fi + systemctl daemon-reload + systemctl reset-failed "${SERVICE_NAME}.service" 2>/dev/null || true +} + +remove_binary() { + if [[ -e "${BIN_PATH}" || -e "${BIN_PATH}.old" ]]; then + log "Removing binary: ${BIN_PATH} (and ${BIN_PATH}.old)" + rm -f "${BIN_PATH}" "${BIN_PATH}.old" + fi +} + +remove_config() { + if [[ -d "${CONFIG_DIR}" ]]; then + log "Removing config directory: ${CONFIG_DIR}" + rm -rf "${CONFIG_DIR}" + fi +} + +handle_data_dir() { + if [[ ! -d "${DATA_DIR}" ]]; then + return + fi + if [[ "${PURGE_DATA}" == "true" ]]; then + log "Removing data directory: ${DATA_DIR}" + rm -rf "${DATA_DIR}" + else + log "Preserving data directory: ${DATA_DIR}" + echo " This holds your datastore (presets, device registrations, certs)." + echo " To delete it as well, run: sudo rm -rf ${DATA_DIR}" + echo " (or re-run this uninstaller with --purge / PURGE_DATA=true)" + fi +} + +# Remove the shared soundtouch:soundtouch user/group only when no other +# soundtouch-{service,player,web} install remains on this host. +remove_user_group_if_unused() { + local n + for n in service player web; do + if [[ -f "/etc/systemd/system/soundtouch-${n}.service" ]] || \ + [[ -e "/usr/local/bin/soundtouch-${n}" ]]; then + log "Keeping ${SERVICE_USER}:${SERVICE_GROUP} — still used by soundtouch-${n}." + return + fi + done + + if id -u "${SERVICE_USER}" >/dev/null 2>&1; then + log "No other soundtouch installs remain; removing user ${SERVICE_USER}" + userdel "${SERVICE_USER}" 2>/dev/null || true + fi + if getent group "${SERVICE_GROUP}" >/dev/null 2>&1; then + groupdel "${SERVICE_GROUP}" 2>/dev/null || true + fi +} + +main() { + need_root + ensure_cmd systemctl + + stop_remove_service + remove_binary + remove_config + handle_data_dir + remove_user_group_if_unused + + log "✅ soundtouch-service has been removed." +} + +main "$@"