diff --git a/hosts/fw/modules/pyload/default.nix b/hosts/fw/modules/pyload/default.nix index 758e58a..9fc9b82 100644 --- a/hosts/fw/modules/pyload/default.nix +++ b/hosts/fw/modules/pyload/default.nix @@ -2,7 +2,6 @@ let cids = import ../staticids.nix; networkPrefix = config.networkPrefix; - filebotScript = pkgs.callPackage ./filebot-process.nix {}; pyloadUser = { isSystemUser = true; @@ -10,7 +9,7 @@ let group = "pyload"; home = "/var/lib/pyload"; createHome = true; - extraGroups = [ "jellyfin" ]; # Access to multimedia directories + extraGroups = [ "jellyfin" ]; # Access to multimedia directories }; pyloadGroup = { gid = cids.gids.pyload; @@ -46,17 +45,8 @@ in # PyLoad hook scripts directory "d /var/lib/pyload/config 0755 pyload pyload - -" "d /var/lib/pyload/config/scripts 0755 pyload pyload - -" - "d /var/lib/pyload/config/scripts/package_extracted 0755 pyload pyload - -" - "L+ /var/lib/pyload/config/scripts/package_extracted/filebot-process.sh - - - - ${filebotScript}/bin/filebot-process" ]; - # FileBot license secret - sops.secrets.filebot-license = { - mode = "0440"; - owner = "pyload"; - group = "pyload"; - }; - containers.pyload = { autoStart = true; ephemeral = false; @@ -106,10 +96,6 @@ in hostPath = "/var/lib/multimedia"; isReadOnly = false; }; - "/var/lib/pyload/filebot-license.psm" = { - hostPath = config.sops.secrets.filebot-license.path; - isReadOnly = true; - }; }; config = { lib, config, pkgs, ... }: { @@ -124,7 +110,6 @@ in nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "unrar" - "filebot" ]; networking = { diff --git a/hosts/fw/modules/pyload/filebot-process.nix b/hosts/fw/modules/pyload/filebot-process.nix deleted file mode 100644 index b1055ff..0000000 --- a/hosts/fw/modules/pyload/filebot-process.nix +++ /dev/null @@ -1,89 +0,0 @@ -{ pkgs }: - -pkgs.writeShellScriptBin "filebot-process" '' - #!/usr/bin/env bash - set -euo pipefail - - # FileBot AMC script for automated media organization - # Called by PyLoad's package_extracted hook with parameters: - # $1 = package_id - # $2 = package_name - # $3 = download_folder (actual path to extracted files) - # $4 = password (optional) - - PACKAGE_ID="''${1:-}" - PACKAGE_NAME="''${2:-unknown}" - DOWNLOAD_DIR="''${3:-/downloads}" - PASSWORD="''${4:-}" - - OUTPUT_DIR="/multimedia" - LOG_FILE="/var/lib/pyload/filebot-amc.log" - EXCLUDE_LIST="/var/lib/pyload/filebot-exclude-list.txt" - - # Ensure FileBot data directory exists - mkdir -p /var/lib/pyload/.local/share/filebot/data - mkdir -p "$(dirname "$LOG_FILE")" - touch "$EXCLUDE_LIST" - - # Install FileBot license if not already installed - if [ ! -f /var/lib/pyload/.local/share/filebot/data/.license ]; then - echo "$(date): Installing FileBot license..." >> "$LOG_FILE" - ${pkgs.filebot}/bin/filebot --license /var/lib/pyload/filebot-license.psm || true - fi - - echo "===========================================" >> "$LOG_FILE" - echo "$(date): PyLoad package extracted hook triggered" >> "$LOG_FILE" - echo "Package ID: $PACKAGE_ID" >> "$LOG_FILE" - echo "Package Name: $PACKAGE_NAME" >> "$LOG_FILE" - echo "Download Directory: $DOWNLOAD_DIR" >> "$LOG_FILE" - echo "===========================================" >> "$LOG_FILE" - - # Check if download directory exists and has media files - if [ ! -d "$DOWNLOAD_DIR" ]; then - echo "$(date): Download directory does not exist: $DOWNLOAD_DIR" >> "$LOG_FILE" - exit 0 - fi - - # Check if there are any video/media files to process - if ! find "$DOWNLOAD_DIR" -type f \( -iname "*.mkv" -o -iname "*.mp4" -o -iname "*.avi" -o -iname "*.m4v" -o -iname "*.mov" \) -print -quit | grep -q .; then - echo "$(date): No media files found in: $DOWNLOAD_DIR" >> "$LOG_FILE" - echo "$(date): Skipping FileBot processing" >> "$LOG_FILE" - exit 0 - fi - - echo "$(date): Starting FileBot processing" >> "$LOG_FILE" - - # Run FileBot AMC script - set +e # Temporarily disable exit on error to capture exit code - ${pkgs.filebot}/bin/filebot \ - -script fn:amc \ - --output "$OUTPUT_DIR" \ - --action move \ - --conflict auto \ - -non-strict \ - --log-file "$LOG_FILE" \ - --def \ - excludeList="$EXCLUDE_LIST" \ - movieFormat="$OUTPUT_DIR/movies/{n} ({y})/{n} ({y}) - {vf}" \ - seriesFormat="$OUTPUT_DIR/tv-shows/{n}/Season {s.pad(2)}/{n} - {s00e00} - {t}" \ - ut_dir="$DOWNLOAD_DIR" \ - ut_kind=multi \ - clean=y \ - skipExtract=y - - FILEBOT_EXIT_CODE=$? - set -e # Re-enable exit on error - - if [ $FILEBOT_EXIT_CODE -ne 0 ]; then - echo "$(date): FileBot processing failed with exit code $FILEBOT_EXIT_CODE" >> "$LOG_FILE" - exit 0 # Don't fail the hook even if FileBot fails - fi - - echo "$(date): FileBot processing completed successfully" >> "$LOG_FILE" - - # Clean up any remaining empty directories - find "$DOWNLOAD_DIR" -type d -empty -delete 2>/dev/null || true - - echo "$(date): All processing completed" >> "$LOG_FILE" - exit 0 -'' diff --git a/hosts/fw/modules/pyload/pyload.nix b/hosts/fw/modules/pyload/pyload.nix index 402e65b..2899b94 100644 --- a/hosts/fw/modules/pyload/pyload.nix +++ b/hosts/fw/modules/pyload/pyload.nix @@ -1,8 +1,8 @@ -{ pkgs, lib, ... }: +{ pkgs, ... }: { environment.systemPackages = with pkgs; [ - unrar # Required for RAR archive extraction - p7zip # Required for 7z and other archive formats + unrar # Required for RAR archive extraction + p7zip # Required for 7z and other archive formats ]; services.pyload = { @@ -16,8 +16,8 @@ systemd.services.pyload = { # Add extraction tools to service PATH path = with pkgs; [ - unrar # For RAR extraction - p7zip # For 7z extraction + unrar # For RAR extraction + p7zip # For 7z extraction ]; environment = { @@ -38,7 +38,7 @@ # Enable ExternalScripts plugin for hooks PYLOAD__EXTERNALSCRIPTS__ENABLED = "1"; - PYLOAD__EXTERNALSCRIPTS__UNLOCK = "1"; # Run hooks asynchronously + PYLOAD__EXTERNALSCRIPTS__UNLOCK = "1"; # Run hooks asynchronously }; # Bind-mount DNS configuration files into the chroot @@ -50,20 +50,6 @@ "/etc/ssl" "/etc/static/ssl" ]; - # Bind mount multimedia directory as writable for FileBot hook scripts - BindPaths = [ "/multimedia" ]; - - # Override SystemCallFilter to allow @resources syscalls - # FileBot (Java) needs resource management syscalls like setpriority - # during cleanup operations. Still block privileged syscalls for security. - # Use mkForce to completely replace the NixOS module's default filter. - SystemCallFilter = lib.mkForce [ - "@system-service" - "@resources" # Explicitly allow resource management syscalls - "~@privileged" # Still block privileged operations - "fchown" # Re-allow fchown for FileBot file operations - "fchown32" # 32-bit compatibility - ]; }; }; } diff --git a/hosts/fw/modules/staticids.nix b/hosts/fw/modules/staticids.nix index 90b3973..7c25212 100644 --- a/hosts/fw/modules/staticids.nix +++ b/hosts/fw/modules/staticids.nix @@ -7,7 +7,6 @@ foundry-vtt = 10005; pyload = 10006; jellyfin = 10007; - filebot = 10008; forgejo = 10009; }; gids = { @@ -18,7 +17,6 @@ foundry-vtt = 10005; pyload = 10006; jellyfin = 10007; - filebot = 10008; forgejo = 10009; }; } diff --git a/utils/overlays/packages.nix b/utils/overlays/packages.nix index 719929a..c5bf8bc 100644 --- a/utils/overlays/packages.nix +++ b/utils/overlays/packages.nix @@ -7,6 +7,7 @@ self: super: { ai-mailer = self.callPackage ../pkgs/ai-mailer.nix { }; mautrix-mattermost = self.callPackage ../pkgs/mautrix-mattermost { }; claude-code = self.callPackage ../pkgs/claude-code { claude-code = super.claude-code; }; + filebot = self.callPackage ../pkgs/filebot { filebot = super.filebot; }; # Python packages python3 = super.python3.override { @@ -46,33 +47,35 @@ self: super: { in super.invidious.overrideAttrs (oldAttrs: { inherit version src; - postPatch = let - branchTemplate = ''{{ "#{`git branch | sed -n '/* /s///p'`.strip}" }}''; - commitTemplate = ''{{ "#{`git rev-list HEAD --max-count=1 --abbrev-commit`.strip}" }}''; - versionTemplate = ''{{ "#{`git log -1 --format=%ci | awk '{print $1}' | sed s/-/./g`.strip}" }}''; - tagTemplate = ''{{ "#{`git tag --points-at HEAD`.strip}" }}''; - assetCommitTemplate = ''{{ "#{`git rev-list HEAD --max-count=1 --abbrev-commit -- assets`.strip}" }}''; - in '' - for d in ${videojs}/*; do ln -s "$d" assets/videojs; done + postPatch = + let + branchTemplate = ''{{ "#{`git branch | sed -n '/* /s///p'`.strip}" }}''; + commitTemplate = ''{{ "#{`git rev-list HEAD --max-count=1 --abbrev-commit`.strip}" }}''; + versionTemplate = ''{{ "#{`git log -1 --format=%ci | awk '{print $1}' | sed s/-/./g`.strip}" }}''; + tagTemplate = ''{{ "#{`git tag --points-at HEAD`.strip}" }}''; + assetCommitTemplate = ''{{ "#{`git rev-list HEAD --max-count=1 --abbrev-commit -- assets`.strip}" }}''; + in + '' + for d in ${videojs}/*; do ln -s "$d" assets/videojs; done - substituteInPlace src/invidious.cr \ - --replace-fail ${super.lib.escapeShellArg branchTemplate} '"master"' \ - --replace-fail ${super.lib.escapeShellArg commitTemplate} '"${commit}"' \ - --replace-fail ${super.lib.escapeShellArg versionTemplate} '"${date}"' \ - --replace-fail ${super.lib.escapeShellArg tagTemplate} '""' \ - --replace-fail ${super.lib.escapeShellArg assetCommitTemplate} '"${commit}"' + substituteInPlace src/invidious.cr \ + --replace-fail ${super.lib.escapeShellArg branchTemplate} '"master"' \ + --replace-fail ${super.lib.escapeShellArg commitTemplate} '"${commit}"' \ + --replace-fail ${super.lib.escapeShellArg versionTemplate} '"${date}"' \ + --replace-fail ${super.lib.escapeShellArg tagTemplate} '""' \ + --replace-fail ${super.lib.escapeShellArg assetCommitTemplate} '"${commit}"' - substituteInPlace src/invidious.cr \ - --replace-fail 'public_folder "assets"' 'public_folder "${placeholder "out"}/share/invidious/assets"' - substituteInPlace src/invidious/helpers/i18n.cr \ - --replace-fail 'File.read("locales/' 'File.read("${placeholder "out"}/share/invidious/locales/' + substituteInPlace src/invidious.cr \ + --replace-fail 'public_folder "assets"' 'public_folder "${placeholder "out"}/share/invidious/assets"' + substituteInPlace src/invidious/helpers/i18n.cr \ + --replace-fail 'File.read("locales/' 'File.read("${placeholder "out"}/share/invidious/locales/' - substituteInPlace src/invidious/database/base.cr \ - --replace-fail 'config/sql' '${placeholder "out"}/share/invidious/config/sql' + substituteInPlace src/invidious/database/base.cr \ + --replace-fail 'config/sql' '${placeholder "out"}/share/invidious/config/sql' - substituteInPlace src/invidious/user/captcha.cr \ - --replace-fail 'Process.run(%(rsvg-convert' 'Process.run(%(${super.lib.getBin super.librsvg}/bin/rsvg-convert' - ''; + substituteInPlace src/invidious/user/captcha.cr \ + --replace-fail 'Process.run(%(rsvg-convert' 'Process.run(%(${super.lib.getBin super.librsvg}/bin/rsvg-convert' + ''; }); # vscode-insiders = (super.callPackage ../pkgs/vscode-insiders.nix { }); diff --git a/utils/pkgs/filebot/default.nix b/utils/pkgs/filebot/default.nix new file mode 100644 index 0000000..0981a26 --- /dev/null +++ b/utils/pkgs/filebot/default.nix @@ -0,0 +1,15 @@ +{ fetchurl, filebot }: + +# Override the nixpkgs filebot source URL. +# +# Upstream nixpkgs fetches from a single web.archive.org mirror which started +# returning HTTP 429, breaking builds. The same tarball is still hosted at +# get.filebot.net, so we swap the URL. The hash matches nixpkgs' pinned one +# because archive.org captured a byte-identical copy. Run ./update.sh to +# refresh if upstream re-uploads. +filebot.overrideAttrs (oldAttrs: { + src = fetchurl { + url = "https://get.filebot.net/filebot/FileBot_${oldAttrs.version}/FileBot_${oldAttrs.version}-portable.tar.xz"; + hash = "sha256-OcXXKaZcBuP584SJWeQB+aaxO0kih6Oiud0Vm8e9kPo="; + }; +}) diff --git a/utils/pkgs/filebot/update.sh b/utils/pkgs/filebot/update.sh new file mode 100755 index 0000000..97b1c9a --- /dev/null +++ b/utils/pkgs/filebot/update.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i bash -p curl cacert nix + +set -euo pipefail +cd "$(dirname "${BASH_SOURCE[0]}")" + +# Discover latest upstream version (same logic as nixpkgs' genericUpdater). +echo "Fetching latest FileBot version from filebot.net..." +version=$(curl -fsSL https://www.filebot.net \ + | sed -rne 's,^.*FileBot_([0-9]+\.[0-9]+\.[0-9]+)-portable\.tar\.xz.*,\1,p' \ + | sort -uV | tail -n1) + +# Allow pinning: ./update.sh --version 5.2.0 +if [ "${1:-}" = "--version" ] && [ -n "${2:-}" ]; then + version="$2" +fi + +if [ -z "${version:-}" ]; then + echo "Error: could not determine FileBot version" >&2 + exit 1 +fi +echo "Version: $version" + +url="https://get.filebot.net/filebot/FileBot_${version}/FileBot_${version}-portable.tar.xz" +hash=$(nix-prefetch-url --type sha256 "$url") +sri_hash=$(nix-hash --type sha256 --to-sri "$hash") +echo "SRI hash: $sri_hash" + +sed -i "s|hash = \"sha256-[A-Za-z0-9+/=]\{43,\}=\";|hash = \"$sri_hash\";|" default.nix + +echo "Updated utils/pkgs/filebot/default.nix -> $sri_hash"