#!/usr/bin/env bash # Install Sunshine, Moonlight, and GPU-specific hardware-encode dependencies. # Branches by $DISTRO (set by lib/distro.sh). # --- Arch defaults -------------------------------------------------------- # Default to the precompiled AUR build for a fast install (~seconds instead of # the ~10 minute source compile). Override with SUNSHINE_PKG=sunshine to build # from source. : "${SUNSHINE_PKG:=sunshine-bin}" : "${MOONLIGHT_PKG:=moonlight-qt}" # --- Debian/Ubuntu defaults ---------------------------------------------- # LizardByte ships official .deb builds per Ubuntu release on GitHub. # Resolved at runtime by _ubuntu_sunshine_deb_url to match this host. : "${SUNSHINE_DEB_URL:=}" : "${SUNSHINE_DEB_VERSION:=latest}" install_sunshine() { case "$DISTRO" in arch) _install_sunshine_arch ;; debian) _install_sunshine_debian ;; *) err "install_sunshine: unsupported distro '$DISTRO'"; return 1 ;; esac } # --- Arch implementation ------------------------------------------------- _install_sunshine_arch() { # Ensure runtime deps useful for capture/diagnostics across vendors. yay_install pipewire-pulse vulkan-tools libva-utils jq # If a working sunshine (source build) is already installed, don't touch it. # Otherwise re-running install.sh would replace the source build with # sunshine-bin (the default $SUNSHINE_PKG), trip the ldd check, then rebuild # from source — a redundant ~10 minute compile on every re-run. if pkg_installed sunshine && sunshine_runtime_deps_ok; then ok "sunshine (source build) already installed with all shared libs resolved" return 0 fi # Same idempotency for sunshine-bin when it happens to be working. if pkg_installed sunshine-bin && sunshine_runtime_deps_ok; then ok "sunshine-bin already installed and runtime deps resolved" return 0 fi yay_install "$SUNSHINE_PKG" # sunshine-bin is a precompiled AUR package; it breaks whenever Arch bumps a # major shared library (most commonly ICU). Detect that and fall back to the # source build, which links against whatever's currently installed. if ! sunshine_runtime_deps_ok; then if [[ "$SUNSHINE_PKG" == "sunshine-bin" ]]; then warn "sunshine-bin has unresolved shared library deps (rolling-Arch library drift)." warn "Falling back to the source build (AUR 'sunshine'). This takes ~5-10 minutes." ldd "$(command -v sunshine)" 2>/dev/null | grep 'not found' | sed 's/^/ /' >&2 || true # Remove both sunshine-bin AND its sibling sunshine-bin-debug, otherwise # the source build's sunshine-debug package collides on # /usr/lib/debug/usr/bin/sunshine.debug. for stale in sunshine-bin-debug sunshine-bin; do if pacman -Qi "$stale" >/dev/null 2>&1; then as_root pacman -Rns --noconfirm "$stale" fi done SUNSHINE_PKG="sunshine" yay_install sunshine if ! sunshine_runtime_deps_ok; then err "Source build also reports unresolved deps. Inspect: ldd \$(command -v sunshine)" return 1 fi ok "Recovered with source build" else err "sunshine binary has unresolved shared library deps:" ldd "$(command -v sunshine)" 2>/dev/null | grep 'not found' | sed 's/^/ /' >&2 || true err "Try: SUNSHINE_PKG=sunshine ./install.sh" return 1 fi fi } # --- Debian/Ubuntu implementation ---------------------------------------- # LizardByte's sunshine .deb assets are named per Ubuntu codename / version, # e.g. sunshine-ubuntu-24.04-amd64.deb. Resolve the right one for this host. _ubuntu_sunshine_deb_filename() { local arch arch="$(dpkg --print-architecture 2>/dev/null || echo amd64)" local v="${DISTRO_VERSION:-24.04}" echo "sunshine-ubuntu-${v}-${arch}.deb" } # Resolve the download URL. If SUNSHINE_DEB_URL is set, honor it (escape hatch # for offline mirrors / version pinning). Otherwise build a GitHub Releases # URL — 'latest' uses the redirecting /latest/download/ alias. _ubuntu_sunshine_deb_url() { if [[ -n "$SUNSHINE_DEB_URL" ]]; then echo "$SUNSHINE_DEB_URL" return 0 fi local file file="$(_ubuntu_sunshine_deb_filename)" if [[ "$SUNSHINE_DEB_VERSION" == "latest" ]]; then echo "https://github.com/LizardByte/Sunshine/releases/latest/download/${file}" else echo "https://github.com/LizardByte/Sunshine/releases/download/${SUNSHINE_DEB_VERSION}/${file}" fi } _install_sunshine_debian() { # Universal runtime deps. libva-utils gives `vainfo`; jq is used by hooks. # pipewire-pulse is the Ubuntu 24.04+ default audio path; on older releases # `pulseaudio-utils` works too — we don't force the codename split since # sunshine just needs *a* PulseAudio API endpoint. pkg_install jq vulkan-tools libva-utils curl ca-certificates if pkg_installed sunshine; then ok "sunshine already installed (dpkg)" return 0 fi local url url="$(_ubuntu_sunshine_deb_url)" local tmpdir deb_path tmpdir="$(mktemp -d /tmp/omarchy-sunshine.XXXXXX)" # shellcheck disable=SC2064 trap "rm -rf '$tmpdir'" RETURN deb_path="$tmpdir/$(_ubuntu_sunshine_deb_filename)" info "Downloading Sunshine .deb: $url" if ! curl -fL --retry 3 -o "$deb_path" "$url"; then err "Failed to download $url" err "If your Ubuntu version doesn't have a prebuilt .deb, set SUNSHINE_DEB_URL" err "or SUNSHINE_DEB_VERSION (e.g. SUNSHINE_DEB_VERSION=v2025.118.84544 ./install.sh)." return 1 fi deb_install_local "$deb_path" if ! command -v sunshine >/dev/null 2>&1; then err "sunshine command not on PATH after install — package layout unexpected." return 1 fi ok "Installed sunshine from $(basename "$deb_path")" } # True if every shared library sunshine links against resolves on this system. sunshine_runtime_deps_ok() { local bin bin="$(command -v sunshine 2>/dev/null || true)" [[ -n "$bin" ]] || return 1 ! ldd "$bin" 2>/dev/null | grep -q 'not found' } install_moonlight() { case "$DISTRO" in arch) yay_install "$MOONLIGHT_PKG" ;; debian) # moonlight-qt is published as a PPA + flatpak. On a typical Ubuntu host # the flatpak is the lowest-friction install path; falling back to apt # requires adding the cloudsmith PPA. For a headless server (the primary # Ubuntu target here) the client side is almost never wanted — so this # is best-effort. if pkg_installed moonlight-qt; then ok "moonlight-qt already installed" return 0 fi if command -v flatpak >/dev/null 2>&1; then info "Installing moonlight-qt via flatpak" as_root flatpak install -y flathub com.moonlight_stream.Moonlight || { warn "flatpak install of Moonlight failed — install it manually if needed." } else warn "moonlight-qt: no apt package in Ubuntu's default repos and no flatpak available." warn " Install flatpak first, or grab the .deb from https://github.com/moonlight-stream/moonlight-qt/releases" warn " Skipping — headless hosts rarely need the client anyway." fi ;; esac } install_gpu_encoder_packages() { case "$DISTRO:$GPU_VENDOR" in arch:nvidia) yay_install nvidia-utils libva-nvidia-driver ;; arch:amd) # VAAPI (mesa) + Vulkan for AMD hardware encode paths. # libva-mesa-driver is now provided by mesa (merged upstream); mesa-vdpau # was removed from official repos. Naming them here makes yay fall back to # AUR and pull in random forks like mesa-rk35xx-git that advertise # `provides=(libva-mesa-driver mesa-vdpau)`. yay_install mesa vulkan-radeon ;; arch:intel) yay_install intel-media-driver vulkan-intel ;; debian:nvidia) # On Ubuntu, the proprietary driver is usually already installed via # `ubuntu-drivers autoinstall` or the Server install path. Don't force a # specific nvidia-* version — they vary by release / driver branch. # Pull only the userspace VAAPI bridge if available; harmless if missing. pkg_install libnvidia-encode-no-dkms 2>/dev/null \ || pkg_install libnvidia-encode-575 2>/dev/null \ || pkg_install libnvidia-encode-565 2>/dev/null \ || pkg_install libnvidia-encode-560 2>/dev/null \ || info "NVENC userspace library not found via a known package name — relying on the existing driver install." ;; debian:amd) pkg_install mesa-va-drivers mesa-vulkan-drivers vainfo ;; debian:intel) pkg_install intel-media-va-driver-non-free mesa-vulkan-drivers ;; *) info "Unknown distro/GPU combination ($DISTRO:$GPU_VENDOR); skipping vendor-specific encoder packages." ;; esac } # Install a wlroots-based compositor for headless capture on systems without # Hyprland. Currently means: Sway on Debian/Ubuntu. On Arch the existing # Hyprland flow is the canonical path; we only fall back to Sway if Hyprland # isn't installed (rare on Omarchy). install_headless_compositor() { case "$DISTRO" in debian) pkg_install sway wlr-randr ;; arch) # Hyprland is presumed installed on Omarchy. Only act if it's missing. if ! command -v hyprctl >/dev/null 2>&1; then warn "hyprctl not found on Arch — falling back to Sway for headless capture." yay_install sway wlr-randr fi ;; esac }