{ pkgs, lib }: let pluginVersion = "18.0.0.0"; # GUID from the plugin's meta.json; addresses it on /Plugins//Configuration. pluginGuid = "71552a5a-5c5c-4350-a2ae-ebe451a30173"; package = pkgs.stdenvNoCC.mkDerivation { pname = "jellyfin-plugin-webhook"; version = pluginVersion; src = pkgs.fetchurl { url = "https://repo.jellyfin.org/files/plugin/webhook/webhook_${pluginVersion}.zip"; hash = "sha256-LFFojiPnBGl9KJ0xVyPBnCmatcaeVbllRwRkz5Z3dqI="; }; nativeBuildInputs = [ pkgs.unzip ]; unpackPhase = ''unzip "$src"''; installPhase = '' mkdir -p "$out" cp *.dll meta.json "$out/" ''; dontFixup = true; # managed .NET assemblies must not be patched }; # Minimal Handlebars template, base64 encoded. The monitor only needs the POST; # NotificationType is parsed for the debug log line. # Decoded: {"NotificationType":"{{NotificationType}}"} templateB64 = "eyJOb3RpZmljYXRpb25UeXBlIjoie3tOb3RpZmljYXRpb25UeXBlfX0ifQ=="; # Build a PluginConfiguration payload accepted by Jellyfin's JSON deserializer. # Each webhook is `{ name, uri, notificationTypes }`. mkConfigJson = webhooks: builtins.toJSON { ServerUrl = ""; GenericOptions = map (w: { NotificationTypes = w.notificationTypes; WebhookName = w.name; WebhookUri = w.uri; EnableMovies = true; EnableEpisodes = true; EnableVideos = true; EnableWebhook = true; Template = templateB64; Headers = [ { Key = "Content-Type"; Value = "application/json"; } ]; }) webhooks; }; # Oneshot that POSTs the plugin configuration. Retries past the window # between Jellyfin API health and plugin registration. mkConfigureScript = { jellyfinUrl, webhooks }: pkgs.writeShellScript "jellyfin-webhook-configure" '' set -euo pipefail export PATH=${ lib.makeBinPath [ pkgs.coreutils pkgs.curl ] } URL=${lib.escapeShellArg jellyfinUrl} AUTH="Authorization: MediaBrowser Token=\"$(cat "$CREDENTIALS_DIRECTORY/jellyfin-api-key")\"" CONFIG=${lib.escapeShellArg (mkConfigJson webhooks)} for _ in $(seq 1 120); do curl -sf -o /dev/null "$URL/health" && break; sleep 1; done curl -sf -o /dev/null "$URL/health" for _ in $(seq 1 60); do if printf '%s' "$CONFIG" | curl -sf -X POST \ -H "$AUTH" -H "Content-Type: application/json" --data-binary @- \ "$URL/Plugins/${pluginGuid}/Configuration"; then echo "Jellyfin webhook plugin configured"; exit 0 fi sleep 1 done echo "Failed to configure webhook plugin" >&2; exit 1 ''; # Materialise a writable copy of the plugin. Jellyfin rewrites meta.json at # runtime, so a read-only nix-store symlink would EACCES. mkInstallScript = { pluginsDir }: pkgs.writeShellScript "jellyfin-webhook-install" '' set -euo pipefail export PATH=${lib.makeBinPath [ pkgs.coreutils ]} dst=${lib.escapeShellArg "${pluginsDir}/Webhook_${pluginVersion}"} mkdir -p ${lib.escapeShellArg pluginsDir} rm -rf "$dst" && mkdir -p "$dst" cp ${package}/*.dll ${package}/meta.json "$dst/" chmod u+rw "$dst"/* ''; in { inherit package pluginVersion pluginGuid mkConfigureScript mkInstallScript ; }