Audiofy – Musikwünsche für Partys

Server (Website, Admin-Bereich, Player-API) und Player (raspotify +
Controller) mit Install-Skripten für Debian-Systeme.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-17 19:16:39 +01:00
co-authored by Claude Opus 5.5
commit 3610de7bae
45 changed files with 5666 additions and 0 deletions
+146
View File
@@ -0,0 +1,146 @@
# Audiofy – gemeinsame Hilfsfunktionen für die Install-Skripte (wird per "source" geladen)
# shellcheck shell=bash
AUDIOFY_DIR="${AUDIOFY_DIR:-/opt/audiofy}"
AUDIOFY_USER="${AUDIOFY_USER:-audiofy}"
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
VORLAGEN="$REPO/install/vorlagen"
if [ "$(id -u)" -eq 0 ]; then
SUDO=""
ALS_DIENST=(runuser -u "$AUDIOFY_USER" --)
else
SUDO="sudo"
ALS_DIENST=(sudo -u "$AUDIOFY_USER")
fi
# --- Ausgabe ------------------------------------------------------------------
schritt() { printf '\n\033[1m==> %s\033[0m\n' "$*"; }
info() { printf ' %s\n' "$*"; }
warnung() { printf '\033[33m ! %s\033[0m\n' "$*"; }
abbruch() { printf '\033[31m ✗ %s\033[0m\n' "$*" >&2; exit 1; }
# --- Abfragen -----------------------------------------------------------------
# frage VAR "Text" [Standard]
frage() {
local antwort
read -r -p " $2${3:+ [$3]}: " antwort
printf -v "$1" '%s' "${antwort:-${3:-}}"
}
# frage_geheim VAR "Text" – Eingabe wird nicht angezeigt
frage_geheim() {
local antwort
read -r -s -p " $2: " antwort
echo
printf -v "$1" '%s' "$antwort"
}
# ja_nein "Frage" [j|n] – Rückgabewert 0 = ja
ja_nein() {
local standard="${2:-j}" antwort
read -r -p " $1 [$([ "$standard" = j ] && echo J/n || echo j/N)]: " antwort
antwort="${antwort:-$standard}"
[[ "$antwort" =~ ^[jJyY] ]]
}
# --- System -------------------------------------------------------------------
pruefe_system() {
[ -f /etc/debian_version ] || abbruch "Nur für Debian und Debian-basierte Systeme (z. B. Raspberry Pi OS)."
command -v systemctl >/dev/null || abbruch "systemd wird benötigt."
command -v apt-get >/dev/null || abbruch "apt wird benötigt."
if [ -n "$SUDO" ]; then
command -v sudo >/dev/null || abbruch "Bitte als root ausführen oder sudo installieren."
info "Für die Einrichtung sind root-Rechte nötig – sudo fragt ggf. nach deinem Passwort."
sudo true || abbruch "sudo nicht möglich."
fi
python3 -c 'import sys; sys.exit(sys.version_info < (3, 11))' 2>/dev/null \
|| abbruch "Python 3.11 oder neuer wird benötigt (Debian 12 oder neuer)."
}
pakete() {
info "Installiere: $*"
$SUDO env DEBIAN_FRONTEND=noninteractive apt-get install -y -qq "$@" >/dev/null
}
dienst_benutzer() {
if ! id "$AUDIOFY_USER" >/dev/null 2>&1; then
$SUDO useradd --system --home-dir "$AUDIOFY_DIR" --no-create-home --shell /usr/sbin/nologin "$AUDIOFY_USER"
info "Dienst-Benutzer '$AUDIOFY_USER' angelegt (ohne Login, ohne sudo)"
fi
}
# code_kopieren QUELLE ZIEL – ersetzt den Inhalt von ZIEL (gehört root, Dienst darf nur lesen)
code_kopieren() {
local quelle="$1" ziel="$2"
$SUDO install -d -o root -g "$AUDIOFY_USER" -m 750 "$ziel"
$SUDO find "$ziel" -mindepth 1 -delete
tar -C "$quelle" --exclude='__pycache__' --exclude='.venv' --exclude='.pytest_cache' --exclude='tests' -cf - . \
| $SUDO tar -C "$ziel" --no-same-owner -xf -
$SUDO chown -R root:"$AUDIOFY_USER" "$ziel"
$SUDO find "$ziel" -type d -exec chmod 750 {} +
$SUDO find "$ziel" -type f -exec chmod 640 {} +
}
# venv_einrichten REQUIREMENTS
venv_einrichten() {
local venv="$AUDIOFY_DIR/.venv"
[ -x "$venv/bin/python" ] || $SUDO python3 -m venv "$venv"
$SUDO "$venv/bin/pip" install -q --upgrade pip
$SUDO "$venv/bin/pip" install -q -r "$1"
$SUDO chown -R root:"$AUDIOFY_USER" "$venv"
$SUDO chmod -R g+rX,o-rwx "$venv"
}
# vorlage DATEI ZIEL – ersetzt {{NAME}} durch den Wert der Shell-Variablen NAME
vorlage() {
local inhalt
inhalt="$(python3 - "$VORLAGEN/$1" <<'PY'
import os, re, sys
text = open(sys.argv[1]).read()
def ersetzen(m):
wert = os.environ.get(m.group(1))
if wert is None:
sys.exit(f"Vorlage {sys.argv[1]}: {m.group(1)} ist nicht gesetzt")
return wert
print(re.sub(r"\{\{([A-Z_]+)\}\}", ersetzen, text), end="")
PY
)" || abbruch "Vorlage $1 konnte nicht ausgefüllt werden"
printf '%s\n' "$inhalt" | $SUDO tee "$2" >/dev/null
}
# --- .env (gehört dem Dienst-Benutzer, Rechte 600) ------------------------------
# env_setzen DATEI KEY WERT – der Wert läuft über stdin (nicht in der Prozessliste sichtbar)
env_setzen() {
printf '%s' "$3" | "${ALS_DIENST[@]}" python3 -c '
import os, sys
pfad, key = sys.argv[1], sys.argv[2]
wert = sys.stdin.read()
zeilen = open(pfad).read().splitlines() if os.path.exists(pfad) else []
zeilen = [z for z in zeilen if not z.strip().startswith(key + "=")] + [f"{key}={wert}"]
fd = os.open(pfad, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
with os.fdopen(fd, "w") as f:
f.write("\n".join(zeilen) + "\n")
' "$1" "$2"
}
# env_wert DATEI KEY – gibt den Wert aus (leer, wenn nicht vorhanden)
env_wert() {
"${ALS_DIENST[@]}" sh -c 'grep -m1 "^$2=" "$1" 2>/dev/null | cut -d= -f2-' _ "$1" "$2" || true
}
# env_umbenennen DATEI ALT NEU – übernimmt alte Schlüsselnamen (für Updates)
env_umbenennen() {
local wert
if [ -z "$(env_wert "$1" "$3")" ]; then
wert="$(env_wert "$1" "$2")"
if [ -n "$wert" ]; then
env_setzen "$1" "$3" "$wert"
info "$2 → $3 übernommen"
fi
fi
}