#!/usr/bin/env bash
#
# spotify-setup.sh
#
# Turns a freshly installed, desktop-less Debian machine into a
# dedicated "Spotify appliance": power on -> auto-login -> X starts ->
# Openbox starts -> Spotify appears full-screen. No taskbar, no file
# manager, no desktop environment.
#
# ---------------------------------------------------------------------
# PREREQUISITES (must already be true before you run this):
#   - Debian 13 (or current Stable) installed with ONLY the "Standard
#     system utilities" task selected. No GNOME/KDE/XFCE/etc.
#   - You are logged in as the regular (non-root) user who should own
#     the Spotify session, and that user is in the sudoers group.
#   - The machine has a working internet connection.
#
# USAGE:
#   1. Download this script: `wget 'https://gist.pother.ca/spotify-setup.sh'`
#   2. Run it: `bash ./spotify-setup.sh`
#   3. Answer the prompts as they appear
#
# DESIGN NOTES FOR FUTURE MAINTAINERS:
#   - Re-running this script is mostly safe (apt installs are
#     idempotent, config files are regenerated from scratch or guarded
#     against duplicate appends) but always skim the diff of anything
#     it overwrites if you're re-running after a partial failure.
#
# For debugging, packages can be installed, command run and output shared
# through pastebin:
#
#   sudo apt install -y alsa-utils pastebinit pulseaudio-utils
#   systemctl --user status pipewire pipewire-pulse wireplumber \
#       | pastebinit -b pastebin.com -E
#
# Or output multiple commands to an output file and paste that:
#   systemctl --no-pager --user status pipewire pipewire-pulse wireplumber >> output.txt
#   grep -nE '(pipewire|wireplumber|systemctl)' spotify-setup.sh >> output.txt
#   aplay -l >> output.txt
#   ps -ef | grep -E '(pipe|pulse)' >> output.txt
#   pactl info >> output.txt
#   wpctl status >> output.txt
#   journalctl --user -u wireplumber -b --no-pager | grep -i fail >> output.txt
#   systemctl --user show-environment >> output.txt
#   pastebinit -b pastebin.com -i output.txt

set -e   # Fail fast - don't limp forward with a half-configured box.

# We ask a lot of yes/no questions, so we need a real terminal attached.
if [ ! -t 0 ]; then
    echo "This script asks interactive yes/no questions and needs a real"
    echo "terminal on stdin. Run it directly, e.g.:"
    echo "    ./spotify-setup.sh"
    echo "rather than piping input into it or redirecting stdin."
    exit 1
fi

echo "=================================================================="
echo " Spotify Appliance Setup"
echo "=================================================================="
echo "This will install Xorg, Openbox, PipeWire and the official"
echo "Spotify client, and configure this machine to boot straight into"
echo "a full-screen Spotify window with no desktop environment."
echo
read -p "Continue? [y/N]: " CONFIRM_START
if [[ ! "$CONFIRM_START" =~ ^[Yy]$ ]]; then
    echo "Aborted - nothing was changed."
    exit 0
fi

# Refuse to run as root: we need a real, non-root "appliance user" whose
# home directory we can drop dotfiles into, and who we can autologin as.
if [ "$(id -u)" -eq 0 ]; then
    echo "Please run this as the regular user who should own the Spotify"
    echo "session (it calls sudo itself whenever it needs root), not as root."
    exit 1
fi

APPLIANCE_USER="$(whoami)"
APPLIANCE_HOME="$HOME"
echo "Setting up the appliance for user: $APPLIANCE_USER (home: $APPLIANCE_HOME)"
echo

# Cache sudo credentials up front, then keep the sudo timestamp alive in
# the background for the rest of the script. Without this, a slow step
# like 'apt full-upgrade' can cause sudo's cached credentials to expire
# and re-prompt for a password in the middle of an unattended-feeling run.
echo "This script needs sudo for package installs and system config -"
echo "you may be asked for your password now."
sudo -v
( while true; do sudo -n true; sleep 60; kill -0 "$$" 2>/dev/null || exit; done ) 2>/dev/null &
SUDO_KEEPALIVE_PID=$!
trap 'kill "$SUDO_KEEPALIVE_PID" 2>/dev/null || true' EXIT


