From 34362c2f1bf9f3f539ef88e14ad5aac8bb8862f8 Mon Sep 17 00:00:00 2001 From: Frank LoTurco Date: Thu, 23 Jul 2026 16:17:57 -0400 Subject: [PATCH] nixos: add affinity thumbnailer for kde --- flake.lock | 17 ++ flake.nix | 8 + hosts/shared/nixos.nix | 1 + packages/affinity-thumbnailer-kde/flake.nix | 201 ++++++++++++++++++++ 4 files changed, 227 insertions(+) create mode 100644 packages/affinity-thumbnailer-kde/flake.nix diff --git a/flake.lock b/flake.lock index 09fdce3..a87db7d 100644 --- a/flake.lock +++ b/flake.lock @@ -28,6 +28,22 @@ "type": "github" } }, + "affinity-thumbnailer-kde": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "path": "./packages/affinity-thumbnailer-kde", + "type": "path" + }, + "original": { + "path": "./packages/affinity-thumbnailer-kde", + "type": "path" + }, + "parent": [] + }, "appimage-manager": { "inputs": { "nixpkgs": "nixpkgs_4" @@ -705,6 +721,7 @@ "root": { "inputs": { "affinity-nix": "affinity-nix", + "affinity-thumbnailer-kde": "affinity-thumbnailer-kde", "appimage-manager": "appimage-manager", "home-manager": "home-manager", "i915-sriov-dkms": "i915-sriov-dkms", diff --git a/flake.nix b/flake.nix index acaedf4..1f6e6b6 100644 --- a/flake.nix +++ b/flake.nix @@ -22,6 +22,11 @@ affinity-nix.url = "github:mrshmllow/affinity-nix"; + affinity-thumbnailer-kde = { + url = "path:./packages/affinity-thumbnailer-kde"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + scopebuddy.url = "github:HikariKnight/ScopeBuddy"; nix-gaming.url = "github:fufexan/nix-gaming"; nixpkgs-xr.url = "github:nix-community/nixpkgs-xr"; @@ -36,6 +41,7 @@ nix-darwin, nix-flatpak, affinity-nix, + affinity-thumbnailer-kde, i915-sriov-dkms, ... }: @@ -60,6 +66,7 @@ nixpkgs-xr.nixosModules.nixpkgs-xr nix-flatpak.nixosModules.nix-flatpak + affinity-thumbnailer-kde.nixosModules.default ./hosts/nero/hardware.nix ./hosts/nero/configuration.nix @@ -74,6 +81,7 @@ home-manager-configuration nix-flatpak.nixosModules.nix-flatpak + affinity-thumbnailer-kde.nixosModules.default i915-sriov-dkms.nixosModules.default ./hosts/pdw-frankiel/hardware.nix diff --git a/hosts/shared/nixos.nix b/hosts/shared/nixos.nix index 2ddf215..3ad420c 100644 --- a/hosts/shared/nixos.nix +++ b/hosts/shared/nixos.nix @@ -102,6 +102,7 @@ in }; openssh.enable = true; + affinity-thumbnailer.enable = true; # Enables m(ulticast)DNS (a.k.a. Bonjour) support. avahi = { diff --git a/packages/affinity-thumbnailer-kde/flake.nix b/packages/affinity-thumbnailer-kde/flake.nix new file mode 100644 index 0000000..d488bb3 --- /dev/null +++ b/packages/affinity-thumbnailer-kde/flake.nix @@ -0,0 +1,201 @@ +{ + description = "Thumbnailer for Affinity files (.afpub, .afdesign, .afphoto, .af, .afpackage) on KDE Plasma / freedesktop"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + + outputs = + { self, nixpkgs }: + let + systems = [ + "x86_64-linux" + "aarch64-linux" + ]; + forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system}); + in + { + packages = forAllSystems (pkgs: { + default = self.packages.${pkgs.stdenv.hostPlatform.system}.affinity-thumbnailer; + + affinity-thumbnailer = pkgs.stdenv.mkDerivation { + pname = "affinity-thumbnailer"; + version = "0.1.0"; + + # Don't pull source from GitHub — we build everything from inline content + src = pkgs.runCommand "affinity-thumbnailer-src" { } '' + mkdir -p $out + ''; + + nativeBuildInputs = [ pkgs.makeWrapper ]; + + # Runtime dependencies + buildInputs = [ pkgs.python3 ]; + + dontConfigure = true; + dontBuild = true; + + installPhase = '' + runHook preInstall + + # --- 1. Install the Python thumbnailer script --- + mkdir -p $out/bin + cat > $out/bin/affinity-thumbnailer << 'PYEOF' + #!/usr/bin/env python3 + """Extract embedded PNG thumbnails from Affinity files (.afpub, .afdesign, .afphoto, .af, .afpackage).""" + + import argparse + import os + import sys + + PNG_SIGNATURE = b"\x89PNG" + PNG_IEND = b"IEND" + SUPPORTED_EXTENSIONS = {".afpub", ".afdesign", ".afphoto", ".af", ".afpackage"} + + + def find_png(data: bytes) -> bytes | None: + """Find and extract the first complete PNG from binary data.""" + sig_offset = data.find(PNG_SIGNATURE) + if sig_offset == -1: + return None + + iend_offset = data.find(PNG_IEND, sig_offset) + if iend_offset == -1: + return None + + png_end = iend_offset + len(PNG_IEND) + 4 + return data[sig_offset:png_end] + + + def extract_thumbnail(filepath: str) -> bytes | None: + """Read a file and extract its embedded PNG thumbnail.""" + ext = os.path.splitext(filepath)[1].lower() + if ext not in SUPPORTED_EXTENSIONS: + print( + f"Error: unsupported extension '{ext}'. " + f"Supported: {', '.join(sorted(SUPPORTED_EXTENSIONS))}", + file=sys.stderr, + ) + return None + + if not os.path.isfile(filepath): + print(f"Error: file not found: {filepath}", file=sys.stderr) + return None + + with open(filepath, "rb") as f: + data = f.read() + + return find_png(data) + + + def main() -> None: + parser = argparse.ArgumentParser( + description="Extract embedded PNG thumbnail from Affinity files." + ) + parser.add_argument("input", help="Input Affinity file (.afpub, .afdesign, .afphoto, .af, .afpackage)") + parser.add_argument( + "output", + nargs="?", + default=None, + help="Output PNG file (default: stdout)", + ) + args = parser.parse_args() + + png_data = extract_thumbnail(args.input) + if png_data is None: + print("Error: no embedded PNG thumbnail found.", file=sys.stderr) + sys.exit(1) + + if args.output: + with open(args.output, "wb") as f: + f.write(png_data) + print(f"Thumbnail saved: {args.output} ({len(png_data)} bytes)") + else: + sys.stdout.buffer.write(png_data) + + + if __name__ == "__main__": + main() + PYEOF + chmod +x $out/bin/affinity-thumbnailer + + # Wrap so it finds python3 in NixOS + wrapProgram $out/bin/affinity-thumbnailer \ + --prefix PATH : ${pkgs.lib.makeBinPath [ pkgs.python3 ]} + + # --- 2. Install the .thumbnailer freedesktop entry --- + mkdir -p $out/share/thumbnailers + cat > $out/share/thumbnailers/affinity.thumbnailer << EOF + [Thumbnailer Entry] + TryExec=$out/bin/affinity-thumbnailer + Exec=$out/bin/affinity-thumbnailer %i %o + MimeType=application/affinity-publisher;application/affinity-designer;application/affinity-photo;application/affinity-v3;application/affinity-package; + EOF + + # --- 3. Install MIME type definitions --- + mkdir -p $out/share/mime/packages + cat > $out/share/mime/packages/application-affinity.xml << 'MIMEXML' + + + + Affinity File + + + + Affinity File + + + + Affinity File + + + + Affinity File + + + + Affinity File + + + + MIMEXML + + runHook postInstall + ''; + + meta = with pkgs.lib; { + description = "Thumbnailer for Affinity files on KDE Plasma / freedesktop"; + homepage = "https://github.com/Nczer/Affinity-thumbnailer-KDE-plasma"; + license = licenses.mit; # No license specified in the repo + platforms = platforms.linux; + }; + }; + }); + + # --- NixOS module for easy integration --- + nixosModules.default = + { + pkgs, + config, + lib, + ... + }: + { + options.services.affinity-thumbnailer = { + enable = lib.mkEnableOption "Affinity file thumbnailer for KDE Plasma / freedesktop"; + }; + + config = lib.mkIf config.services.affinity-thumbnailer.enable { + environment.systemPackages = [ + self.packages.${pkgs.stdenv.hostPlatform.system}.default + ]; + + # Make sure thumbnailer and MIME directories are linked + environment.pathsToLink = [ + "/share/thumbnailers" + "/share/mime" + ]; + }; + }; + }; +}