From 8b92f16af8bd306cddc37121908332a35d1c4369 Mon Sep 17 00:00:00 2001 From: xaltsc Date: Sat, 20 Jun 2026 16:22:02 +0200 Subject: [PATCH 01/14] flake: add Closes #513 --- .gitignore | 3 + dev/nix/flake/flake-parts.nix | 5 ++ dev/nix/flake/systems.nix | 3 + dev/nix/package/_package.nix | 131 ++++++++++++++++++++++++++++++++++ dev/nix/package/default.nix | 24 +++++++ flake.lock | 111 ++++++++++++++++++++++++++++ flake.nix | 18 +++++ 7 files changed, 295 insertions(+) create mode 100644 dev/nix/flake/flake-parts.nix create mode 100644 dev/nix/flake/systems.nix create mode 100644 dev/nix/package/_package.nix create mode 100644 dev/nix/package/default.nix create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.gitignore b/.gitignore index cbe6031ed..df6b2d86e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ build/ dist/ src/target/ +# Nix +result + # Python __pycache__/ *.pyc diff --git a/dev/nix/flake/flake-parts.nix b/dev/nix/flake/flake-parts.nix new file mode 100644 index 000000000..9469fe7d6 --- /dev/null +++ b/dev/nix/flake/flake-parts.nix @@ -0,0 +1,5 @@ +{ inputs, ... }: { + imports = [ + inputs.flake-parts.flakeModules.modules + ]; +} diff --git a/dev/nix/flake/systems.nix b/dev/nix/flake/systems.nix new file mode 100644 index 000000000..804b32fcf --- /dev/null +++ b/dev/nix/flake/systems.nix @@ -0,0 +1,3 @@ +{ inputs, ... }: { + systems = inputs.nixpkgs.lib.systems.flakeExposed; +} diff --git a/dev/nix/package/_package.nix b/dev/nix/package/_package.nix new file mode 100644 index 000000000..804e271c7 --- /dev/null +++ b/dev/nix/package/_package.nix @@ -0,0 +1,131 @@ +{ + lib, + fetchFromGitHub, + rustPlatform, + stdenv, + symlinkJoin, + pkg-config, + wrapGAppsHook4, + + # Needed at runtime by CEF + libGL, + + # Dependencies + ffmpeg, + mpv-unwrapped, + cef-binary, + libxkbcommon, + libxcb, +}: +let + mpvPrefix = symlinkJoin { + name = "mpv-external-prefix"; + paths = [ + (lib.getDev mpv-unwrapped) + (lib.getLib mpv-unwrapped) + ]; + }; + + # Jellyfin expects CEF in a certain layout. + # Cf the Stremio package for the same issue. + # Can't symlinkJoin here though because CEF uses the realpaths to determine icudtl.dat path + # Trivial compilation and should stay correctly linked. + # There's likely a Rust issue that is the reason why for the fixup. + cef = stdenv.mkDerivation (finalAttrs: { + name = "cef-for-jellyfin"; + dontUnpack = true; + installPhase = '' + mkdir -p $out + cp -r ${cef-binary}/Release/* $out/ + cp -r ${cef-binary}/Resources/* $out/ + ''; + }); +in +rustPlatform.buildRustPackage (finalAttrs: { + pname = "jellyfin-desktop"; + version = "3.0.0-unstable-2026-06-18"; + + src = fetchFromGitHub { + owner = "jellyfin"; + repo = "jellyfin-desktop"; + rev = "096a01257fc4993d1b3eb785c5d8176f734d9364"; + hash = "sha256-pP+NXdI39+vxNkKLsHuibiKhMCOk7+iHC4lBhJpRfeg="; + }; + + # Fixes some Cargo.lock issues + cargoRoot = "src"; + cargoHash = "sha256-GqSk6ZjY34esHGBmaY7sbFjQI6q9e4J3Qu87tFEW6O0="; + cargoLock = { + # Fixes some other Cargo.lock issues + lockFile = "${finalAttrs.src}/src/Cargo.lock"; + outputHashes = { + "wl-proxy-0.1.2" = "sha256-8NMNPhBSW2gLXc9bwyg2kmHb12XIaV6b4PjM62xLldQ="; + }; + }; + + strictDeps = true; + + nativeBuildInputs = [ + wrapGAppsHook4 + rustPlatform.bindgenHook # fixes clang issues + pkg-config + ]; + + buildInputs = [ + libxcb + libxkbcommon + ffmpeg + ]; + + buildPhase = '' + runHook preBuild + cargo xtask build \ + --cef-path ${cef} \ + --external-mpv ${mpvPrefix} \ + --out build/ + ''; + + installPhase = '' + runHook preInstall + + install -Dm755 \ + build/jellyfin-desktop \ + $out/bin/jellyfin-desktop + + install -Dm644 \ + resources/linux/org.jellyfin.JellyfinDesktop.desktop \ + $out/share/applications/org.jellyfin.JellyfinDesktop.desktop + install -Dm644 \ + resources/linux/org.jellyfin.JellyfinDesktop.metainfo.xml \ + $out/share/metainfo/org.jellyfin.JellyfinDesktop.metainfo.xml + install -Dm644 \ + resources/linux/org.jellyfin.JellyfinDesktop.svg \ + $out/share/icons/hicolor/scalable/apps/org.jellyfin.JellyfinDesktop.svg + + runHook postInstall + ''; + + preFixup = '' + gappsWrapperArgs+=( + --prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath [ libGL ]}" \ + ) + ''; + + doCheck = false; + + meta = { + description = "Jellyfin desktop client"; + homepage = "https://github.com/jellyfin/jellyfin-desktop"; + license = lib.licenses.gpl2Only; + mainProgram = "jellyfin-desktop"; + # TODO: add myself once this goes on nixpkgs. + maintainers = with lib.maintainers; [ + { + email = "hey+dev@xaltsc.dev"; + name = "xaltsc"; + github = "xaltsc"; + githubId = 41400742; + } + ]; + }; +}) diff --git a/dev/nix/package/default.nix b/dev/nix/package/default.nix new file mode 100644 index 000000000..f9ab1c4e9 --- /dev/null +++ b/dev/nix/package/default.nix @@ -0,0 +1,24 @@ +{ inputs, ... }: { + imports = [ + inputs.flake-parts.flakeModules.easyOverlay + ]; + + perSystem = + { + system, + pkgs, + config, + ... + }: + { + overlayAttrs = { + inherit (config.packages) jellyfin-desktop; + }; + packages = rec { + jellyfin-desktop = pkgs.callPackage ./_package.nix { + inherit (inputs.cef-update.legacyPackages.${system}) cef-binary; + }; + default = jellyfin-desktop; + }; + }; +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..228ac4601 --- /dev/null +++ b/flake.lock @@ -0,0 +1,111 @@ +{ + "nodes": { + "cef-update": { + "locked": { + "lastModified": 1781788986, + "narHash": "sha256-aZFFyEnt1t4eZf2FCfDDlKD6bmFc1hzKueTCn85pZbY=", + "owner": "r-ryantm", + "repo": "nixpkgs", + "rev": "0a22c82e7765034aa210d581608266b368e89f2e", + "type": "github" + }, + "original": { + "owner": "r-ryantm", + "ref": "auto-update/cef-binary", + "repo": "nixpkgs", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1778716662, + "narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "import-tree": { + "locked": { + "lastModified": 1778781969, + "narHash": "sha256-Jjuz5CmSkur8KvLDoGa+vylEp+RkQtv4mt/qcMznpH0=", + "owner": "denful", + "repo": "import-tree", + "rev": "d321337efd0f23a9eb14a42adb7b2c29313ab274", + "type": "github" + }, + "original": { + "owner": "denful", + "repo": "import-tree", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1781577229, + "narHash": "sha256-lrp67w8AulE9Ks53n27I45ADSzbOCn4H+CNW1Ck8B+8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "567a49d1913ce81ac6e9582e3553dd90a955875f", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1777168982, + "narHash": "sha256-GOkGPcboWE9BmGCRMLX3worL4EMnsnG8MyKmXNeYuhQ=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "f5901329dade4a6ea039af1433fb087bd9c1fe14", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1781216227, + "narHash": "sha256-9mUW6gNwoN2SWc/l0fW4svPNOulXLl8ijqKyeSOGgJE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a0374025a863d007d98e3297f6aa46cc3141c2f0", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-26.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "cef-update": "cef-update", + "flake-parts": "flake-parts", + "import-tree": "import-tree", + "nixpkgs": "nixpkgs", + "nixpkgs-stable": "nixpkgs-stable" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..c9d77ac04 --- /dev/null +++ b/flake.nix @@ -0,0 +1,18 @@ +{ + description = "Jellyfin Desktop Client"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-26.05"; + + # TODO: Temporary until nixpkgs is updated + cef-update.url = "github:r-ryantm/nixpkgs/auto-update/cef-binary"; + + flake-parts.url = "github:hercules-ci/flake-parts"; + import-tree.url = "github:denful/import-tree"; + }; + + outputs = + inputs@{ flake-parts, import-tree, ... }: + flake-parts.lib.mkFlake { inherit inputs; } (import-tree ./dev/nix); +} From 00f6602229b903e7092417ba81791ca82bf7ed19 Mon Sep 17 00:00:00 2001 From: xaltsc Date: Mon, 22 Jun 2026 04:43:03 +0200 Subject: [PATCH 02/14] flake: expose knob to manually update CEF Closes #525 --- dev/nix/package/_package.nix | 29 +++++++++++++++++-------- dev/nix/package/_updated-cef-binary.nix | 10 +++++++++ dev/nix/package/default.nix | 12 +++------- flake.lock | 23 +++----------------- flake.nix | 3 --- 5 files changed, 36 insertions(+), 41 deletions(-) create mode 100644 dev/nix/package/_updated-cef-binary.nix diff --git a/dev/nix/package/_package.nix b/dev/nix/package/_package.nix index 804e271c7..b713d8a9d 100644 --- a/dev/nix/package/_package.nix +++ b/dev/nix/package/_package.nix @@ -10,6 +10,9 @@ # Needed at runtime by CEF libGL, + # Updated CEF + new-cef, + # Dependencies ffmpeg, mpv-unwrapped, @@ -26,6 +29,19 @@ let ]; }; + src = ../../..; + + cef-required-version = + let + lockFile = lib.importTOML "${src}/src/Cargo.lock"; + cefPackage = lib.findFirst (p: p.name == "cef") { version = "+FAILED"; } lockFile.package; + in + builtins.elemAt (lib.splitString "+" cefPackage.version) 1; + + # Assuming new-cef version always matches this repo needed version + # If it's the same version, then avoid unnecessary compilation and use the provided one. + cef-bin = if cef-required-version != cef-binary.version then new-cef else cef-binary; + # Jellyfin expects CEF in a certain layout. # Cf the Stremio package for the same issue. # Can't symlinkJoin here though because CEF uses the realpaths to determine icudtl.dat path @@ -36,22 +52,17 @@ let dontUnpack = true; installPhase = '' mkdir -p $out - cp -r ${cef-binary}/Release/* $out/ - cp -r ${cef-binary}/Resources/* $out/ + cp -r ${cef-bin}/Release/* $out/ + cp -r ${cef-bin}/Resources/* $out/ ''; }); + in rustPlatform.buildRustPackage (finalAttrs: { + inherit src; pname = "jellyfin-desktop"; version = "3.0.0-unstable-2026-06-18"; - src = fetchFromGitHub { - owner = "jellyfin"; - repo = "jellyfin-desktop"; - rev = "096a01257fc4993d1b3eb785c5d8176f734d9364"; - hash = "sha256-pP+NXdI39+vxNkKLsHuibiKhMCOk7+iHC4lBhJpRfeg="; - }; - # Fixes some Cargo.lock issues cargoRoot = "src"; cargoHash = "sha256-GqSk6ZjY34esHGBmaY7sbFjQI6q9e4J3Qu87tFEW6O0="; diff --git a/dev/nix/package/_updated-cef-binary.nix b/dev/nix/package/_updated-cef-binary.nix new file mode 100644 index 000000000..661e66d9e --- /dev/null +++ b/dev/nix/package/_updated-cef-binary.nix @@ -0,0 +1,10 @@ +{ cef-binary }: +cef-binary.override { + version = "149.0.4"; + gitRevision = "2f1bfd8"; + chromiumVersion = "149.0.7827.156"; + srcHashes = { + aarch64-linux = "sha256-iQmnlonux7I+2ACEtpdmlS1E4A+aNFgylRsykD+KgKA="; + x86_64-linux = "sha256-bUNgdnXkfta/pA0c/OE20E53IFKfjxxENdS6Hc0YObI="; + }; +} diff --git a/dev/nix/package/default.nix b/dev/nix/package/default.nix index f9ab1c4e9..9528a8e38 100644 --- a/dev/nix/package/default.nix +++ b/dev/nix/package/default.nix @@ -4,20 +4,14 @@ ]; perSystem = - { - system, - pkgs, - config, - ... - }: + { pkgs, config, ... }: { overlayAttrs = { inherit (config.packages) jellyfin-desktop; }; packages = rec { - jellyfin-desktop = pkgs.callPackage ./_package.nix { - inherit (inputs.cef-update.legacyPackages.${system}) cef-binary; - }; + jellyfin-desktop = pkgs.callPackage ./_package.nix { new-cef = cef-binary; }; + cef-binary = pkgs.callPackage ./_updated-cef-binary.nix { }; default = jellyfin-desktop; }; }; diff --git a/flake.lock b/flake.lock index 228ac4601..56a464a03 100644 --- a/flake.lock +++ b/flake.lock @@ -1,21 +1,5 @@ { "nodes": { - "cef-update": { - "locked": { - "lastModified": 1781788986, - "narHash": "sha256-aZFFyEnt1t4eZf2FCfDDlKD6bmFc1hzKueTCn85pZbY=", - "owner": "r-ryantm", - "repo": "nixpkgs", - "rev": "0a22c82e7765034aa210d581608266b368e89f2e", - "type": "github" - }, - "original": { - "owner": "r-ryantm", - "ref": "auto-update/cef-binary", - "repo": "nixpkgs", - "type": "github" - } - }, "flake-parts": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" @@ -82,11 +66,11 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1781216227, - "narHash": "sha256-9mUW6gNwoN2SWc/l0fW4svPNOulXLl8ijqKyeSOGgJE=", + "lastModified": 1781808408, + "narHash": "sha256-0sOb6OIaD/K1zHxBhpmlKvkADfe0n8+l4Jj2e1Q0r3w=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "a0374025a863d007d98e3297f6aa46cc3141c2f0", + "rev": "e8210c649915deed7080033cdbabcc19e40bb899", "type": "github" }, "original": { @@ -98,7 +82,6 @@ }, "root": { "inputs": { - "cef-update": "cef-update", "flake-parts": "flake-parts", "import-tree": "import-tree", "nixpkgs": "nixpkgs", diff --git a/flake.nix b/flake.nix index c9d77ac04..224176eb7 100644 --- a/flake.nix +++ b/flake.nix @@ -5,9 +5,6 @@ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-26.05"; - # TODO: Temporary until nixpkgs is updated - cef-update.url = "github:r-ryantm/nixpkgs/auto-update/cef-binary"; - flake-parts.url = "github:hercules-ci/flake-parts"; import-tree.url = "github:denful/import-tree"; }; From 1b80f9e3b9023cd51ae27c446d56d83dd48039e8 Mon Sep 17 00:00:00 2001 From: xaltsc Date: Fri, 26 Jun 2026 00:39:44 +0200 Subject: [PATCH 03/14] flake: better packaging and related improvements --- dev/nix/package/_packages/cef-binary.nix | 26 ++++++ dev/nix/package/_packages/cef-lib.nix | 16 ++++ .../jellyfin-desktop.nix} | 68 ++++------------ .../package/_packages/mpv-external-prefix.nix | 13 +++ dev/nix/package/_packages/update-cef.sh | 81 +++++++++++++++++++ dev/nix/package/_updated-cef-binary.nix | 10 --- dev/nix/package/default.nix | 32 ++++++-- 7 files changed, 177 insertions(+), 69 deletions(-) create mode 100644 dev/nix/package/_packages/cef-binary.nix create mode 100644 dev/nix/package/_packages/cef-lib.nix rename dev/nix/package/{_package.nix => _packages/jellyfin-desktop.nix} (57%) create mode 100644 dev/nix/package/_packages/mpv-external-prefix.nix create mode 100755 dev/nix/package/_packages/update-cef.sh delete mode 100644 dev/nix/package/_updated-cef-binary.nix diff --git a/dev/nix/package/_packages/cef-binary.nix b/dev/nix/package/_packages/cef-binary.nix new file mode 100644 index 000000000..7667f5020 --- /dev/null +++ b/dev/nix/package/_packages/cef-binary.nix @@ -0,0 +1,26 @@ +{ cef-binary }: +let + version = "149.0.4"; +in +( + if (cef-binary.version == version) then + cef-binary + else + (cef-binary.override { + inherit version; + gitRevision = "2f1bfd8"; + chromiumVersion = "149.0.7827.156"; + srcHashes = { + aarch64-linux = "sha256-iQmnlonux7I+2ACEtpdmlS1E4A+aNFgylRsykD+KgKA="; + x86_64-linux = "sha256-bUNgdnXkfta/pA0c/OE20E53IFKfjxxENdS6Hc0YObI="; + }; + }) +).overrideAttrs + (old: { + # make nix understand that src and version are defined in this file + inherit (old) src version; + + passthru = old.passthru // { + updateScript = ./update-cef.sh; + }; + }) diff --git a/dev/nix/package/_packages/cef-lib.nix b/dev/nix/package/_packages/cef-lib.nix new file mode 100644 index 000000000..4dd953372 --- /dev/null +++ b/dev/nix/package/_packages/cef-lib.nix @@ -0,0 +1,16 @@ +{ stdenv, cef-binary }: +# Jellyfin expects CEF in a certain layout. +# Cf the Stremio package for the same issue. +# Can't symlinkJoin here though because CEF uses the realpaths to determine icudtl.dat path +# Trivial compilation and should stay correctly linked. +# There's likely a Rust issue that is the reason why for the fixup. +stdenv.mkDerivation (finalAttrs: { + pname = "cef-lib"; + inherit (cef-binary) version; + dontUnpack = true; + installPhase = '' + mkdir -p $out + cp -r ${cef-binary}/Release/* $out/ + cp -r ${cef-binary}/Resources/* $out/ + ''; +}) diff --git a/dev/nix/package/_package.nix b/dev/nix/package/_packages/jellyfin-desktop.nix similarity index 57% rename from dev/nix/package/_package.nix rename to dev/nix/package/_packages/jellyfin-desktop.nix index b713d8a9d..af5fe4c1e 100644 --- a/dev/nix/package/_package.nix +++ b/dev/nix/package/_packages/jellyfin-desktop.nix @@ -1,74 +1,40 @@ { lib, - fetchFromGitHub, rustPlatform, - stdenv, - symlinkJoin, pkg-config, wrapGAppsHook4, # Needed at runtime by CEF libGL, - # Updated CEF - new-cef, - # Dependencies ffmpeg, - mpv-unwrapped, - cef-binary, libxkbcommon, libxcb, -}: -let - mpvPrefix = symlinkJoin { - name = "mpv-external-prefix"; - paths = [ - (lib.getDev mpv-unwrapped) - (lib.getLib mpv-unwrapped) - ]; - }; - - src = ../../..; + cef-lib, + mpv-external-prefix, - cef-required-version = - let - lockFile = lib.importTOML "${src}/src/Cargo.lock"; - cefPackage = lib.findFirst (p: p.name == "cef") { version = "+FAILED"; } lockFile.package; - in - builtins.elemAt (lib.splitString "+" cefPackage.version) 1; - - # Assuming new-cef version always matches this repo needed version - # If it's the same version, then avoid unnecessary compilation and use the provided one. - cef-bin = if cef-required-version != cef-binary.version then new-cef else cef-binary; - - # Jellyfin expects CEF in a certain layout. - # Cf the Stremio package for the same issue. - # Can't symlinkJoin here though because CEF uses the realpaths to determine icudtl.dat path - # Trivial compilation and should stay correctly linked. - # There's likely a Rust issue that is the reason why for the fixup. - cef = stdenv.mkDerivation (finalAttrs: { - name = "cef-for-jellyfin"; - dontUnpack = true; - installPhase = '' - mkdir -p $out - cp -r ${cef-bin}/Release/* $out/ - cp -r ${cef-bin}/Resources/* $out/ - ''; - }); - -in + lastModifiedDate, +}: rustPlatform.buildRustPackage (finalAttrs: { - inherit src; + src = ../../../..; pname = "jellyfin-desktop"; - version = "3.0.0-unstable-2026-06-18"; + version = + let + majorVersion = + (lib.importTOML "${finalAttrs.src}/${finalAttrs.cargoRoot}/Cargo.toml").workspace.package.version; + + s = b: l: builtins.substring b l lastModifiedDate; + date = "${s 0 4}-${s 4 2}-${s 6 2}"; + in + "${majorVersion}-${date}"; # Fixes some Cargo.lock issues cargoRoot = "src"; cargoHash = "sha256-GqSk6ZjY34esHGBmaY7sbFjQI6q9e4J3Qu87tFEW6O0="; cargoLock = { # Fixes some other Cargo.lock issues - lockFile = "${finalAttrs.src}/src/Cargo.lock"; + lockFile = "${finalAttrs.src}/${finalAttrs.cargoRoot}/Cargo.lock"; outputHashes = { "wl-proxy-0.1.2" = "sha256-8NMNPhBSW2gLXc9bwyg2kmHb12XIaV6b4PjM62xLldQ="; }; @@ -91,8 +57,8 @@ rustPlatform.buildRustPackage (finalAttrs: { buildPhase = '' runHook preBuild cargo xtask build \ - --cef-path ${cef} \ - --external-mpv ${mpvPrefix} \ + --cef-path ${cef-lib} \ + --external-mpv ${mpv-external-prefix} \ --out build/ ''; diff --git a/dev/nix/package/_packages/mpv-external-prefix.nix b/dev/nix/package/_packages/mpv-external-prefix.nix new file mode 100644 index 000000000..a7e6d69c4 --- /dev/null +++ b/dev/nix/package/_packages/mpv-external-prefix.nix @@ -0,0 +1,13 @@ +{ + symlinkJoin, + lib, + mpv-unwrapped, +}: +symlinkJoin { + pname = "mpv-external-prefix"; + inherit (mpv-unwrapped) version; + paths = [ + (lib.getDev mpv-unwrapped) + (lib.getLib mpv-unwrapped) + ]; +} diff --git a/dev/nix/package/_packages/update-cef.sh b/dev/nix/package/_packages/update-cef.sh new file mode 100755 index 000000000..2ac604467 --- /dev/null +++ b/dev/nix/package/_packages/update-cef.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i bash -p curl gnused jq nix-update cargo rustc git + +set -euo pipefail + +export NIX_CONFIG="experimental-features = nix-command flakes" + +if [ -f "${PWD}/flake.nix" ]; then + ROOT="${PWD}" +else + ROOT="$(git rev-parse --show-toplevel)" +fi + +PACKAGE_PATH="${ROOT}/dev/nix/package/_packages" +PACKAGE="cef-binary" + +target_version=$(cargo metadata --format-version 1 --locked --manifest-path "${ROOT}/src/Cargo.toml" \ + | jq -r 'first(.packages[] | select(.name=="cef") | .version) + // error("Cannot determine version")' \ + | cut -d+ -f2) + +get_attr() { + local attr="$1" + local default_system='${builtins.currentSystem}' + local system="${2:-$default_system}" + nix-instantiate --eval -E \ + "(builtins.getFlake \"${ROOT}\") + .packages.${system} + .${PACKAGE}.${attr}" \ + | tr -d '"' +} + +current_version="$(get_attr 'version')" + +echo "Target version : $target_version" +echo "Current version: $current_version" + +if [[ "$target_version" == "$current_version" ]]; then + echo "Package is up-to-date" + exit 0 +fi + + +version_json=$(curl --silent https://cef-builds.spotifycdn.com/index.json \ + | jq '[ .linux64.versions[] + | select (.channel == "stable") + | select (.cef_version | startswith("'"${target_version}"'")) + ][0]') + +cef_version=$(echo "$version_json" | jq -r '.cef_version' | cut -d'+' -f1) +git_revision=$(echo "$version_json" | jq -r '.cef_version' | cut -d'+' -f2 | cut -c 2-) +chromium_version=$(echo "$version_json" | jq -r '.chromium_version') + +echo "Latest version: $cef_version" + +if [[ "$cef_version" == "$current_version" ]]; then + echo "Package is up-to-date, rust crate cef has an unsupported version" + exit 1 +fi + + + +update_nix_value() { + local key="$1" + local value="${2:-}" + sed -i "s|$key = \".*\"|$key = \"$value\"|" "${PACKAGE_PATH}/${PACKAGE}.nix" +} + +update_nix_value version "$cef_version" +update_nix_value gitRevision "$git_revision" +update_nix_value chromiumVersion "$chromium_version" + +update_hashes () { + local system="$1" + local url="$(get_attr 'src.url' "${system}")" + local hash=$(nix store prefetch-file --json "${url}" | jq -r .hash) + update_nix_value "${system}" "${hash}" +} + +update_hashes x86_64-linux +update_hashes aarch64-linux diff --git a/dev/nix/package/_updated-cef-binary.nix b/dev/nix/package/_updated-cef-binary.nix deleted file mode 100644 index 661e66d9e..000000000 --- a/dev/nix/package/_updated-cef-binary.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ cef-binary }: -cef-binary.override { - version = "149.0.4"; - gitRevision = "2f1bfd8"; - chromiumVersion = "149.0.7827.156"; - srcHashes = { - aarch64-linux = "sha256-iQmnlonux7I+2ACEtpdmlS1E4A+aNFgylRsykD+KgKA="; - x86_64-linux = "sha256-bUNgdnXkfta/pA0c/OE20E53IFKfjxxENdS6Hc0YObI="; - }; -} diff --git a/dev/nix/package/default.nix b/dev/nix/package/default.nix index 9528a8e38..bfc0db4ba 100644 --- a/dev/nix/package/default.nix +++ b/dev/nix/package/default.nix @@ -1,18 +1,34 @@ -{ inputs, ... }: { +{ inputs, self, ... }: { imports = [ inputs.flake-parts.flakeModules.easyOverlay ]; - perSystem = - { pkgs, config, ... }: + { + pkgs, + lib, + config, + ... + }: { overlayAttrs = { inherit (config.packages) jellyfin-desktop; }; - packages = rec { - jellyfin-desktop = pkgs.callPackage ./_package.nix { new-cef = cef-binary; }; - cef-binary = pkgs.callPackage ./_updated-cef-binary.nix { }; - default = jellyfin-desktop; - }; + packages = ( + lib.fix ( + p: + (lib.mapAttrs (n: d: pkgs.callPackage ./_packages/${n}.nix d) { + cef-binary = { }; + cef-lib = { inherit (p) cef-binary; }; + jellyfin-desktop = { + inherit (p) cef-lib mpv-external-prefix; + inherit (self) lastModifiedDate; + }; + mpv-external-prefix = { }; + }) + // { + default = p.jellyfin-desktop; + } + ) + ); }; } From 1c5e8e0d5c685185184059fe3b44cb8cdf9a9d57 Mon Sep 17 00:00:00 2001 From: xaltsc Date: Mon, 22 Jun 2026 17:49:25 +0200 Subject: [PATCH 04/14] flake: enable renovate nix flake update --- renovate.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/renovate.json b/renovate.json index 7c629d044..19f2b510b 100644 --- a/renovate.json +++ b/renovate.json @@ -3,5 +3,8 @@ "extends": [ "config:recommended" ], - "ignorePaths": ["third_party/**"] + "ignorePaths": ["third_party/**"], + "nix": { + "enabled": true + } } From 7bd3b562dfeffa032083335d4c73fb0aab761255 Mon Sep 17 00:00:00 2001 From: xaltsc Date: Mon, 22 Jun 2026 21:40:27 +0200 Subject: [PATCH 05/14] flake: add home module Closes #531 --- dev/nix/home/default.nix | 126 +++++++++++++++++++++++++++++++++++++++ flake.lock | 51 +++++++++++++--- flake.nix | 2 + 3 files changed, 171 insertions(+), 8 deletions(-) create mode 100644 dev/nix/home/default.nix diff --git a/dev/nix/home/default.nix b/dev/nix/home/default.nix new file mode 100644 index 000000000..f9fd694e9 --- /dev/null +++ b/dev/nix/home/default.nix @@ -0,0 +1,126 @@ +{ inputs, config, ... }: +let + packages = config.flake.packages; +in +{ + imports = [ inputs.home-manager.flakeModules.home-manager ]; + flake.homeModules = rec { + default = jellyfin-desktop; + jellyfin-desktop = + { + config, + lib, + pkgs, + ... + }: + let + cfg = config.programs.jellyfin-desktop; + inherit (lib) + mkEnableOption + mkOption + mkIf + types + literalExpression + ; + + inherit (types) nullOr; + + jsonFormat = pkgs.formats.json { }; + + in + { + options.programs.jellyfin-desktop = { + enable = mkEnableOption "Enable jellyfin-desktop"; + package = mkOption { + description = "Package for jellyfin-desktop"; + type = types.package; + default = packages.${pkgs.stdenv.hostPlatform.system}.jellyfin-desktop; + defaultText = literalExpression ".packages.$system.jellyfin-desktop"; + }; + settings = + let + warningSuffix = "(if set, this option will overwrite settings on each generation)"; + in + (lib.mapAttrs + ( + _: v: + v + // { + type = nullOr v.type; + description = "${v.description} ${warningSuffix}."; + default = null; + } + ) + { + serverUrl = mkOption { + description = "Server URL"; + type = types.str; + example = "https://myserver.mydomain.tld"; + }; + + transparentTitlebar = mkOption { + description = "Whether to enable transparent title bar"; + type = types.bool; + example = true; + }; + + hideScrollbar = mkOption { + description = "Whether to hide scrollbar"; + type = types.bool; + example = true; + }; + + deviceName = mkOption { + description = "Device Name"; + type = types.str; + example = "myhost (jellyfin-desktop)"; + }; + + } + ); + extraConfig = mkOption { + description = '' + Extra configuration to append to `settings.json`. + Translated from nix to JSON. + Values defined here overwrite those in settings. + ''; + type = jsonFormat.type; + default = { }; + example = { + someOption = true; + someOtherOption = false; + }; + }; + }; + + config = mkIf cfg.enable { + home.packages = [ + # don't use the overlay as there's already a "jellyfin-desktop" package on nixpkgs. + cfg.package + ]; + + home.activation.mergeJellyfinDesktopConfiguration = + let + nonNullSettings = (lib.attrsets.filterAttrs (_: v: v != null) cfg.settings) // cfg.extraConfig; + newSettingsFile = pkgs.writeText "jfnd-settings.json" (builtins.toJSON nonNullSettings); + target = "${config.xdg.configHome}/jellyfin-desktop/settings.json"; + jq = lib.getExe pkgs.jq; + in + (mkIf (nonNullSettings != { }) ( + lib.hm.dag.entryAfter [ "writeBoundary" ] '' + if [ -f "${target}" ]; then + ${jq} -s '.[0] * .[1]' "${target}" "${newSettingsFile}" > "${target}.hm-tmp.json" + chmod --reference="${target}" "${target}.hm-tmp.json" + chown --reference="${target}" "${target}.hm-tmp.json" + $DRY_RUN_CMD mv "${target}.hm-tmp.json" "${target}" + else + $DRY_RUN_CMD mkdir -p "$(dirname "${target}")" + $DRY_RUN_CMD cp "${newSettingsFile}" "${target}" + $DRY_RUN_CMD chmod u+rw "${target}" + fi + '' + )); + }; + }; + }; +} diff --git a/flake.lock b/flake.lock index 56a464a03..e6483d347 100644 --- a/flake.lock +++ b/flake.lock @@ -18,6 +18,24 @@ "type": "github" } }, + "home-manager": { + "inputs": { + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1782233665, + "narHash": "sha256-h/xOtrByoA/Ak1lWHn0O1lVZz4qWYbwOSLQ8YSwQO0I=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "062581938b4a378a82dfbb294b494808157153a1", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, "import-tree": { "locked": { "lastModified": 1778781969, @@ -35,16 +53,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1781577229, - "narHash": "sha256-lrp67w8AulE9Ks53n27I45ADSzbOCn4H+CNW1Ck8B+8=", + "lastModified": 1781607440, + "narHash": "sha256-rxO+uc/KFbSJp+pgyXRuAX6QlG9hJdnt0BXpEQRXY+U=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "567a49d1913ce81ac6e9582e3553dd90a955875f", + "rev": "3e41b24abd260e8f71dbe2f5737d24122f972158", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-unstable", + "ref": "nixpkgs-unstable", "repo": "nixpkgs", "type": "github" } @@ -66,11 +84,11 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1781808408, - "narHash": "sha256-0sOb6OIaD/K1zHxBhpmlKvkADfe0n8+l4Jj2e1Q0r3w=", + "lastModified": 1782116945, + "narHash": "sha256-G3tw/IXmaH6IQ2upZvhuN9sG8CkuX+BLuJDpE8hz0Ds=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e8210c649915deed7080033cdbabcc19e40bb899", + "rev": "34268251cf5547d39063f2c5ea9a196246f7f3a6", "type": "github" }, "original": { @@ -80,11 +98,28 @@ "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1781577229, + "narHash": "sha256-lrp67w8AulE9Ks53n27I45ADSzbOCn4H+CNW1Ck8B+8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "567a49d1913ce81ac6e9582e3553dd90a955875f", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "flake-parts": "flake-parts", + "home-manager": "home-manager", "import-tree": "import-tree", - "nixpkgs": "nixpkgs", + "nixpkgs": "nixpkgs_2", "nixpkgs-stable": "nixpkgs-stable" } } diff --git a/flake.nix b/flake.nix index 224176eb7..d57a644ae 100644 --- a/flake.nix +++ b/flake.nix @@ -7,6 +7,8 @@ flake-parts.url = "github:hercules-ci/flake-parts"; import-tree.url = "github:denful/import-tree"; + + home-manager.url = "github:nix-community/home-manager"; }; outputs = From 96ec3c77b14cb86b4574788276ae65af686ebad1 Mon Sep 17 00:00:00 2001 From: xaltsc Date: Fri, 26 Jun 2026 00:39:44 +0200 Subject: [PATCH 06/14] flake: better packaging and related improvements --- dev/nix/package/_packages/cef-binary.nix | 26 ++++++ dev/nix/package/_packages/cef-lib.nix | 16 ++++ .../jellyfin-desktop.nix} | 68 ++++------------ .../package/_packages/mpv-external-prefix.nix | 13 +++ dev/nix/package/_packages/update-cef.sh | 81 +++++++++++++++++++ dev/nix/package/_updated-cef-binary.nix | 10 --- dev/nix/package/default.nix | 32 ++++++-- 7 files changed, 177 insertions(+), 69 deletions(-) create mode 100644 dev/nix/package/_packages/cef-binary.nix create mode 100644 dev/nix/package/_packages/cef-lib.nix rename dev/nix/package/{_package.nix => _packages/jellyfin-desktop.nix} (57%) create mode 100644 dev/nix/package/_packages/mpv-external-prefix.nix create mode 100755 dev/nix/package/_packages/update-cef.sh delete mode 100644 dev/nix/package/_updated-cef-binary.nix diff --git a/dev/nix/package/_packages/cef-binary.nix b/dev/nix/package/_packages/cef-binary.nix new file mode 100644 index 000000000..7667f5020 --- /dev/null +++ b/dev/nix/package/_packages/cef-binary.nix @@ -0,0 +1,26 @@ +{ cef-binary }: +let + version = "149.0.4"; +in +( + if (cef-binary.version == version) then + cef-binary + else + (cef-binary.override { + inherit version; + gitRevision = "2f1bfd8"; + chromiumVersion = "149.0.7827.156"; + srcHashes = { + aarch64-linux = "sha256-iQmnlonux7I+2ACEtpdmlS1E4A+aNFgylRsykD+KgKA="; + x86_64-linux = "sha256-bUNgdnXkfta/pA0c/OE20E53IFKfjxxENdS6Hc0YObI="; + }; + }) +).overrideAttrs + (old: { + # make nix understand that src and version are defined in this file + inherit (old) src version; + + passthru = old.passthru // { + updateScript = ./update-cef.sh; + }; + }) diff --git a/dev/nix/package/_packages/cef-lib.nix b/dev/nix/package/_packages/cef-lib.nix new file mode 100644 index 000000000..d5aca5027 --- /dev/null +++ b/dev/nix/package/_packages/cef-lib.nix @@ -0,0 +1,16 @@ +{ stdenv, cef-binary }: +# Jellyfin expects CEF in a certain layout. +# Cf the Stremio package for the same issue. +# Can't symlinkJoin here though because CEF uses the realpaths to determine icudtl.dat path +# Trivial compilation and should stay correctly linked. +# There's likely a Rust issue that is the reason why for the fixup. +stdenv.mkDerivation (finalAttrs: { + pname = "cef-lib"; + inherit (cef-binary) version; + dontUnpack = true; + installPhase = '' + mkdir -p $out + cp -r ${cef-binary}/Release/* $out/ + cp -r ${cef-binary}/Resources/* $out/ + ''; +}) diff --git a/dev/nix/package/_package.nix b/dev/nix/package/_packages/jellyfin-desktop.nix similarity index 57% rename from dev/nix/package/_package.nix rename to dev/nix/package/_packages/jellyfin-desktop.nix index b713d8a9d..af5fe4c1e 100644 --- a/dev/nix/package/_package.nix +++ b/dev/nix/package/_packages/jellyfin-desktop.nix @@ -1,74 +1,40 @@ { lib, - fetchFromGitHub, rustPlatform, - stdenv, - symlinkJoin, pkg-config, wrapGAppsHook4, # Needed at runtime by CEF libGL, - # Updated CEF - new-cef, - # Dependencies ffmpeg, - mpv-unwrapped, - cef-binary, libxkbcommon, libxcb, -}: -let - mpvPrefix = symlinkJoin { - name = "mpv-external-prefix"; - paths = [ - (lib.getDev mpv-unwrapped) - (lib.getLib mpv-unwrapped) - ]; - }; - - src = ../../..; + cef-lib, + mpv-external-prefix, - cef-required-version = - let - lockFile = lib.importTOML "${src}/src/Cargo.lock"; - cefPackage = lib.findFirst (p: p.name == "cef") { version = "+FAILED"; } lockFile.package; - in - builtins.elemAt (lib.splitString "+" cefPackage.version) 1; - - # Assuming new-cef version always matches this repo needed version - # If it's the same version, then avoid unnecessary compilation and use the provided one. - cef-bin = if cef-required-version != cef-binary.version then new-cef else cef-binary; - - # Jellyfin expects CEF in a certain layout. - # Cf the Stremio package for the same issue. - # Can't symlinkJoin here though because CEF uses the realpaths to determine icudtl.dat path - # Trivial compilation and should stay correctly linked. - # There's likely a Rust issue that is the reason why for the fixup. - cef = stdenv.mkDerivation (finalAttrs: { - name = "cef-for-jellyfin"; - dontUnpack = true; - installPhase = '' - mkdir -p $out - cp -r ${cef-bin}/Release/* $out/ - cp -r ${cef-bin}/Resources/* $out/ - ''; - }); - -in + lastModifiedDate, +}: rustPlatform.buildRustPackage (finalAttrs: { - inherit src; + src = ../../../..; pname = "jellyfin-desktop"; - version = "3.0.0-unstable-2026-06-18"; + version = + let + majorVersion = + (lib.importTOML "${finalAttrs.src}/${finalAttrs.cargoRoot}/Cargo.toml").workspace.package.version; + + s = b: l: builtins.substring b l lastModifiedDate; + date = "${s 0 4}-${s 4 2}-${s 6 2}"; + in + "${majorVersion}-${date}"; # Fixes some Cargo.lock issues cargoRoot = "src"; cargoHash = "sha256-GqSk6ZjY34esHGBmaY7sbFjQI6q9e4J3Qu87tFEW6O0="; cargoLock = { # Fixes some other Cargo.lock issues - lockFile = "${finalAttrs.src}/src/Cargo.lock"; + lockFile = "${finalAttrs.src}/${finalAttrs.cargoRoot}/Cargo.lock"; outputHashes = { "wl-proxy-0.1.2" = "sha256-8NMNPhBSW2gLXc9bwyg2kmHb12XIaV6b4PjM62xLldQ="; }; @@ -91,8 +57,8 @@ rustPlatform.buildRustPackage (finalAttrs: { buildPhase = '' runHook preBuild cargo xtask build \ - --cef-path ${cef} \ - --external-mpv ${mpvPrefix} \ + --cef-path ${cef-lib} \ + --external-mpv ${mpv-external-prefix} \ --out build/ ''; diff --git a/dev/nix/package/_packages/mpv-external-prefix.nix b/dev/nix/package/_packages/mpv-external-prefix.nix new file mode 100644 index 000000000..a7e6d69c4 --- /dev/null +++ b/dev/nix/package/_packages/mpv-external-prefix.nix @@ -0,0 +1,13 @@ +{ + symlinkJoin, + lib, + mpv-unwrapped, +}: +symlinkJoin { + pname = "mpv-external-prefix"; + inherit (mpv-unwrapped) version; + paths = [ + (lib.getDev mpv-unwrapped) + (lib.getLib mpv-unwrapped) + ]; +} diff --git a/dev/nix/package/_packages/update-cef.sh b/dev/nix/package/_packages/update-cef.sh new file mode 100755 index 000000000..2ac604467 --- /dev/null +++ b/dev/nix/package/_packages/update-cef.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i bash -p curl gnused jq nix-update cargo rustc git + +set -euo pipefail + +export NIX_CONFIG="experimental-features = nix-command flakes" + +if [ -f "${PWD}/flake.nix" ]; then + ROOT="${PWD}" +else + ROOT="$(git rev-parse --show-toplevel)" +fi + +PACKAGE_PATH="${ROOT}/dev/nix/package/_packages" +PACKAGE="cef-binary" + +target_version=$(cargo metadata --format-version 1 --locked --manifest-path "${ROOT}/src/Cargo.toml" \ + | jq -r 'first(.packages[] | select(.name=="cef") | .version) + // error("Cannot determine version")' \ + | cut -d+ -f2) + +get_attr() { + local attr="$1" + local default_system='${builtins.currentSystem}' + local system="${2:-$default_system}" + nix-instantiate --eval -E \ + "(builtins.getFlake \"${ROOT}\") + .packages.${system} + .${PACKAGE}.${attr}" \ + | tr -d '"' +} + +current_version="$(get_attr 'version')" + +echo "Target version : $target_version" +echo "Current version: $current_version" + +if [[ "$target_version" == "$current_version" ]]; then + echo "Package is up-to-date" + exit 0 +fi + + +version_json=$(curl --silent https://cef-builds.spotifycdn.com/index.json \ + | jq '[ .linux64.versions[] + | select (.channel == "stable") + | select (.cef_version | startswith("'"${target_version}"'")) + ][0]') + +cef_version=$(echo "$version_json" | jq -r '.cef_version' | cut -d'+' -f1) +git_revision=$(echo "$version_json" | jq -r '.cef_version' | cut -d'+' -f2 | cut -c 2-) +chromium_version=$(echo "$version_json" | jq -r '.chromium_version') + +echo "Latest version: $cef_version" + +if [[ "$cef_version" == "$current_version" ]]; then + echo "Package is up-to-date, rust crate cef has an unsupported version" + exit 1 +fi + + + +update_nix_value() { + local key="$1" + local value="${2:-}" + sed -i "s|$key = \".*\"|$key = \"$value\"|" "${PACKAGE_PATH}/${PACKAGE}.nix" +} + +update_nix_value version "$cef_version" +update_nix_value gitRevision "$git_revision" +update_nix_value chromiumVersion "$chromium_version" + +update_hashes () { + local system="$1" + local url="$(get_attr 'src.url' "${system}")" + local hash=$(nix store prefetch-file --json "${url}" | jq -r .hash) + update_nix_value "${system}" "${hash}" +} + +update_hashes x86_64-linux +update_hashes aarch64-linux diff --git a/dev/nix/package/_updated-cef-binary.nix b/dev/nix/package/_updated-cef-binary.nix deleted file mode 100644 index 661e66d9e..000000000 --- a/dev/nix/package/_updated-cef-binary.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ cef-binary }: -cef-binary.override { - version = "149.0.4"; - gitRevision = "2f1bfd8"; - chromiumVersion = "149.0.7827.156"; - srcHashes = { - aarch64-linux = "sha256-iQmnlonux7I+2ACEtpdmlS1E4A+aNFgylRsykD+KgKA="; - x86_64-linux = "sha256-bUNgdnXkfta/pA0c/OE20E53IFKfjxxENdS6Hc0YObI="; - }; -} diff --git a/dev/nix/package/default.nix b/dev/nix/package/default.nix index 9528a8e38..bfc0db4ba 100644 --- a/dev/nix/package/default.nix +++ b/dev/nix/package/default.nix @@ -1,18 +1,34 @@ -{ inputs, ... }: { +{ inputs, self, ... }: { imports = [ inputs.flake-parts.flakeModules.easyOverlay ]; - perSystem = - { pkgs, config, ... }: + { + pkgs, + lib, + config, + ... + }: { overlayAttrs = { inherit (config.packages) jellyfin-desktop; }; - packages = rec { - jellyfin-desktop = pkgs.callPackage ./_package.nix { new-cef = cef-binary; }; - cef-binary = pkgs.callPackage ./_updated-cef-binary.nix { }; - default = jellyfin-desktop; - }; + packages = ( + lib.fix ( + p: + (lib.mapAttrs (n: d: pkgs.callPackage ./_packages/${n}.nix d) { + cef-binary = { }; + cef-lib = { inherit (p) cef-binary; }; + jellyfin-desktop = { + inherit (p) cef-lib mpv-external-prefix; + inherit (self) lastModifiedDate; + }; + mpv-external-prefix = { }; + }) + // { + default = p.jellyfin-desktop; + } + ) + ); }; } From 8ffe8624b50fe9e7aed099f8613daae180b49c25 Mon Sep 17 00:00:00 2001 From: xaltsc Date: Sun, 28 Jun 2026 17:06:03 +0200 Subject: [PATCH 07/14] ci: flake: add workflow to update flake lock --- .github/workflows/update-flake-lock.yml | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/update-flake-lock.yml diff --git a/.github/workflows/update-flake-lock.yml b/.github/workflows/update-flake-lock.yml new file mode 100644 index 000000000..25842cc0a --- /dev/null +++ b/.github/workflows/update-flake-lock.yml @@ -0,0 +1,27 @@ +name: update-flake-lock + +on: + workflow_dispatch: + schedule: + - cron: '0 4 * * 1' # weekly, Monday 04:00 UTC + +jobs: + lockfile: + permissions: + contents: write + id-token: write + issues: write + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - uses: DeterminateSystems/determinate-nix-action@v3 + + - uses: DeterminateSystems/update-flake-lock@main + with: + pr-title: "Update flake.lock" + pr-labels: | + dependencies + automated + nix From 2bd4cfe56c3a3ddb86207619e0bbc66e27e01823 Mon Sep 17 00:00:00 2001 From: xaltsc Date: Tue, 30 Jun 2026 17:06:18 +0200 Subject: [PATCH 08/14] =?UTF-8?q?ci:=20add=20workflow=20to=20automatically?= =?UTF-8?q?=20sync=20nix=20CEF=E2=80=AFversion=20wth=20rust=20CEF=E2=80=AF?= =?UTF-8?q?version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #526 --- .github/workflows/cef-nix-sync-in-pr.yml | 74 ++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 .github/workflows/cef-nix-sync-in-pr.yml diff --git a/.github/workflows/cef-nix-sync-in-pr.yml b/.github/workflows/cef-nix-sync-in-pr.yml new file mode 100644 index 000000000..7411a9222 --- /dev/null +++ b/.github/workflows/cef-nix-sync-in-pr.yml @@ -0,0 +1,74 @@ +name: Sync nix CEF version on PR open + +on: + pull_request: + types: [ opened ] + +permissions: + contents: write + pull-requests: write + +jobs: + update-nix-ver: + runs-on: ubuntu-latest + # Only fire on a Renovate update CEF PR + if: >- + github.event.pull_request.user.login == 'renovate[bot]' && + startsWith(github.event.pull_request.title, 'Update Rust crate cef') + steps: + - name: Check out the PR + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.ref }} + + - name: Install Nix + uses: cachix/install-nix-action@v31 + with: + nix_path: nixpkgs=channel:nixos-unstable + + - name: Update nix cef-binary if needed + run: | + set -o pipefail + UPDATE_SCRIPT="$(nix-instantiate --eval -E '(builtins.getFlake (toString ./.)).packages.${builtins.currentSystem}.cef-binary.passthru.updateScript')" + "${UPDATE_SCRIPT}" 2>&1 | tee /tmp/script.log + + - name: Build the PR body + run: | + { + echo 'Automated changes produced by `cef-binary` `updateScript`.' + echo + echo "
Script output" + echo + echo '````' + cat /tmp/script.log + echo '````' + echo + echo "
" + } > /tmp/pr-body.md + + - name: Commit any changes + run: | + git config user.name 'github-actions[bot]' + git config user.email 'github-actions[bot]@users.noreply.github.com' + if git diff --quiet && git diff --cached --quiet; then + echo "Nothing to commit" + echo "EMPTY_COMMIT=1" >> "${GITHUB_ENV}" + else + VER="$(cat /tmp/pr-body.md | grep -e '^Target' | cut -d':' -f2 | tr -d ' ')" + git add -A + git commit -m "Updated nix cef-binary to match rust version v${VER}" + git push + fi + + - name: Comment on the PR + env: + GH_TOKEN: ${{ github.token }} + PR_NUM: ${{ github.event.pull_request.number }} + run: | + if [ "$EMPTY_COMMIT" = 1 ]; then + gh pr comment "${PR_NUM}" \ + --body 'Everything seems up to date on `nix` side.' + else + gh pr comment "${PR_NUM}" \ + --body-file /tmp/pr-body.md + fi From 46fcb5158cb541928036d473cc67014f3fd38970 Mon Sep 17 00:00:00 2001 From: xaltsc Date: Fri, 26 Jun 2026 03:12:01 +0200 Subject: [PATCH 09/14] flake: add crane as the default build mechanism Closes #545 --- dev/nix/package/_packages/cef-lib.nix | 15 ++++- .../_packages/jellyfin-desktop_crane-deps.nix | 13 +++++ .../_packages/jellyfin-desktop_crane.nix | 48 ++++++++++++++++ ...sktop.nix => jellyfin-desktop_nixpkgs.nix} | 0 .../_packages/jellyfin-desktop_resources.nix | 15 +++++ dev/nix/package/default.nix | 55 +++++++++++++++++-- flake.lock | 16 ++++++ flake.nix | 2 + 8 files changed, 157 insertions(+), 7 deletions(-) create mode 100644 dev/nix/package/_packages/jellyfin-desktop_crane-deps.nix create mode 100644 dev/nix/package/_packages/jellyfin-desktop_crane.nix rename dev/nix/package/_packages/{jellyfin-desktop.nix => jellyfin-desktop_nixpkgs.nix} (100%) create mode 100644 dev/nix/package/_packages/jellyfin-desktop_resources.nix diff --git a/dev/nix/package/_packages/cef-lib.nix b/dev/nix/package/_packages/cef-lib.nix index d5aca5027..ff7737eb7 100644 --- a/dev/nix/package/_packages/cef-lib.nix +++ b/dev/nix/package/_packages/cef-lib.nix @@ -1,4 +1,16 @@ -{ stdenv, cef-binary }: +{ + stdenv, + cef-binary, + writers, + lib, +}: +let + archive = writers.writeJSON "archive.json" { + type = "minimal"; + name = baseNameOf cef-binary.src.url; + sha1 = lib.fakeHash; + }; +in # Jellyfin expects CEF in a certain layout. # Cf the Stremio package for the same issue. # Can't symlinkJoin here though because CEF uses the realpaths to determine icudtl.dat path @@ -12,5 +24,6 @@ stdenv.mkDerivation (finalAttrs: { mkdir -p $out cp -r ${cef-binary}/Release/* $out/ cp -r ${cef-binary}/Resources/* $out/ + cp ${archive} $out/archive.json ''; }) diff --git a/dev/nix/package/_packages/jellyfin-desktop_crane-deps.nix b/dev/nix/package/_packages/jellyfin-desktop_crane-deps.nix new file mode 100644 index 000000000..631d90428 --- /dev/null +++ b/dev/nix/package/_packages/jellyfin-desktop_crane-deps.nix @@ -0,0 +1,13 @@ +{ + craneLib, + craneCommonArgs, + metaSkeleton, +}: +craneLib.buildDepsOnly ( + craneCommonArgs + // { + meta = metaSkeleton // { + description = "${metaSkeleton.description} - dependencies"; + }; + } +) diff --git a/dev/nix/package/_packages/jellyfin-desktop_crane.nix b/dev/nix/package/_packages/jellyfin-desktop_crane.nix new file mode 100644 index 000000000..4b6e3fee4 --- /dev/null +++ b/dev/nix/package/_packages/jellyfin-desktop_crane.nix @@ -0,0 +1,48 @@ +{ + craneLib, + craneCommonArgs, + rustPlatform, + pkg-config, + wrapGAppsHook4, + + # Dependencies + jellyfin-desktop_crane-deps, + jellyfin-desktop_resources, + + jellyfin-desktop_nixpkgs, +}: +craneLib.buildPackage ( + craneCommonArgs + // { + + nativeBuildInputs = [ + wrapGAppsHook4 + pkg-config + rustPlatform.bindgenHook + ]; + + cargoArtifacts = jellyfin-desktop_crane-deps; + + installPhase = '' + runHook preInstall + + install -Dm755 \ + target/release/jellyfin-desktop \ + $out/bin/jellyfin-desktop + + install -Dm644 \ + ${jellyfin-desktop_resources}/linux/org.jellyfin.JellyfinDesktop.desktop \ + $out/share/applications/org.jellyfin.JellyfinDesktop.desktop + install -Dm644 \ + ${jellyfin-desktop_resources}/linux/org.jellyfin.JellyfinDesktop.metainfo.xml \ + $out/share/metainfo/org.jellyfin.JellyfinDesktop.metainfo.xml + install -Dm644 \ + ${jellyfin-desktop_resources}/linux/org.jellyfin.JellyfinDesktop.svg \ + $out/share/icons/hicolor/scalable/apps/org.jellyfin.JellyfinDesktop.svg + + runHook postInstall + ''; + + inherit (jellyfin-desktop_nixpkgs) preFixup; + } +) diff --git a/dev/nix/package/_packages/jellyfin-desktop.nix b/dev/nix/package/_packages/jellyfin-desktop_nixpkgs.nix similarity index 100% rename from dev/nix/package/_packages/jellyfin-desktop.nix rename to dev/nix/package/_packages/jellyfin-desktop_nixpkgs.nix diff --git a/dev/nix/package/_packages/jellyfin-desktop_resources.nix b/dev/nix/package/_packages/jellyfin-desktop_resources.nix new file mode 100644 index 000000000..a1ac2df5e --- /dev/null +++ b/dev/nix/package/_packages/jellyfin-desktop_resources.nix @@ -0,0 +1,15 @@ +{ + stdenv, + jellyfin-desktop_nixpkgs, + metaSkeleton, +}: +stdenv.mkDerivation (finalAttrs: { + src = ../../../../resources; + pname = "${jellyfin-desktop_nixpkgs.pname}-resources"; + inherit (jellyfin-desktop_nixpkgs) version; + dontUnpack = true; + installPhase = '' + cp -r $src $out + ''; + meta = metaSkeleton; +}) diff --git a/dev/nix/package/default.nix b/dev/nix/package/default.nix index bfc0db4ba..1b2c0776a 100644 --- a/dev/nix/package/default.nix +++ b/dev/nix/package/default.nix @@ -13,22 +13,65 @@ overlayAttrs = { inherit (config.packages) jellyfin-desktop; }; - packages = ( - lib.fix ( + packages = + let + craneLib = inputs.crane.mkLib pkgs; + in + (lib.fix ( p: - (lib.mapAttrs (n: d: pkgs.callPackage ./_packages/${n}.nix d) { + let + metaSkeleton = { + inherit (p.jellyfin-desktop_nixpkgs.meta) + homepage + license + maintainers + description + ; + }; + craneCommonArgs = { + src = ../../../src; + strictDeps = true; + + inherit (p.jellyfin-desktop_nixpkgs) + version + pname + passthru + buildInputs + ; + + CEF_PATH = p.cef-lib; + EXTERNAL_MPV_DIR = p.mpv-external-prefix; + + cargoExtraArgs = "--bin jellyfin-desktop"; + + meta = metaSkeleton; + }; + + in + (lib.mapAttrs (n: pkgs.callPackage ./_packages/${n}.nix) { cef-binary = { }; cef-lib = { inherit (p) cef-binary; }; - jellyfin-desktop = { + jellyfin-desktop_resources = { + inherit (p) jellyfin-desktop_nixpkgs; + inherit metaSkeleton; + }; + jellyfin-desktop_nixpkgs = { inherit (p) cef-lib mpv-external-prefix; inherit (self) lastModifiedDate; }; + jellyfin-desktop_crane-deps = { + inherit craneLib craneCommonArgs metaSkeleton; + }; + jellyfin-desktop_crane = { + inherit craneLib craneCommonArgs; + inherit (p) jellyfin-desktop_crane-deps jellyfin-desktop_resources jellyfin-desktop_nixpkgs; + }; mpv-external-prefix = { }; }) // { + jellyfin-desktop = p.jellyfin-desktop_crane; default = p.jellyfin-desktop; } - ) - ); + )); }; } diff --git a/flake.lock b/flake.lock index 56a464a03..1abaf6b0f 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,20 @@ { "nodes": { + "crane": { + "locked": { + "lastModified": 1781825982, + "narHash": "sha256-SlXKwIRIhrOSAcTjCB3ftPLzJWZStQIPS7J1FlZPnKk=", + "owner": "ipetkov", + "repo": "crane", + "rev": "469fd08d0bcf6926321fa973c6777fbc87785dd7", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" @@ -82,6 +97,7 @@ }, "root": { "inputs": { + "crane": "crane", "flake-parts": "flake-parts", "import-tree": "import-tree", "nixpkgs": "nixpkgs", diff --git a/flake.nix b/flake.nix index 224176eb7..9d838d476 100644 --- a/flake.nix +++ b/flake.nix @@ -7,6 +7,8 @@ flake-parts.url = "github:hercules-ci/flake-parts"; import-tree.url = "github:denful/import-tree"; + + crane.url = "github:ipetkov/crane"; }; outputs = From 17590a1b466653c1b32b3ef92b543a7762e9033d Mon Sep 17 00:00:00 2001 From: xaltsc Date: Wed, 1 Jul 2026 19:31:38 +0200 Subject: [PATCH 10/14] ci+flake: add workflow to automatically build jfnd and push it to the cache Closes #542 --- .github/workflows/build-nix.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/build-nix.yml diff --git a/.github/workflows/build-nix.yml b/.github/workflows/build-nix.yml new file mode 100644 index 000000000..534a7aad6 --- /dev/null +++ b/.github/workflows/build-nix.yml @@ -0,0 +1,26 @@ +name: build-nix+cache + +on: + workflow_dispatch: + pull_request: + push: + branches: + - main + tags: + - 'v*' + +jobs: + build-and-push: + name: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v31 + with: + nix_path: nixpkgs=channel:nixos-unstable + - uses: cachix/cachix-action@v14 + with: + name: xaltsc-jfnd + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + - name: Build packages and push to cachix + run: nix build .#packages.x86_64-linux.default From 148c4af251166a0db822a22203798c77febfcc03 Mon Sep 17 00:00:00 2001 From: xaltsc Date: Wed, 1 Jul 2026 19:42:04 +0200 Subject: [PATCH 11/14] flake: add option to enable cache --- dev/nix/home/default.nix | 64 +++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/dev/nix/home/default.nix b/dev/nix/home/default.nix index f9fd694e9..e53c399c2 100644 --- a/dev/nix/home/default.nix +++ b/dev/nix/home/default.nix @@ -18,6 +18,7 @@ in inherit (lib) mkEnableOption mkOption + mkMerge mkIf types literalExpression @@ -30,6 +31,7 @@ in in { options.programs.jellyfin-desktop = { + cache.enable = mkEnableOption "Enable jellyfin cache"; enable = mkEnableOption "Enable jellyfin-desktop"; package = mkOption { description = "Package for jellyfin-desktop"; @@ -93,34 +95,42 @@ in }; }; - config = mkIf cfg.enable { - home.packages = [ - # don't use the overlay as there's already a "jellyfin-desktop" package on nixpkgs. - cfg.package - ]; + config = mkMerge [ + (mkIf cfg.enable { + home.packages = [ + # don't use the overlay as there's already a "jellyfin-desktop" package on nixpkgs. + cfg.package + ]; - home.activation.mergeJellyfinDesktopConfiguration = - let - nonNullSettings = (lib.attrsets.filterAttrs (_: v: v != null) cfg.settings) // cfg.extraConfig; - newSettingsFile = pkgs.writeText "jfnd-settings.json" (builtins.toJSON nonNullSettings); - target = "${config.xdg.configHome}/jellyfin-desktop/settings.json"; - jq = lib.getExe pkgs.jq; - in - (mkIf (nonNullSettings != { }) ( - lib.hm.dag.entryAfter [ "writeBoundary" ] '' - if [ -f "${target}" ]; then - ${jq} -s '.[0] * .[1]' "${target}" "${newSettingsFile}" > "${target}.hm-tmp.json" - chmod --reference="${target}" "${target}.hm-tmp.json" - chown --reference="${target}" "${target}.hm-tmp.json" - $DRY_RUN_CMD mv "${target}.hm-tmp.json" "${target}" - else - $DRY_RUN_CMD mkdir -p "$(dirname "${target}")" - $DRY_RUN_CMD cp "${newSettingsFile}" "${target}" - $DRY_RUN_CMD chmod u+rw "${target}" - fi - '' - )); - }; + home.activation.mergeJellyfinDesktopConfiguration = + let + nonNullSettings = (lib.attrsets.filterAttrs (_: v: v != null) cfg.settings) // cfg.extraConfig; + newSettingsFile = pkgs.writeText "jfnd-settings.json" (builtins.toJSON nonNullSettings); + target = "${config.xdg.configHome}/jellyfin-desktop/settings.json"; + jq = lib.getExe pkgs.jq; + in + (mkIf (nonNullSettings != { }) ( + lib.hm.dag.entryAfter [ "writeBoundary" ] '' + if [ -f "${target}" ]; then + ${jq} -s '.[0] * .[1]' "${target}" "${newSettingsFile}" > "${target}.hm-tmp.json" + chmod --reference="${target}" "${target}.hm-tmp.json" + chown --reference="${target}" "${target}.hm-tmp.json" + $DRY_RUN_CMD mv "${target}.hm-tmp.json" "${target}" + else + $DRY_RUN_CMD mkdir -p "$(dirname "${target}")" + $DRY_RUN_CMD cp "${newSettingsFile}" "${target}" + $DRY_RUN_CMD chmod u+rw "${target}" + fi + '' + )); + }) + (mkIf cfg.cache.enable { + nix.settings = { + substituters = [ "https://xaltsc-jfnd.cachix.org" ]; + trusted-public-keys = [ "xaltsc-jfnd.cachix.org-1:cCD4MB/Hqw1ktSbT+Dtv0clFpK1/YksbIQExL1hBxqo=" ]; + }; + }) + ]; }; }; } From c88bde070fc2cdd264a5b3038ec0d8aabacdbba1 Mon Sep 17 00:00:00 2001 From: xaltsc Date: Wed, 1 Jul 2026 04:40:55 +0200 Subject: [PATCH 12/14] Add an option to export settings Resolves part of #559 --- src/Cargo.lock | 63 +++++++++++++++++++++++++++++++++++++++++ src/config/Cargo.toml | 1 + src/config/src/lib.rs | 34 +++++++++++++++++++++- src/jfn_rust/src/app.rs | 4 +++ src/jfn_rust/src/cli.rs | 4 +++ 5 files changed, 105 insertions(+), 1 deletion(-) diff --git a/src/Cargo.lock b/src/Cargo.lock index 3f032fb16..6be2f4fef 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -807,6 +807,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + [[package]] name = "either" version = "1.16.0" @@ -2279,6 +2285,7 @@ dependencies = [ "jfn-platform-abi", "libc", "parking_lot", + "schemars", "serde_json", ] @@ -3484,6 +3491,26 @@ dependencies = [ "bitflags 2.13.0", ] +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "regex" version = "1.12.4" @@ -3629,6 +3656,31 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.117", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -3687,6 +3739,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "serde_json" version = "1.0.150" diff --git a/src/config/Cargo.toml b/src/config/Cargo.toml index d2a87ff35..21af3c12b 100644 --- a/src/config/Cargo.toml +++ b/src/config/Cargo.toml @@ -12,6 +12,7 @@ crate-type = ["rlib"] jfn-paths = { path = "../paths" } jfn-platform-abi = { path = "../platform_abi" } parking_lot.workspace = true +schemars = "1.2.1" serde_json = { workspace = true, features = ["preserve_order"] } [target.'cfg(unix)'.dependencies] diff --git a/src/config/src/lib.rs b/src/config/src/lib.rs index a09f4f5b5..ea051b22d 100644 --- a/src/config/src/lib.rs +++ b/src/config/src/lib.rs @@ -8,6 +8,7 @@ use jfn_platform_abi::WindowDecorations; use parking_lot::{Condvar, Mutex}; +use schemars::{JsonSchema, schema_for}; use serde_json::{Map, Value, json}; use std::fs; use std::path::{Path, PathBuf}; @@ -44,20 +45,51 @@ impl Default for JfnWindowGeometry { } } -#[derive(Clone, Debug)] +/// Produce a schema describing available settings. +pub fn print_settings_schema() { + let schema = schema_for!(SettingsData); + println!("{}", serde_json::to_string_pretty(&schema).unwrap()); +} + +#[derive(Clone, Debug, JsonSchema)] +#[schemars(default, rename_all = "camelCase")] struct SettingsData { + /// Server URL + #[schemars(url, example = "https://jellyfin.domain.tld")] server_url: String, + // TODO: should be enum or bool ? + /// Hardware decoding mode (default: no) hwdec: String, + // TODO: Should be Vec ? + /// Audio passthrough codecs, e.g. ac3,dts-hd,eac3,truehd + #[schemars(example = "ac3,dts-hd")] audio_passthrough: String, + // TODO: Should be enum ? + /// Audio channel layout, e.g. stereo, 5.1, 7.1 + #[schemars(example = "7.1")] audio_channels: String, + // TODO: should be enum ? + /// Log level + #[schemars(example = &"debug")] log_level: String, + /// Device name + #[schemars(example = &"MYHOST")] device_name: String, + // IDK (state?) + #[schemars(skip)] window: JfnWindowGeometry, + /// Use exclusive audio output audio_exclusive: bool, + /// Disable CEF GPU compositing disable_gpu_compositing: bool, + /// Transparent title bar transparent_titlebar: bool, + /// Force transcoding force_transcoding: bool, + /// Enable window decorations + #[schemars(skip)] // for now window_decorations: Option, + /// Enable hidden scrollbar hide_scrollbar: bool, } diff --git a/src/jfn_rust/src/app.rs b/src/jfn_rust/src/app.rs index ffa9d8d0a..c116bd521 100644 --- a/src/jfn_rust/src/app.rs +++ b/src/jfn_rust/src/app.rs @@ -579,6 +579,10 @@ pub fn jfn_app_main() -> c_int { print_version(); return 0; } + if cli.generate_settings_schema { + jfn_config::print_settings_schema(); + return 0; + } if let Some(path) = &cli.config_dir { jfn_paths::set_config_dir_override(path.into()); } diff --git a/src/jfn_rust/src/cli.rs b/src/jfn_rust/src/cli.rs index c880e42f3..40f8c5061 100644 --- a/src/jfn_rust/src/cli.rs +++ b/src/jfn_rust/src/cli.rs @@ -70,6 +70,10 @@ pub struct Cli { #[arg(long, action = ArgAction::SetTrue)] pub disable_gpu_compositing: bool, + /// Generate JSON settings schema + #[arg(long, hide = true, action = ArgAction::SetTrue)] + pub generate_settings_schema: bool, + #[cfg(target_os = "linux")] #[command(flatten)] pub linux: jfn_linux_util::cli::LinuxArgs, From f6efed6e55a61e75838b6fbfb809c4012c854605 Mon Sep 17 00:00:00 2001 From: xaltsc Date: Thu, 2 Jul 2026 00:17:20 +0200 Subject: [PATCH 13/14] flake: use the generated settings schema to produce options --- dev/nix/home/_settings.json | 78 +++++++++++++++++++++++++++++++++++++ dev/nix/home/default.nix | 59 +++++++++++++++------------- 2 files changed, 109 insertions(+), 28 deletions(-) create mode 100644 dev/nix/home/_settings.json diff --git a/dev/nix/home/_settings.json b/dev/nix/home/_settings.json new file mode 100644 index 000000000..ee526e422 --- /dev/null +++ b/dev/nix/home/_settings.json @@ -0,0 +1,78 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "SettingsData", + "type": "object", + "properties": { + "serverUrl": { + "description": "Server URL", + "type": "string", + "format": "uri", + "examples": [ + "https://jellyfin.domain.tld" + ], + "default": "" + }, + "hwdec": { + "description": "Hardware decoding mode (default: no)", + "type": "string", + "default": "" + }, + "audioPassthrough": { + "description": "Audio passthrough codecs, e.g. ac3,dts-hd,eac3,truehd", + "type": "string", + "examples": [ + "ac3,dts-hd" + ], + "default": "" + }, + "audioChannels": { + "description": "Audio channel layout, e.g. stereo, 5.1, 7.1", + "type": "string", + "examples": [ + "7.1" + ], + "default": "" + }, + "logLevel": { + "description": "Log level", + "type": "string", + "examples": [ + "debug" + ], + "default": "" + }, + "deviceName": { + "description": "Device name", + "type": "string", + "examples": [ + "MYHOST" + ], + "default": "" + }, + "audioExclusive": { + "description": "Use exclusive audio output", + "type": "boolean", + "default": false + }, + "disableGpuCompositing": { + "description": "Disable CEF GPU compositing", + "type": "boolean", + "default": false + }, + "transparentTitlebar": { + "description": "Transparent title bar", + "type": "boolean", + "default": true + }, + "forceTranscoding": { + "description": "Force transcoding", + "type": "boolean", + "default": false + }, + "hideScrollbar": { + "description": "Enable hidden scrollbar", + "type": "boolean", + "default": true + } + } +} diff --git a/dev/nix/home/default.nix b/dev/nix/home/default.nix index f9fd694e9..5da448978 100644 --- a/dev/nix/home/default.nix +++ b/dev/nix/home/default.nix @@ -45,38 +45,41 @@ in ( _: v: v - // { + // (lib.optionalAttrs (v.type != types.bool) { type = nullOr v.type; - description = "${v.description} ${warningSuffix}."; default = null; + }) + // { + description = "${v.description} ${warningSuffix}."; } ) - { - serverUrl = mkOption { - description = "Server URL"; - type = types.str; - example = "https://myserver.mydomain.tld"; - }; - - transparentTitlebar = mkOption { - description = "Whether to enable transparent title bar"; - type = types.bool; - example = true; - }; - - hideScrollbar = mkOption { - description = "Whether to hide scrollbar"; - type = types.bool; - example = true; - }; - - deviceName = mkOption { - description = "Device Name"; - type = types.str; - example = "myhost (jellyfin-desktop)"; - }; - - } + ( + let + settings = (lib.fromJSON (builtins.readFile ./_settings.json)).properties; + translateTypes = + typeString: + with lib.types; + { + string = str; + boolean = bool; + } + .${typeString}; + maybeExample = v: if v ? examples then { example = builtins.head v.examples; } else { }; + in + lib.mapAttrs ( + n: v: + if v.type == "boolean" then + (lib.mkEnableOption v.description) // { inherit (v) default; } + else + (lib.mkOption ( + { + inherit (v) description; + type = translateTypes v.type; + } + // (maybeExample v) + )) + ) settings + ) ); extraConfig = mkOption { description = '' From 8c254ae0860bbc2bcdb7947fa8f06039facf594d Mon Sep 17 00:00:00 2001 From: xaltsc Date: Thu, 2 Jul 2026 01:04:21 +0200 Subject: [PATCH 14/14] flake+ci: add regen settings job to build-nix workflow --- .github/workflows/build-nix.yml | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-nix.yml b/.github/workflows/build-nix.yml index 534a7aad6..85964f81a 100644 --- a/.github/workflows/build-nix.yml +++ b/.github/workflows/build-nix.yml @@ -1,4 +1,4 @@ -name: build-nix+cache +name: Build Nix and cache it. Regenerate settings. on: workflow_dispatch: @@ -24,3 +24,33 @@ jobs: authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - name: Build packages and push to cachix run: nix build .#packages.x86_64-linux.default + + regen-settings: + permissions: + contents: write + pull-requests: write + needs: build-and-push + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v31 + with: + nix_path: nixpkgs=channel:nixos-unstable + - uses: cachix/cachix-action@v14 + with: + name: xaltsc-jfnd + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + - name: Build packages and push to cachix + run: nix run . -- --generate-settings-schema > dev/nix/home/_settings.json + - name: Create PR if needed + uses: peter-evans/create-pull-request@v8 + with: + commit-message: "flake: update settings schema" + title: "Update settings schema" + body: "Auto-generated by CI." + branch: auto-update-settings-schema + delete-branch: true + labels: | + settings + automated + nix