# =======================================================================
# STEP 1/11: Update the system
# =======================================================================
echo
echo "---- Step 1/11: Updating system packages ----"
sudo apt update
sudo apt full-upgrade -y


# =======================================================================
# STEP 2/11: Install packages
# =======================================================================
echo
echo "---- Step 2/11: Installing packages ----"
sudo apt install -y \
    dbus-x11 \
    fonts-dejavu \
    gnupg \
    mesa-utils \
    openbox \
    pipewire \
    pipewire-pulse \
    wireplumber \
    xinit \
    xorg

# =======================================================================
# STEP 3/11: Install Spotify from its official Debian repository
# =======================================================================
# Copied from https://www.spotify.com/us/download/linux/
echo
echo "---- Step 3/11: Install Spotify ----"

wget --output-document - --quiet https://download.spotify.com/debian/pubkey_5384CE82BA52C83A.asc \
    | sudo gpg --dearmor --yes -o /etc/apt/trusted.gpg.d/spotify.gpg
echo "deb https://repository.spotify.com stable non-free" \
    | sudo tee /etc/apt/sources.list.d/spotify.list

sudo apt-get update && sudo apt-get install -y spotify-client

# Electron apps (Spotify included) sometimes refuse to start on a minimal
# system with a sandbox permission error. If that happens, the quick
# troubleshooting step is launching it with `spotify --no-sandbox` to
# confirm that's the cause, then fix the chrome-sandbox helper's setuid
# bit properly rather than leaving --no-sandbox on permanently.

# =======================================================================
# STEP 4/11 (OPTIONAL, default yes): Manual smoke test
# =======================================================================
echo
echo "---- Step 4/11: Manual test (optional, recommended) ----"
echo "Before wiring up autologin and auto-start, it's worth confirming"
echo "X actually starts, Spotify logs in, and audio plays on THIS"
echo "hardware, rather than finding out after autologin is already live."
echo
echo "If you say yes, this starts a temporary X session with Spotify as"
echo "its only window (no window manager yet - that comes later)."
echo "Close the Spotify window to end the test and return here."
echo "This must be run from the local console, not over SSH."
read -p "Run a manual test of Spotify now? [Y/n]: " DO_TEST

if [[ ! "$DO_TEST" =~ ^[Nn]$ ]]; then
    if [ -n "$DISPLAY" ]; then
        echo "It looks like you're already inside a graphical session -"
        echo "skipping the test to avoid starting a nested X server."
    else
        startx /usr/bin/spotify || echo "Test session exited."
        echo "Test session ended. Continuing setup..."
    fi
else
    echo "Skipping manual test."
fi


# =======================================================================
# STEP 5/11: Configure Openbox autostart (core step, with two optional
# sub-choices baked into the generated file: hiding the mouse cursor,
# and auto-restarting Spotify if it crashes)
# =======================================================================
echo
echo "---- Step 5/11: Configuring Openbox autostart ----"
mkdir -p "$APPLIANCE_HOME/.config/openbox"
AUTOSTART_FILE="$APPLIANCE_HOME/.config/openbox/autostart"

echo
echo "OPTIONAL: hide the mouse cursor when idle."
echo "Installs 'unclutter', which hides the mouse pointer after 1 second"
echo "of inactivity - useful for a kiosk display with no cursor wanted"
echo "on screen. If this machine has no monitor (audio-only), this has"
echo "no visible effect either way."
read -p "Install and enable unclutter (hide mouse cursor)? [y/N]: " USE_UNCLUTTER

if [[ "$USE_UNCLUTTER" =~ ^[Yy]$ ]]; then
    sudo apt install -y unclutter
