diff --git a/pkgs/by-name/td/tdarr/package.nix b/pkgs/by-name/td/tdarr/package.nix new file mode 100644 index 000000000000..dec4a5163a38 --- /dev/null +++ b/pkgs/by-name/td/tdarr/package.nix @@ -0,0 +1,35 @@ +{ + lib, + symlinkJoin, + tdarr-server, + tdarr-node, +}: + +symlinkJoin { + name = "tdarr-${tdarr-server.version}"; + pname = "tdarr"; + inherit (tdarr-server) version; + + paths = [ + tdarr-server + tdarr-node + ]; + + passthru = { + server = tdarr-server; + node = tdarr-node; + }; + + meta = { + description = "Distributed transcode automation using FFmpeg/HandBrake (includes both server and node)"; + homepage = "https://tdarr.io"; + license = lib.licenses.unfree; + platforms = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + maintainers = with lib.maintainers; [ mistyttm ]; + }; +} diff --git a/pkgs/tools/misc/tdarr/common.nix b/pkgs/tools/misc/tdarr/common.nix new file mode 100644 index 000000000000..dd08fb81adde --- /dev/null +++ b/pkgs/tools/misc/tdarr/common.nix @@ -0,0 +1,213 @@ +{ + lib, + stdenv, + fetchzip, + autoPatchelfHook, + makeWrapper, + copyDesktopItems, + makeDesktopItem, + ffmpeg, + handbrake, + mkvtoolnix, + ccextractor, + gtk3, + libayatana-appindicator, + wayland, + libxkbcommon, + mesa, + libxcb, + leptonica, + glib, + gobject-introspection, + libx11, + libxcursor, + libxfixes, + tesseract4, + perl, +}: +{ + 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"; + x86_64-darwin = "darwin_x64"; + 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 ( + [ + ffmpeg + mkvtoolnix + ] + ++ 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" + "${ffmpeg}/bin/ffmpeg" + "--set-default" + "ffprobePath" + "${ffmpeg}/bin/ffprobe" + "--set-default" + "mkvpropeditPath" + "${mkvtoolnix}/bin/mkvpropedit" + ] + ++ lib.optionals (component == "server") [ + "--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.58.02"; + + 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.isLinux [ autoPatchelfHook ]; + + buildInputs = lib.optionals stdenv.isLinux [ + stdenv.cc.cc.lib + gtk3 + libayatana-appindicator + wayland + libxkbcommon + libxcb + mesa + tesseract4 + leptonica + glib + gobject-introspection + libx11 + libxcursor + libxfixes + ]; + + 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' + ''; + + 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 = '' + makeWrapper $out/share/${pname}/${componentName} $out/bin/${pname} ${commonWrapperArgs} + '' + # TODO: Check on each update to see if the Tdarr_Node_tray gets re-added to the aarch64-linux build. Reach out to upstream? + + lib.optionalString (stdenv.hostPlatform.system != "aarch64-linux") '' + 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.isLinux && stdenv.hostPlatform.system != "aarch64-linux") [ + (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" ]; + }; + } + // passthru; + + meta = { + description = "Distributed transcode automation ${component} using FFmpeg/HandBrake"; + homepage = "https://tdarr.io"; + license = lib.licenses.unfree; + platforms = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + maintainers = with lib.maintainers; [ mistyttm ]; + mainProgram = pname; + }; +}) diff --git a/pkgs/tools/misc/tdarr/default.nix b/pkgs/tools/misc/tdarr/default.nix new file mode 100644 index 000000000000..048c02b60963 --- /dev/null +++ b/pkgs/tools/misc/tdarr/default.nix @@ -0,0 +1,6 @@ +{ callPackage, ccextractor }: + +{ + server = callPackage ./server.nix { inherit ccextractor; }; + node = callPackage ./node.nix { }; +} diff --git a/pkgs/tools/misc/tdarr/node.nix b/pkgs/tools/misc/tdarr/node.nix new file mode 100644 index 000000000000..45c910ba00e4 --- /dev/null +++ b/pkgs/tools/misc/tdarr/node.nix @@ -0,0 +1,13 @@ +{ callPackage }: + +callPackage ./common.nix { } { + pname = "tdarr-node"; + component = "node"; + + hashes = { + linux_x64 = "sha256-+vD5oaoYh/bOCuk/Bxc8Fsm9UnFICownSKvg9i726nk="; + linux_arm64 = "sha256-2uPtEno0dSdVBg5hCiUuvBCB5tuTOcpeU2BuXPiqdUU="; + darwin_x64 = "sha256-8O5J1qFpQxD6fzojxjWnbkS4XQoCZauxCtbl/drplfI="; + darwin_arm64 = "sha256-oA+nTkO4LDAX5/cGkjNOLnPu0Rss9el+4JF8PBEfsPQ="; + }; +} diff --git a/pkgs/tools/misc/tdarr/server.nix b/pkgs/tools/misc/tdarr/server.nix new file mode 100644 index 000000000000..96e40e403508 --- /dev/null +++ b/pkgs/tools/misc/tdarr/server.nix @@ -0,0 +1,16 @@ +{ callPackage, ccextractor }: + +callPackage ./common.nix { } { + pname = "tdarr-server"; + component = "server"; + + hashes = { + linux_x64 = "sha256-+nxwSGAkA+BPf481N6KHW7s0iJzoGFPWp0XCbsVEwrI="; + linux_arm64 = "sha256-tA5VX27XmH3C4Bkll2mJlr1BYz5V7PPvzbJeaDht7uI="; + darwin_x64 = "sha256-jgHEezqtzUWTIvmxsmV1VgaXY9wHePkg6bQO16eSSGI="; + darwin_arm64 = "sha256-pcPpqFbqYsXf5Og9uC+eF/1kOQ1ZiletDzkk3qavPS0="; + }; + + includeInPath = [ ccextractor ]; + installIcons = true; +} diff --git a/pkgs/tools/misc/tdarr/update-hashes.sh b/pkgs/tools/misc/tdarr/update-hashes.sh new file mode 100755 index 000000000000..822ff243d9ca --- /dev/null +++ b/pkgs/tools/misc/tdarr/update-hashes.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Updates tdarr packages to the latest version +# This script updates both server and node packages since they share the same version + +SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" +COMMON_FILE="$SCRIPT_DIR/common.nix" +SERVER_FILE="$SCRIPT_DIR/server.nix" +NODE_FILE="$SCRIPT_DIR/node.nix" + +# Fetch the latest version from the versions.json endpoint +echo "Fetching latest version..." >&2 +LATEST_VERSION=$(curl -s https://storage.tdarr.io/versions.json | jq -r 'keys_unsorted | .[0]') + +if [[ -z "$LATEST_VERSION" ]]; then + echo "Error: Could not fetch latest version from versions.json" >&2 + exit 1 +fi + +echo "Latest version: $LATEST_VERSION" >&2 + +# Check current version in common.nix +CURRENT_VERSION=$(grep -oP '(?<=version = ")[^"]+' "$COMMON_FILE" 2>/dev/null) + +if [[ "$CURRENT_VERSION" == "$LATEST_VERSION" ]]; then + echo "Tdarr packages are already on the latest version ($LATEST_VERSION)" >&2 + exit 0 +fi + +echo "Updating from $CURRENT_VERSION to $LATEST_VERSION..." >&2 + +fetch_and_convert() { + local url=$1 + nix-prefetch-url --unpack "$url" 2>/dev/null | xargs nix hash convert --hash-algo sha256 --to sri +} + +# Fetch all hashes for both server and node +echo "Fetching hashes for server version $LATEST_VERSION..." >&2 +server_linux_x64=$(fetch_and_convert "https://storage.tdarr.io/versions/$LATEST_VERSION/linux_x64/Tdarr_Server.zip") +server_linux_arm64=$(fetch_and_convert "https://storage.tdarr.io/versions/$LATEST_VERSION/linux_arm64/Tdarr_Server.zip") +server_darwin_x64=$(fetch_and_convert "https://storage.tdarr.io/versions/$LATEST_VERSION/darwin_x64/Tdarr_Server.zip") +server_darwin_arm64=$(fetch_and_convert "https://storage.tdarr.io/versions/$LATEST_VERSION/darwin_arm64/Tdarr_Server.zip") + +echo "Fetching hashes for node version $LATEST_VERSION..." >&2 +node_linux_x64=$(fetch_and_convert "https://storage.tdarr.io/versions/$LATEST_VERSION/linux_x64/Tdarr_Node.zip") +node_linux_arm64=$(fetch_and_convert "https://storage.tdarr.io/versions/$LATEST_VERSION/linux_arm64/Tdarr_Node.zip") +node_darwin_x64=$(fetch_and_convert "https://storage.tdarr.io/versions/$LATEST_VERSION/darwin_x64/Tdarr_Node.zip") +node_darwin_arm64=$(fetch_and_convert "https://storage.tdarr.io/versions/$LATEST_VERSION/darwin_arm64/Tdarr_Node.zip") + +# Update common.nix version +tmpfile=$(mktemp) +awk -v ver="$LATEST_VERSION" ' +/^ version = / { + print " version = \"" ver "\";" + next +} +{ print } +' "$COMMON_FILE" > "$tmpfile" +mv "$tmpfile" "$COMMON_FILE" +echo "Updated version in $COMMON_FILE" >&2 + +# Update server.nix hashes +tmpfile=$(mktemp) +awk -v lx64="$server_linux_x64" -v la64="$server_linux_arm64" -v dx64="$server_darwin_x64" -v da64="$server_darwin_arm64" ' +/^ hashes = {$/ { + print $0 + getline; print " linux_x64 = \"" lx64 "\";" + getline; print " linux_arm64 = \"" la64 "\";" + getline; print " darwin_x64 = \"" dx64 "\";" + getline; print " darwin_arm64 = \"" da64 "\";" + getline; print $0 + next +} +{ print } +' "$SERVER_FILE" > "$tmpfile" +mv "$tmpfile" "$SERVER_FILE" +echo "Updated hashes in $SERVER_FILE" >&2 + +# Update node.nix hashes +tmpfile=$(mktemp) +awk -v lx64="$node_linux_x64" -v la64="$node_linux_arm64" -v dx64="$node_darwin_x64" -v da64="$node_darwin_arm64" ' +/^ hashes = {$/ { + print $0 + getline; print " linux_x64 = \"" lx64 "\";" + getline; print " linux_arm64 = \"" la64 "\";" + getline; print " darwin_x64 = \"" dx64 "\";" + getline; print " darwin_arm64 = \"" da64 "\";" + getline; print $0 + next +} +{ print } +' "$NODE_FILE" > "$tmpfile" +mv "$tmpfile" "$NODE_FILE" +echo "Updated hashes in $NODE_FILE" >&2 + +echo "Successfully updated tdarr to version $LATEST_VERSION" >&2 + +cat << EOF +[ + { + "attrPath": "tdarr-server", + "oldVersion": "$CURRENT_VERSION", + "newVersion": "$LATEST_VERSION", + "files": ["$COMMON_FILE", "$SERVER_FILE"] + }, + { + "attrPath": "tdarr-node", + "oldVersion": "$CURRENT_VERSION", + "newVersion": "$LATEST_VERSION", + "files": ["$COMMON_FILE", "$NODE_FILE"] + } +] +EOF diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index c027bf9930b6..4e98a750691d 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -3287,6 +3287,11 @@ with pkgs; tabview = with python3Packages; toPythonApplication tabview; + tdarrPackages = callPackage ../tools/misc/tdarr { }; + + tdarr-server = tdarrPackages.server; + tdarr-node = tdarrPackages.node; + inherit (callPackage ../development/tools/pnpm { }) pnpm_8 pnpm_9