working app
This commit is contained in:
191
README.md
Normal file
191
README.md
Normal file
@@ -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 <your-repo-url>
|
||||||
|
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).
|
||||||
1
default.nix
Normal file
1
default.nix
Normal file
@@ -0,0 +1 @@
|
|||||||
|
(import <nixpkgs> {}).callPackage ./package.nix {}
|
||||||
7
gitignore
Normal file
7
gitignore
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
result
|
||||||
|
result-*
|
||||||
|
.direnv/
|
||||||
|
.nix-*
|
||||||
|
*.swp
|
||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
127
package.nix
Normal file
127
package.nix
Normal file
@@ -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 ];
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user