fi

echo
echo "OPTIONAL: automatically restart Spotify if it crashes or is force-"
echo "closed (e.g. by an update). Recommended for a machine nobody will"
echo "be sitting in front of to manually relaunch Spotify."
read -p "Enable auto-restart for Spotify? [Y/n]: " AUTO_RESTART_SPOTIFY

# Regenerate the autostart file from scratch each run, based on current
# answers - simplest way to keep it idempotent across re-runs.
cat > "$AUTOSTART_FILE" << 'EOF'
#!/bin/sh
# Openbox autostart - runs once the Openbox session begins.
# Generated by spotify-setup.sh - edits here will be
# overwritten if that script is re-run.
EOF

if [[ "$USE_UNCLUTTER" =~ ^[Yy]$ ]]; then
    echo 'unclutter --timeout 1 &' >> "$AUTOSTART_FILE"
fi

if [[ ! "$AUTO_RESTART_SPOTIFY" =~ ^[Nn]$ ]]; then
    cat >> "$AUTOSTART_FILE" << 'EOF'
# Auto-restart loop: if Spotify exits for any reason, relaunch it.
while true
do
    spotify
    sleep 2
done &
EOF
else
    echo 'spotify &' >> "$AUTOSTART_FILE"
fi

chmod +x "$AUTOSTART_FILE"


# =======================================================================
# STEP 6/11: Create .xinitrc (core step; bundles in disabling screen
# blanking, since that's what the original guide does too)
# =======================================================================
echo
echo "---- Step 6/11: Creating .xinitrc ----"
cat > "$APPLIANCE_HOME/.xinitrc" << 'EOF'
#!/bin/sh
# .xinitrc - run by 'startx' to set up the X session.
# Generated by spotify-setup.sh.

# Disable screen blanking / DPMS power-saving / screensaver so a
# connected display never goes dark - appropriate for a kiosk that
# should always show the Spotify UI. Harmless no-op if no monitor
# is attached.
xset s off
xset -dpms
xset s noblank

exec openbox-session
EOF
chmod +x "$APPLIANCE_HOME/.xinitrc"


# =======================================================================
# STEP 7/11 (OPTIONAL, default no): Force Spotify to always open
# maximized/fullscreen via an Openbox window rule
# =======================================================================
echo
echo "---- Step 7/11: Force Spotify fullscreen (optional) ----"
echo "Spotify remembers its own window size between runs, so pressing"
echo "F11 (or maximizing) once by hand is often enough. If this machine"
echo "has no keyboard/mouse attached to do that with, you can instead"
echo "force it via an Openbox rule so it ALWAYS opens maximized."
read -p "Force Spotify to always open maximized/fullscreen? [y/N]: " FORCE_FULLSCREEN

if [[ "$FORCE_FULLSCREEN" =~ ^[Yy]$ ]]; then
    RC_XML="$APPLIANCE_HOME/.config/openbox/rc.xml"

    if [ ! -f "$RC_XML" ]; then
        # No custom rc.xml yet - start from Openbox's own default config
        # so we don't have to hand-write the rest of it (it's long).
        cp /etc/xdg/openbox/rc.xml "$RC_XML"
    fi

    if grep -q 'class="Spotify"' "$RC_XML"; then
        echo "rc.xml already has a Spotify rule - leaving it as-is."
    elif grep -q '</applications>' "$RC_XML"; then
        # Default rc.xml already has an <applications> section - add
        # our rule inside it rather than creating a second one, since
        # Openbox's handling of duplicate top-level sections is not
        # something we want to rely on.
        sed -i '/<\/applications>/i\
        <application class="Spotify">\
            <maximized>yes</maximized>\
            <fullscreen>yes</fullscreen>\
        </application>' "$RC_XML"
    else
        # No <applications> section at all - add a whole new one just
        # before the closing </openbox_config> tag.
        sed -i '/<\/openbox_config>/i\
    <applications>\
        <application class="Spotify">\
            <maximized>yes</maximized>\
            <fullscreen>yes</fullscreen>\
        </application>\
    </applications>' "$RC_XML"
    fi
    echo "rc.xml updated. This is read fresh each time Openbox starts,"
    echo "so it will take effect on next boot/login."
