Files

202 lines
7.1 KiB
Nix

{
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"
];
};
};
};
}