From 279c6cb0978fccb526f255a9c3a14c318462b211 Mon Sep 17 00:00:00 2001 From: brian Date: Sat, 2 May 2026 10:49:42 -0400 Subject: [PATCH] working app --- README.md | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++++ default.nix | 1 + gitignore | 7 ++ package.nix | 127 ++++++++++++++++++++++++++++++++++ 4 files changed, 326 insertions(+) create mode 100644 README.md create mode 100644 default.nix create mode 100644 gitignore create mode 100644 package.nix diff --git a/README.md b/README.md new file mode 100644 index 0000000..8fcbdf4 --- /dev/null +++ b/README.md @@ -0,0 +1,191 @@ +# psysonic-nix + +Unofficial NixOS packaging for [Psysonic](https://github.com/Psychotoxical/psysonic) — a Navidrome / Subsonic desktop client built with Tauri (Rust + WebKitGTK). + +## Files + +| File | Purpose | +|---|---| +| `package.nix` | Main derivation (`stdenv.mkDerivation` with `finalAttrs`) | +| `default.nix` | Local build/test entry-point (`nix-build` / `nix-shell`) | + +--- + +## Quick start + +```bash +# 1. Clone this repo +git clone +cd psysonic-nix + +# 2. Build +nix-build # produces ./result + +# 3. Run +./result/bin/psysonic +``` + +`allowUnfree = true` is set automatically by `default.nix`. +If you build via NixOS `configuration.nix` you may need: + +```nix +nixpkgs.config.allowUnfree = true; +``` + +--- + +## NixOS system installation + +Add to your `configuration.nix`: + +```nix +{ config, pkgs, ... }: +let + psysonic = pkgs.callPackage /path/to/psysonic-nix/package.nix { }; +in +{ + nixpkgs.config.allowUnfree = true; + environment.systemPackages = [ psysonic ]; +} +``` + +Or with a flake overlay — see the [Flake usage](#flake-usage) section below. + +--- + +## Flake usage (optional) + +Create a `flake.nix` alongside `package.nix`: + +```nix +{ + description = "Psysonic Navidrome client"; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + + outputs = { self, nixpkgs }: let + system = "x86_64-linux"; + pkgs = import nixpkgs { inherit system; config.allowUnfree = true; }; + in { + packages.${system}.psysonic = pkgs.callPackage ./package.nix { }; + packages.${system}.default = self.packages.${system}.psysonic; + }; +} +``` + +Then: + +```bash +nix build .#psysonic +nix run .#psysonic +``` + +--- + +## AppImage internals & patching strategy + +### AppImage type + +Psysonic ships a **type-2 SquashFS AppImage** (the standard format used by Tauri's bundler). No DwarFS or custom offsets are needed — `appimageTools.extractType2` in nixpkgs handles extraction with `unsquashfs` automatically. + +### Why not `appimageTools.wrapType2`? + +`wrapType2` mounts the AppImage at runtime via FUSE, which requires the `fusermount` setuid binary. On NixOS that works but adds friction. Extracting the binary and patching its ELF headers is cleaner and more hermetic. + +### What gets patched + +| What | How | +|---|---| +| ELF interpreter (`ld-linux`) | `patchelf --set-interpreter` | +| Library search path (`rpath`) | `patchelf --set-rpath` with all runtime deps | +| GIO / TLS modules | `GIO_MODULE_DIR` env var via `makeWrapper` | +| Desktop entry `Exec=` line | `substituteInPlace` | + +### Runtime dependencies (Tauri-specific) + +Because Psysonic is a **Tauri v2** app it links against: + +- **WebKitGTK 4.1** (`webkitgtk_4_1`) — renders the UI +- **GTK 3** — window chrome, file dialogs +- **libsoup 3** — HTTP client used by WebKit +- **glib-networking** — TLS support for GIO (critical for HTTPS to your Navidrome) +- **PipeWire / PulseAudio / ALSA** — audio output (the release notes confirm Psysonic prefers PipeWire, then PulseAudio) +- **libGL / Mesa** — WebKitGTK's WebGL compositor; needed even if the app itself doesn't do 3D +- **xdg-utils** — `xdg-open` for external links +- **libxkbcommon + wayland libs** — Wayland input/display (Tauri supports both X11 and Wayland natively) + +### Does the app download additional binaries? + +Based on the release notes, Psysonic does **not** download additional native binaries at runtime. LUFS analysis is done by the bundled Rust binary itself. If a future release bundles a helper binary (e.g. `ffmpeg`), you will need to `patchelf` that too — check `${appimageContents}/usr/bin/` after extraction. + +### OpenGL / Vulkan + +WebKitGTK uses **libGL** for its accelerated compositor. The `libGL` and `mesa` entries in `buildInputs` cover this. You do **not** need Vulkan for a Navidrome client. + +### AMD GPU (RX 580) + +Your RX 580 uses the open-source `amdgpu` kernel driver + Mesa radeonsi. NixOS 25.11 enables this by default. No extra configuration should be needed. + +### Wayland vs X11 + +Tauri 2 detects `WAYLAND_DISPLAY` / `DISPLAY` at startup and chooses accordingly. The wrapper does not force either session type, so the app will work in both. + +--- + +## Troubleshooting + +### `error while loading shared libraries: libwebkit2gtk-4.1.so.0` + +The `rpath` patch didn't find the library. Run: +```bash +ldd result/lib/psysonic/psysonic | grep "not found" +``` +and add the missing package to `buildInputs` in `package.nix`. + +### App launches but shows a blank white screen + +WebKitGTK sandbox issue. Try: +```bash +WEBKIT_DISABLE_COMPOSITING_MODE=1 psysonic +``` +or add `--set WEBKIT_DISABLE_COMPOSITING_MODE 1` to the `makeWrapper` call. + +### TLS / HTTPS errors connecting to Navidrome + +`glib-networking` may not be picked up. Verify: +```bash +echo $GIO_MODULE_DIR +ls $GIO_MODULE_DIR # should contain libgiognutls.so or libgiotls.so +``` + +### Audio device not found + +Psysonic prefers PipeWire. Make sure `services.pipewire.enable = true` in your NixOS config. If you use PulseAudio only, set: +```bash +PSYSONIC_AUDIO_BACKEND=pulse psysonic +``` +(or configure in Settings → Audio once the app opens). + +### Hash mismatch on `fetchurl` + +Re-fetch the hash: +```bash +nix-prefetch-url --type sha256 \ + https://github.com/Psychotoxical/psysonic/releases/download/app-v1.44.0/Psysonic_1.44.0_amd64.AppImage +``` +Paste the result as `sha256 = "...";` in `package.nix`. + +--- + +## Updating to a new version + +1. Change `version` in `package.nix`. +2. Update the `hash` using `nix-prefetch-url` (see above). +3. Re-run `nix-build` and verify the app starts. + +--- + +## License + +The packaging files in this repository are released under the **MIT License**. +Psysonic itself has its own license — check the [upstream repository](https://github.com/Psychotoxical/psysonic). diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..4e68aac --- /dev/null +++ b/default.nix @@ -0,0 +1 @@ +(import {}).callPackage ./package.nix {} diff --git a/gitignore b/gitignore new file mode 100644 index 0000000..c74864c --- /dev/null +++ b/gitignore @@ -0,0 +1,7 @@ +result +result-* +.direnv/ +.nix-* +*.swp +*~ +.DS_Store diff --git a/package.nix b/package.nix new file mode 100644 index 0000000..634b513 --- /dev/null +++ b/package.nix @@ -0,0 +1,127 @@ +{ lib +, stdenv +, fetchurl +, appimageTools +, makeWrapper +, patchelf +, webkitgtk_4_1 +, gtk3 +, glib +, glib-networking +, libsoup_3 +, dbus +, pipewire +, libpulseaudio +, alsa-lib +, libGL +, mesa +, xdg-utils +, libxkbcommon +, wayland +, xorg +, pango +, cairo +, gdk-pixbuf +}: + +let + pname = "psysonic"; + version = "1.44.0"; + + src = fetchurl { + url = "https://github.com/Psychotoxical/psysonic/releases/download/app-v${version}/Psysonic_${version}_amd64.AppImage"; + hash = "sha256-5dtEgFAA2nQf9cj+M0TThcSk7s+3R3LkgVEzCzfUjWw="; + }; + + appimageContents = appimageTools.extractType2 { inherit pname version src; }; + + runtimeLibs = lib.makeLibraryPath [ + webkitgtk_4_1 + gtk3 + glib + glib-networking + libsoup_3 + dbus + pipewire + libpulseaudio + alsa-lib + libGL + mesa + xdg-utils + libxkbcommon + wayland + xorg.libX11 + xorg.libXext + xorg.libxcb + pango + cairo + gdk-pixbuf + ]; + +in + +stdenv.mkDerivation { + inherit pname version src; + + dontUnpack = true; + dontBuild = true; + dontConfigure = true; + + nativeBuildInputs = [ makeWrapper patchelf ]; + + installPhase = '' + runHook preInstall + + mkdir -p $out/bin $out/lib/psysonic $out/share + + install -Dm755 ${appimageContents}/usr/bin/psysonic $out/lib/psysonic/psysonic + + patchelf \ + --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \ + --set-rpath "${runtimeLibs}" \ + $out/lib/psysonic/psysonic + + makeWrapper $out/lib/psysonic/psysonic $out/bin/psysonic \ + --set GIO_MODULE_DIR "${glib-networking}/lib/gio/modules" \ + --set GIO_EXTRA_MODULES "${glib-networking}/lib/gio/modules" \ + --prefix LD_LIBRARY_PATH : "${runtimeLibs}" \ + --prefix PATH : "${lib.makeBinPath [ xdg-utils dbus ]}" + + for size in 16 32 48 64 128 256 512; do + icon="${appimageContents}/usr/share/icons/hicolor/''${size}x''${size}/apps/psysonic.png" + if [ -f "$icon" ]; then + install -Dm644 "$icon" $out/share/icons/hicolor/''${size}x''${size}/apps/psysonic.png + fi + done + + if [ -f "${appimageContents}/usr/share/applications/psysonic.desktop" ]; then + install -Dm644 "${appimageContents}/usr/share/applications/psysonic.desktop" \ + $out/share/applications/psysonic.desktop + substituteInPlace $out/share/applications/psysonic.desktop \ + --replace-fail 'Exec=psysonic' "Exec=$out/bin/psysonic" + else + mkdir -p $out/share/applications + cat > $out/share/applications/psysonic.desktop << DESKTOP +[Desktop Entry] +Name=Psysonic +Comment=Navidrome client for self-hosted music +Exec=$out/bin/psysonic +Icon=psysonic +Type=Application +Categories=Audio;Music; +StartupWMClass=psysonic +DESKTOP + fi + + runHook postInstall + ''; + + meta = with lib; { + description = "Navidrome desktop client for self-hosted music streaming"; + homepage = "https://github.com/Psychotoxical/psysonic"; + license = licenses.gpl3Only; + platforms = [ "x86_64-linux" ]; + mainProgram = "psysonic"; + sourceProvenance = [ sourceTypes.binaryNativeCode ]; + }; +}