feat(scripts): add Raspberry Pi uninstallers + fix stale install-web.sh docs

soundtouch-web was renamed to soundtouch-player and the web UI merged into
soundtouch-service, so the player is now optional. The Raspberry Pi / host
installers had no matching uninstaller (removal was only documented as manual
commands), and users who installed the old soundtouch-web have a leftover
service with no scripted way to remove it.

Add three uninstallers under scripts/raspberry-pi/, each mirroring its
installer's conventions and tolerant of already-missing pieces:

- uninstall.sh        — soundtouch-service; preserves the data directory by
                        default, --purge / PURGE_DATA=true to delete it.
- uninstall-player.sh — soundtouch-player (stateless).
- uninstall-web.sh    — leftover soundtouch-web; points users at install-player.sh.

The shared soundtouch:soundtouch user/group is removed only once no other
soundtouch-{service,player,web} install remains on the host.

Docs: the README and guides still told users to fetch install-web.sh to install
the player. Switch those to install-player.sh, keep but improve the manual
removal commands (note the service datastore is preserved unless explicitly
deleted), document the new uninstallers, and add a "Migrating from soundtouch-web"
section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tobias Gesellchen
2026-06-25 09:47:19 +02:00
co-authored by Claude Opus 4.8
parent 902b9d8402
commit 2e6e88dd8c
5 changed files with 426 additions and 1 deletions
+65 -1
View File
@@ -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:
+31
View File
@@ -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.
+97
View File
@@ -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 "$@"
+108
View File
@@ -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 "$@"
+125
View File
@@ -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 "$@"