#!/usr/bin/env bash # Install VoicePult for the logged-in Linux desktop user. set -Eeuo pipefail SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" NO_START=0 NO_OPEN=0 AUTOSTART=1 SKIP_PACKAGES=0 DRY_RUN=0 LAN_ARGS=() usage() { cat <<'HELP' VoicePult – automatische Einrichtung für Linux Aufruf: ./setup.sh [Optionen] Ohne sudo in der angemeldeten Desktop-Sitzung ausführen. Nur fehlende Systempakete werden über sudo installiert. Unterstützt apt, dnf und pacman. --lan Tablet-/Handyzugriff mit Zugangscode aktivieren --no-start Dateien einrichten, aber keine Dienste starten --no-open Nach der Einrichtung keinen Browser öffnen --no-autostart Nicht automatisch bei der Anmeldung starten --skip-packages Paketinstallation überspringen (Voraussetzungen prüfen) --dry-run Einrichtung anzeigen, ohne Änderungen vorzunehmen -h, --help Diese Hilfe anzeigen Die Installation erfolgt benutzerspezifisch in den XDG-Daten-, Konfigurations- und Statusverzeichnissen. Vorhandene Einstellungen bleiben bei erneuter Ausführung erhalten. Die heruntergeladene Projektkopie kann anschließend verschoben werden. HELP } for arg in "$@"; do case "$arg" in --lan) LAN_ARGS=(--lan) ;; --no-start) NO_START=1; NO_OPEN=1 ;; --no-open) NO_OPEN=1 ;; --no-autostart) AUTOSTART=0 ;; --skip-packages) SKIP_PACKAGES=1 ;; --dry-run) DRY_RUN=1 ;; -h|--help) usage; exit 0 ;; *) printf 'Unbekannte Option: %s\n' "$arg" >&2; usage >&2; exit 2 ;; esac done say() { printf '\n[VoicePult] %s\n' "$*"; } fail() { printf '\n[VoicePult] Fehler: %s\n' "$*" >&2; exit 1; } trap 'printf "\n[VoicePult] Einrichtung fehlgeschlagen (Zeile %s). Nach Behebung erneut ./setup.sh ausführen.\n" "$LINENO" >&2' ERR [[ "$(uname -s)" == Linux ]] || fail 'Diese Version unterstützt Linux, nicht Windows oder macOS.' [[ "$EUID" -ne 0 ]] || fail 'Bitte ./setup.sh als normalen Desktop-Benutzer ausführen, nicht mit sudo. Paketinstallationen fragen sudo selbst an.' for file in lan_access.py audio_meter.py server.py start.sh web/index.html web/app.js web/style.css web/icon.svg scripts/install.py scripts/launcher.py; do [[ -f "$SCRIPT_DIR/$file" ]] || fail "Projektdatei fehlt: $file. Bitte den vollständigen VoicePult-Ordner kopieren." done # Validate XDG destinations before using them as installation paths. for destination in "${XDG_DATA_HOME:-$HOME/.local/share}" "${XDG_CONFIG_HOME:-$HOME/.config}" "${XDG_STATE_HOME:-$HOME/.local/state}"; do [[ "$destination" == /* && "$destination" != / && "$destination" != *$'\n'* && "$destination" != *$'\r'* && "$destination" != *$'\t'* ]] || fail 'XDG-Verzeichnisse müssen absolute Pfade ohne Steuerzeichen sein.' done manager='' if command -v apt-get >/dev/null; then manager=apt elif command -v dnf >/dev/null; then manager=dnf elif command -v pacman >/dev/null; then manager=pacman fi packages=() if ! command -v python3 >/dev/null || ! python3 -c 'import sys; sys.exit(sys.version_info < (3, 10))' 2>/dev/null; then if [[ "$manager" == pacman ]]; then packages+=(python); else packages+=(python3); fi fi if ! command -v pactl >/dev/null || ! command -v parec >/dev/null; then if [[ "$manager" == pacman ]]; then packages+=(libpulse); else packages+=(pulseaudio-utils); fi fi command -v xdg-open >/dev/null || packages+=(xdg-utils) browser_found=0 for browser_cmd in firefox firefox-esr chromium chromium-browser google-chrome brave-browser microsoft-edge epiphany; do if command -v "$browser_cmd" >/dev/null; then browser_found=1; break; fi done if [[ "$browser_found" == 0 ]] && command -v xdg-settings >/dev/null; then # Also recognize browsers installed through Flatpak or desktop launchers. if [[ -n "$(xdg-settings get default-web-browser 2>/dev/null || true)" ]]; then browser_found=1; fi fi if [[ "$browser_found" == 0 ]]; then os_id="$(awk -F= '$1=="ID" {gsub(/"/,"",$2); print $2}' /etc/os-release 2>/dev/null || true)" if [[ "$manager" == apt && "$os_id" != ubuntu && "$os_id" != linuxmint && "$os_id" != pop && "$os_id" != neon ]]; then packages+=(firefox-esr) else packages+=(firefox) fi fi # Do not replace an existing PulseAudio or PipeWire installation. audio_ready=0 new_audio=0 if command -v pactl >/dev/null && timeout 5 pactl info >/dev/null 2>&1; then audio_ready=1; fi if [[ "$audio_ready" == 0 ]] && ! command -v pipewire-pulse >/dev/null && ! command -v pulseaudio >/dev/null; then new_audio=1 case "$manager" in apt) packages+=(pipewire pipewire-pulse wireplumber) ;; dnf) packages+=(pipewire pipewire-pulseaudio wireplumber) ;; pacman) packages+=(pipewire pipewire-audio pipewire-pulse wireplumber) ;; *) [[ "$SKIP_PACKAGES" == 1 ]] || fail 'Kein unterstützter Paketmanager. Python ≥ 3.10, pactl, xdg-open und PipeWire-Pulse installieren und --skip-packages verwenden.' ;; esac fi say "Installation für $USER" printf ' Programm: %s/voicepult/app\n' "${XDG_DATA_HOME:-$HOME/.local/share}" printf ' Einstellungen: %s/voicepult/config.json\n' "${XDG_CONFIG_HOME:-$HOME/.config}" printf ' Protokoll: %s/voicepult/server.log\n' "${XDG_STATE_HOME:-$HOME/.local/state}" printf ' Autostart: %s\n' "$([[ "$AUTOSTART" == 1 ]] && printf ja || printf nein)" if ((${#packages[@]})); then printf ' Fehlende Pakete (%s): %s\n' "${manager:-manuell}" "${packages[*]}"; fi if [[ "$DRY_RUN" == 1 ]]; then say 'Vorschau beendet. Keine Dateien, Pakete oder Dienste geändert.' exit 0 fi if ((${#packages[@]})) && [[ "$SKIP_PACKAGES" == 0 ]]; then [[ -n "$manager" ]] || fail 'Kein unterstützter Paketmanager; Voraussetzungen manuell installieren und --skip-packages verwenden.' command -v sudo >/dev/null || fail 'sudo fehlt. Bitte die oben genannten Pakete administrativ installieren und Setup erneut ausführen.' say 'Installiere fehlende Systempakete. sudo fragt gegebenenfalls dein Passwort ab.' sudo -v case "$manager" in apt) sudo apt-get update; sudo apt-get install -y --no-remove "${packages[@]}" ;; dnf) sudo dnf install -y "${packages[@]}" ;; pacman) sudo pacman -S --needed --noconfirm "${packages[@]}" ;; esac fi command -v python3 >/dev/null || fail 'Python 3 fehlt.' python3 -c 'import sys; sys.exit(sys.version_info < (3, 10))' || fail 'Python ≥ 3.10 erforderlich. Bitte das Betriebssystem bzw. Python aktualisieren.' command -v pactl >/dev/null || fail 'pactl fehlt. Bitte pulseaudio-utils (Arch: libpulse) installieren.' command -v xdg-open >/dev/null || fail 'xdg-open fehlt. Bitte xdg-utils installieren.' if [[ "$browser_found" == 0 && "$SKIP_PACKAGES" == 1 ]]; then say 'Kein Browser erkannt. Bitte einen Browser installieren oder die lokale URL in einem vorhandenen Browser öffnen.' fi # Only start the already-installed audio stack if it is currently unreachable. if [[ "$NO_START" == 0 && "$audio_ready" == 0 ]]; then say 'Prüfe den Audioserver der Desktop-Sitzung.' if command -v systemctl >/dev/null && systemctl --user show-environment >/dev/null 2>&1; then if command -v pipewire-pulse >/dev/null; then if [[ "$new_audio" == 1 ]]; then systemctl --user enable --now pipewire.socket pipewire-pulse.socket wireplumber.service || fail 'Der neue PipeWire-Dienst konnte nicht aktiviert werden.' else systemctl --user start pipewire.socket pipewire-pulse.socket wireplumber.service || fail 'PipeWire konnte nicht gestartet werden. Bitte in einer angemeldeten Desktop-Sitzung erneut versuchen.' fi elif command -v pulseaudio >/dev/null; then systemctl --user start pulseaudio.socket || fail 'PulseAudio konnte nicht gestartet werden.' fi elif command -v pulseaudio >/dev/null; then pulseaudio --start || fail 'PulseAudio konnte nicht gestartet werden.' fi for attempt in {1..10}; do if timeout 5 pactl info >/dev/null 2>&1; then audio_ready=1; break; fi sleep 1 done [[ "$audio_ready" == 1 ]] || fail 'Kein Audioserver erreichbar. Bitte in deiner grafischen Desktop-Sitzung ausführen; pactl info muss funktionieren. Bei Systemen ohne systemd PipeWire über die Sitzungsverwaltung starten.' fi say 'Installiere Programm, Menüeintrag und Benutzer-Autostart.' python3 "$SCRIPT_DIR/scripts/install.py" --source "$SCRIPT_DIR" --autostart "$AUTOSTART" --start "$((1-NO_START))" --open "$((1-NO_OPEN))" "${LAN_ARGS[@]}" say 'Einrichtung abgeschlossen. VoicePult findest du im Anwendungsmenü.' printf ' Oberfläche: http://127.0.0.1:8765\n'