nixos: add affinity thumbnailer for kde

This commit is contained in:
2026-07-23 16:17:57 -04:00
parent e16e34c4bb
commit 34362c2f1b
4 changed files with 227 additions and 0 deletions
Generated
+17
View File
@@ -28,6 +28,22 @@
"type": "github" "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": { "appimage-manager": {
"inputs": { "inputs": {
"nixpkgs": "nixpkgs_4" "nixpkgs": "nixpkgs_4"
@@ -705,6 +721,7 @@
"root": { "root": {
"inputs": { "inputs": {
"affinity-nix": "affinity-nix", "affinity-nix": "affinity-nix",
"affinity-thumbnailer-kde": "affinity-thumbnailer-kde",
"appimage-manager": "appimage-manager", "appimage-manager": "appimage-manager",
"home-manager": "home-manager", "home-manager": "home-manager",
"i915-sriov-dkms": "i915-sriov-dkms", "i915-sriov-dkms": "i915-sriov-dkms",
+8
View File
@@ -22,6 +22,11 @@
affinity-nix.url = "github:mrshmllow/affinity-nix"; 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"; scopebuddy.url = "github:HikariKnight/ScopeBuddy";
nix-gaming.url = "github:fufexan/nix-gaming"; nix-gaming.url = "github:fufexan/nix-gaming";
nixpkgs-xr.url = "github:nix-community/nixpkgs-xr"; nixpkgs-xr.url = "github:nix-community/nixpkgs-xr";
@@ -36,6 +41,7 @@
nix-darwin, nix-darwin,
nix-flatpak, nix-flatpak,
affinity-nix, affinity-nix,
affinity-thumbnailer-kde,
i915-sriov-dkms, i915-sriov-dkms,
... ...
}: }:
@@ -60,6 +66,7 @@
nixpkgs-xr.nixosModules.nixpkgs-xr nixpkgs-xr.nixosModules.nixpkgs-xr
nix-flatpak.nixosModules.nix-flatpak nix-flatpak.nixosModules.nix-flatpak
affinity-thumbnailer-kde.nixosModules.default
./hosts/nero/hardware.nix ./hosts/nero/hardware.nix
./hosts/nero/configuration.nix ./hosts/nero/configuration.nix
@@ -74,6 +81,7 @@
home-manager-configuration home-manager-configuration
nix-flatpak.nixosModules.nix-flatpak nix-flatpak.nixosModules.nix-flatpak
affinity-thumbnailer-kde.nixosModules.default
i915-sriov-dkms.nixosModules.default i915-sriov-dkms.nixosModules.default
./hosts/pdw-frankiel/hardware.nix ./hosts/pdw-frankiel/hardware.nix
+1
View File
@@ -102,6 +102,7 @@ in
}; };
openssh.enable = true; openssh.enable = true;
affinity-thumbnailer.enable = true;
# Enables m(ulticast)DNS (a.k.a. Bonjour) support. # Enables m(ulticast)DNS (a.k.a. Bonjour) support.
avahi = { avahi = {
+201
View File
@@ -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'
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/affinity-publisher">
<comment>Affinity File</comment>
<glob pattern="*.afpub"/>
</mime-type>
<mime-type type="application/affinity-designer">
<comment>Affinity File</comment>
<glob pattern="*.afdesign"/>
</mime-type>
<mime-type type="application/affinity-photo">
<comment>Affinity File</comment>
<glob pattern="*.afphoto"/>
</mime-type>
<mime-type type="application/affinity-v3">
<comment>Affinity File</comment>
<glob pattern="*.af"/>
</mime-type>
<mime-type type="application/affinity-package">
<comment>Affinity File</comment>
<glob pattern="*.afpackage"/>
</mime-type>
</mime-info>
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"
];
};
};
};
}