chore(release): drop the transitional soundtouch-web alias

soundtouch-web was a transitional alias of soundtouch-player. Stop
building and publishing it everywhere, and refresh the release notes
while at it:

- release.yml: remove the soundtouch-web binary, its individual and
  combined checksums, and its release assets (EXPECTED_COUNT 35 -> 28);
  drop the ghcr.io/...-web Docker image steps. Also slim the
  workflow_dispatch release notes to an accurate AfterTouch header plus
  GitHub's auto-generated changelog, with the bare tag as the title.
- Dockerfile: drop the soundtouch-web image stage.
- Makefile: remove WEB_NAME and the build-web target (and its use in
  build/install).
- Delete scripts/raspberry-pi/install-web.sh (it fetched a release asset
  that is no longer published) and point the docs at install-player.sh.
- Correct README, CLAUDE.md, and main.go wording that claimed the alias
  was still published.

The runtime notice for a binary still run under the soundtouch-web name
is kept, so anyone who renamed the binary is nudged to soundtouch-player.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tobias Gesellchen
2026-06-22 22:29:23 +02:00
co-authored by Claude Opus 4.8
parent 9331e63b2d
commit 2744708a9d
10 changed files with 57 additions and 579 deletions
+27 -169
View File
@@ -173,10 +173,6 @@ jobs:
# Build Player (formerly soundtouch-web)
build_binary "soundtouch-player" "./cmd/soundtouch-player"
# Build Web: transitional alias of the player, built from the same
# source. Dropped in a future release; keep in sync with player.
build_binary "soundtouch-web" "./cmd/soundtouch-player"
# Build Backup
build_binary "soundtouch-backup" "./cmd/soundtouch-backup"
id: build
@@ -186,7 +182,6 @@ jobs:
CLI_NAME="${{ steps.build.outputs.soundtouch-cli }}"
SVC_NAME="${{ steps.build.outputs.soundtouch-service }}"
PLAYER_NAME="${{ steps.build.outputs.soundtouch-player }}"
WEB_NAME="${{ steps.build.outputs.soundtouch-web }}"
BCK_NAME="${{ steps.build.outputs.soundtouch-backup }}"
# Use atomic operations to avoid conflicts
@@ -204,7 +199,6 @@ jobs:
generate_checksums "$CLI_NAME"
generate_checksums "$SVC_NAME"
generate_checksums "$PLAYER_NAME"
generate_checksums "$WEB_NAME"
generate_checksums "$BCK_NAME"
# Cleanup
@@ -219,7 +213,6 @@ jobs:
build/soundtouch-cli-v*
build/soundtouch-service-v*
build/soundtouch-player-v*
build/soundtouch-web-v*
build/soundtouch-backup-v*
retention-days: 1
@@ -247,7 +240,7 @@ jobs:
mkdir -p release-files
# Move all files from subdirectories to the collection directory
find . -mindepth 2 -type f \( -name "soundtouch-cli-*" -o -name "soundtouch-service-*" -o -name "soundtouch-player-*" -o -name "soundtouch-web-*" -o -name "soundtouch-backup-*" \) -exec mv {} release-files/ \;
find . -mindepth 2 -type f \( -name "soundtouch-cli-*" -o -name "soundtouch-service-*" -o -name "soundtouch-player-*" -o -name "soundtouch-backup-*" \) -exec mv {} release-files/ \;
# Remove empty directories
find . -type d -empty -delete
@@ -262,14 +255,14 @@ jobs:
# Generate combined checksums (exclude individual .sha256/.sha512 files)
if ls soundtouch-* 1> /dev/null 2>&1; then
# Only checksum the actual binaries, not the .sha256/.sha512 files
ls soundtouch-cli-* soundtouch-service-* soundtouch-player-* soundtouch-web-* soundtouch-backup-* | grep -v '\.sha256$' | grep -v '\.sha512$' | xargs sha256sum > checksums.sha256
ls soundtouch-cli-* soundtouch-service-* soundtouch-player-* soundtouch-web-* soundtouch-backup-* | grep -v '\.sha256$' | grep -v '\.sha512$' | xargs sha512sum > checksums.sha512
ls soundtouch-cli-* soundtouch-service-* soundtouch-player-* soundtouch-backup-* | grep -v '\.sha256$' | grep -v '\.sha512$' | xargs sha256sum > checksums.sha256
ls soundtouch-cli-* soundtouch-service-* soundtouch-player-* soundtouch-backup-* | grep -v '\.sha256$' | grep -v '\.sha512$' | xargs sha512sum > checksums.sha512
echo "📋 Generated combined checksums:"
cat checksums.sha256
# Verify all expected files are present (binaries only, not checksum files)
EXPECTED_COUNT=35 # 7 platforms * 5 binaries (player + its web alias)
EXPECTED_COUNT=28 # 7 platforms * 4 binaries
ACTUAL_COUNT=$(ls soundtouch-* | grep -v '\.sha256$' | grep -v '\.sha512$' | wc -l)
if [[ $ACTUAL_COUNT -ne $EXPECTED_COUNT ]]; then
@@ -332,148 +325,41 @@ jobs:
fi
VERSION="${TAG_NAME#v}"
# Generate comprehensive release notes
# Short, accurate header. GitHub's auto-generated "What's Changed"
# + "Full Changelog" are appended after this (generate_release_notes).
cat > release_notes.md << EOF
# Bose SoundTouch Go Library $TAG_NAME
# AfterTouch $TAG_NAME
A comprehensive Go library for controlling Bose SoundTouch speakers with 100% API coverage, real-time WebSocket events, and production-ready features.
**Bose SoundTouch Toolkit.** Keep your Bose SoundTouch speakers alive after the Bose cloud shutdown. No Bose infrastructure required.
## 🎯 Key Features
## What's included
- **100% API Coverage**: All 19 official endpoints + 6 useful extensions (25 total)
- **Real-time Events**: WebSocket support with auto-reconnect and comprehensive event handling
- **Multiroom Control**: Complete zone management and coordination
- **Production Ready**: Connection pooling, error handling, circuit breakers, monitoring
- **Excellent Documentation**: 4000+ lines including Getting Started, Cookbook, Troubleshooting, and Deployment guides
- **CLI Tool**: Full-featured command-line interface with all endpoints
Pre-built binaries for Linux (amd64, arm64, armv7), macOS (Intel & Apple Silicon), Windows (amd64), and FreeBSD (amd64):
## 🚀 Quick Start
- **soundtouch-service**: local server that replaces the Bose cloud. Point your speaker at it and you keep full control; the built-in web UI on port 8000 handles setup.
- **soundtouch-player**: standalone LAN web UI for device control: play/pause, volume, presets, live status. (Formerly \`soundtouch-web\`.)
- **soundtouch-cli**: command-line control of any device: playback, presets, sources, multiroom zones, discovery, and migration. Good for scripting and home automation.
- **soundtouch-backup**: back up your Bose cloud account and each speaker's local state. \`soundtouch-backup all\` captures everything in one step.
## Documentation
Full guides, setup walkthroughs, and troubleshooting: https://gesellix.github.io/Bose-SoundTouch/
## Use as a Go library
The core client is also importable:
\`\`\`bash
go get github.com/gesellix/bose-soundtouch@$TAG_NAME
\`\`\`
\`\`\`go
package main
## Verifying downloads
import (
"fmt"
"log"
"github.com/gesellix/bose-soundtouch/pkg/client"
)
func main() {
// Create client
c := client.New("192.0.2.100", 8090)
// Get device info
info, err := c.GetInfo()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Device: %s\\n", info.Name)
}
\`\`\`
## 📚 Documentation
- [Getting Started Guide](docs/GETTING-STARTED.md) - 10-minute tutorial from discovery to WebSocket monitoring
- [API Cookbook](docs/API-COOKBOOK.md) - 1000+ lines of real-world patterns and examples
- [Troubleshooting Guide](docs/TROUBLESHOOTING.md) - Systematic issue resolution
- [Deployment Guide](docs/DEPLOYMENT.md) - Production deployment examples (Docker, K8s, systemd)
## 🔧 CLI & Service Tools
Download the tools for your platform from the assets below:
### CLI Tool
\`\`\`bash
# Quick device discovery
./soundtouch-cli -discover
\`\`\`
### SoundTouch Service
\`\`\`bash
# Start the service
./soundtouch-service
\`\`\`
### SoundTouch Player (formerly soundtouch-web)
\`\`\`bash
# Start the LAN web player
./soundtouch-player
\`\`\`
> Note: \`soundtouch-web\` has been renamed to \`soundtouch-player\`.
> The \`soundtouch-web\` assets are still published as a transitional
> alias and will be removed in a future release. Please switch your
> downloads and scripts to \`soundtouch-player\`.
### SoundTouch Backup
\`\`\`bash
# Back up cloud account and all paired speakers in one go
./soundtouch-backup all
\`\`\`
## 🧪 Tested Hardware
- Bose SoundTouch 10
- Bose SoundTouch 20
- All core functionality validated on real devices
## 📈 What's New in $TAG_NAME
$(git log --pretty=format:"- %s" $(git describe --tags --abbrev=0 HEAD^)..HEAD 2>/dev/null || echo "- Initial release with complete feature set")
## 🏗️ Supported Platforms
This release includes pre-built binaries for:
- Linux (amd64, arm64, armv7)
- macOS (Intel & Apple Silicon)
- Windows (amd64)
- FreeBSD (amd64)
`soundtouch-cli`, `soundtouch-service`, `soundtouch-player` (with `soundtouch-web` as a transitional alias), and `soundtouch-backup` are included.
## 🔐 Checksums
Multiple checksum options are provided for download verification:
### Combined Checksums (Recommended)
- \`checksums.sha256\` - SHA256 checksums for all binaries
- \`checksums.sha512\` - SHA512 checksums for all binaries
Each binary has its own \`.sha256\`/\`.sha512\`, and combined \`checksums.sha256\` / \`checksums.sha512\` cover all of them:
\`\`\`bash
# Download any binary + combined checksums
curl -L -O https://github.com/.../soundtouch-cli-v$TAG_NAME-linux-amd64
curl -L -O https://github.com/.../checksums.sha256
# Verify your specific download
sha256sum -c checksums.sha256 --ignore-missing
\`\`\`
### Individual Checksums (Per Binary)
Each binary also has its own dedicated checksum files:
- \`soundtouch-cli-v$TAG_NAME-platform.sha256\`
- \`soundtouch-cli-v$TAG_NAME-platform.sha512\`
\`\`\`bash
# Download binary + its individual checksum
curl -L -O https://github.com/.../soundtouch-cli-v$TAG_NAME-linux-amd64
curl -L -O https://github.com/.../soundtouch-cli-v$TAG_NAME-linux-amd64.sha256
# Verify with individual checksum
sha256sum -c soundtouch-cli-v$TAG_NAME-linux-amd64.sha256
\`\`\`
## 🤝 Contributing
Contributions welcome! See our documentation for examples and patterns.
## 📄 License
MIT License - see [LICENSE](LICENSE) file.
EOF
echo "release_notes_file=release_notes.md" >> $GITHUB_OUTPUT
@@ -482,15 +368,15 @@ jobs:
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
with:
tag_name: ${{ github.event.inputs.tag }}
name: "Bose SoundTouch Go Library ${{ github.event.inputs.tag }}"
name: ${{ github.event.inputs.tag }}
body_path: ${{ steps.release_notes.outputs.release_notes_file }}
generate_release_notes: true
draft: false
prerelease: ${{ needs.validate.outputs.is_prerelease == 'true' }}
files: |
release-assets/soundtouch-cli-v*
release-assets/soundtouch-service-v*
release-assets/soundtouch-player-v*
release-assets/soundtouch-web-v*
release-assets/soundtouch-backup-v*
release-assets/checksums.sha256
release-assets/checksums.sha512
@@ -519,7 +405,6 @@ jobs:
release-assets/soundtouch-cli-v*
release-assets/soundtouch-service-v*
release-assets/soundtouch-player-v*
release-assets/soundtouch-web-v*
release-assets/soundtouch-backup-v*
release-assets/checksums.sha256
release-assets/checksums.sha512
@@ -602,33 +487,6 @@ jobs:
cache-from: type=gha
cache-to: type=gha,mode=max
# Transitional alias image (formerly the only web image). Dropped later.
- name: Extract metadata (tags, labels) for soundtouch-web
id: meta-web
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
with:
images: ghcr.io/${{ github.repository }}-web
tags: |
type=semver,pattern={{version}},value=v${{ needs.validate.outputs.version }}
type=semver,pattern={{major}}.{{minor}},value=v${{ needs.validate.outputs.version }}
type=raw,value=latest,enable=${{ needs.validate.outputs.is_prerelease == 'false' }}
- name: Build and push soundtouch-web Docker image
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
context: .
target: soundtouch-web
platforms: linux/amd64,linux/arm64,linux/arm64/v8,linux/arm/v7
push: true
tags: ${{ steps.meta-web.outputs.tags }}
labels: ${{ steps.meta-web.outputs.labels }}
build-args: |
VERSION=v${{ needs.validate.outputs.version }}
COMMIT=${{ github.sha }}
DATE=${{ steps.build_date.outputs.date }}
cache-from: type=gha
cache-to: type=gha,mode=max
notify:
name: Post-Release Notifications
runs-on: ubuntu-latest
@@ -639,7 +497,7 @@ jobs:
- name: Notify success
run: |
echo "🎉 Release ${{ needs.validate.outputs.version }} completed successfully!"
echo "📦 Binaries built for 7 platforms (CLI, Service, Web, and Backup)"
echo "📦 Binaries built for 7 platforms (CLI, Service, Player, and Backup)"
echo "🐳 Docker image published to ghcr.io"
echo "🔐 Checksums generated and verified"
echo "📋 Release notes automatically generated"
+1 -1
View File
@@ -43,7 +43,7 @@ Per-session pickup notes live in two local files at the repo root (they are `.gi
make build # All binaries
make build-cli # Just CLI
make build-service # Just service
make build-web # Just web UI
make build-player # Just web player
make build-all # Cross-platform builds (Linux, macOS, Windows)
make install # Install to $GOPATH/bin
-19
View File
@@ -112,22 +112,3 @@ EXPOSE 8080
USER nobody
ENTRYPOINT ["/app/soundtouch-player"]
# soundtouch-web image: transitional alias of soundtouch-player. Built from the
# same binary; the entrypoint name makes the binary print a rename notice on
# start. Will be dropped in a future release.
FROM alpine:3.24 AS soundtouch-web
RUN apk add --no-cache ca-certificates tzdata
WORKDIR /app
COPY --from=builder /soundtouch-player /app/soundtouch-web
ENV PORT=8080
EXPOSE 8080
USER nobody
ENTRYPOINT ["/app/soundtouch-web"]
+2 -13
View File
@@ -19,9 +19,6 @@ SERVICE_NAME=soundtouch-service
SERVICE_PATH=./cmd/$(SERVICE_NAME)
PLAYER_NAME=soundtouch-player
PLAYER_PATH=./cmd/$(PLAYER_NAME)
# WEB_NAME is the previous name for the player, kept as a transitional alias
# built from the same PLAYER_PATH source. It will be dropped in a future release.
WEB_NAME=soundtouch-web
EXAMPLE_MDNS_NAME=example-mdns
EXAMPLE_MDNS_PATH=./cmd/$(EXAMPLE_MDNS_NAME)
EXAMPLE_UPNP_NAME=example-upnp
@@ -55,7 +52,7 @@ AUTH_SERVICE_URL ?= $(BACKEND_URL)
all: check build
build: build-cli build-service build-player build-web build-examples build-favicon-gen build-backup
build: build-cli build-service build-player build-examples build-favicon-gen build-backup
build-cli:
@echo "Building $(BINARY_NAME)..."
@@ -72,13 +69,6 @@ build-player:
@mkdir -p $(BUILD_DIR)
$(GOBUILD) $(BUILDFLAGS) -o $(BUILD_DIR)/$(PLAYER_NAME) $(PLAYER_PATH)
# Transitional alias: builds the same source as build-player under the old
# soundtouch-web name. Drop this target once the alias is retired.
build-web:
@echo "Building $(WEB_NAME) (transitional alias of $(PLAYER_NAME))..."
@mkdir -p $(BUILD_DIR)
$(GOBUILD) $(BUILDFLAGS) -o $(BUILD_DIR)/$(WEB_NAME) $(PLAYER_PATH)
build-examples:
@echo "Building $(EXAMPLE_MDNS_NAME)..."
@mkdir -p $(BUILD_DIR)
@@ -376,12 +366,11 @@ dev-player-host: build-player
fi
cd cmd/soundtouch-player && ../../$(BUILD_DIR)/$(PLAYER_NAME) -host $(HOST)
install: build-cli build-service build-player build-web build-backup
install: build-cli build-service build-player build-backup
@echo "Installing binaries to $(GOPATH)/bin..."
cp $(BUILD_DIR)/$(BINARY_NAME) $(GOPATH)/bin/
cp $(BUILD_DIR)/$(SERVICE_NAME) $(GOPATH)/bin/
cp $(BUILD_DIR)/$(PLAYER_NAME) $(GOPATH)/bin/
cp $(BUILD_DIR)/$(WEB_NAME) $(GOPATH)/bin/
cp $(BUILD_DIR)/$(BACKUP_NAME) $(GOPATH)/bin/
update-static-deps:
+1 -1
View File
@@ -72,7 +72,7 @@ See the [CLI Reference](https://gesellix.github.io/Bose-SoundTouch/docs/guides/C
### soundtouch-player
> Formerly `soundtouch-web`. The `soundtouch-web` binary, Docker image, and install script are still published as a transitional alias and will be removed in a future release; please switch to `soundtouch-player`.
> Formerly `soundtouch-web`. The `soundtouch-web` binary, Docker image, and install script are no longer published; please use `soundtouch-player`. (If you still run the binary under its old name, it prints a rename notice and works as before.)
A standalone, LAN-resident web UI for device control — play, pause, volume, preset selection, real-time status — served from a local Go binary. Because it reaches speakers directly on your network and can delegate cloud-only features (e.g. TTS) to a remote AfterTouch service via `--service-url`, it stays useful when `soundtouch-service` runs off-LAN (for example in the cloud), where the embedded `/app` player cannot reach your speakers.
+6 -7
View File
@@ -4,8 +4,9 @@
// remote AfterTouch service via --service-url, which is why it stays useful
// when soundtouch-service runs off-LAN (e.g. in the cloud).
//
// It was previously named soundtouch-web; that name is still published as a
// transitional alias and will be dropped in a future release.
// It was previously named soundtouch-web; that name is no longer published.
// If you still run the binary under the old name, it prints a rename notice
// and otherwise behaves identically.
package main
import (
@@ -56,9 +57,8 @@ func updateBuildInfo() {
}
// warnIfInvokedAsWeb prints a one-line deprecation notice when the binary is
// run under its old name (soundtouch-web). The soundtouch-web artifact is a
// transitional alias built from this same source; this nudges operators to
// switch to soundtouch-player before the alias is dropped.
// run under its old name (soundtouch-web). That name is no longer published,
// but anyone who renamed the binary still gets nudged to soundtouch-player.
func warnIfInvokedAsWeb() {
if len(os.Args) == 0 {
return
@@ -67,8 +67,7 @@ func warnIfInvokedAsWeb() {
name := filepath.Base(os.Args[0])
if name == "soundtouch-web" || name == "soundtouch-web.exe" {
log.Println("notice: 'soundtouch-web' has been renamed to 'soundtouch-player'. " +
"This name is a transitional alias and will stop being published in a future release; " +
"please switch to 'soundtouch-player'.")
"The 'soundtouch-web' name is no longer published; please switch to 'soundtouch-player'.")
}
}
@@ -176,12 +176,12 @@ open **`http://<host-ip>:8080`** in your browser (default port 8080).
### Installing soundtouch-player on a Raspberry Pi
`install.sh` only installs `soundtouch-service`. Use the dedicated
`install-web.sh` script to add soundtouch-player:
`install-player.sh` script to add soundtouch-player:
```bash
curl -fsSL -o install-web.sh \
https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/main/scripts/raspberry-pi/install-web.sh
sudo bash install-web.sh
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
```
For configuration, service management, updates, and removal see the
+12 -12
View File
@@ -6,10 +6,10 @@ host) using the provided installer scripts.
Two scripts are available, one per binary:
| Script | Binary | Role | Default port |
|------------------|----------------------|-------------------------------------|--------------|
| `install.sh` | `soundtouch-service` | Cloud-replacement relay — always-on | 80 / 443 |
| `install-web.sh` | `soundtouch-player` | Browser control panel | 8080 |
| Script | Binary | Role | Default port |
|---------------------|----------------------|-------------------------------------|--------------|
| `install.sh` | `soundtouch-service` | Cloud-replacement relay — always-on | 80 / 443 |
| `install-player.sh` | `soundtouch-player` | Browser control panel | 8080 |
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.
@@ -126,15 +126,15 @@ data and can be stopped or restarted at any time without data loss.
### Installation
```bash
curl -fsSL -o install-web.sh \
https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/main/scripts/raspberry-pi/install-web.sh
sudo bash install-web.sh
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
```
Install a specific version:
```bash
sudo bash install-web.sh v0.107.0
sudo bash install-player.sh v0.107.0
```
Override defaults at install time:
@@ -143,7 +143,7 @@ Override defaults at install time:
sudo \
VERSION=v0.107.0 \
HTTP_PORT=8081 \
bash install-web.sh
bash install-player.sh
```
Once running, open **`http://<pi-ip>:8080`** in a browser.
@@ -228,8 +228,8 @@ journalctl -u soundtouch-player -f
### Updates
```bash
sudo bash install-web.sh # update to latest release
sudo bash install-web.sh v0.107.0 # update to a specific version
sudo bash install-player.sh # update to latest release
sudo bash install-player.sh v0.107.0 # update to a specific version
```
### Removal
@@ -258,7 +258,7 @@ Override if needed:
```bash
sudo ARCH_ASSET=linux-arm64 bash install.sh
sudo ARCH_ASSET=linux-arm64 bash install-web.sh
sudo ARCH_ASSET=linux-arm64 bash install-player.sh
```
---
+4 -4
View File
@@ -21,14 +21,14 @@ sudo bash install.sh
**soundtouch-player** (browser control panel):
```bash
curl -fsSL -o install-web.sh \
https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/main/scripts/raspberry-pi/install-web.sh
sudo bash install-web.sh
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
```
Pass a version tag as the first argument to pin a specific release:
```bash
sudo bash install.sh v0.107.0
sudo bash install-web.sh v0.107.0
sudo bash install-player.sh v0.107.0
```
-349
View File
@@ -1,349 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# ==============================================================================
# DEPRECATED: soundtouch-web has been renamed to soundtouch-player.
# This installer still works (the soundtouch-web release asset is published as
# a transitional alias) but will be removed in a future release. Please switch
# to install-player.sh, which installs the soundtouch-player binary/service.
# ==============================================================================
echo "NOTICE: soundtouch-web has been renamed to soundtouch-player." >&2
echo " This installer (install-web.sh) is deprecated and will be removed" >&2
echo " in a future release; please switch to install-player.sh." >&2
echo >&2
# ==============================================================================
# Bose-SoundTouch soundtouch-web installer (systemd, headless)
#
# Usage:
# sudo bash install-web.sh [vX.Y.Z]
#
# Examples (override defaults via env vars):
#
# sudo \
# VERSION=v0.107.0 \
# HTTP_PORT=8081 \
# bash install-web.sh
#
# # With an AfterTouch service link for TTS (HTTPS + self-signed CA):
# sudo \
# SERVICE_URL=https://soundtouch.local \
# SERVICE_CA=/var/lib/soundtouch-service/certs/ca.crt \
# bash install-web.sh
#
# Or with a version argument to perform an update:
# sudo bash install-web.sh v0.107.0
#
# Notes:
# - This script downloads a release binary for your CPU (auto-detects armv7/arm64/amd64).
# - soundtouch-web is stateless (no data directory) — it is safe to stop/restart freely.
# - Default port is 8080 (unprivileged — no special capabilities needed).
# - If soundtouch-service is already installed, soundtouch-web reuses the
# existing soundtouch:soundtouch user/group.
# - Safe to re-run; it will update the binary, env file, and unit and restart.
# ==============================================================================
VERSION="${1:-${VERSION:-v0.107.0}}"
# Normalize version prefix
if [[ ! "$VERSION" =~ ^v ]]; then
VERSION="v${VERSION}"
fi
SERVICE_NAME="${SERVICE_NAME:-soundtouch-web}"
BIN_PATH="${BIN_PATH:-/usr/local/bin/soundtouch-web}"
CONFIG_DIR="${CONFIG_DIR:-/etc/soundtouch-web}"
ENV_FILE="${ENV_FILE:-$CONFIG_DIR/soundtouch-web.env}"
SERVICE_USER="${SERVICE_USER:-soundtouch}"
SERVICE_GROUP="${SERVICE_GROUP:-soundtouch}"
# Port (unprivileged — no CAP_NET_BIND_SERVICE needed)
HTTP_PORT="${HTTP_PORT:-8080}"
# Optional discovery / device config
BIND_ADDR="${BIND_ADDR:-}"
DISCOVERY_INTERFACE="${DISCOVERY_INTERFACE:-}"
SOUNDTOUCH_DEVICES="${SOUNDTOUCH_DEVICES:-}"
# Optional AfterTouch service link (needed for TTS / "Speak").
# SERVICE_URL: base URL of soundtouch-service, e.g. https://soundtouch.local
# SERVICE_CA: path to the service CA cert when it serves HTTPS with its own
# self-signed certificate, e.g. /var/lib/soundtouch-service/certs/ca.crt
SERVICE_URL="${SERVICE_URL:-}"
SERVICE_CA="${SERVICE_CA:-}"
# Override if you want to force a specific asset suffix:
# ARCH_ASSET=linux-armv7|linux-arm64|linux-amd64
ARCH_ASSET="${ARCH_ASSET:-}"
# Internal variables
SCRIPT_PATH="$(realpath "$0" 2>/dev/null || echo "$0")"
IS_SELF_UPDATE="${IS_SELF_UPDATE:-false}"
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"
}
apt_install_if_missing() {
log "Installing dependencies: $*"
apt-get update -y
apt-get install -y --no-install-recommends "$@"
}
detect_arch_asset() {
local m
m="$(uname -m)"
case "$m" in
armv7l|armv6l)
echo "linux-armv7"
;;
aarch64)
echo "linux-arm64"
;;
x86_64|amd64)
echo "linux-amd64"
;;
*)
die "Unsupported architecture from uname -m: $m (set ARCH_ASSET manually)"
;;
esac
}
download_url_for() {
local asset="$1"
echo "https://github.com/gesellix/Bose-SoundTouch/releases/download/${VERSION}/soundtouch-web-${VERSION}-${asset}"
}
ensure_user_group() {
log "Ensuring service user/group exist: ${SERVICE_USER}:${SERVICE_GROUP}"
if ! getent group "${SERVICE_GROUP}" >/dev/null; then
groupadd --system "${SERVICE_GROUP}"
fi
if ! id -u "${SERVICE_USER}" >/dev/null 2>&1; then
useradd --system \
--no-create-home \
--shell /usr/sbin/nologin \
--gid "${SERVICE_GROUP}" \
"${SERVICE_USER}"
fi
}
ensure_dirs() {
log "Creating config directory"
mkdir -p "${CONFIG_DIR}"
chmod 0755 "${CONFIG_DIR}"
}
download_binary() {
local asset url tmp=""
asset="${ARCH_ASSET:-$(detect_arch_asset)}"
url="$(download_url_for "$asset")"
log "Downloading binary for ${asset}: ${url}"
tmp="$(mktemp -d)"
trap 'rm -rf "${tmp}"' EXIT
if command -v curl >/dev/null 2>&1; then
curl -fsSL -o "${tmp}/soundtouch-web" "${url}"
else
wget -qO "${tmp}/soundtouch-web" "${url}"
fi
chmod +x "${tmp}/soundtouch-web"
if [[ -f "${BIN_PATH}" ]]; then
log "Backing up existing binary to ${BIN_PATH}.old"
cp -p "${BIN_PATH}" "${BIN_PATH}.old"
fi
install -m 0755 "${tmp}/soundtouch-web" "${BIN_PATH}"
log "Installed binary to ${BIN_PATH}"
}
self_update() {
if [[ "$IS_SELF_UPDATE" == "true" ]]; then
return
fi
local url="https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/${VERSION}/scripts/raspberry-pi/install-web.sh"
local tmp_script="/tmp/soundtouch-web-install-${VERSION}.sh"
log "Checking for installer updates for ${VERSION}..."
log "URL: ${url}"
if command -v curl >/dev/null 2>&1; then
if ! curl -fsSL -o "${tmp_script}" "${url}"; then
log "⚠️ Could not fetch installer for ${VERSION}, continuing with current script."
return
fi
else
if ! wget -qO "${tmp_script}" "${url}"; then
log "⚠️ Could not fetch installer for ${VERSION}, continuing with current script."
return
fi
fi
if diff -q "${SCRIPT_PATH}" "${tmp_script}" >/dev/null 2>&1; then
log "Installer is already up to date."
rm -f "${tmp_script}"
return
fi
log "Newer installer found for ${VERSION}. Updating ${SCRIPT_PATH} and re-executing..."
install -m 0755 "${tmp_script}" "${SCRIPT_PATH}"
rm -f "${tmp_script}"
export IS_SELF_UPDATE="true"
export VERSION HTTP_PORT BIND_ADDR DISCOVERY_INTERFACE SOUNDTOUCH_DEVICES
export SERVICE_URL SERVICE_CA
export BIN_PATH CONFIG_DIR ENV_FILE SERVICE_USER SERVICE_GROUP
exec "${SCRIPT_PATH}" "$@"
}
write_env_file() {
log "Updating env file: ${ENV_FILE}"
local vars=(
"PORT=${HTTP_PORT}"
"BIND_ADDR=${BIND_ADDR}"
"DISCOVERY_INTERFACE=${DISCOVERY_INTERFACE}"
"SOUNDTOUCH_DEVICES=${SOUNDTOUCH_DEVICES}"
"SERVICE_URL=${SERVICE_URL}"
"SERVICE_CA=${SERVICE_CA}"
)
if [[ ! -f "${ENV_FILE}" ]]; then
for entry in "${vars[@]}"; do
echo "${entry}" >> "${ENV_FILE}"
done
else
for entry in "${vars[@]}"; do
local key="${entry%%=*}"
local val="${entry#*=}"
if ! grep -q "^${key}=" "${ENV_FILE}"; then
echo "${key}=${val}" >> "${ENV_FILE}"
fi
done
fi
chmod 0640 "${ENV_FILE}"
chown root:"${SERVICE_GROUP}" "${ENV_FILE}" || true
}
write_systemd_unit() {
log "Writing systemd unit: /etc/systemd/system/${SERVICE_NAME}.service"
cat > "/etc/systemd/system/${SERVICE_NAME}.service" <<EOF
[Unit]
Description=Bose SoundTouch Web UI
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=${SERVICE_USER}
Group=${SERVICE_GROUP}
EnvironmentFile=${ENV_FILE}
ExecStart=${BIN_PATH}
Restart=on-failure
RestartSec=2
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
[Install]
WantedBy=multi-user.target
EOF
}
reload_enable_start() {
log "Reloading systemd, enabling and starting service"
systemctl daemon-reload
systemctl enable "${SERVICE_NAME}.service"
systemctl restart "${SERVICE_NAME}.service"
log "Verifying service health..."
local health_url="http://localhost:${HTTP_PORT}/health"
local max_retries=5
local count=0
local success=false
while [[ $count -lt $max_retries ]]; do
if curl -fs "$health_url" >/dev/null 2>&1; then
success=true
break
fi
echo "Waiting for service to respond at $health_url... ($((count+1))/$max_retries)"
sleep 2
count=$((count+1))
done
if [[ "$success" = true ]]; then
log "✅ soundtouch-web is healthy and responding!"
else
log "⚠️ Service started but did not respond at $health_url within timeout."
log "Check logs with: journalctl -u ${SERVICE_NAME}.service -n 50"
fi
}
show_status() {
log "Service status"
systemctl --no-pager --full status "${SERVICE_NAME}.service" || true
log "Listening socket (:${HTTP_PORT})"
ss -tulpn | grep -E ":${HTTP_PORT}\b" || true
if command -v ufw >/dev/null 2>&1 && ufw status | grep -q "Status: active"; then
log "Firewall check (UFW is active)"
if ! ufw status | grep -qE "${HTTP_PORT}.*ALLOW"; then
log "⚠️ UFW is active but port ${HTTP_PORT} might be blocked."
log "Run: sudo ufw allow ${HTTP_PORT}/tcp"
else
log "✅ UFW rule for port ${HTTP_PORT} appears to be in place."
fi
fi
cat <<EOF
Open in your browser:
http://<pi-ip>:${HTTP_PORT}/
soundtouch-web is a control panel — you can stop it when not in use:
sudo systemctl stop ${SERVICE_NAME}
sudo systemctl start ${SERVICE_NAME}
Logs:
journalctl -u ${SERVICE_NAME}.service -e --no-pager
EOF
}
main() {
need_root
ensure_cmd systemctl
ensure_cmd ss
if ! command -v curl >/dev/null 2>&1 && ! command -v wget >/dev/null 2>&1; then
apt_install_if_missing curl
fi
self_update "$@"
ensure_user_group
ensure_dirs
download_binary
write_env_file
write_systemd_unit
reload_enable_start
show_status
}
main "$@"