Navidrome Plugin Schemas

This directory contains XTP schemas that define the plugin capabilities for Navidrome. These schemas can be used to bootstrap new plugins using the xtp CLI tool.

Available Schemas

Schema Description
metadata_agent.yaml Metadata agent for retrieving artist/album information
scheduler_callback.yaml Scheduler callback for plugins using the scheduler host service
scrobbler.yaml Scrobbler capability for sending play data to external services

Prerequisites

Install the xtp CLI tool. See the XTP CLI documentation for installation instructions, or install via:

curl -fsSL https://static.dylibso.com/cli/install.sh | bash

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:

xtp plugin init \
  --schema-file plugins/schemas/scrobbler.yaml \
  --template go \
  --path ./my-scrobbler \
  --name my-scrobbler

Create a Rust metadata agent plugin:

xtp plugin init \
  --schema-file plugins/schemas/metadata_agent.yaml \
  --template rust \
  --path ./my-agent \
  --name my-agent

Create a TypeScript scrobbler plugin:

xtp plugin init \
  --schema-file plugins/schemas/scrobbler.yaml \
  --template typescript \
  --path ./ts-scrobbler \
  --name ts-scrobbler

Generated Files

After running xtp plugin init, you'll get a project structure with:

  • 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

Implementing the Plugin

Edit the generated main.go file and replace the panic() calls with your implementation.

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:

import (
    "encoding/json"
    "github.com/extism/go-pdk"
)

type Manifest struct {
    Name        string `json:"name"`
    Author      string `json:"author"`
    Version     string `json:"version"`
    Description string `json:"description"`
}

//go:wasmexport nd_manifest
func ndManifest() int32 {
    manifest := Manifest{
        Name:        "My Scrobbler 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
    }
    pdk.Output(out)
    return 0
}

Python example:

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:

// 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
}

Building the Plugin

Build the plugin to WebAssembly:

xtp plugin build

This creates a .wasm file that can be loaded by Navidrome.

Schema Format

These schemas use the XTP Schema v1-draft format, which is based on JSON Schema with extensions for defining plugin exports and imports.

Resources