Name, Seitentitel, Dienste (playwish-web, playwish-controller), Dienst-Benutzer (playwish), Pfade (/opt/playwish, /usr/local/src/playwish), Vorlagen und Repository-Adresse. „Audiofy“ war bereits von mehreren Produkten belegt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
199 lines
9.2 KiB
Bash
Executable File
199 lines
9.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Playwish – Installation der Rolle "Player": Spotify-Connect-Gerät (raspotify) und Controller.
|
||
#
|
||
# install/install-player.sh Neuinstallation (fragt alles Nötige ab)
|
||
# install/install-player.sh --update nur Code, Python-Umgebung und Dienst aktualisieren
|
||
#
|
||
# Läuft auf Raspberry Pi, Mini-PC oder VM mit Debian 12+ (arm64, armhf oder amd64).
|
||
# Ausgabe wahlweise über einen Bluetooth-Lautsprecher (BlueALSA) oder einen ALSA-Ausgang
|
||
# (Klinke, HDMI, USB-DAC). Der Player baut nur ausgehende Verbindungen auf.
|
||
set -euo pipefail
|
||
source "$(dirname "$0")/lib.sh"
|
||
|
||
UPDATE=0
|
||
BLUETOOTH=0
|
||
for arg in "$@"; do
|
||
case "$arg" in
|
||
--update) UPDATE=1 ;;
|
||
-h|--help) sed -n '2,10p' "$0"; exit 0 ;;
|
||
*) abbruch "Unbekannte Option: $arg" ;;
|
||
esac
|
||
done
|
||
|
||
KONFIG="$PLAYWISH_DIR/config"
|
||
ENV_DATEI="$KONFIG/.env"
|
||
RASPOTIFY_CONF=/etc/raspotify/conf
|
||
export PLAYWISH_DIR PLAYWISH_USER
|
||
|
||
schritt "Playwish-Player: $([ $UPDATE = 1 ] && echo Update || echo Neuinstallation)"
|
||
pruefe_system
|
||
ARCH="$(dpkg --print-architecture)"
|
||
case "$ARCH" in arm64|armhf|amd64) ;; *) abbruch "raspotify gibt es nicht für $ARCH (nur arm64, armhf, amd64)." ;; esac
|
||
if [ $UPDATE = 1 ] && ! $SUDO test -f "$ENV_DATEI"; then
|
||
abbruch "Keine bestehende Installation in $PLAYWISH_DIR gefunden – ohne --update ausführen."
|
||
fi
|
||
|
||
# ------------------------------------------------------------------ Abfragen
|
||
if [ $UPDATE = 0 ]; then
|
||
schritt "Angaben"
|
||
frage GERAETENAME "Name des Spotify-Connect-Geräts" "Playwish"
|
||
echo " Audio-Ausgabe: 1) Bluetooth-Lautsprecher 2) Kabel/HDMI/USB (ALSA)"
|
||
frage AUSGABE "Auswahl" "1"
|
||
if [ "$AUSGABE" = 2 ]; then
|
||
BLUETOOTH=0
|
||
if command -v aplay >/dev/null; then
|
||
info "Verfügbare ALSA-Ausgänge:"
|
||
aplay -L 2>/dev/null | grep -E '^(default|sysdefault|plughw|hdmi)' | sed 's/^/ /' | head -20 || true
|
||
fi
|
||
frage ALSA_GERAET "ALSA-Gerät" "default"
|
||
else
|
||
BLUETOOTH=1
|
||
ALSA_GERAET="bluealsa:DEV=00:00:00:00:00:00,PROFILE=a2dp" # = zuletzt verbundener Lautsprecher
|
||
fi
|
||
frage SERVER_URL "Adresse des Playwish-Servers (leer = ohne Wünsche, nur Playlist)" ""
|
||
SERVER_URL="${SERVER_URL%/}"
|
||
PLAYER_TOKEN=""
|
||
[ -n "$SERVER_URL" ] && frage_geheim PLAYER_TOKEN "PLAYER_API_TOKEN vom Server (verdeckt)"
|
||
info "Spotify-App (developer.spotify.com): Redirect-URI http://127.0.0.1:8888/callback eintragen."
|
||
frage SPOTIFY_CLIENT_ID "Spotify Client ID"
|
||
frage_geheim SPOTIFY_CLIENT_SECRET "Spotify Client Secret (verdeckt)"
|
||
frage PLAYLIST "Grund-Playlist (Link oder ID; mit Server wird sie dort eingestellt)"
|
||
PLAYLIST_ID="$(sed -E 's#.*playlist[/:]([A-Za-z0-9]{22}).*#\1#' <<<"$PLAYLIST")"
|
||
[[ "$PLAYLIST_ID" =~ ^[A-Za-z0-9]{22}$ ]] || { warnung "Keine gültige Playlist – bitte später eintragen."; PLAYLIST_ID=""; }
|
||
fi
|
||
|
||
# ------------------------------------------------------------------ Pakete
|
||
schritt "Pakete"
|
||
$SUDO apt-get update -qq
|
||
PAKETE=(python3-venv curl ca-certificates alsa-utils)
|
||
if [ $UPDATE = 0 ] && [ "$BLUETOOTH" = 1 ]; then
|
||
PAKETE+=(bluez bluez-alsa-utils libasound2-plugin-bluez)
|
||
fi
|
||
pakete "${PAKETE[@]}"
|
||
|
||
if ! dpkg -s raspotify >/dev/null 2>&1; then
|
||
schritt "raspotify (Spotify Connect)"
|
||
$SUDO curl -sSfL https://dtcooper.github.io/raspotify/key.asc -o /usr/share/keyrings/raspotify_key.asc
|
||
$SUDO chmod 644 /usr/share/keyrings/raspotify_key.asc
|
||
echo "deb [signed-by=/usr/share/keyrings/raspotify_key.asc] https://dtcooper.github.io/raspotify raspotify main" \
|
||
| $SUDO tee /etc/apt/sources.list.d/raspotify.list >/dev/null
|
||
$SUDO apt-get update -qq
|
||
pakete raspotify
|
||
fi
|
||
|
||
# ------------------------------------------------------------------ raspotify einstellen (nur Neuinstallation)
|
||
if [ $UPDATE = 0 ]; then
|
||
schritt "raspotify einstellen"
|
||
$SUDO test -f "$RASPOTIFY_CONF.orig" || $SUDO cp "$RASPOTIFY_CONF" "$RASPOTIFY_CONF.orig"
|
||
setze_raspotify() { # KEY WERT – vorhandene (auch auskommentierte) Zeile ersetzen, sonst anhängen
|
||
if $SUDO grep -qE "^#?\s*$1=" "$RASPOTIFY_CONF"; then
|
||
$SUDO sed -i -E "0,/^#?\s*$1=.*/s||$1=\"$2\"|" "$RASPOTIFY_CONF"
|
||
else
|
||
echo "$1=\"$2\"" | $SUDO tee -a "$RASPOTIFY_CONF" >/dev/null
|
||
fi
|
||
}
|
||
setze_raspotify LIBRESPOT_NAME "$GERAETENAME"
|
||
setze_raspotify LIBRESPOT_DEVICE_TYPE "speaker"
|
||
setze_raspotify LIBRESPOT_BACKEND "alsa"
|
||
setze_raspotify LIBRESPOT_DEVICE "$ALSA_GERAET"
|
||
setze_raspotify LIBRESPOT_BITRATE "320"
|
||
# Anmeldedaten speichern (sonst nach jedem Neustart unsichtbar) und normal protokollieren
|
||
$SUDO sed -i -E 's/^(LIBRESPOT_DISABLE_CREDENTIAL_CACHE=|LIBRESPOT_QUIET=)/#\1/' "$RASPOTIFY_CONF"
|
||
info "Name „$GERAETENAME“, Ausgabe $ALSA_GERAET"
|
||
fi
|
||
$SUDO install -d /etc/systemd/system/raspotify.service.d
|
||
vorlage raspotify-10-playwish.conf /etc/systemd/system/raspotify.service.d/10-playwish.conf
|
||
$SUDO systemctl daemon-reload
|
||
$SUDO systemctl enable raspotify >/dev/null 2>&1
|
||
[ $UPDATE = 0 ] && $SUDO systemctl restart raspotify
|
||
|
||
# ------------------------------------------------------------------ Bluetooth (nur Neuinstallation)
|
||
BT_MAC=""
|
||
if [ $UPDATE = 0 ] && [ "$BLUETOOTH" = 1 ]; then
|
||
schritt "Bluetooth-Lautsprecher"
|
||
# Auf frischen Systemen ist Bluetooth oft per rfkill gesperrt (z. B. ohne gesetztes WLAN-Land)
|
||
command -v rfkill >/dev/null && { $SUDO rfkill unblock bluetooth || true; }
|
||
$SUDO systemctl enable --now bluetooth >/dev/null 2>&1 || warnung "Bluetooth-Dienst ließ sich nicht starten."
|
||
# Der Player soll nicht selbst als Bluetooth-Empfänger für fremde Handys dienen
|
||
$SUDO systemctl disable --now bluealsa-aplay >/dev/null 2>&1 || true
|
||
info "Lautsprecher jetzt in den Kopplungsmodus versetzen (meist Bluetooth-Taste halten, bis die LED blinkt)."
|
||
if ja_nein "Jetzt suchen und koppeln?" j; then
|
||
BT_MAC="$(cd "$REPO/player" && python3 - <<'PY'
|
||
import sys, lautsprecher as l
|
||
print(" Suche 15 s …", file=sys.stderr)
|
||
geraete = l.suchen()
|
||
if not geraete:
|
||
print(" Kein Audiogerät gefunden.", file=sys.stderr); sys.exit(0)
|
||
for i, g in enumerate(geraete, 1):
|
||
print(f" {i}) {g['name'] or 'Unbekannt'} ({g['mac']})", file=sys.stderr)
|
||
print(" Nummer: ", end="", file=sys.stderr, flush=True)
|
||
wahl = open("/dev/tty").readline().strip()
|
||
if not wahl.isdigit() or not 1 <= int(wahl) <= len(geraete):
|
||
sys.exit(0)
|
||
mac = geraete[int(wahl) - 1]["mac"]
|
||
ok, meldung = l.koppeln(mac)
|
||
print(f" {meldung}", file=sys.stderr)
|
||
if ok:
|
||
print(mac)
|
||
PY
|
||
)" || true
|
||
[ -n "$BT_MAC" ] || warnung "Kein Lautsprecher gekoppelt – später im Admin-Bereich unter Einstellungen → Lautsprecher."
|
||
fi
|
||
fi
|
||
|
||
# ------------------------------------------------------------------ Benutzer, Ordner, Code
|
||
schritt "Dienst-Benutzer, Ordner und Code"
|
||
dienst_benutzer
|
||
$SUDO install -d -o root -g "$PLAYWISH_USER" -m 750 "$PLAYWISH_DIR"
|
||
$SUDO install -d -o "$PLAYWISH_USER" -g "$PLAYWISH_USER" -m 700 "$KONFIG"
|
||
code_kopieren "$REPO/player" "$PLAYWISH_DIR/app"
|
||
venv_einrichten "$PLAYWISH_DIR/app/requirements.txt"
|
||
info "Code in $PLAYWISH_DIR/app, Konfiguration in $KONFIG"
|
||
|
||
# ------------------------------------------------------------------ Konfiguration
|
||
schritt "Konfiguration ($ENV_DATEI)"
|
||
if [ $UPDATE = 0 ]; then
|
||
$SUDO test -f "$ENV_DATEI" || $SUDO install -o "$PLAYWISH_USER" -g "$PLAYWISH_USER" -m 600 /dev/null "$ENV_DATEI"
|
||
env_setzen "$ENV_DATEI" SPOTIFY_CLIENT_ID "$SPOTIFY_CLIENT_ID"
|
||
env_setzen "$ENV_DATEI" SPOTIFY_CLIENT_SECRET "$SPOTIFY_CLIENT_SECRET"
|
||
env_setzen "$ENV_DATEI" SPOTIFY_REDIRECT_URI "http://127.0.0.1:8888/callback"
|
||
env_setzen "$ENV_DATEI" DEVICE_NAME "$GERAETENAME"
|
||
env_setzen "$ENV_DATEI" WISH_EVERY_N "3"
|
||
[ -n "$PLAYLIST_ID" ] && env_setzen "$ENV_DATEI" SPOTIFY_PLAYLIST_ID "$PLAYLIST_ID"
|
||
[ -n "$BT_MAC" ] && env_setzen "$ENV_DATEI" BT_SPEAKER_MAC "$BT_MAC"
|
||
if [ -n "$SERVER_URL" ]; then
|
||
env_setzen "$ENV_DATEI" SERVER_URL "$SERVER_URL"
|
||
env_setzen "$ENV_DATEI" PLAYER_API_TOKEN "$PLAYER_TOKEN"
|
||
fi
|
||
unset SPOTIFY_CLIENT_SECRET PLAYER_TOKEN
|
||
|
||
schritt "Spotify-Login für die Wiedergabe-Steuerung"
|
||
info "Öffne gleich die angezeigte Adresse im Browser, melde dich an und füge danach die"
|
||
info "Adresse aus der Adresszeile (http://127.0.0.1:8888/callback?...) hier ein."
|
||
"${ALS_DIENST[@]}" "$PLAYWISH_DIR/.venv/bin/python" "$PLAYWISH_DIR/app/auth.py" --profil player --env "$ENV_DATEI" \
|
||
|| warnung "Login fehlgeschlagen – später nachholen (siehe README)."
|
||
else
|
||
# alte Schlüsselnamen übernehmen
|
||
env_umbenennen "$ENV_DATEI" VPS_URL SERVER_URL
|
||
env_umbenennen "$ENV_DATEI" PI_API_TOKEN PLAYER_API_TOKEN
|
||
fi
|
||
|
||
# ------------------------------------------------------------------ Dienst
|
||
schritt "Dienst playwish-controller"
|
||
vorlage playwish-controller.service /etc/systemd/system/playwish-controller.service
|
||
$SUDO systemctl daemon-reload
|
||
$SUDO systemctl enable playwish-controller >/dev/null 2>&1
|
||
$SUDO systemctl restart playwish-controller
|
||
sleep 5
|
||
systemctl is-active --quiet playwish-controller || abbruch "playwish-controller läuft nicht – siehe: journalctl -u playwish-controller"
|
||
info "playwish-controller läuft"
|
||
|
||
# ------------------------------------------------------------------ Zusammenfassung
|
||
schritt "Fertig"
|
||
info "Logs: journalctl -u playwish-controller -f · journalctl -u raspotify -f"
|
||
if [ $UPDATE = 0 ]; then
|
||
info ""
|
||
info "Wichtig: Wähle das Gerät „$GERAETENAME“ EINMAL in der Spotify-App aus (im selben Netz)."
|
||
info "Danach sind die Anmeldedaten gespeichert, und der Player startet nach jedem Neustart selbst."
|
||
fi
|