Files
Deluan Quintão 1072e9f7eb chore(plugins): document requiredHosts rules and deprecate pdk.NewHTTPRequest (#6129)
* fix(plugins): align the Python HTTP example with the repo's host-call pattern

Bind http_send with raw memory offsets like nowplaying-py does, drop
guards for fields the host always sends, and document how plugins
without a PDK call host services and which built-in HTTP APIs are
disabled.

* docs(plugins): document the private-address rules for HTTP requiredHosts

Explain in the README and manifest schema that named hosts can't reach
private addresses while IP/CIDR entries and a bare "*" can.

* docs(plugins): document the private-address rules for requiredHosts

Explain in the README and manifest schema that named hosts can't reach
private addresses while IP/CIDR entries and a bare "*" can, for both
HTTP and WebSocket. Inline the single-use HTTP isHostAllowed wrapper.

* feat(plugins): derive Default for Rust host service structs

The ndpgen client.rs template now adds Default to the derive list of host
service structs, as the capability and shared types templates already do.
Plugin authors can now set only the fields they need, for example
HTTPRequest { method, url, ..Default::default() }. The webhook-rs and
discord-rich-presence-rs examples use this form now. The golden files and
the generated nd-pdk-host crate are updated to match.

* feat(plugins): deprecate pdk.NewHTTPRequest in the Go PDK

Navidrome no longer enables extism's http_request host function, so a
request built with pdk.NewHTTPRequest always fails. ndpgen now reads a small
deprecation table and writes a Deprecated: paragraph for the listed extism
functions, in both the WASM wrapper and the native stub. Linters and IDEs
now point plugin authors to host.HTTPSend. The PDK example tests used to
teach NewHTTPRequest. They now use host.HTTPSend and host.HTTPMock.

* docs(plugins): correct requiredHosts rules for websocket and private addresses

Two statements in the plugin docs did not match the code.

The WebSocket section claimed requiredHosts behaves like HTTP. It does not:
host_httpclient.go only consults the allowlist when the list is non-empty and
otherwise falls back to allowing public addresses, while host_websocket.go
always calls isHostInAllowlist, so an absent list blocks every connection.

The HTTP section claimed a named host can never reach a private address.
checkPrivateDial scans the whole requiredHosts list, so a named host does
reach a private address when the same list also holds a covering IP or CIDR.

Reworded both, plus the matching requiredHosts descriptions in
manifest-schema.json, and regenerated manifest_gen.go.
2026-09-12 13:59:46 -04:00
..

Webhook Scrobbler Plugin (Rust)

A Navidrome plugin written in Rust that sends HTTP webhook notifications when tracks are scrobbled. This is useful for integrating with external services like home automation systems, Discord bots, monitoring tools, or any service that can receive HTTP requests.

Features

  • Sends HTTP GET requests to configured URLs on every scrobble event
  • Includes track metadata (title, artist, album, username, timestamp) as query parameters
  • Supports multiple webhook URLs (comma-separated)
  • All users are automatically authorized (no external service authentication required)
  • Now playing events are ignored (webhooks fire only on completed scrobbles)

Prerequisites

  • Rust toolchain
  • WebAssembly target: rustup target add wasm32-unknown-unknown

Building

From the plugins/examples directory:

make webhook-rs.ndp

Or build directly with cargo:

cd webhook-rs
cargo build --release
zip -j webhook-rs.ndp manifest.json target/wasm32-unknown-unknown/release/webhook_rs.wasm

Installation

Copy webhook-rs.ndp to your Navidrome plugins folder (configured via Plugins.Folder in your config).

Configuration

Configure in the Navidrome UI (Settings → Plugins → webhook-rs):

Key Description Example
urls Comma-separated list of webhook URLs https://example.com/hook1,https://example.com/hook2

Webhook Request Format

When a scrobble occurs, the plugin sends an HTTP GET request to each configured URL with the following query parameters:

Parameter Description
title Track title
artist Track artist
album Album name
user Username who scrobbled
timestamp Unix timestamp when the track started playing

Example request:

GET https://example.com/webhook?title=Song%20Name&artist=Artist%20Name&album=Album%20Name&user=john&timestamp=1703270400

Use Cases

  • Home Automation: Trigger lights or displays when music starts playing
  • Discord/Slack Notifications: Post currently playing tracks to a channel
  • Logging/Analytics: Track listening history in an external system
  • IFTTT/Zapier Integration: Connect to thousands of services via webhook triggers

Development

The plugin is built using the Extism Rust PDK. Key exports:

  • nd_manifest - Returns plugin metadata and permissions
  • nd_scrobbler_is_authorized - Always returns true (all users authorized)
  • nd_scrobbler_now_playing - No-op (returns success without action)
  • nd_scrobbler_scrobble - Sends webhooks to configured URLs