docs: update READMEs

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan 2025-12-28 14:18:38 -05:00
parent 505b3c529f
commit aa83d3af6c
5 changed files with 675 additions and 693 deletions

View File

@ -89,7 +89,6 @@ type configOptions struct {
PasswordEncryptionKey string
ExtAuth extAuthOptions
Plugins pluginsOptions
PluginConfig map[string]map[string]string
HTTPHeaders httpHeaderOptions `json:",omitzero"`
Prometheus prometheusOptions `json:",omitzero"`
Scanner scannerOptions `json:",omitzero"`

File diff suppressed because it is too large Load Diff

View File

@ -1,55 +1,57 @@
# Navidrome Plugin Examples
This folder contains example plugins for Navidrome that demonstrate how to build metadata agents using the plugin system.
This folder contains example plugins demonstrating various capabilities and languages supported by Navidrome's plugin system.
## Available Examples
| Plugin | Language | Capabilities | Description |
|-------------------------------------------------|----------|-------------------------------------------------|--------------------------------|
| [minimal](minimal/) | Go | MetadataAgent | Basic plugin structure |
| [wikimedia](wikimedia/) | Go | MetadataAgent | Wikidata/Wikipedia metadata |
| [crypto-ticker](crypto-ticker/) | Go | Scheduler, WebSocket, Cache | Real-time crypto prices (demo) |
| [discord-rich-presence](discord-rich-presence/) | Go | Scrobbler, Scheduler, WebSocket, Cache, Artwork | Discord integration |
| [coverartarchive-py](coverartarchive-py/) | Python | MetadataAgent | Cover Art Archive |
| [nowplaying-py](nowplaying-py/) | Python | Scheduler, SubsonicAPI | Now playing logger |
| [webhook-rs](webhook-rs/) | Rust | Scrobbler | HTTP webhook on scrobble |
## Building
### Prerequisites
- [TinyGo](https://tinygo.org/getting-started/install/) (recommended) or Go 1.23+ (for Go plugins)
- [extism-py](https://github.com/extism/python-pdk) (for Python plugins)
- [Rust](https://rustup.rs/) with `wasm32-unknown-unknown` target (for Rust plugins)
- [Extism CLI](https://extism.org/docs/install) (optional, for testing)
- **Go plugins:** [TinyGo](https://tinygo.org/getting-started/install/) 0.30+
- **Python plugins:** [extism-py](https://github.com/extism/python-pdk)
- **Rust plugins:** [Rust](https://rustup.rs/) with `wasm32-unknown-unknown` target
### Build all plugins
### Build All (Go plugins)
```bash
make all
```
This will compile all (Go) example plugins and place the `.wasm` files in this directory.
### Build a specific plugin
### Build Individual Plugin
```bash
make minimal.wasm
make wikimedia.wasm
make discord-rich-presence.wasm
```
### Clean build artifacts
### Clean
```bash
make clean
```
## Available Examples
## Testing Plugins
| Plugin | Language | Description |
|-------------------------------------------------|----------|-------------------------------------------------------------------------|
| [minimal](minimal/) | Go | A minimal example showing the basic plugin structure |
| [wikimedia](wikimedia/) | Go | Fetches artist metadata from Wikidata, DBpedia, and Wikipedia |
| [crypto-ticker](crypto-ticker/) | Go | Real-time cryptocurrency prices from Coinbase using WebSocket |
| [discord-rich-presence](discord-rich-presence/) | Go | Discord Rich Presence integration using Scrobbler, WebSocket, Scheduler |
| [coverartarchive-py](coverartarchive-py/) | Python | Album cover art from Cover Art Archive (Python example) |
| [nowplaying-py](nowplaying-py/) | Python | Logs currently playing tracks using Scheduler and SubsonicAPI |
| [webhook-rs](webhook-rs/) | Rust | Sends HTTP webhooks on scrobble events (Rust example) |
### With Extism CLI
## Testing with Extism CLI
You can test any plugin using the Extism CLI:
Test any plugin without running Navidrome:
```bash
# Test the manifest
# Install: https://extism.org/docs/install
# Test manifest
extism call minimal.wasm nd_manifest --wasi
# Test with input
@ -57,58 +59,112 @@ extism call minimal.wasm nd_get_artist_biography --wasi \
--input '{"id":"1","name":"The Beatles"}'
```
For plugins that make HTTP requests, use `--allow-host` to permit access:
For plugins that make HTTP requests, allow the hosts:
```bash
extism call wikimedia.wasm nd_get_artist_biography --wasi \  3s   ▼
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"
--allow-host "query.wikidata.org" \
--allow-host "en.wikipedia.org"
```
## Installation
### With Navidrome
Copy any `.wasm` file to your Navidrome plugins folder:
```bash
cp wikimedia.wasm /path/to/navidrome/plugins/
```
Then enable plugins in your `navidrome.toml`:
```toml
[Plugins]
Enabled = true
Folder = "/path/to/navidrome/plugins"
```
And add the plugin to your agents list:
```toml
Agents = "lastfm,spotify,wikimedia"
```
1. Copy the `.wasm` file to your plugins folder
2. Enable plugins in `navidrome.toml`:
```toml
[Plugins]
Enabled = true
Folder = "/path/to/plugins"
```
3. For metadata agents, add to your agents list:
```toml
Agents = "lastfm,spotify,wikimedia"
```
## Creating Your Own Plugin
The plugin system supports multiple languages. See the [minimal](minimal/) example for the simplest Go starting point,
[discord-rich-presence](discord-rich-presence/) for a more complete Go example with HTTP requests, [coverartarchive-py](coverartarchive-py/)
for a Python example, or [webhook-rs](webhook-rs/) for a Rust example.
### Option 1: Start from Minimal
### Bootstrapping a New Plugin
Use the XTP CLI to bootstrap a new plugin from a schema:
Copy the [minimal](minimal/) example and modify:
```bash
cp -r minimal my-plugin
cd my-plugin
# Edit main.go
tinygo build -o my-plugin.wasm -target wasip1 -buildmode=c-shared .
```
### Option 2: Bootstrap with XTP CLI
Generate boilerplate from a schema:
```bash
# Install XTP: https://docs.xtp.dylibso.com/docs/cli
xtp plugin init \
--schema-file plugins/schemas/metadata_agent.yaml \
--schema-file ../schemas/metadata_agent.yaml \
--template go \
--path ./my-plugin \
--name my-plugin
```
See the [schemas README](../schemas/README.md) for more information about available schemas
and supported languages.
Available schemas in [../schemas/](../schemas/):
- `metadata_agent.yaml` Artist/album metadata
- `scrobbler.yaml` Scrobbling integration
- `lifecycle.yaml` Init callbacks
- `scheduler_callback.yaml` Scheduled tasks
- `websocket_callback.yaml` WebSocket events
For the simplest starting point, look at [minimal](minimal/). For a more complete example
with HTTP requests, see [wikimedia](wikimedia/).
### Option 3: Different Language
See language-specific examples:
- **Python:** [coverartarchive-py](coverartarchive-py/)
- **Rust:** [webhook-rs](webhook-rs/)
For full documentation, see the [Plugin System README](../README.md).
## 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
## Resources
- [Plugin System Documentation](../README.md)
- [Extism PDK Docs](https://extism.org/docs/concepts/pdk)
- [TinyGo WebAssembly](https://tinygo.org/docs/guides/webassembly/)
- [XTP CLI](https://docs.xtp.dylibso.com/docs/cli)

View File

@ -104,7 +104,6 @@ func createTestManagerWithPlugins(pluginConfig map[string]map[string]string, plu
conf.Server.Plugins.Folder = tmpDir
conf.Server.Plugins.AutoReload = false
conf.Server.CacheFolder = filepath.Join(tmpDir, "cache")
conf.Server.PluginConfig = pluginConfig
// Setup mock DataStore with pre-enabled plugins
mockPluginRepo := tests.CreateMockPluginRepo()

View File

@ -1,66 +1,72 @@
# Navidrome Plugin Schemas
This directory contains [XTP schemas](https://docs.xtp.dylibso.com/docs/concepts/xtp-schema)
that define the plugin capabilities for Navidrome. These schemas can be used to bootstrap
new plugins using the `xtp` CLI tool.
This directory contains [XTP schemas](https://docs.xtp.dylibso.com/docs/concepts/xtp-schema) that define plugin capabilities. Use these schemas to bootstrap new plugins with the `xtp` CLI.
## Available Schemas
| Schema | Description |
|----------------------------------------------------|---------------------------------------------------------------------|
| [lifecycle.yaml](lifecycle.yaml) | Lifecycle callbacks (init) for plugin initialization |
| [metadata_agent.yaml](metadata_agent.yaml) | Metadata agent for retrieving artist/album information |
| [scheduler_callback.yaml](scheduler_callback.yaml) | Scheduler callback for plugins using the scheduler host service |
| [scrobbler.yaml](scrobbler.yaml) | Scrobbler capability for sending play data to external services |
| [websocket_callback.yaml](websocket_callback.yaml) | WebSocket callbacks for handling messages, errors, and close events |
| Schema | Description |
|----------------------------------------------------|---------------------------------|
| [metadata_agent.yaml](metadata_agent.yaml) | Artist/album metadata retrieval |
| [scrobbler.yaml](scrobbler.yaml) | Scrobbling to external services |
| [lifecycle.yaml](lifecycle.yaml) | Plugin initialization callback |
| [scheduler_callback.yaml](scheduler_callback.yaml) | Scheduled task callbacks |
| [websocket_callback.yaml](websocket_callback.yaml) | WebSocket event callbacks |
## Prerequisites
Install the `xtp` CLI tool. See the [XTP CLI documentation](https://docs.xtp.dylibso.com/docs/cli)
for installation instructions, or install via:
Install the XTP CLI:
```bash
curl -fsSL https://static.dylibso.com/cli/install.sh | bash
```
Or see [XTP CLI documentation](https://docs.xtp.dylibso.com/docs/cli) for other methods.
## Bootstrapping a Plugin
Use the `xtp plugin init` command to generate boilerplate code from a schema.
### Supported Languages
The XTP CLI supports multiple languages via bindgen templates:
- Go
- Rust
- TypeScript
- Python
- C#
- Zig
- C++
### Examples
**Create a Go scrobbler plugin:**
### Basic Usage
```bash
xtp plugin init \
--schema-file plugins/schemas/scrobbler.yaml \
--template go \
--path ./my-scrobbler \
--name my-scrobbler
--schema-file <schema> \
--template <language> \
--path <output-dir> \
--name <plugin-name>
```
**Create a Rust metadata agent plugin:**
### Supported Languages
- `go` Go (recommended, use with TinyGo)
- `rust` Rust
- `typescript` TypeScript
- `python` Python
- `csharp` C#
- `zig` Zig
- `cpp` C++
### Examples
**Go metadata agent:**
```bash
xtp plugin init \
--schema-file plugins/schemas/metadata_agent.yaml \
--template rust \
--template go \
--path ./my-agent \
--name my-agent
```
**Create a TypeScript scrobbler plugin:**
**Rust scrobbler:**
```bash
xtp plugin init \
--schema-file plugins/schemas/scrobbler.yaml \
--template rust \
--path ./my-scrobbler \
--name my-scrobbler
```
**TypeScript scrobbler:**
```bash
xtp plugin init \
@ -70,30 +76,23 @@ xtp plugin init \
--name ts-scrobbler
```
### Generated Files
## Generated Files
After running `xtp plugin init`, you'll get a project structure with:
After running `xtp plugin init`, you'll get:
- `main.go` (or equivalent for your language) - Plugin implementation with stub functions
- `pdk.gen.go` - Generated types from the schema
- `xtp.toml` - Plugin configuration
- Build scripts for your language
```
my-plugin/
├── main.go # Plugin implementation (stubs)
├── pdk.gen.go # Generated types from schema
├── xtp.toml # Plugin configuration
└── go.mod # Go module (for Go plugins)
```
### Implementing the Plugin
## Implementing Your Plugin
Edit the generated `main.go` file and replace the `panic()` calls with your implementation.
### 1. Add the Manifest
> **Note:** You don't need to implement all generated functions. Remove any functions that
> your plugin doesn't need. Navidrome will only call the functions that are exported by your
> plugin, and will gracefully handle missing capabilities.
#### Required: The `nd_manifest` Function
In addition to the capability functions generated from the schema, **every plugin must
implement the `nd_manifest` function**. This function returns metadata about your plugin
that Navidrome uses to identify and describe it.
**Go example:**
Every plugin **must** implement `nd_manifest`. This is not in the schemas—add it manually:
```go
import (
@ -102,78 +101,82 @@ import (
)
type Manifest struct {
Name string `json:"name"`
Author string `json:"author"`
Version string `json:"version"`
Description string `json:"description"`
Name string `json:"name"`
Author string `json:"author"`
Version string `json:"version"`
Description string `json:"description,omitempty"`
Website string `json:"website,omitempty"`
Permissions *Permissions `json:"permissions,omitempty"`
}
//go:wasmexport nd_manifest
func ndManifest() int32 {
manifest := Manifest{
Name: "My Scrobbler Plugin",
Name: "My Plugin",
Author: "Your Name",
Version: "1.0.0",
Description: "A custom scrobbler for My Service",
}
out, err := json.Marshal(manifest)
if err != nil {
pdk.SetError(err)
return 1
Description: "What this plugin does",
}
out, _ := json.Marshal(manifest)
pdk.Output(out)
return 0
}
```
**Python example:**
### 2. Implement Capability Functions
```python
import extism
@extism.plugin_fn
def nd_manifest():
import json
manifest = {
"name": "My Scrobbler Plugin",
"author": "Your Name",
"version": "1.0.0",
"description": "A custom scrobbler for My Service"
}
extism.output_str(json.dumps(manifest))
```
#### Implementing Capability Functions
Replace the `panic()` calls in the generated stubs with your implementation:
Replace the generated `panic()` stubs with your implementation:
```go
// Example: Implement the IsAuthorized function
func NdScrobblerIsAuthorized(input AuthInput) (AuthOutput, error) {
// Your authorization logic here
authorized := checkUserAuthorization(input.UserID, input.Username)
return AuthOutput{Authorized: authorized}, nil
// Generated stub:
func NdGetArtistBiography(input ArtistInput) (BiographyOutput, error) {
panic("not implemented")
}
// Your implementation:
func NdGetArtistBiography(input ArtistInput) (BiographyOutput, error) {
bio := fetchBiography(input.Name)
return BiographyOutput{Biography: bio}, nil
}
```
### Building the Plugin
### 3. Remove Unused Functions
Build the plugin to WebAssembly:
You don't need to implement all functions from a schema. Delete any you don't need—Navidrome only calls functions that exist.
### 4. Build
```bash
xtp plugin build
```
This creates a `.wasm` file that can be loaded by Navidrome.
Or manually with TinyGo:
```bash
tinygo build -o my-plugin.wasm -target wasip1 -buildmode=c-shared .
```
## Combining Capabilities
A single plugin can implement multiple capabilities. Generate from one schema, then manually add functions from others:
```bash
# Start with metadata agent
xtp plugin init --schema-file metadata_agent.yaml --template go --path ./my-plugin --name my-plugin
# Manually add scrobbler functions from scrobbler.yaml
# Manually add scheduler callback from scheduler_callback.yaml
```
Or combine schemas manually before generating (advanced).
## Schema Format
These schemas use the [XTP Schema v1-draft](https://docs.xtp.dylibso.com/docs/concepts/xtp-schema) format,
which is based on JSON Schema with extensions for defining plugin exports and imports.
These schemas use [XTP Schema v1-draft](https://docs.xtp.dylibso.com/docs/concepts/xtp-schema) format, which extends JSON Schema with plugin-specific extensions for exports and imports.
## Resources
- [Plugin System Documentation](../README.md)
- [XTP Documentation](https://docs.xtp.dylibso.com/)
- [XTP Bindgen Repository](https://github.com/dylibso/xtp-bindgen)
- [XTP Schema Definition](https://raw.githubusercontent.com/dylibso/xtp-bindgen/5090518dd86ba5e734dc225a33066ecc0ed2e12d/plugin/schema.json)
- [Extism Plugin Development Kit](https://extism.org/docs/concepts/pdk)
- [XTP Schema Reference](https://docs.xtp.dylibso.com/docs/concepts/xtp-schema)
- [Extism PDK](https://extism.org/docs/concepts/pdk)