mirror of
https://github.com/navidrome/navidrome.git
synced 2026-02-07 13:31:10 -05:00
* feat(plugins): add JSONForms schema for plugin configuration Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance error handling by formatting validation errors with field names Signed-off-by: Deluan <deluan@navidrome.org> * feat: enforce required fields in config validation and improve error handling Signed-off-by: Deluan <deluan@navidrome.org> * format JS code Signed-off-by: Deluan <deluan@navidrome.org> * feat: add config schema validation and enhance manifest structure Signed-off-by: Deluan <deluan@navidrome.org> * feat: refactor plugin config parsing and add unit tests Signed-off-by: Deluan <deluan@navidrome.org> * feat: add config validation error message in Portuguese * feat: enhance AlwaysExpandedArrayLayout with description support and improve array control testing Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Discord Rust plugin configuration to use JSONForm for user tokens and enhance schema validation Signed-off-by: Deluan <deluan@navidrome.org> * fix: resolve React Hooks linting issues in plugin UI components * Apply suggestions from code review Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * format code Signed-off-by: Deluan <deluan@navidrome.org> * feat: migrate schema validation to use santhosh-tekuri/jsonschema and improve error formatting Signed-off-by: Deluan <deluan@navidrome.org> * address PR comments Signed-off-by: Deluan <deluan@navidrome.org> * fix flaky test Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance array layout and configuration handling with AJV defaults Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement custom tester to exclude enum arrays from AlwaysExpandedArrayLayout Signed-off-by: Deluan <deluan@navidrome.org> * feat: add error boundary for schema rendering and improve error messages Signed-off-by: Deluan <deluan@navidrome.org> * feat: refine non-enum array control logic by utilizing JSONForms schema resolution Signed-off-by: Deluan <deluan@navidrome.org> * feat: add error styling to ToggleEnabledSwitch for disabled state Signed-off-by: Deluan <deluan@navidrome.org> * feat: adjust label positioning and styling in SchemaConfigEditor for improved layout Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement outlined input controls renderers to replace custom fragile CSS Signed-off-by: Deluan <deluan@navidrome.org> * feat: remove margin from last form control inside array items for better spacing Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance AJV error handling to transform required errors for field-level validation Signed-off-by: Deluan <deluan@navidrome.org> * feat: set default value for User Tokens in manifest.json to improve user experience Signed-off-by: Deluan <deluan@navidrome.org> * format Signed-off-by: Deluan <deluan@navidrome.org> * feat: add margin to outlined input controls for improved spacing Signed-off-by: Deluan <deluan@navidrome.org> * feat: remove redundant margin rule for last form control in array items Signed-off-by: Deluan <deluan@navidrome.org> * feat: adjust font size of label elements in SchemaConfigEditor for improved readability Signed-off-by: Deluan <deluan@navidrome.org> --------- Signed-off-by: Deluan <deluan@navidrome.org> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Navidrome Plugin Examples
This folder contains example plugins demonstrating various capabilities and languages supported by Navidrome's plugin system.
Available Examples
| Plugin | Language | Capabilities | Description |
|---|---|---|---|
| minimal | Go | MetadataAgent | Basic plugin structure |
| wikimedia | Go | MetadataAgent | Wikidata/Wikipedia metadata |
| crypto-ticker | Go | Scheduler, WebSocket, Cache | Real-time crypto prices (demo) |
| discord-rich-presence | Go | Scrobbler, Scheduler, WebSocket, Cache, Artwork | Discord integration |
| coverartarchive-py | Python | MetadataAgent | Cover Art Archive |
| nowplaying-py | Python | Scheduler, SubsonicAPI | Now playing logger |
| webhook-rs | Rust | Scrobbler | HTTP webhook on scrobble |
| library-inspector-rs | Rust | Library, Scheduler | Periodic library stats logging |
| discord-rich-presence-rs | Rust | Scrobbler, Scheduler, WebSocket, Cache, Artwork | Discord integration (Rust) |
Building
Prerequisites
- Go plugins: TinyGo 0.30+
- Python plugins: extism-py
- Rust plugins: Rust with
wasm32-unknown-unknowntarget
Build All Plugins
make all
This creates .ndp package files for each plugin.
Build Individual Plugin
make minimal.ndp
make wikimedia.ndp
make discord-rich-presence.ndp
Clean
make clean
Testing Plugins
With Extism CLI
Test any plugin without running Navidrome. First extract the .wasm file from the .ndp package:
# Install: https://extism.org/docs/install
# Extract the wasm file from the package
unzip -p minimal.ndp plugin.wasm > minimal.wasm
# Test a capability function
extism call minimal.wasm nd_get_artist_biography --wasi \
--input '{"id":"1","name":"The Beatles"}'
For plugins that make HTTP requests, allow the hosts:
unzip -p wikimedia.ndp plugin.wasm > wikimedia.wasm
extism call wikimedia.wasm nd_get_artist_biography --wasi \
--input '{"id":"1","name":"Yussef Dayes"}' \
--allow-host "query.wikidata.org" \
--allow-host "en.wikipedia.org"
With Navidrome
- Copy the
.ndpfile to your plugins folder - Enable plugins in
navidrome.toml:[Plugins] Enabled = true Folder = "/path/to/plugins" - For metadata agents, add to your agents list:
Agents = "lastfm,spotify,wikimedia"
Creating Your Own Plugin
Option 1: Start from Minimal
Copy the minimal example and modify:
cp -r minimal my-plugin
cd my-plugin
# Edit main.go and manifest.json
tinygo build -o plugin.wasm -target wasip1 -buildmode=c-shared .
zip -j my-plugin.ndp manifest.json plugin.wasm
Option 2: Bootstrap with XTP CLI
Generate boilerplate from a schema:
# Install XTP: https://docs.xtp.dylibso.com/docs/cli
xtp plugin init \
--schema-file ../schemas/metadata_agent.yaml \
--template go \
--path ./my-plugin \
--name my-plugin
# Then create manifest.json and package
cd my-plugin
xtp plugin build
zip -j my-plugin.ndp manifest.json dist/plugin.wasm
Available schemas in ../schemas/:
metadata_agent.yaml– Artist/album metadatascrobbler.yaml– Scrobbling integrationlifecycle.yaml– Init callbacksscheduler_callback.yaml– Scheduled taskswebsocket_callback.yaml– WebSocket events
Option 3: Different Language
See language-specific examples:
- Python: coverartarchive-py
- Rust: webhook-rs
Example Breakdown
Minimal (Go)
The simplest possible plugin. Shows:
- Manifest export
- Single capability function
- Basic input/output handling
Wikimedia (Go)
Real-world metadata agent. Shows:
- HTTP requests to external APIs
- SPARQL queries (Wikidata)
- Error handling
- Host allowlisting
Discord Rich Presence (Go)
Complex multi-capability plugin. Shows:
- Scrobbler – Receives play events
- WebSocket – Maintains Discord gateway connection
- Scheduler – Heartbeat and timeout management
- Cache – Connection state storage
- Artwork – Getting album art URLs
Cover Art Archive (Python)
Python metadata agent. Shows:
- extism-py plugin structure
- HTTP requests
- JSON handling
Webhook (Rust)
Rust scrobbler. Shows:
- extism-rs plugin structure
- HTTP POST requests
- Minimal dependencies