{ lib, stdenv, fetchzip, autoPatchelfHook, makeWrapper, copyDesktopItems, makeDesktopItem, jellyfin-ffmpeg, handbrake, mkvtoolnix, ccextractor, gtk3, libayatana-appindicator, wayland, libxkbcommon, libxcb, leptonica, glib, gobject-introspection, libx11, libxcursor, libxfixes, tesseract4, perl, apprise, openssl, nixosTests, pnpm_10, nodejs, }: { pname, component, # "server" or "node" hashes, includeInPath ? [ ], # Additional packages to include in PATH installIcons ? false, # Whether to install icon files passthru ? { }, # Additional passthru attributes }: let platform = { x86_64-linux = "linux_x64"; aarch64-linux = "linux_arm64"; aarch64-darwin = "darwin_arm64"; } .${stdenv.hostPlatform.system} or (throw "Unsupported system: ${stdenv.hostPlatform.system}"); componentUpper = lib.toUpper (builtins.substring 0 1 component) + builtins.substring 1 (builtins.stringLength component) component; componentName = "Tdarr_${componentUpper}"; componentTrayName = "${componentName}_Tray"; binPath = lib.makeBinPath ( [ jellyfin-ffmpeg mkvtoolnix pnpm_10 nodejs ] ++ includeInPath # ! Handbrake is currently marked as broken on darwin ++ lib.optional (!stdenv.hostPlatform.isDarwin) handbrake ); commonWrapperArgs = lib.escapeShellArgs ( [ "--prefix" "PATH" ":" binPath "--run" "export rootDataPath=\${rootDataPath:-\${XDG_DATA_HOME:-$HOME/.local/share}/tdarr/${component}}; mkdir -p \"$rootDataPath\"/configs \"$rootDataPath\"/logs; cd \"$rootDataPath\"" ] ++ lib.optionals (component == "node") [ "--run" "mkdir -p \"$rootDataPath\"/assets/app/plugins" ] ++ [ "--run" ''_cfg="$rootDataPath/configs/${componentName}_Config.json"; if [ -f "$_cfg" ]; then grep -q ffprobePath "$_cfg" || sed -i '1s/{/{"ffprobePath":"",/' "$_cfg"; else printf '{"ffprobePath":""}' > "$_cfg"; fi'' "--set-default" "ffmpegPath" "${jellyfin-ffmpeg}/bin/ffmpeg" "--set-default" "ffprobePath" "${jellyfin-ffmpeg}/bin/ffprobe" "--set-default" "mkvpropeditPath" "${mkvtoolnix}/bin/mkvpropedit" "--set-default" "ccextractorPath" "${ccextractor}/bin/ccextractor" ] # ! Handbrake is currently marked as broken on darwin ++ lib.optionals (!stdenv.hostPlatform.isDarwin) [ "--set-default" "handbrakePath" "${handbrake}/bin/HandBrakeCLI" ] ); in stdenv.mkDerivation (finalAttrs: { inherit pname; version = "2.85.01"; src = fetchzip { url = "https://storage.tdarr.io/versions/${finalAttrs.version}/${platform}/${componentName}.zip"; sha256 = hashes.${platform} or (throw "Unsupported platform: ${platform}"); stripRoot = false; }; nativeBuildInputs = [ makeWrapper copyDesktopItems ] ++ lib.optionals stdenv.hostPlatform.isLinux [ autoPatchelfHook ]; buildInputs = lib.optionals stdenv.hostPlatform.isLinux [ stdenv.cc.cc.lib gtk3 libayatana-appindicator wayland libxkbcommon libxcb tesseract4 leptonica glib gobject-introspection libx11 libxcursor libxfixes apprise openssl ]; postPatch = '' rm -rf ./assets/app/ffmpeg rm -rf ./assets/app/ccextractor substituteInPlace node_modules/exiftool-vendored.pl/bin/exiftool \ --replace-fail "#!/usr/bin/perl" "#!${perl}/bin/perl" # * exiftool-vendored checks for /usr/bin/perl existence; when missing (NixOS), it sets ignoreShebang=true which breaks spawn by using shell:true with an env lacking PATH. Since we patched the shebang, force ignoreShebang to false. substituteInPlace node_modules/exiftool-vendored/dist/ExifTool.js \ --replace-fail '!_fs.existsSync("/usr/bin/perl")' 'false' # * Substitute in nixpkgs version of pnpm as autopatchelf breaks the bundled copy see: https://github.com/NixOS/nixpkgs/issues/544841 rm ./runtime/pnpm makeWrapper ${pnpm_10}/bin/pnpm ./runtime/pnpm \ --prefix PATH : ${lib.makeBinPath [ nodejs ]} ''; preInstall = '' mkdir -p $out/{bin,share/${pname}} ''; installPhase = '' runHook preInstall # Copy contents (source is already unpacked) cp -r . $out/share/${pname}/ chmod +x $out/share/${pname}/${componentName} runHook postInstall ''; postInstall = '' # Remove musl-only prebuilt Node addons on glibc systems. # autoPatchelf scans all ELF files in $out and fails if musl libc is missing. ${lib.optionalString (stdenv.hostPlatform.isLinux && !stdenv.hostPlatform.isMusl) '' find $out/share/${pname} -type f -name '*.musl.node' -delete ''} makeWrapper $out/share/${pname}/${componentName} $out/bin/${pname} ${commonWrapperArgs} makeWrapper $out/share/${pname}/${componentTrayName} $out/bin/${pname}-tray ${commonWrapperArgs} '' + lib.optionalString installIcons '' # Install icons from the copied source files for size in 192 512; do if [ -f $out/share/${pname}/public/logo''${size}.png ]; then install -Dm644 $out/share/${pname}/public/logo''${size}.png \ $out/share/icons/hicolor/''${size}x''${size}/apps/${pname}.png fi done '' + ""; desktopItems = lib.optionals stdenv.hostPlatform.isLinux [ (makeDesktopItem { desktopName = "Tdarr ${componentUpper} Tray"; name = "Tdarr ${componentUpper} Tray"; exec = "${pname}-tray"; terminal = false; type = "Application"; icon = if installIcons then pname else ""; categories = [ "Utility" ]; }) ]; passthru = { updateScript = { command = [ ./update-hashes.sh ]; supportedFeatures = [ "commit" ]; }; tests.nixos = nixosTests.tdarr; } // passthru; meta = { description = "Distributed transcode automation ${component} using FFmpeg/HandBrake"; homepage = "https://tdarr.io"; license = lib.licenses.unfree; platforms = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" ]; maintainers = with lib.maintainers; [ mistyttm ]; mainProgram = pname; }; })