202 lines
7.1 KiB
Nix
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"
|
|
];
|
|
};
|
|
};
|
|
};
|
|
}
|