* 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.
Discord Rich Presence Plugin (Rust)
A Navidrome plugin that displays your currently playing track on Discord using Rich Presence. This is the Rust implementation demonstrating how to use the nd-pdk library.
⚠️ Warning
This plugin is for demonstration purposes only. It requires storing your Discord token in the Navidrome configuration file, which:
- Is not secure (tokens should never be stored in plain text)
- May violate Discord's Terms of Service
Use at your own risk.
Features
- Shows currently playing track on Discord Rich Presence
- Displays album artwork
- Shows track progress with start/end timestamps
- Automatically clears presence when track finishes
- Supports multiple users
Capabilities
This plugin implements multiple capabilities to demonstrate the nd-pdk library:
- Scrobbler: Receives now-playing events from Navidrome
- SchedulerCallback: Handles heartbeat and activity clearing timers
- WebSocketCallback: Communicates with Discord gateway (text, binary, error, and close handlers)
Configuration
Configure in the Navidrome UI (Settings → Plugins → discord-rich-presence-rs):
| Key | Description | Example |
|---|---|---|
clientid |
Your Discord application ID | 123456789012345678 |
user.<name> |
Discord token for the specified user | user.alice = token123 |
Each user is configured as a separate key with the user. prefix.
Getting Configuration Values
-
Client ID: Create a Discord Application at https://discord.com/developers/applications and copy the Application ID
-
Discord Token: This requires extracting your user token from Discord (not recommended for security reasons)
-
Multiple Users: Add multiple user keys:
user.user1 = "token1" user.user2 = "token2"
Building
# From the plugins/examples directory
make discord-rich-presence-rs.ndp
# This creates discord-rich-presence-rs.ndp containing:
# - manifest.json
# - plugin.wasm
Installation
- Build the plugin using the command above
- Copy the
.ndpfile to your Navidrome plugins directory - Enable and configure the plugin in the Navidrome UI (Settings → Plugins)
- Restart Navidrome if needed
Using nd-pdk Library
This plugin demonstrates how to use the Rust plugin development kit:
use nd_pdk::host::{artwork, cache, scheduler, websocket};
use std::collections::HashMap;
// Get artwork URL
let url = artwork::get_track_url(track_id, 300)?;
// Cache operations
cache::set_string("key", "value", 3600)?;
if let Some(value) = cache::get_string("key")? {
// Use the cached value
}
// Schedule tasks
scheduler::schedule_one_time(60, "payload", "task-id")?;
scheduler::schedule_recurring("@every 30s", "heartbeat", "heartbeat-task")?;
// WebSocket operations
let conn_id = websocket::connect("wss://example.com/socket", HashMap::new(), "my-conn")?;
websocket::send_text(&conn_id, "Hello")?;
License
GPL-3.0