#!/usr/bin/env bash set -euo pipefail GITHUB_REPO="nekename/OpenDeck" FLATHUB_APP_ID="me.amankhanna.opendeck" UDEV_RULES_URL="https://raw.githubusercontent.com/nekename/OpenDeck/refs/heads/release/src-tauri/bundle/40-streamdeck.rules" if [ -t 1 ]; then RED="\033[0;31m" GREEN="\033[0;32m" YELLOW="\033[0;33m" BLUE="\033[0;34m" BOLD="\033[1m" RESET="\033[0m" else RED="" GREEN="" YELLOW="" BLUE="" RESET="" fi trap 'echo -e "${YELLOW}Need help? Join the Discord: ${BLUE}https://discord.gg/26Nf8rHvaj${RESET}"' EXIT msg_info() { echo -e "${BLUE}[*]${RESET} $*"; } msg_ok() { echo -e "${GREEN}[✓] $*${RESET}"; } msg_warn() { echo -e "${YELLOW}[!] $*${RESET}"; } msg_error() { echo -e "${RED}[✗] $*${RESET}" >&2; } has_cmd() { command -v "$1" >/dev/null 2>&1; } confirm() { printf "%b$1 [y/N]: %b" "$YELLOW$BOLD" "$RESET" read -r ans case "$ans" in [yY] | [yY][eE][sS]) return 0 ;; *) return 1 ;; esac } detect_family() { if has_cmd rpm-ostree || grep -qi "universal blue" /etc/os-release 2>/dev/null; then echo "ublue" elif has_cmd pacman; then echo "arch" elif has_cmd zypper || has_cmd dnf || has_cmd rpm; then echo "rpm" elif has_cmd apt-get || has_cmd dpkg; then echo "debian" else echo "unknown" fi } fetch_latest_asset() { local ext="$1" local api_url="https://api.github.com/repos/${GITHUB_REPO}/releases/latest" local arch arch_deb arch_rpm arch_pattern download_url arch="$(uname -m)" case "$arch" in x86_64) arch_deb="amd64" arch_rpm="x86_64" ;; aarch64) arch_deb="arm64" arch_rpm="aarch64" ;; *) msg_error "Unsupported architecture $arch" exit 1 ;; esac if [ "$ext" = "deb" ]; then arch_pattern="${arch_deb}" else arch_pattern="${arch_rpm}" fi if has_cmd curl; then download_url=$(curl -s --retry 3 --retry-delay 3 "$api_url" | grep -Eo "https://[^ \"]+${arch_pattern}\.${ext}([^\"]*)" | head -n1 || true) elif has_cmd wget; then download_url=$(wget -qO- --tries=3 "$api_url" | grep -Eo "https://[^ \"]+${arch_pattern}\.${ext}([^\"]*)" | head -n1 || true) fi if [ -n "$download_url" ]; then echo "$download_url" else msg_error "Failed to find a .${ext} release asset for arch $arch" return 1 fi } download_with_retry() { local url="$1" local dest="$2" if has_cmd curl; then curl -L --retry 3 --retry-delay 3 -o "$dest" "$url" else wget --tries=3 -O "$dest" "$url" fi } reload_udev_rules() { msg_info "Reloading udev rules" if ! sudo udevadm control --reload-rules || ! sudo udevadm trigger; then msg_error "Failed to reload udev rules; you may be able to restart your computer instead" return 0 fi msg_ok "Reloaded udev rules" } install_flatpak() { msg_info "Installing ${FLATHUB_APP_ID} from Flathub" if confirm "Install OpenDeck system-wide? (No = user install)"; then if ! flatpak remote-list | grep -q flathub; then msg_error "Flathub remote not found; please add Flathub before running this script" exit 1 fi flatpak install flathub "${FLATHUB_APP_ID}" else if ! flatpak remote-list --user | grep -q flathub; then msg_error "Flathub remote not found; please add Flathub before running this script or try installing system-wide" exit 1 fi flatpak install --user flathub "${FLATHUB_APP_ID}" fi msg_ok "Installed ${FLATHUB_APP_ID} from Flathub" msg_info "Installing udev rules" tmp_rules="$(mktemp --suffix=.rules)" download_with_retry "$UDEV_RULES_URL" "$tmp_rules" sudo mv "$tmp_rules" /etc/udev/rules.d/40-streamdeck.rules sudo chmod 644 /etc/udev/rules.d/40-streamdeck.rules msg_ok "Installed udev rules" reload_udev_rules } install_deb() { local dl tmpf dl=$(fetch_latest_asset "deb") msg_info "Downloading ${dl##*/}" tmpf="$(mktemp --suffix=.deb)" download_with_retry "$dl" "$tmpf" msg_ok "Downloaded ${dl##*/}" msg_info "Installing .deb package" sudo apt-get install --fix-broken "$tmpf" rm -f "$tmpf" msg_ok "Installed .deb package" reload_udev_rules } install_rpm() { local dl tmpf dl=$(fetch_latest_asset "rpm") msg_info "Downloading ${dl##*/}" tmpf="$(mktemp --suffix=.rpm)" download_with_retry "$dl" "$tmpf" msg_ok "Downloaded ${dl##*/}" msg_info "Installing .rpm package" if has_cmd zypper; then sudo zypper install --allow-unsigned-rpm "$tmpf" elif has_cmd dnf; then sudo dnf install --nogpgcheck "$tmpf" else sudo rpm -i --nosignature "$tmpf" fi rm -f "$tmpf" msg_ok "Installed .rpm package" reload_udev_rules } install_aur() { msg_info "Installing from AUR" msg_info "${BOLD}This script will attempt to use yay, paru, aura, pikaur, or trizen, in that order" confirm "If you use another AUR helper, you should install OpenDeck manually. Continue?" if has_cmd yay; then yay -Sy opendeck elif has_cmd paru; then paru -Sy opendeck elif has_cmd aura; then aura -Ak opendeck elif has_cmd pikaur; then pikaur -Sy opendeck elif has_cmd trizen; then trizen -Sy opendeck else msg_error "No AUR helper found; install yay, paru, aura, pikaur, or trizen, or install manually" return 1 fi msg_ok "Installed from AUR" reload_udev_rules } install_wine_if_needed() { if has_cmd wine; then msg_info "Wine already installed" return fi if confirm "Wine is required for many plugins. Install Wine now?"; then msg_info "Installing Wine" case "$PKG_FAMILY" in debian) sudo apt-get update && sudo apt-get install wine ;; rpm | ublue) if has_cmd rpm-ostree; then sudo rpm-ostree install wine wine-mono elif has_cmd zypper; then sudo zypper install wine wine-mono elif has_cmd dnf; then sudo dnf install wine wine-mono else msg_error "No supported package manager found to install Wine; please install it manually" fi ;; arch) sudo pacman -Sy wine wine-mono ;; *) msg_error "No supported package manager found to install Wine; please install it manually" ;; esac msg_ok "Installed Wine" else msg_warn "Not installing Wine; many plugins may not function" fi } PKG_FAMILY="$(detect_family)" msg_info "Detected ${PKG_FAMILY} package family" case "$PKG_FAMILY" in debian) install_deb ;; rpm) install_rpm ;; arch) install_aur ;; ublue) install_flatpak ;; unknown) if has_cmd flatpak; then msg_warn "No native package method found" msg_info "You can continue by installing with Flatpak; if you experience issues, manually install OpenDeck natively" if confirm "Install with Flatpak?"; then install_flatpak else msg_error "Installation aborted" exit 1 fi else msg_error "No usable installation method found; please install OpenDeck manually" exit 1 fi ;; esac install_wine_if_needed msg_ok "Installation complete!" echo -e "${YELLOW}If you enjoy OpenDeck, please consider starring the project on GitHub: ${BLUE}https://github.com/${GITHUB_REPO}${RESET}" if confirm "Launch OpenDeck now?"; then if [[ "$PKG_FAMILY" == "ublue" ]] || { [[ "$PKG_FAMILY" == "unknown" ]] && has_cmd flatpak; }; then flatpak run "${FLATHUB_APP_ID}" & msg_ok "Launched OpenDeck using Flatpak" else if [ -x /bin/opendeck ]; then /bin/opendeck & msg_ok "Launched OpenDeck from /bin" elif has_cmd opendeck; then opendeck & msg_ok "Launched OpenDeck from PATH" else msg_warn "OpenDeck executable not found" fi fi fi