Files
navidrome/plugins/capabilities
Deluan Quintão 7b7721f002 feat(matcher): match similar/top songs by multiple artists (#5668)
* feat(matcher): add Song.Artists (agents.Artist) and field-wise song dedup

* refactor(matcher): make song equality an agents.Song.Equals method via hashstructure

Move the sameSong free function from core/matcher into an Equals method on
agents.Song, following the model.MediaFile/Album.Equals convention. Uses strict
hashstructure hashing (nil opts, no IgnoreZeroValue) to preserve the original
whole-value equality contract. Tests moved to core/agents.

* feat(matcher): match by multiple artists with overlap ranking and artist-ID fast-path

* refactor(matcher): rank artist overlap and specificity above the preferred-track flag

Identity signals (specificityLevel, artistOverlap) now outrank the taste
signal (preferredMatch) in betterThan. A starred/4-star track that is a worse
identity match no longer beats a more specific or higher-overlap track.
PreferStarred still breaks ties when specificity and overlap are equal.

* fix(matcher): score artist-MBID specificity against all credited artists, not just the last

sanitizedTrack.artistMBID (string) replaced with artistMBIDs (map[string]struct{}) so
bucketTracks collects all credited owned MBIDs per query instead of last-write-wins.
computeSpecificityLevel tests set membership, letting each of a collaboration's
MBID-bearing artists reach the proper specificity level (4/5) independently.

* feat(plugins): carry multiple artists (with IDs) through SongRef conversions

* feat(plugins): regenerate schemas and PDK wrappers for multi-artist SongRef

* refactor(matcher): tidy bucketTracks accumulator and artist resolution

Replace bucketTracks' two parallel per-track maps (overlapByQuery/mbidsByQuery)
with a named queryAccum struct (F2). Collapse resolveArtists' four hand-mutated
parallel maps into a pendingArtist slice with derived nameToQueries/mbidToQueries
maps (F1). Replace the own() method on resolvedArtists with a package-level
addToSet helper that drops the method/receiver indirection (F3).

* docs(matcher): trim comments that restate the code

* fix(plugins): use Vec::is_empty for slice fields in generated Rust PDK

* fix(matcher): treat a resolved artist ID as an identity match for specificity

* docs(matcher): reflect artist-ID identity in the specificity ladder
2026-06-26 14:46:09 -04:00
..

Navidrome Plugin Capabilities

This directory contains the Go interface definitions for Navidrome plugin capabilities. These interfaces are the source of truth for plugin development and are used to generate:

  1. Go PDK packages (pdk/go/*/) - Type-safe wrappers for Go plugin developers
  2. Rust PDK crates (pdk/rust/*/) - Type-safe wrappers for Rust plugin developers
  3. XTP YAML schemas (*.yaml) - Schema files for other Extism plugin languages (TypeScript, Python, C#, Zig, C++, ...)

For Go Plugin Developers

Go developers should use the generated PDK packages in plugins/pdk/go/. See the example Go plugins in plugins/examples/ for usage patterns.

For Rust Plugin Developers

Rust developers should use the generated PDK crate in plugins/pdk/rust/nd-pdk. See the example Rust plugins in plugins/examples for usage patterns.

For Non-Go Plugin Developers

If you're developing plugins in other languages (TypeScript, Rust, Python, C#, Zig, C++), you can use the XTP CLI to generate type-safe bindings from the YAML schema files in this directory.

Prerequisites

Install the XTP CLI:

# macOS
brew install dylibso/tap/xtp

# Other platforms - see https://docs.xtp.dylibso.com/docs/cli
curl https://static.dylibso.com/cli/install.sh | bash

Generating Plugin Scaffolding

Use the XTP CLI to generate plugin boilerplate from any capability schema:

# TypeScript
xtp plugin init --schema-file plugins/capabilities/metadata_agent.yaml \
    --template typescript --path my-plugin

# Rust
xtp plugin init --schema-file plugins/capabilities/scrobbler.yaml \
    --template rust --path my-plugin

# Python
xtp plugin init --schema-file plugins/capabilities/lifecycle.yaml \
    --template python --path my-plugin

# C#
xtp plugin init --schema-file plugins/capabilities/scheduler_callback.yaml \
    --template csharp --path my-plugin

# Go (alternative to using the PDK packages)
xtp plugin init --schema-file plugins/capabilities/websocket_callback.yaml \
    --template go --path my-plugin

Available Capabilities

Capability Schema File Description
Metadata Agent metadata_agent.yaml Fetch artist biographies, album images, and similar artists
Scrobbler scrobbler.yaml Report listening activity to external services
Lifecycle lifecycle.yaml Plugin initialization callbacks
Scheduler Callback scheduler_callback.yaml Scheduled task execution
WebSocket Callback websocket_callback.yaml Real-time WebSocket message handling

Building Your Plugin

After generating the scaffolding, implement the required functions and build your plugin as a WebAssembly module. The exact build process depends on your chosen language - see the Extism PDK documentation for language-specific guides.

XTP Schema Generation

The YAML schemas in this package are automatically generated from the capability Go interfaces using ndpgen. To regenerate the schemas after modifying the interfaces, run:

cd plugins/cmd/ndpgen && go run . -schemas -input=./plugins/capabilities

Resources