fi


# =======================================================================
# STEP 8/11: Automatic login on tty1 (confirm - real security trade-off)
# =======================================================================
echo
echo "---- Step 8/11: Automatic login on tty1 ----"
echo "This makes '$APPLIANCE_USER' log in automatically on the text"
echo "console (tty1), no password prompt. Combined with the next step"
echo "(auto-starting X), this is what makes 'power on' turn straight"
echo "into Spotify with no login screen at all."
echo
echo "SECURITY NOTE: anyone who can power this machine on gets a logged"
echo "-in session as '$APPLIANCE_USER' with no password. Only enable"
echo "this on a machine that is physically dedicated to this one job."
read -p "Enable automatic login for '$APPLIANCE_USER' on tty1? [y/N]: " ENABLE_AUTOLOGIN

if [[ "$ENABLE_AUTOLOGIN" =~ ^[Yy]$ ]]; then
    sudo mkdir -p /etc/systemd/system/getty@tty1.service.d
    sudo tee /etc/systemd/system/getty@tty1.service.d/autologin.conf > /dev/null << EOF
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin $APPLIANCE_USER --noclear %I \$TERM
EOF
    sudo systemctl daemon-reload
    AUTOLOGIN_ENABLED=1
    echo "Autologin configured. Takes effect on next boot."
else
    AUTOLOGIN_ENABLED=0
    echo "Skipping autologin - you'll log in manually at each boot."
fi


# =======================================================================
# STEP 9/11: Automatically start X after login (core step, with one
# optional sub-choice: restart X in a loop if it ever exits)
# =======================================================================
echo
echo "---- Step 9/11: Auto-starting X after login ----"
echo "This appends a snippet to ~/.bash_profile so that as soon as"
echo "'$APPLIANCE_USER' logs into tty1 (whether typed manually or via"
echo "autologin above), X starts automatically -> Openbox -> Spotify."
echo
echo "OPTIONAL: if X ever exits or crashes (e.g. a graphics driver"
echo "hiccup), should it restart automatically in a loop, or drop back"
echo "to a plain text login prompt so you can investigate by hand?"
read -p "Automatically restart X if it exits? [Y/n]: " RESTART_X

BASH_PROFILE="$APPLIANCE_HOME/.bash_profile"

if grep -q "Spotify appliance" "$BASH_PROFILE" 2>/dev/null; then
    echo "~/.bash_profile already has an auto-start-X block from a"
    echo "previous run of this script - leaving it as-is. Edit"
    echo "$BASH_PROFILE by hand if you want to change this behavior."
elif [[ ! "$RESTART_X" =~ ^[Nn]$ ]]; then
    cat >> "$BASH_PROFILE" << 'EOF'

# --- Spotify appliance: auto-start X on tty1, restart if it exits ---
if [ -z "$DISPLAY" ] && [ "$(tty)" = "/dev/tty1" ]; then
    while true
    do
        startx
        sleep 2
    done
fi
EOF
else
    cat >> "$BASH_PROFILE" << 'EOF'

# --- Spotify appliance: auto-start X on tty1 (once) ---
if [ -z "$DISPLAY" ] && [ "$(tty)" = "/dev/tty1" ]; then
    exec startx
fi
EOF
fi


