* 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
|
||
|---|---|---|
| .. | ||
| doc.go | ||
| lifecycle.go | ||
| lifecycle.yaml | ||
| lyrics.go | ||
| lyrics.yaml | ||
| metadata_agent.go | ||
| metadata_agent.yaml | ||
| README.md | ||
| scheduler_callback.go | ||
| scheduler_callback.yaml | ||
| scrobbler.go | ||
| scrobbler.yaml | ||
| sonic_similarity.go | ||
| sonic_similarity.yaml | ||
| taskworker.go | ||
| taskworker.yaml | ||
| websocket_callback.go | ||
| websocket_callback.yaml | ||
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:
- Go PDK packages (
pdk/go/*/) - Type-safe wrappers for Go plugin developers - Rust PDK crates (
pdk/rust/*/) - Type-safe wrappers for Rust plugin developers - 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