# =======================================================================
# STEP 10/11 (OPTIONAL, default no): Restrict virtual terminals
# =======================================================================
echo
echo "---- Step 10/11: Restrict virtual terminals (optional) ----"
echo "This is a 'best effort' step, not a hard lock. It reduces the"
echo "number of virtual terminals systemd auto-spawns a login prompt on,"
echo "from the default of 6 down to just tty1 (where Spotify runs)."
echo "It does NOT stop someone with a physical keyboard from pressing"
echo "Ctrl+Alt+F2..F6 to try to switch away - it only stops systemd from"
echo "pre-spawning logins on the other consoles. For a real guarantee,"
echo "the simplest fix (straight from the original guide) is to just"
echo "not attach a keyboard to this machine."
read -p "Reduce auto-spawned virtual terminals to 1 (tty1 only)? [y/N]: " LIMIT_VTS

if [[ "$LIMIT_VTS" =~ ^[Yy]$ ]]; then
    sudo sed -i 's/^#\?NAutoVTs=.*/NAutoVTs=1/' /etc/systemd/logind.conf
    sudo sed -i 's/^#\?ReserveVT=.*/ReserveVT=1/' /etc/systemd/logind.conf
    grep -q '^NAutoVTs=1' /etc/systemd/logind.conf || echo "NAutoVTs=1" | sudo tee -a /etc/systemd/logind.conf > /dev/null
    grep -q '^ReserveVT=1' /etc/systemd/logind.conf || echo "ReserveVT=1" | sudo tee -a /etc/systemd/logind.conf > /dev/null
    echo "Updated /etc/systemd/logind.conf - takes effect after a reboot."
fi


# =======================================================================
# STEP 11/11 (OPTIONAL, default no per service): Disable unnecessary
# services
# =======================================================================
echo
echo "---- Step 11/11: Disable unnecessary services (optional) ----"
echo "These are 'commonly' unneeded on a dedicated Spotify appliance -"
echo "'commonly' is not 'always', so each one is asked separately:"
echo "  - ModemManager : cellular/mobile broadband modems. Safe to"
echo "                   disable unless this PC gets online via a"
echo "                   3G/4G USB modem."
echo "  - cups         : printing support. Safe to disable - this"
echo "                   machine doesn't print."
echo "  - bluetooth    : Bluetooth stack. DO NOT disable this if you"
echo "                   play audio through Bluetooth speakers or a"
echo "                   Bluetooth keyboard/remote."
echo "  - avahi-daemon : mDNS/Bonjour network discovery. Safe to"
echo "                   disable unless something needs to find this"
echo "                   machine on the network by name."
echo

for svc in ModemManager cups bluetooth avahi-daemon; do
    if systemctl list-unit-files | grep -q "^${svc}\.service"; then
        read -p "Disable '$svc'? [y/N]: " DISABLE_SVC
        if [[ "$DISABLE_SVC" =~ ^[Yy]$ ]]; then
            sudo systemctl disable --now "$svc"
            echo "$svc disabled."
        fi
    else
        echo "$svc is not installed - nothing to disable."
    fi
done


# =======================================================================
# Done - summary
# =======================================================================
echo
echo "=================================================================="
echo " Setup complete!"
echo "=================================================================="
echo "Configured for user '$APPLIANCE_USER':"
echo "  - Xorg + Openbox + PipeWire + Spotify installed"
echo "  - $AUTOSTART_FILE"
echo "  - $APPLIANCE_HOME/.xinitrc"
if [ "$AUTOLOGIN_ENABLED" -eq 1 ]; then
    echo "  - Automatic login: ENABLED"
else
    echo "  - Automatic login: not enabled (manual login required)"
fi
echo "  - $BASH_PROFILE (auto-starts X on tty1 login)"
echo
echo "Reboot to see the full appliance experience:"
echo "    sudo reboot"
echo
echo "Files worth knowing about if something looks wrong later:"
echo "  ~/.config/openbox/autostart"
echo "  ~/.xinitrc"
echo "  ~/.config/openbox/rc.xml            (only if fullscreen rule enabled)"
echo "  /etc/systemd/system/getty@tty1.service.d/autologin.conf"
echo "  ~/.bash_profile"
echo "  /etc/systemd/logind.conf            (only if VT step enabled)"
