navidrome/plugins/capabilities/metadata_agent.go

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

238 lines
8.1 KiB
Go
Raw Permalink Normal View History

feat(plugins): New Plugin System with multi-language PDK support (#4833) * chore(plugins): remove the old plugins system implementation Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement new plugin system with using Extism Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add capability detection for plugins based on exported functions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add auto-reload functionality for plugins with file watcher support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add auto-reload functionality for plugins with file watcher support Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): standardize variable names and remove superfluous wrapper functions Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): improve error handling and logging in plugin manager Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): implement plugin function call helper and refactor MetadataAgent methods Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): race condition in plugin manager * tests(plugins): change BeforeEach to BeforeAll in MetadataAgent tests Signed-off-by: Deluan <deluan@navidrome.org> * tests(plugins): optimize tests Signed-off-by: Deluan <deluan@navidrome.org> * tests(plugins): more optimizations Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): ignore goroutine leaks from notify library in tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Wikimedia plugin for Navidrome to fetch artist metadata Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): enhance plugin logging and set User-Agent header Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement scrobbler plugin with authorization and scrobbling capabilities Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): integrate logs Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): clean up manifest struct and improve plugin loading logic Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add metadata agent and scrobbler schemas for bootstrapping plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat(hostgen): add hostgen tool for generating Extism host function wrappers - Implemented hostgen tool to generate wrappers from annotated Go interfaces. - Added command-line flags for input/output directories and package name. - Introduced parsing and code generation logic for host services. - Created test data for various service interfaces and expected generated code. - Added documentation for host services and annotations for code generation. - Implemented SubsonicAPI service with corresponding generated code. * feat(subsonicapi): update Call method to return JSON string response Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement SubsonicAPI host function integration with permissions Signed-off-by: Deluan <deluan@navidrome.org> * fix(generator): error-only methods in response handling Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate client wrappers for host functions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(generator): remove error handling for response.Error in client templates Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): add Scheduler service interface with host function wrappers for scheduling tasks * feat(plugins): add WASI build constraints to client wrapper templates, to avoid lint errors Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): implement Scheduler service with one-time and recurring scheduling capabilities Signed-off-by: Deluan <deluan@navidrome.org> * refactor(manifest): remove unused ConfigPermission from permissions schema Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): add scheduler callback schema and implementation for plugins Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): streamline scheduling logic and remove unused callback tracking Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): add Close method for resource cleanup on plugin unload Signed-off-by: Deluan <deluan@navidrome.org> * docs(scheduler): clarify SchedulerCallback requirement for scheduling functions Signed-off-by: Deluan <deluan@navidrome.org> * fix: update wasm build rule to include all Go files in the directory Signed-off-by: Deluan <deluan@navidrome.org> * feat: rewrite the wikimedia plugin using the XTP CLI Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): replace uuid with id.NewRandom for schedule ID generation Signed-off-by: Deluan <deluan@navidrome.org> * refactor: capabilities registration Signed-off-by: Deluan <deluan@navidrome.org> * test: add scheduler service isolation test for plugin instances Signed-off-by: Deluan <deluan@navidrome.org> * refactor: update plugin manager initialization and encapsulate logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: add WebSocket service definitions for plugin communication Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement WebSocket service for plugin integration and connection management Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Crypto Ticker example plugin for real-time cryptocurrency price updates via Coinbase WebSocket API Also add the lifecycle capability Signed-off-by: Deluan <deluan@navidrome.org> * fix: use context.Background() in invokeCallback for scheduled tasks Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename plugin.create() to plugin.instance() Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename pluginInstance to plugin for consistency across the codebase Signed-off-by: Deluan <deluan@navidrome.org> * refactor: simplify schedule cloning in Close method and enhance plugin cleanup error handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement Artwork service for generating artwork URLs in Navidrome plugins - WIP Signed-off-by: Deluan <deluan@navidrome.org> * refactor: moved public URL builders to avoid import cycles Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Cache service for in-memory TTL-based caching in plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Discord Rich Presence example plugin for Navidrome integration Signed-off-by: Deluan <deluan@navidrome.org> * refactor: host function wrappers to use structured request and response types - Updated the host function signatures in `nd_host_artwork.go`, `nd_host_scheduler.go`, `nd_host_subsonicapi.go`, and `nd_host_websocket.go` to accept a single parameter for JSON requests. - Introduced structured request and response types for various cache operations in `nd_host_cache.go`. - Modified cache functions to marshal requests to JSON and unmarshal responses, improving error handling and code clarity. - Removed redundant memory allocation for string parameters in favor of JSON marshaling. - Enhanced error handling in WebSocket and cache operations to return structured error responses. * refactor: error handling in various plugins to convert response.Error to Go errors - Updated error handling in `nd_host_scheduler.go`, `nd_host_websocket.go`, `nd_host_artwork.go`, `nd_host_cache.go`, and `nd_host_subsonicapi.go` to convert string errors from responses into Go errors. - Removed redundant error checks in test data plugins for cleaner code. - Ensured consistent error handling across all plugins to improve reliability and maintainability. * refactor: rename fake plugins to test plugins for clarity in integration tests Signed-off-by: Deluan <deluan@navidrome.org> * feat: add help target to Makefile for plugin usage instructions Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Cover Art Archive plugin as an example of Python plugin Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Makefile and README to clarify Go plugin usage Signed-off-by: Deluan <deluan@navidrome.org> * feat: include plugin capabilities in loading log message Signed-off-by: Deluan <deluan@navidrome.org> * feat: add trace logging for plugin availability and error handling in agents Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Now Playing Logger plugin to showcase calling host functions from Python plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: generate Python client wrappers for various host services Signed-off-by: Deluan <deluan@navidrome.org> * feat: add generated host function wrappers for Scheduler and SubsonicAPI services Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Python plugin documentation and usage instructions for host function wrappers Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Webhook Scrobbler plugin in Rust to send HTTP notifications on scrobble events Signed-off-by: Deluan <deluan@navidrome.org> * feat: enable parallel loading of plugins during startup Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to include WebSocket callback schema in plugin documentation Signed-off-by: Deluan <deluan@navidrome.org> * feat: extend plugin watcher with improved logging and debounce duration adjustment Signed-off-by: Deluan <deluan@navidrome.org> * add trace message for plugin recompiles Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement plugin cache purging functionality Signed-off-by: Deluan <deluan@navidrome.org> * test: move purgeCacheBySize unit tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add plugin repository and database support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add plugin management routes and middleware Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): implement plugin synchronization with database for add, update, and remove actions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add PluginList and PluginShow components with plugin management functionality Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): optimize plugin change detection Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins UI): improve PluginList structure Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): enhance PluginShow with author, website, and permissions display Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): refactor to use MUI and RA components Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add error handling for plugin enable/disable actions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): inject PluginManager into native API Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update GetManager to accept DataStore parameter Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add subsonicRouter to Manager and refactor host service registration Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): enhance debug logging for plugin actions and recompile logic Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): break manager.go into smaller, focused files Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): streamline error handling and improve plugin retrieval logic Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update newWebSocketService to use WebSocketPermission for allowed hosts Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): introduce ToggleEnabledSwitch for managing plugin enable/disable state Signed-off-by: Deluan <deluan@navidrome.org> * docs: update READMEs Signed-off-by: Deluan <deluan@navidrome.org> * feat(library): add Library service for metadata access and filesystem integration Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Library Inspector plugin for periodic library inspection and file size logging Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to reflect JSON configuration format for plugins Signed-off-by: Deluan <deluan@navidrome.org> * fix(build): update target to wasm32-wasip1 for improved WASI support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement configuration management UI with key-value pairs support Signed-off-by: Deluan <deluan@navidrome.org> * feat(ui): adjust grid layout in InfoRow component for improved responsiveness Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): rename ErrorIndicator to EnabledOrErrorField and enhance error handling logic Signed-off-by: Deluan <deluan@navidrome.org> * feat(i18n): add Portuguese translations for plugin management and notifications Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add support for .ndp plugin packages and update build process Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README for .ndp plugin packaging and installation instructions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement KVStore service for persistent key-value storage Signed-off-by: Deluan <deluan@navidrome.org> * docs: enhance README with Extism plugin development resources and recommendations Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): integrate event broker into plugin manager Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): update config handling in PluginShow to track last record state Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Rust host function library and example implementation of Discord Rich Presence plugin in Rust Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate Rust lib.rs file to expose host function wrappers Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update JSON field names to camelCase for consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor: reduce cyclomatic complexity by refactoring main function Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): enhance Rust code generation with typed struct support and improved type handling Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Go client library with host function wrappers and documentation Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate Go client stubs for non-WASM platforms Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): update client template file names for consistency Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add initial implementation of the Navidrome Plugin Development Kit code generator - Pahse 1 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 2 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 2 (2) Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 3 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 4 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 5 Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): consistent naming/types across PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): streamline plugin function signatures and error handling Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update scrobbler interface to return errors directly instead of response structs Signed-off-by: Deluan <deluan@navidrome.org> * test: make all test plugins use the PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): reorganize and sort type definitions for consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update error handling for methods to return errors directly Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update function signatures to return values directly instead of response structs Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update request/response types to use private naming conventions Signed-off-by: Deluan <deluan@navidrome.org> * build: mark .wasm files as intermediate for cleanup after building .ndp Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): consolidate PDK module path and update Go version to 1.25 Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement Rust PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): reorganize Rust output structure to follow standard conventions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update Discord Rich Presence and Library Inspector plugins to use nd-pdk for service calls and implement lifecycle management Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update macro names for websocket and metadata registration to improve clarity and consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): rename scheduler callback methods for consistency and clarity Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update export wrappers to use `//go:wasmexport` for WebAssembly compatibility Signed-off-by: Deluan <deluan@navidrome.org> * docs: update plugin registration docs Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): generate host wrappers Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): conditionally run goleak checks based on CI environment Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to reflect changes in plugin import paths Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update plugin instance creation to accept context for cancellation support Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update return types in metadata interfaces to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): enhance type handling for Rust and XTP output in capability generation Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update IsAuthorized method to return boolean instead of response object Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): add unit tests for rustOutputType and isPrimitiveRustType functions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement XTP JSONSchema validation for generated schemas Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update response types in testMetadataAgent methods to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * docs: update Go and Rust plugin developer sections for clarity Signed-off-by: Deluan <deluan@navidrome.org> * docs: correct example link for library inspector in README Signed-off-by: Deluan <deluan@navidrome.org> * docs: clarify artwork URL generation capabilities in service descriptions Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to include Rust PDK crate information for plugin developers Signed-off-by: Deluan <deluan@navidrome.org> * fix: handle URL parsing errors and use atomic upsert in plugin repository Added proper error handling for url.Parse calls in PublicURL and AbsoluteURL functions. When parsing fails, PublicURL now falls back to AbsoluteURL, and AbsoluteURL logs the error and returns an empty string, preventing malformed URLs from being generated. Replaced the non-atomic UPDATE-then-INSERT pattern in plugin repository Put method with a single atomic INSERT ... ON CONFLICT statement. This eliminates potential race conditions and improves consistency with the upsert pattern already used in host_kvstore.go. * feat: implement mock service instances for non-WASM builds using testify/mock Signed-off-by: Deluan <deluan@navidrome.org> * refactor: Discord RPC struct to encapsulate WebSocket logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: add support for experimental WebAssembly threads Signed-off-by: Deluan <deluan@navidrome.org> * feat: add PDK abstraction layer with mock support for non-WASM builds Signed-off-by: Deluan <deluan@navidrome.org> * feat: add unit tests for Discord plugin and RPC functionality Signed-off-by: Deluan <deluan@navidrome.org> * fix: update return types in minimalPlugin and wikimediaPlugin methods to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * fix: context cancellation and implement WebSocket callback timeout for improved error handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: conditionally include error handling in generated client code templates Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement ConfigService for plugin configuration management Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance plugin manager to support metrics recording Signed-off-by: Deluan <deluan@navidrome.org> * refactor: make MockPDK private Signed-off-by: Deluan <deluan@navidrome.org> * refactor: update interface types to use 'any' in plugin repository methods Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename List method to Keys for clarity in configuration management Signed-off-by: Deluan <deluan@navidrome.org> * test: add ndpgen plugin tests in the pipeline and update Makefile Signed-off-by: Deluan <deluan@navidrome.org> * feat: add users permission management to plugin system Signed-off-by: Deluan <deluan@navidrome.org> * refactor: streamline users integration tests and enhance plugin user management Signed-off-by: Deluan <deluan@navidrome.org> * refactor: remove UserID from scrobbler request structure Signed-off-by: Deluan <deluan@navidrome.org> * test: add integration tests for UsersService enable gate behavior Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement user permissions for SubsonicAPI and scrobbler plugins Signed-off-by: Deluan <deluan@navidrome.org> * fix: show proper error in the UI when enabling a plugin fails Signed-off-by: Deluan <deluan@navidrome.org> * feat: add library permission management to plugin system Signed-off-by: Deluan <deluan@navidrome.org> * feat: add user permission for processing scrobbles in Discord Rich Presence plugin Signed-off-by: Deluan <deluan@navidrome.org> * fix: implement dynamic loading for buffered scrobbler plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: add GetAdmins method to retrieve admin users from the plugin Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Portuguese translations for user and library permissions Signed-off-by: Deluan <deluan@navidrome.org> * reorder migrations Signed-off-by: Deluan <deluan@navidrome.org> * fix: remove unnecessary bulkActionButtons prop from PluginList component * feat: add manual plugin rescan functionality and corresponding UI action Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement user/library and plugin management integration with cleanup on deletion Signed-off-by: Deluan <deluan@navidrome.org> * feat: replace core mock services with test-specific implementations to avoid import cycles * feat: add ID fields to Artist and Song structs and enhance track loading logic by prioritizing ID matches Signed-off-by: Deluan <deluan@navidrome.org> * feat: update plugin permissions from allowedHosts to requiredHosts for better clarity and consistency * feat: refactor plugin host permissions to use RequiredHosts directly for improved clarity * fix: don't record metrics for plugin calls that aren't implemented at all Signed-off-by: Deluan <deluan@navidrome.org> * fix: enhance connection management with improved error handling and cleanup logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: introduce ArtistRef struct for better artist representation and update track metadata handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: update user configuration handling to use user key prefix for improved clarity Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance ConfigCard input fields with multiline support and vertical resizing Signed-off-by: Deluan <deluan@navidrome.org> * fix: rust plugin compilation error Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement IsOptionPattern method for better return type handling in Rust PDK generation Signed-off-by: Deluan <deluan@navidrome.org> --------- Signed-off-by: Deluan <deluan@navidrome.org>
2026-01-14 19:22:48 -05:00
package capabilities
feat(plugins): share plugin DTOs via a types package (#5655) * refactor(plugins): remove Python PDK generation from ndpgen * feat(plugins): parse Go type aliases distinctly in ndpgen * feat(plugins): resolve shared-type aliases against a registry in ndpgen * fix(plugins): resolve host-service shared aliases package-wide Mirror the capability approach in ParseDirectoryWithShared: do a first pass over all package files to build a package-wide alias map, then pass it into parseServiceFile so that a shared-type alias declared in a sibling file is visible when resolving types in the service interface file. Add a focused test that writes the alias in one file and the hostservice in another, confirming RED before the fix and GREEN after. Also strengthens the existing Task 3 test with an ArtistRef.Target assertion. * feat(plugins): add ndpgen -shared-types mode for the Go types package * feat(plugins): generate the nd-pdk-types Rust crate from -shared-types * feat(plugins): inject types import and emit deprecated aliases in Go output * feat(plugins): emit deprecated Rust aliases to the shared types crate * feat(plugins): inline shared-type shapes into XTP schemas * feat(plugins): add nd-pdk-types crate and wire dependents * feat(plugins): move shared capability types to plugins/types with deprecated aliases * fix(plugins): point Rust deprecated-alias note at the replacement type * fix(plugins): include shared aliases in KnownStructs so Rust fields keep their type Capability.KnownStructs() and Service.KnownStructs() previously only registered names from .Structs. After the shared-types migration, types like ArtistRef/TrackInfo/SongRef live in .SharedAliases instead, so ToRustTypeWithStructs could not find them and fell back to serde_json::Value for every struct field referencing a shared type. Add the shared-alias names to the knownStructs map in both methods. Regenerate the Rust capability files; track/song/artist fields now render as their named types (TrackInfo, SongRef, ArtistRef, etc.). Add a regression test that verifies a struct field whose type is only in SharedAliases renders as the named type and not serde_json::Value. * docs(plugins): remove stale Python references from ndpgen and plugins READMEs ndpgen no longer has a -python flag; remove it from the usage synopsis, flags table, and defaults note in ndpgen/README.md. Delete the "Python Client Library" section that described its output. plugins/README.md referenced plugins/pdk/python/host/ (deleted) as the source for Python host-service stubs. Remove that paragraph; Python plugins still work via the XTP-schema / extism-py path (see examples/*-py). * refactor(plugins): dedupe ndpgen helpers and tidy shared-type codegen * docs(plugins): restore Python as a supported XTP schema target The ndpgen-generated Python PDK was removed, but the XTP YAML schemas are language-neutral and the XTP CLI still generates Python bindings from them (as the extism-py examples demonstrate). Only the ndpgen Python output was dropped, not Python support itself. * test(plugins): use the shared types package in test plugins The test fixtures referenced the now-deprecated capability aliases (sonicsimilarity.SongRef, metadata.ArtistRef/SongRef). Point them at the canonical types package so our own fixtures don't depend on symbols slated for removal. * refactor(plugins): use the shared types package in host adapters Replace deprecated capabilities.TrackInfo, capabilities.ArtistRef, and capabilities.SongRef aliases with the canonical types.TrackInfo, types.ArtistRef, and types.SongRef from plugins/types. * fix(plugins): reference shared types by canonical path in generated Rust Previously the generator emitted `pub field: SongRef` (the local deprecated alias) for struct fields whose type came from SharedAliases. Refactored ToRustTypeWithStructs into a private toRustType that accepts a shared map, and added ToRustTypeWithShared which resolves shared-alias names to their canonical nd_pdk_types::X path before falling through to the knownStructs check. Both rustCapabilityFuncMap and rustFuncMap now build the shared map from SharedAliases and use it for fieldRustType, so the generated capability files reference nd_pdk_types::SongRef / nd_pdk_types::TrackInfo directly. The deprecated pub type aliases remain in place as the external back-compat surface. Deprecation warning count from cargo build drops to 0. * fix(examples): implement missing Scrobbler.playback_report in Rust examples The Scrobbler trait gained a playback_report method but the two Rust example plugins (webhook-rs and discord-rich-presence-rs) were not updated, causing E0046 compile errors. Added the missing fn playback_report to both: webhook-rs logs and returns Ok(()) mirroring its now_playing handler; discord-rich-presence-rs is a no-op since Discord presence does not need playback reports. make all-rust now exits 0. * refactor(plugins): point Rust deprecation notes at the nd_pdk::types umbrella path Plugin authors depend on the nd-pdk umbrella crate, which re-exports nd_pdk_types as 'types', so the migration target they should type is nd_pdk::types::X. The alias target stays nd_pdk_types::X (the real path inside nd-pdk-capabilities). * fix(plugins): error when a shared-type alias can't be resolved against the registry * refactor(plugins): parse each Go source file once in ndpgen * fix(plugins): correct ndpgen review nits (flag name, unused dep, docs) * refactor(plugins): drop the now-unused path param from parseServiceFile * refactor(plugins): use shared types directly, rename TrackInfo to Track Capability interfaces now reference the shared `types` package by qualified name (types.Track, types.SongRef, types.ArtistRef) instead of the package-local deprecated aliases, and the shared TrackInfo type is renamed to Track to match its role as the plugin-facing projection of a library media file. The deprecated bare aliases (scrobbler.TrackInfo, metadata.ArtistRef, sonicsimilarity.SongRef, etc.) are kept as re-exports so existing plugins keep compiling, with a deprecation warning steering them to the canonical types. To support this, ndpgen now resolves qualified types.X references: it collects them during type discovery, maps each used canonical type back to its declared deprecated alias for re-export, emits nd_pdk_types::X paths in Rust, and names the XTP schema components by their canonical type. Regenerated the Go and Rust PDK and the XTP schemas, and added generator tests covering the qualified-ref path. Also adds clarifying doc comments to the shared types. * refactor(plugins): extract shared types selector into a named const Replace the "types." string literal that detects and strips the shared types package selector with a single sharedTypesPrefix constant across the ndpgen generator (parser, types, generator, xtp_schema), giving the package one source of truth for the selector. Also restore the single reused scratch map (cleared each iteration) in the resolveSharedAliases BFS instead of allocating a fresh map per shared-struct field, matching the prior implementation. Pure cleanup from a /simplify pass: regeneration produces byte-for-byte identical Go, Rust, and XTP output. * refactor(plugins): keep TrackInfo in the capability package for now Move the track type back out of the shared plugins/types package: it is again defined inline as TrackInfo in plugins/capabilities/scrobbler.go and referenced directly by the scrobbler and lyrics capabilities, reverting the rename to types.Track. The host helper is renamed back to mediaFileToTrackInfo and now returns capabilities.TrackInfo. SongRef and ArtistRef stay in the shared types package; TrackInfo keeps using types.ArtistRef for its artist lists. This type is expected to be reshaped in upcoming work, so leaving it in the capability package avoids churning the shared types twice. Regenerated the Go and Rust PDK and the XTP schemas accordingly. * fix(plugins): emit the Go types import for direct shared-type refs ndpgen's Capability/Service.ImportsSharedTypes only reported a shared-types dependency when a deprecated re-export alias (type X = types.X) was declared. A struct field referencing the canonical form directly (e.g. types.SongRef) with no such alias produced an empty SharedAliases slice, so the Go templates skipped the import while still emitting fields/signatures using types.SongRef — leaving generated PDK code for new shared DTOs uncompilable unless an otherwise unnecessary alias was added. ImportsSharedTypes now also returns true when any struct field references the types. package by qualified name, via a new structsReferenceSharedTypes helper that reuses collectReferencedTypes (so []types.X and map[...]types.X are covered too). * fix(plugins): preserve base64 encoding for shared byte fields in Rust The Rust shared-types crate template rendered a []byte field as a plain Vec<u8> without the base64_bytes serde override used by the capability/client templates. Go's encoding/json serializes []byte as a base64 string, so a Rust plugin using nd_pdk::types would have serialized an array of numbers instead of the wire format the Go/server side expects. GenerateSharedTypesRust now registers the base64_bytes partial and passes a HasByteFields flag (new anyFieldIsByteSlice helper); types.rs.tmpl emits the base64_bytes module and a #[serde(with = "base64_bytes")] attribute on []byte fields, mirroring the capability template. * fix(plugins): include directly-referenced shared types in XTP schemas buildSchemas registered shared types into the schema components only by iterating cap.SharedAliases, which records deprecated re-export aliases. A capability that referenced a shared DTO solely as types.Foo (no declared alias) therefore never got Foo into the component set, so the self-contained XTP schema rendered the field as a generic object (or emitted a dangling $ref), breaking the direct shared-type use case enabled by -shared. resolveSharedAliases now also returns the resolved shapes of every used shared type (alias or not); these are carried on the new Capability.SharedTypes field and registered by buildSchemas alongside SharedAliases. Validated end-to-end with the xtp CLI: a direct types.Foo reference now produces a proper component plus a $ref, so xtp generates a typed struct instead of an untyped serde_json::Map. * fix(plugins): resolve renamed shared aliases to canonical schema refs When a deprecated alias renames its canonical type (e.g. type TrackInfo = types.Track) and a capability field is typed with the alias name (TrackInfo), buildProperty emitted a $ref to #/components/schemas/TrackInfo. Components are keyed by the canonical name (Track), so no TrackInfo component was emitted, leaving a dangling reference that crashes the xtp code generator. buildSchemas now builds an alias->canonical map; buildProperty (and the slice item path) resolves $ref targets through it, and a used alias name marks its canonical component used so it is emitted. Validated with the xtp CLI: the renamed-alias schema previously crashed xtp and now generates cleanly. * fix(plugins): detect shared types used directly in method signatures ImportsSharedTypes only inspected struct fields, so a capability method using a shared type directly in its signature (e.g. types.SongRef as input/output rather than inside a local struct) was not detected. The generated Go templates still rendered the provider/export signatures with types.SongRef, so the capability package omitted the types import and failed to compile; the same gap applied to service params/returns. ImportsSharedTypes now also scans capability method input/output types and service method params/returns, via a typeReferencesSharedTypes helper that reuses collectReferencedTypes (covering pointer/slice/map wrappers). * fix(plugins): add base64 dependency to the shared Rust types crate When a shared DTO has a []byte field, ndpgen emits the base64_bytes serde helper and use base64::... imports into nd-pdk-types/src/lib.rs, but the crate manifest declared only serde. In that case make gen produced a crate that failed to compile with 'unresolved module base64'. Add base64 = "0.22" (matching nd-pdk-capabilities) so the generated shared types crate compiles whenever a []byte field is present. Verified by generating a shared crate with a []byte field and confirming cargo check fails before and passes after. * fix(plugins): translate shared method types in generated Rust A capability method using a shared DTO directly as input/output (e.g. types.SongRef) was passed through rustOutputType unchanged, so the Rust template emitted invalid trait and extism_pdk::Json<$crate::pkg::types.SongRef> signatures that do not compile. Method input/output types now resolve through the shared registry: trait signatures use rustTraitType (shared -> nd_pdk_types::X, locals stay bare) and the export macros use rustMethodType (fully qualified: shared -> nd_pdk_types::X, primitives -> Rust, locals -> $crate::<pkg>::X). Verified end-to-end by compiling a generated capability that takes types.SongRef directly against the real nd-pdk-types crate. * fix(plugins): canonicalize XTP export refs for renamed shared aliases buildSchemas canonicalized alias-to-canonical references for struct-field $ref targets, but buildExport built export input/output $refs straight from fieldBaseType. A capability whose export used a renamed deprecated alias directly (e.g. type TrackInfo = types.Track with NowPlaying(TrackInfo)) emitted $ref: #/components/schemas/TrackInfo, while the component is emitted under the canonical name Track — a dangling export reference. Lift the alias-to-canonical map into GenerateSchema (buildAliasToCanonical) and apply it to export refs via canonicalRefName, the same resolution already used for field properties. * fix(plugins): route shared macro types through $crate for plugin builds When a capability method used a shared type directly, the generated export macro named the type as nd_pdk_types::SongRef. The macro expands in the downstream plugin crate, which depends on the umbrella nd-pdk crate and not on nd-pdk-types directly, so that path is unresolvable there and the plugin fails to build. rustMethodType (macro-facing) now emits $crate::types::X, and the generated nd-pdk-capabilities lib.rs re-exports nd_pdk_types as types so $crate resolves it. Trait signatures keep nd_pdk_types::X since they live in nd-pdk-capabilities, which has the direct dependency. Verified end-to-end: a plugin crate depending only on the umbrella that uses a capability with a direct types.X method now compiles via the macro. * fix(plugins): add nd-pdk-types dependency to the Rust host crate When a host service uses a shared type, ndpgen emits nd_pdk_types::X into the generated nd-pdk-host client wrappers, but the host crate's manifest did not depend on nd-pdk-types, so the crate failed to compile with 'unresolved module nd_pdk_types'. Host client wrappers are plain functions resolved in the host crate's own context (not macros expanded downstream), so a direct dependency is the right fix. Add nd-pdk-types = { path = "../nd-pdk-types" } to nd-pdk-host, mirroring nd-pdk-capabilities. Found while auditing all Rust paths against the realistic crate topology after the capability-side $crate fix; verified by generating a host service with a shared-type return and confirming cargo check fails before and passes after. * fix(plugins): resolve shared aliases in Rust host signatures The Rust host client rendered method params and returns through RustTypeWithStructs, which only consults KnownStructs. A host service using a shared alias in a signature (e.g. type Track = types.Track plus MatchSongs(...) ([]Track, error)) therefore emitted a bare Vec<Track>, but the client template emits no Track alias or import, so the generated nd-pdk-host crate did not compile. Only struct fields went through the shared map. rustType/rustParamType now use the shared map too (RustTypeWithShared / RustParamTypeWithShared), so an aliased param/return resolves to its canonical nd_pdk_types::X path, matching field handling. Verified by generating a host service returning a shared alias and confirming cargo check fails before and passes after.
2026-06-29 21:20:33 -04:00
import "github.com/navidrome/navidrome/plugins/types"
feat(plugins): New Plugin System with multi-language PDK support (#4833) * chore(plugins): remove the old plugins system implementation Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement new plugin system with using Extism Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add capability detection for plugins based on exported functions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add auto-reload functionality for plugins with file watcher support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add auto-reload functionality for plugins with file watcher support Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): standardize variable names and remove superfluous wrapper functions Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): improve error handling and logging in plugin manager Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): implement plugin function call helper and refactor MetadataAgent methods Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): race condition in plugin manager * tests(plugins): change BeforeEach to BeforeAll in MetadataAgent tests Signed-off-by: Deluan <deluan@navidrome.org> * tests(plugins): optimize tests Signed-off-by: Deluan <deluan@navidrome.org> * tests(plugins): more optimizations Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): ignore goroutine leaks from notify library in tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Wikimedia plugin for Navidrome to fetch artist metadata Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): enhance plugin logging and set User-Agent header Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement scrobbler plugin with authorization and scrobbling capabilities Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): integrate logs Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): clean up manifest struct and improve plugin loading logic Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add metadata agent and scrobbler schemas for bootstrapping plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat(hostgen): add hostgen tool for generating Extism host function wrappers - Implemented hostgen tool to generate wrappers from annotated Go interfaces. - Added command-line flags for input/output directories and package name. - Introduced parsing and code generation logic for host services. - Created test data for various service interfaces and expected generated code. - Added documentation for host services and annotations for code generation. - Implemented SubsonicAPI service with corresponding generated code. * feat(subsonicapi): update Call method to return JSON string response Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement SubsonicAPI host function integration with permissions Signed-off-by: Deluan <deluan@navidrome.org> * fix(generator): error-only methods in response handling Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate client wrappers for host functions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(generator): remove error handling for response.Error in client templates Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): add Scheduler service interface with host function wrappers for scheduling tasks * feat(plugins): add WASI build constraints to client wrapper templates, to avoid lint errors Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): implement Scheduler service with one-time and recurring scheduling capabilities Signed-off-by: Deluan <deluan@navidrome.org> * refactor(manifest): remove unused ConfigPermission from permissions schema Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): add scheduler callback schema and implementation for plugins Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): streamline scheduling logic and remove unused callback tracking Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): add Close method for resource cleanup on plugin unload Signed-off-by: Deluan <deluan@navidrome.org> * docs(scheduler): clarify SchedulerCallback requirement for scheduling functions Signed-off-by: Deluan <deluan@navidrome.org> * fix: update wasm build rule to include all Go files in the directory Signed-off-by: Deluan <deluan@navidrome.org> * feat: rewrite the wikimedia plugin using the XTP CLI Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): replace uuid with id.NewRandom for schedule ID generation Signed-off-by: Deluan <deluan@navidrome.org> * refactor: capabilities registration Signed-off-by: Deluan <deluan@navidrome.org> * test: add scheduler service isolation test for plugin instances Signed-off-by: Deluan <deluan@navidrome.org> * refactor: update plugin manager initialization and encapsulate logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: add WebSocket service definitions for plugin communication Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement WebSocket service for plugin integration and connection management Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Crypto Ticker example plugin for real-time cryptocurrency price updates via Coinbase WebSocket API Also add the lifecycle capability Signed-off-by: Deluan <deluan@navidrome.org> * fix: use context.Background() in invokeCallback for scheduled tasks Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename plugin.create() to plugin.instance() Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename pluginInstance to plugin for consistency across the codebase Signed-off-by: Deluan <deluan@navidrome.org> * refactor: simplify schedule cloning in Close method and enhance plugin cleanup error handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement Artwork service for generating artwork URLs in Navidrome plugins - WIP Signed-off-by: Deluan <deluan@navidrome.org> * refactor: moved public URL builders to avoid import cycles Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Cache service for in-memory TTL-based caching in plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Discord Rich Presence example plugin for Navidrome integration Signed-off-by: Deluan <deluan@navidrome.org> * refactor: host function wrappers to use structured request and response types - Updated the host function signatures in `nd_host_artwork.go`, `nd_host_scheduler.go`, `nd_host_subsonicapi.go`, and `nd_host_websocket.go` to accept a single parameter for JSON requests. - Introduced structured request and response types for various cache operations in `nd_host_cache.go`. - Modified cache functions to marshal requests to JSON and unmarshal responses, improving error handling and code clarity. - Removed redundant memory allocation for string parameters in favor of JSON marshaling. - Enhanced error handling in WebSocket and cache operations to return structured error responses. * refactor: error handling in various plugins to convert response.Error to Go errors - Updated error handling in `nd_host_scheduler.go`, `nd_host_websocket.go`, `nd_host_artwork.go`, `nd_host_cache.go`, and `nd_host_subsonicapi.go` to convert string errors from responses into Go errors. - Removed redundant error checks in test data plugins for cleaner code. - Ensured consistent error handling across all plugins to improve reliability and maintainability. * refactor: rename fake plugins to test plugins for clarity in integration tests Signed-off-by: Deluan <deluan@navidrome.org> * feat: add help target to Makefile for plugin usage instructions Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Cover Art Archive plugin as an example of Python plugin Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Makefile and README to clarify Go plugin usage Signed-off-by: Deluan <deluan@navidrome.org> * feat: include plugin capabilities in loading log message Signed-off-by: Deluan <deluan@navidrome.org> * feat: add trace logging for plugin availability and error handling in agents Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Now Playing Logger plugin to showcase calling host functions from Python plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: generate Python client wrappers for various host services Signed-off-by: Deluan <deluan@navidrome.org> * feat: add generated host function wrappers for Scheduler and SubsonicAPI services Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Python plugin documentation and usage instructions for host function wrappers Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Webhook Scrobbler plugin in Rust to send HTTP notifications on scrobble events Signed-off-by: Deluan <deluan@navidrome.org> * feat: enable parallel loading of plugins during startup Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to include WebSocket callback schema in plugin documentation Signed-off-by: Deluan <deluan@navidrome.org> * feat: extend plugin watcher with improved logging and debounce duration adjustment Signed-off-by: Deluan <deluan@navidrome.org> * add trace message for plugin recompiles Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement plugin cache purging functionality Signed-off-by: Deluan <deluan@navidrome.org> * test: move purgeCacheBySize unit tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add plugin repository and database support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add plugin management routes and middleware Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): implement plugin synchronization with database for add, update, and remove actions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add PluginList and PluginShow components with plugin management functionality Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): optimize plugin change detection Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins UI): improve PluginList structure Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): enhance PluginShow with author, website, and permissions display Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): refactor to use MUI and RA components Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add error handling for plugin enable/disable actions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): inject PluginManager into native API Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update GetManager to accept DataStore parameter Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add subsonicRouter to Manager and refactor host service registration Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): enhance debug logging for plugin actions and recompile logic Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): break manager.go into smaller, focused files Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): streamline error handling and improve plugin retrieval logic Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update newWebSocketService to use WebSocketPermission for allowed hosts Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): introduce ToggleEnabledSwitch for managing plugin enable/disable state Signed-off-by: Deluan <deluan@navidrome.org> * docs: update READMEs Signed-off-by: Deluan <deluan@navidrome.org> * feat(library): add Library service for metadata access and filesystem integration Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Library Inspector plugin for periodic library inspection and file size logging Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to reflect JSON configuration format for plugins Signed-off-by: Deluan <deluan@navidrome.org> * fix(build): update target to wasm32-wasip1 for improved WASI support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement configuration management UI with key-value pairs support Signed-off-by: Deluan <deluan@navidrome.org> * feat(ui): adjust grid layout in InfoRow component for improved responsiveness Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): rename ErrorIndicator to EnabledOrErrorField and enhance error handling logic Signed-off-by: Deluan <deluan@navidrome.org> * feat(i18n): add Portuguese translations for plugin management and notifications Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add support for .ndp plugin packages and update build process Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README for .ndp plugin packaging and installation instructions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement KVStore service for persistent key-value storage Signed-off-by: Deluan <deluan@navidrome.org> * docs: enhance README with Extism plugin development resources and recommendations Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): integrate event broker into plugin manager Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): update config handling in PluginShow to track last record state Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Rust host function library and example implementation of Discord Rich Presence plugin in Rust Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate Rust lib.rs file to expose host function wrappers Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update JSON field names to camelCase for consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor: reduce cyclomatic complexity by refactoring main function Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): enhance Rust code generation with typed struct support and improved type handling Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Go client library with host function wrappers and documentation Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate Go client stubs for non-WASM platforms Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): update client template file names for consistency Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add initial implementation of the Navidrome Plugin Development Kit code generator - Pahse 1 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 2 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 2 (2) Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 3 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 4 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 5 Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): consistent naming/types across PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): streamline plugin function signatures and error handling Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update scrobbler interface to return errors directly instead of response structs Signed-off-by: Deluan <deluan@navidrome.org> * test: make all test plugins use the PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): reorganize and sort type definitions for consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update error handling for methods to return errors directly Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update function signatures to return values directly instead of response structs Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update request/response types to use private naming conventions Signed-off-by: Deluan <deluan@navidrome.org> * build: mark .wasm files as intermediate for cleanup after building .ndp Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): consolidate PDK module path and update Go version to 1.25 Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement Rust PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): reorganize Rust output structure to follow standard conventions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update Discord Rich Presence and Library Inspector plugins to use nd-pdk for service calls and implement lifecycle management Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update macro names for websocket and metadata registration to improve clarity and consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): rename scheduler callback methods for consistency and clarity Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update export wrappers to use `//go:wasmexport` for WebAssembly compatibility Signed-off-by: Deluan <deluan@navidrome.org> * docs: update plugin registration docs Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): generate host wrappers Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): conditionally run goleak checks based on CI environment Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to reflect changes in plugin import paths Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update plugin instance creation to accept context for cancellation support Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update return types in metadata interfaces to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): enhance type handling for Rust and XTP output in capability generation Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update IsAuthorized method to return boolean instead of response object Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): add unit tests for rustOutputType and isPrimitiveRustType functions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement XTP JSONSchema validation for generated schemas Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update response types in testMetadataAgent methods to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * docs: update Go and Rust plugin developer sections for clarity Signed-off-by: Deluan <deluan@navidrome.org> * docs: correct example link for library inspector in README Signed-off-by: Deluan <deluan@navidrome.org> * docs: clarify artwork URL generation capabilities in service descriptions Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to include Rust PDK crate information for plugin developers Signed-off-by: Deluan <deluan@navidrome.org> * fix: handle URL parsing errors and use atomic upsert in plugin repository Added proper error handling for url.Parse calls in PublicURL and AbsoluteURL functions. When parsing fails, PublicURL now falls back to AbsoluteURL, and AbsoluteURL logs the error and returns an empty string, preventing malformed URLs from being generated. Replaced the non-atomic UPDATE-then-INSERT pattern in plugin repository Put method with a single atomic INSERT ... ON CONFLICT statement. This eliminates potential race conditions and improves consistency with the upsert pattern already used in host_kvstore.go. * feat: implement mock service instances for non-WASM builds using testify/mock Signed-off-by: Deluan <deluan@navidrome.org> * refactor: Discord RPC struct to encapsulate WebSocket logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: add support for experimental WebAssembly threads Signed-off-by: Deluan <deluan@navidrome.org> * feat: add PDK abstraction layer with mock support for non-WASM builds Signed-off-by: Deluan <deluan@navidrome.org> * feat: add unit tests for Discord plugin and RPC functionality Signed-off-by: Deluan <deluan@navidrome.org> * fix: update return types in minimalPlugin and wikimediaPlugin methods to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * fix: context cancellation and implement WebSocket callback timeout for improved error handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: conditionally include error handling in generated client code templates Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement ConfigService for plugin configuration management Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance plugin manager to support metrics recording Signed-off-by: Deluan <deluan@navidrome.org> * refactor: make MockPDK private Signed-off-by: Deluan <deluan@navidrome.org> * refactor: update interface types to use 'any' in plugin repository methods Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename List method to Keys for clarity in configuration management Signed-off-by: Deluan <deluan@navidrome.org> * test: add ndpgen plugin tests in the pipeline and update Makefile Signed-off-by: Deluan <deluan@navidrome.org> * feat: add users permission management to plugin system Signed-off-by: Deluan <deluan@navidrome.org> * refactor: streamline users integration tests and enhance plugin user management Signed-off-by: Deluan <deluan@navidrome.org> * refactor: remove UserID from scrobbler request structure Signed-off-by: Deluan <deluan@navidrome.org> * test: add integration tests for UsersService enable gate behavior Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement user permissions for SubsonicAPI and scrobbler plugins Signed-off-by: Deluan <deluan@navidrome.org> * fix: show proper error in the UI when enabling a plugin fails Signed-off-by: Deluan <deluan@navidrome.org> * feat: add library permission management to plugin system Signed-off-by: Deluan <deluan@navidrome.org> * feat: add user permission for processing scrobbles in Discord Rich Presence plugin Signed-off-by: Deluan <deluan@navidrome.org> * fix: implement dynamic loading for buffered scrobbler plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: add GetAdmins method to retrieve admin users from the plugin Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Portuguese translations for user and library permissions Signed-off-by: Deluan <deluan@navidrome.org> * reorder migrations Signed-off-by: Deluan <deluan@navidrome.org> * fix: remove unnecessary bulkActionButtons prop from PluginList component * feat: add manual plugin rescan functionality and corresponding UI action Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement user/library and plugin management integration with cleanup on deletion Signed-off-by: Deluan <deluan@navidrome.org> * feat: replace core mock services with test-specific implementations to avoid import cycles * feat: add ID fields to Artist and Song structs and enhance track loading logic by prioritizing ID matches Signed-off-by: Deluan <deluan@navidrome.org> * feat: update plugin permissions from allowedHosts to requiredHosts for better clarity and consistency * feat: refactor plugin host permissions to use RequiredHosts directly for improved clarity * fix: don't record metrics for plugin calls that aren't implemented at all Signed-off-by: Deluan <deluan@navidrome.org> * fix: enhance connection management with improved error handling and cleanup logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: introduce ArtistRef struct for better artist representation and update track metadata handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: update user configuration handling to use user key prefix for improved clarity Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance ConfigCard input fields with multiline support and vertical resizing Signed-off-by: Deluan <deluan@navidrome.org> * fix: rust plugin compilation error Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement IsOptionPattern method for better return type handling in Rust PDK generation Signed-off-by: Deluan <deluan@navidrome.org> --------- Signed-off-by: Deluan <deluan@navidrome.org>
2026-01-14 19:22:48 -05:00
// MetadataAgent provides artist and album metadata retrieval.
// This capability allows plugins to provide external metadata for artists and albums,
// such as biographies, images, similar artists, and top songs.
//
// Plugins implementing this capability can choose which methods to implement.
// Each method is optional - plugins only need to provide the functionality they support.
//
// To say "no data for this item", return a nil response and a nil error. Return an error only when
// the plugin itself failed, because Navidrome retries failed calls with backoff.
//
feat(plugins): New Plugin System with multi-language PDK support (#4833) * chore(plugins): remove the old plugins system implementation Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement new plugin system with using Extism Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add capability detection for plugins based on exported functions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add auto-reload functionality for plugins with file watcher support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add auto-reload functionality for plugins with file watcher support Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): standardize variable names and remove superfluous wrapper functions Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): improve error handling and logging in plugin manager Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): implement plugin function call helper and refactor MetadataAgent methods Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): race condition in plugin manager * tests(plugins): change BeforeEach to BeforeAll in MetadataAgent tests Signed-off-by: Deluan <deluan@navidrome.org> * tests(plugins): optimize tests Signed-off-by: Deluan <deluan@navidrome.org> * tests(plugins): more optimizations Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): ignore goroutine leaks from notify library in tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Wikimedia plugin for Navidrome to fetch artist metadata Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): enhance plugin logging and set User-Agent header Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement scrobbler plugin with authorization and scrobbling capabilities Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): integrate logs Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): clean up manifest struct and improve plugin loading logic Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add metadata agent and scrobbler schemas for bootstrapping plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat(hostgen): add hostgen tool for generating Extism host function wrappers - Implemented hostgen tool to generate wrappers from annotated Go interfaces. - Added command-line flags for input/output directories and package name. - Introduced parsing and code generation logic for host services. - Created test data for various service interfaces and expected generated code. - Added documentation for host services and annotations for code generation. - Implemented SubsonicAPI service with corresponding generated code. * feat(subsonicapi): update Call method to return JSON string response Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement SubsonicAPI host function integration with permissions Signed-off-by: Deluan <deluan@navidrome.org> * fix(generator): error-only methods in response handling Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate client wrappers for host functions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(generator): remove error handling for response.Error in client templates Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): add Scheduler service interface with host function wrappers for scheduling tasks * feat(plugins): add WASI build constraints to client wrapper templates, to avoid lint errors Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): implement Scheduler service with one-time and recurring scheduling capabilities Signed-off-by: Deluan <deluan@navidrome.org> * refactor(manifest): remove unused ConfigPermission from permissions schema Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): add scheduler callback schema and implementation for plugins Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): streamline scheduling logic and remove unused callback tracking Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): add Close method for resource cleanup on plugin unload Signed-off-by: Deluan <deluan@navidrome.org> * docs(scheduler): clarify SchedulerCallback requirement for scheduling functions Signed-off-by: Deluan <deluan@navidrome.org> * fix: update wasm build rule to include all Go files in the directory Signed-off-by: Deluan <deluan@navidrome.org> * feat: rewrite the wikimedia plugin using the XTP CLI Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): replace uuid with id.NewRandom for schedule ID generation Signed-off-by: Deluan <deluan@navidrome.org> * refactor: capabilities registration Signed-off-by: Deluan <deluan@navidrome.org> * test: add scheduler service isolation test for plugin instances Signed-off-by: Deluan <deluan@navidrome.org> * refactor: update plugin manager initialization and encapsulate logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: add WebSocket service definitions for plugin communication Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement WebSocket service for plugin integration and connection management Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Crypto Ticker example plugin for real-time cryptocurrency price updates via Coinbase WebSocket API Also add the lifecycle capability Signed-off-by: Deluan <deluan@navidrome.org> * fix: use context.Background() in invokeCallback for scheduled tasks Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename plugin.create() to plugin.instance() Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename pluginInstance to plugin for consistency across the codebase Signed-off-by: Deluan <deluan@navidrome.org> * refactor: simplify schedule cloning in Close method and enhance plugin cleanup error handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement Artwork service for generating artwork URLs in Navidrome plugins - WIP Signed-off-by: Deluan <deluan@navidrome.org> * refactor: moved public URL builders to avoid import cycles Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Cache service for in-memory TTL-based caching in plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Discord Rich Presence example plugin for Navidrome integration Signed-off-by: Deluan <deluan@navidrome.org> * refactor: host function wrappers to use structured request and response types - Updated the host function signatures in `nd_host_artwork.go`, `nd_host_scheduler.go`, `nd_host_subsonicapi.go`, and `nd_host_websocket.go` to accept a single parameter for JSON requests. - Introduced structured request and response types for various cache operations in `nd_host_cache.go`. - Modified cache functions to marshal requests to JSON and unmarshal responses, improving error handling and code clarity. - Removed redundant memory allocation for string parameters in favor of JSON marshaling. - Enhanced error handling in WebSocket and cache operations to return structured error responses. * refactor: error handling in various plugins to convert response.Error to Go errors - Updated error handling in `nd_host_scheduler.go`, `nd_host_websocket.go`, `nd_host_artwork.go`, `nd_host_cache.go`, and `nd_host_subsonicapi.go` to convert string errors from responses into Go errors. - Removed redundant error checks in test data plugins for cleaner code. - Ensured consistent error handling across all plugins to improve reliability and maintainability. * refactor: rename fake plugins to test plugins for clarity in integration tests Signed-off-by: Deluan <deluan@navidrome.org> * feat: add help target to Makefile for plugin usage instructions Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Cover Art Archive plugin as an example of Python plugin Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Makefile and README to clarify Go plugin usage Signed-off-by: Deluan <deluan@navidrome.org> * feat: include plugin capabilities in loading log message Signed-off-by: Deluan <deluan@navidrome.org> * feat: add trace logging for plugin availability and error handling in agents Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Now Playing Logger plugin to showcase calling host functions from Python plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: generate Python client wrappers for various host services Signed-off-by: Deluan <deluan@navidrome.org> * feat: add generated host function wrappers for Scheduler and SubsonicAPI services Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Python plugin documentation and usage instructions for host function wrappers Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Webhook Scrobbler plugin in Rust to send HTTP notifications on scrobble events Signed-off-by: Deluan <deluan@navidrome.org> * feat: enable parallel loading of plugins during startup Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to include WebSocket callback schema in plugin documentation Signed-off-by: Deluan <deluan@navidrome.org> * feat: extend plugin watcher with improved logging and debounce duration adjustment Signed-off-by: Deluan <deluan@navidrome.org> * add trace message for plugin recompiles Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement plugin cache purging functionality Signed-off-by: Deluan <deluan@navidrome.org> * test: move purgeCacheBySize unit tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add plugin repository and database support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add plugin management routes and middleware Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): implement plugin synchronization with database for add, update, and remove actions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add PluginList and PluginShow components with plugin management functionality Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): optimize plugin change detection Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins UI): improve PluginList structure Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): enhance PluginShow with author, website, and permissions display Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): refactor to use MUI and RA components Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add error handling for plugin enable/disable actions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): inject PluginManager into native API Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update GetManager to accept DataStore parameter Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add subsonicRouter to Manager and refactor host service registration Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): enhance debug logging for plugin actions and recompile logic Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): break manager.go into smaller, focused files Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): streamline error handling and improve plugin retrieval logic Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update newWebSocketService to use WebSocketPermission for allowed hosts Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): introduce ToggleEnabledSwitch for managing plugin enable/disable state Signed-off-by: Deluan <deluan@navidrome.org> * docs: update READMEs Signed-off-by: Deluan <deluan@navidrome.org> * feat(library): add Library service for metadata access and filesystem integration Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Library Inspector plugin for periodic library inspection and file size logging Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to reflect JSON configuration format for plugins Signed-off-by: Deluan <deluan@navidrome.org> * fix(build): update target to wasm32-wasip1 for improved WASI support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement configuration management UI with key-value pairs support Signed-off-by: Deluan <deluan@navidrome.org> * feat(ui): adjust grid layout in InfoRow component for improved responsiveness Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): rename ErrorIndicator to EnabledOrErrorField and enhance error handling logic Signed-off-by: Deluan <deluan@navidrome.org> * feat(i18n): add Portuguese translations for plugin management and notifications Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add support for .ndp plugin packages and update build process Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README for .ndp plugin packaging and installation instructions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement KVStore service for persistent key-value storage Signed-off-by: Deluan <deluan@navidrome.org> * docs: enhance README with Extism plugin development resources and recommendations Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): integrate event broker into plugin manager Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): update config handling in PluginShow to track last record state Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Rust host function library and example implementation of Discord Rich Presence plugin in Rust Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate Rust lib.rs file to expose host function wrappers Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update JSON field names to camelCase for consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor: reduce cyclomatic complexity by refactoring main function Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): enhance Rust code generation with typed struct support and improved type handling Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Go client library with host function wrappers and documentation Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate Go client stubs for non-WASM platforms Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): update client template file names for consistency Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add initial implementation of the Navidrome Plugin Development Kit code generator - Pahse 1 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 2 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 2 (2) Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 3 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 4 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 5 Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): consistent naming/types across PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): streamline plugin function signatures and error handling Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update scrobbler interface to return errors directly instead of response structs Signed-off-by: Deluan <deluan@navidrome.org> * test: make all test plugins use the PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): reorganize and sort type definitions for consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update error handling for methods to return errors directly Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update function signatures to return values directly instead of response structs Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update request/response types to use private naming conventions Signed-off-by: Deluan <deluan@navidrome.org> * build: mark .wasm files as intermediate for cleanup after building .ndp Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): consolidate PDK module path and update Go version to 1.25 Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement Rust PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): reorganize Rust output structure to follow standard conventions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update Discord Rich Presence and Library Inspector plugins to use nd-pdk for service calls and implement lifecycle management Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update macro names for websocket and metadata registration to improve clarity and consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): rename scheduler callback methods for consistency and clarity Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update export wrappers to use `//go:wasmexport` for WebAssembly compatibility Signed-off-by: Deluan <deluan@navidrome.org> * docs: update plugin registration docs Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): generate host wrappers Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): conditionally run goleak checks based on CI environment Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to reflect changes in plugin import paths Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update plugin instance creation to accept context for cancellation support Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update return types in metadata interfaces to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): enhance type handling for Rust and XTP output in capability generation Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update IsAuthorized method to return boolean instead of response object Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): add unit tests for rustOutputType and isPrimitiveRustType functions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement XTP JSONSchema validation for generated schemas Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update response types in testMetadataAgent methods to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * docs: update Go and Rust plugin developer sections for clarity Signed-off-by: Deluan <deluan@navidrome.org> * docs: correct example link for library inspector in README Signed-off-by: Deluan <deluan@navidrome.org> * docs: clarify artwork URL generation capabilities in service descriptions Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to include Rust PDK crate information for plugin developers Signed-off-by: Deluan <deluan@navidrome.org> * fix: handle URL parsing errors and use atomic upsert in plugin repository Added proper error handling for url.Parse calls in PublicURL and AbsoluteURL functions. When parsing fails, PublicURL now falls back to AbsoluteURL, and AbsoluteURL logs the error and returns an empty string, preventing malformed URLs from being generated. Replaced the non-atomic UPDATE-then-INSERT pattern in plugin repository Put method with a single atomic INSERT ... ON CONFLICT statement. This eliminates potential race conditions and improves consistency with the upsert pattern already used in host_kvstore.go. * feat: implement mock service instances for non-WASM builds using testify/mock Signed-off-by: Deluan <deluan@navidrome.org> * refactor: Discord RPC struct to encapsulate WebSocket logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: add support for experimental WebAssembly threads Signed-off-by: Deluan <deluan@navidrome.org> * feat: add PDK abstraction layer with mock support for non-WASM builds Signed-off-by: Deluan <deluan@navidrome.org> * feat: add unit tests for Discord plugin and RPC functionality Signed-off-by: Deluan <deluan@navidrome.org> * fix: update return types in minimalPlugin and wikimediaPlugin methods to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * fix: context cancellation and implement WebSocket callback timeout for improved error handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: conditionally include error handling in generated client code templates Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement ConfigService for plugin configuration management Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance plugin manager to support metrics recording Signed-off-by: Deluan <deluan@navidrome.org> * refactor: make MockPDK private Signed-off-by: Deluan <deluan@navidrome.org> * refactor: update interface types to use 'any' in plugin repository methods Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename List method to Keys for clarity in configuration management Signed-off-by: Deluan <deluan@navidrome.org> * test: add ndpgen plugin tests in the pipeline and update Makefile Signed-off-by: Deluan <deluan@navidrome.org> * feat: add users permission management to plugin system Signed-off-by: Deluan <deluan@navidrome.org> * refactor: streamline users integration tests and enhance plugin user management Signed-off-by: Deluan <deluan@navidrome.org> * refactor: remove UserID from scrobbler request structure Signed-off-by: Deluan <deluan@navidrome.org> * test: add integration tests for UsersService enable gate behavior Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement user permissions for SubsonicAPI and scrobbler plugins Signed-off-by: Deluan <deluan@navidrome.org> * fix: show proper error in the UI when enabling a plugin fails Signed-off-by: Deluan <deluan@navidrome.org> * feat: add library permission management to plugin system Signed-off-by: Deluan <deluan@navidrome.org> * feat: add user permission for processing scrobbles in Discord Rich Presence plugin Signed-off-by: Deluan <deluan@navidrome.org> * fix: implement dynamic loading for buffered scrobbler plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: add GetAdmins method to retrieve admin users from the plugin Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Portuguese translations for user and library permissions Signed-off-by: Deluan <deluan@navidrome.org> * reorder migrations Signed-off-by: Deluan <deluan@navidrome.org> * fix: remove unnecessary bulkActionButtons prop from PluginList component * feat: add manual plugin rescan functionality and corresponding UI action Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement user/library and plugin management integration with cleanup on deletion Signed-off-by: Deluan <deluan@navidrome.org> * feat: replace core mock services with test-specific implementations to avoid import cycles * feat: add ID fields to Artist and Song structs and enhance track loading logic by prioritizing ID matches Signed-off-by: Deluan <deluan@navidrome.org> * feat: update plugin permissions from allowedHosts to requiredHosts for better clarity and consistency * feat: refactor plugin host permissions to use RequiredHosts directly for improved clarity * fix: don't record metrics for plugin calls that aren't implemented at all Signed-off-by: Deluan <deluan@navidrome.org> * fix: enhance connection management with improved error handling and cleanup logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: introduce ArtistRef struct for better artist representation and update track metadata handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: update user configuration handling to use user key prefix for improved clarity Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance ConfigCard input fields with multiline support and vertical resizing Signed-off-by: Deluan <deluan@navidrome.org> * fix: rust plugin compilation error Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement IsOptionPattern method for better return type handling in Rust PDK generation Signed-off-by: Deluan <deluan@navidrome.org> --------- Signed-off-by: Deluan <deluan@navidrome.org>
2026-01-14 19:22:48 -05:00
//nd:capability name=metadata
type MetadataAgent interface {
// GetArtistMBID retrieves the MusicBrainz ID for an artist.
//nd:export name=nd_get_artist_mbid
GetArtistMBID(ArtistMBIDRequest) (*ArtistMBIDResponse, error)
// GetArtistURL retrieves the external URL for an artist.
//nd:export name=nd_get_artist_url
GetArtistURL(ArtistRequest) (*ArtistURLResponse, error)
// GetArtistBiography retrieves the biography for an artist.
//nd:export name=nd_get_artist_biography
GetArtistBiography(ArtistRequest) (*ArtistBiographyResponse, error)
// GetSimilarArtists retrieves similar artists for a given artist.
//nd:export name=nd_get_similar_artists
GetSimilarArtists(SimilarArtistsRequest) (*SimilarArtistsResponse, error)
// GetArtistImages retrieves images for an artist.
//nd:export name=nd_get_artist_images
GetArtistImages(ArtistRequest) (*ArtistImagesResponse, error)
// GetArtistTopSongs retrieves top songs for an artist.
//nd:export name=nd_get_artist_top_songs
GetArtistTopSongs(TopSongsRequest) (*TopSongsResponse, error)
// GetAlbumInfo retrieves album information.
//nd:export name=nd_get_album_info
GetAlbumInfo(AlbumRequest) (*AlbumInfoResponse, error)
// GetAlbumImages retrieves images for an album.
//nd:export name=nd_get_album_images
GetAlbumImages(AlbumRequest) (*AlbumImagesResponse, error)
// GetSimilarSongsByTrack retrieves songs similar to a specific track.
//nd:export name=nd_get_similar_songs_by_track
GetSimilarSongsByTrack(SimilarSongsByTrackRequest) (*SimilarSongsResponse, error)
// GetSimilarSongsByAlbum retrieves songs similar to tracks on an album.
//nd:export name=nd_get_similar_songs_by_album
GetSimilarSongsByAlbum(SimilarSongsByAlbumRequest) (*SimilarSongsResponse, error)
// GetSimilarSongsByArtist retrieves songs similar to an artist's catalog.
//nd:export name=nd_get_similar_songs_by_artist
GetSimilarSongsByArtist(SimilarSongsByArtistRequest) (*SimilarSongsResponse, error)
feat(plugins): New Plugin System with multi-language PDK support (#4833) * chore(plugins): remove the old plugins system implementation Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement new plugin system with using Extism Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add capability detection for plugins based on exported functions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add auto-reload functionality for plugins with file watcher support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add auto-reload functionality for plugins with file watcher support Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): standardize variable names and remove superfluous wrapper functions Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): improve error handling and logging in plugin manager Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): implement plugin function call helper and refactor MetadataAgent methods Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): race condition in plugin manager * tests(plugins): change BeforeEach to BeforeAll in MetadataAgent tests Signed-off-by: Deluan <deluan@navidrome.org> * tests(plugins): optimize tests Signed-off-by: Deluan <deluan@navidrome.org> * tests(plugins): more optimizations Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): ignore goroutine leaks from notify library in tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Wikimedia plugin for Navidrome to fetch artist metadata Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): enhance plugin logging and set User-Agent header Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement scrobbler plugin with authorization and scrobbling capabilities Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): integrate logs Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): clean up manifest struct and improve plugin loading logic Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add metadata agent and scrobbler schemas for bootstrapping plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat(hostgen): add hostgen tool for generating Extism host function wrappers - Implemented hostgen tool to generate wrappers from annotated Go interfaces. - Added command-line flags for input/output directories and package name. - Introduced parsing and code generation logic for host services. - Created test data for various service interfaces and expected generated code. - Added documentation for host services and annotations for code generation. - Implemented SubsonicAPI service with corresponding generated code. * feat(subsonicapi): update Call method to return JSON string response Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement SubsonicAPI host function integration with permissions Signed-off-by: Deluan <deluan@navidrome.org> * fix(generator): error-only methods in response handling Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate client wrappers for host functions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(generator): remove error handling for response.Error in client templates Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): add Scheduler service interface with host function wrappers for scheduling tasks * feat(plugins): add WASI build constraints to client wrapper templates, to avoid lint errors Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): implement Scheduler service with one-time and recurring scheduling capabilities Signed-off-by: Deluan <deluan@navidrome.org> * refactor(manifest): remove unused ConfigPermission from permissions schema Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): add scheduler callback schema and implementation for plugins Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): streamline scheduling logic and remove unused callback tracking Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): add Close method for resource cleanup on plugin unload Signed-off-by: Deluan <deluan@navidrome.org> * docs(scheduler): clarify SchedulerCallback requirement for scheduling functions Signed-off-by: Deluan <deluan@navidrome.org> * fix: update wasm build rule to include all Go files in the directory Signed-off-by: Deluan <deluan@navidrome.org> * feat: rewrite the wikimedia plugin using the XTP CLI Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): replace uuid with id.NewRandom for schedule ID generation Signed-off-by: Deluan <deluan@navidrome.org> * refactor: capabilities registration Signed-off-by: Deluan <deluan@navidrome.org> * test: add scheduler service isolation test for plugin instances Signed-off-by: Deluan <deluan@navidrome.org> * refactor: update plugin manager initialization and encapsulate logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: add WebSocket service definitions for plugin communication Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement WebSocket service for plugin integration and connection management Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Crypto Ticker example plugin for real-time cryptocurrency price updates via Coinbase WebSocket API Also add the lifecycle capability Signed-off-by: Deluan <deluan@navidrome.org> * fix: use context.Background() in invokeCallback for scheduled tasks Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename plugin.create() to plugin.instance() Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename pluginInstance to plugin for consistency across the codebase Signed-off-by: Deluan <deluan@navidrome.org> * refactor: simplify schedule cloning in Close method and enhance plugin cleanup error handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement Artwork service for generating artwork URLs in Navidrome plugins - WIP Signed-off-by: Deluan <deluan@navidrome.org> * refactor: moved public URL builders to avoid import cycles Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Cache service for in-memory TTL-based caching in plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Discord Rich Presence example plugin for Navidrome integration Signed-off-by: Deluan <deluan@navidrome.org> * refactor: host function wrappers to use structured request and response types - Updated the host function signatures in `nd_host_artwork.go`, `nd_host_scheduler.go`, `nd_host_subsonicapi.go`, and `nd_host_websocket.go` to accept a single parameter for JSON requests. - Introduced structured request and response types for various cache operations in `nd_host_cache.go`. - Modified cache functions to marshal requests to JSON and unmarshal responses, improving error handling and code clarity. - Removed redundant memory allocation for string parameters in favor of JSON marshaling. - Enhanced error handling in WebSocket and cache operations to return structured error responses. * refactor: error handling in various plugins to convert response.Error to Go errors - Updated error handling in `nd_host_scheduler.go`, `nd_host_websocket.go`, `nd_host_artwork.go`, `nd_host_cache.go`, and `nd_host_subsonicapi.go` to convert string errors from responses into Go errors. - Removed redundant error checks in test data plugins for cleaner code. - Ensured consistent error handling across all plugins to improve reliability and maintainability. * refactor: rename fake plugins to test plugins for clarity in integration tests Signed-off-by: Deluan <deluan@navidrome.org> * feat: add help target to Makefile for plugin usage instructions Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Cover Art Archive plugin as an example of Python plugin Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Makefile and README to clarify Go plugin usage Signed-off-by: Deluan <deluan@navidrome.org> * feat: include plugin capabilities in loading log message Signed-off-by: Deluan <deluan@navidrome.org> * feat: add trace logging for plugin availability and error handling in agents Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Now Playing Logger plugin to showcase calling host functions from Python plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: generate Python client wrappers for various host services Signed-off-by: Deluan <deluan@navidrome.org> * feat: add generated host function wrappers for Scheduler and SubsonicAPI services Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Python plugin documentation and usage instructions for host function wrappers Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Webhook Scrobbler plugin in Rust to send HTTP notifications on scrobble events Signed-off-by: Deluan <deluan@navidrome.org> * feat: enable parallel loading of plugins during startup Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to include WebSocket callback schema in plugin documentation Signed-off-by: Deluan <deluan@navidrome.org> * feat: extend plugin watcher with improved logging and debounce duration adjustment Signed-off-by: Deluan <deluan@navidrome.org> * add trace message for plugin recompiles Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement plugin cache purging functionality Signed-off-by: Deluan <deluan@navidrome.org> * test: move purgeCacheBySize unit tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add plugin repository and database support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add plugin management routes and middleware Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): implement plugin synchronization with database for add, update, and remove actions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add PluginList and PluginShow components with plugin management functionality Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): optimize plugin change detection Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins UI): improve PluginList structure Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): enhance PluginShow with author, website, and permissions display Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): refactor to use MUI and RA components Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add error handling for plugin enable/disable actions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): inject PluginManager into native API Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update GetManager to accept DataStore parameter Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add subsonicRouter to Manager and refactor host service registration Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): enhance debug logging for plugin actions and recompile logic Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): break manager.go into smaller, focused files Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): streamline error handling and improve plugin retrieval logic Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update newWebSocketService to use WebSocketPermission for allowed hosts Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): introduce ToggleEnabledSwitch for managing plugin enable/disable state Signed-off-by: Deluan <deluan@navidrome.org> * docs: update READMEs Signed-off-by: Deluan <deluan@navidrome.org> * feat(library): add Library service for metadata access and filesystem integration Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Library Inspector plugin for periodic library inspection and file size logging Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to reflect JSON configuration format for plugins Signed-off-by: Deluan <deluan@navidrome.org> * fix(build): update target to wasm32-wasip1 for improved WASI support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement configuration management UI with key-value pairs support Signed-off-by: Deluan <deluan@navidrome.org> * feat(ui): adjust grid layout in InfoRow component for improved responsiveness Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): rename ErrorIndicator to EnabledOrErrorField and enhance error handling logic Signed-off-by: Deluan <deluan@navidrome.org> * feat(i18n): add Portuguese translations for plugin management and notifications Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add support for .ndp plugin packages and update build process Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README for .ndp plugin packaging and installation instructions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement KVStore service for persistent key-value storage Signed-off-by: Deluan <deluan@navidrome.org> * docs: enhance README with Extism plugin development resources and recommendations Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): integrate event broker into plugin manager Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): update config handling in PluginShow to track last record state Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Rust host function library and example implementation of Discord Rich Presence plugin in Rust Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate Rust lib.rs file to expose host function wrappers Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update JSON field names to camelCase for consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor: reduce cyclomatic complexity by refactoring main function Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): enhance Rust code generation with typed struct support and improved type handling Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Go client library with host function wrappers and documentation Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate Go client stubs for non-WASM platforms Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): update client template file names for consistency Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add initial implementation of the Navidrome Plugin Development Kit code generator - Pahse 1 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 2 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 2 (2) Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 3 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 4 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 5 Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): consistent naming/types across PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): streamline plugin function signatures and error handling Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update scrobbler interface to return errors directly instead of response structs Signed-off-by: Deluan <deluan@navidrome.org> * test: make all test plugins use the PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): reorganize and sort type definitions for consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update error handling for methods to return errors directly Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update function signatures to return values directly instead of response structs Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update request/response types to use private naming conventions Signed-off-by: Deluan <deluan@navidrome.org> * build: mark .wasm files as intermediate for cleanup after building .ndp Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): consolidate PDK module path and update Go version to 1.25 Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement Rust PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): reorganize Rust output structure to follow standard conventions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update Discord Rich Presence and Library Inspector plugins to use nd-pdk for service calls and implement lifecycle management Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update macro names for websocket and metadata registration to improve clarity and consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): rename scheduler callback methods for consistency and clarity Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update export wrappers to use `//go:wasmexport` for WebAssembly compatibility Signed-off-by: Deluan <deluan@navidrome.org> * docs: update plugin registration docs Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): generate host wrappers Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): conditionally run goleak checks based on CI environment Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to reflect changes in plugin import paths Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update plugin instance creation to accept context for cancellation support Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update return types in metadata interfaces to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): enhance type handling for Rust and XTP output in capability generation Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update IsAuthorized method to return boolean instead of response object Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): add unit tests for rustOutputType and isPrimitiveRustType functions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement XTP JSONSchema validation for generated schemas Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update response types in testMetadataAgent methods to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * docs: update Go and Rust plugin developer sections for clarity Signed-off-by: Deluan <deluan@navidrome.org> * docs: correct example link for library inspector in README Signed-off-by: Deluan <deluan@navidrome.org> * docs: clarify artwork URL generation capabilities in service descriptions Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to include Rust PDK crate information for plugin developers Signed-off-by: Deluan <deluan@navidrome.org> * fix: handle URL parsing errors and use atomic upsert in plugin repository Added proper error handling for url.Parse calls in PublicURL and AbsoluteURL functions. When parsing fails, PublicURL now falls back to AbsoluteURL, and AbsoluteURL logs the error and returns an empty string, preventing malformed URLs from being generated. Replaced the non-atomic UPDATE-then-INSERT pattern in plugin repository Put method with a single atomic INSERT ... ON CONFLICT statement. This eliminates potential race conditions and improves consistency with the upsert pattern already used in host_kvstore.go. * feat: implement mock service instances for non-WASM builds using testify/mock Signed-off-by: Deluan <deluan@navidrome.org> * refactor: Discord RPC struct to encapsulate WebSocket logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: add support for experimental WebAssembly threads Signed-off-by: Deluan <deluan@navidrome.org> * feat: add PDK abstraction layer with mock support for non-WASM builds Signed-off-by: Deluan <deluan@navidrome.org> * feat: add unit tests for Discord plugin and RPC functionality Signed-off-by: Deluan <deluan@navidrome.org> * fix: update return types in minimalPlugin and wikimediaPlugin methods to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * fix: context cancellation and implement WebSocket callback timeout for improved error handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: conditionally include error handling in generated client code templates Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement ConfigService for plugin configuration management Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance plugin manager to support metrics recording Signed-off-by: Deluan <deluan@navidrome.org> * refactor: make MockPDK private Signed-off-by: Deluan <deluan@navidrome.org> * refactor: update interface types to use 'any' in plugin repository methods Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename List method to Keys for clarity in configuration management Signed-off-by: Deluan <deluan@navidrome.org> * test: add ndpgen plugin tests in the pipeline and update Makefile Signed-off-by: Deluan <deluan@navidrome.org> * feat: add users permission management to plugin system Signed-off-by: Deluan <deluan@navidrome.org> * refactor: streamline users integration tests and enhance plugin user management Signed-off-by: Deluan <deluan@navidrome.org> * refactor: remove UserID from scrobbler request structure Signed-off-by: Deluan <deluan@navidrome.org> * test: add integration tests for UsersService enable gate behavior Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement user permissions for SubsonicAPI and scrobbler plugins Signed-off-by: Deluan <deluan@navidrome.org> * fix: show proper error in the UI when enabling a plugin fails Signed-off-by: Deluan <deluan@navidrome.org> * feat: add library permission management to plugin system Signed-off-by: Deluan <deluan@navidrome.org> * feat: add user permission for processing scrobbles in Discord Rich Presence plugin Signed-off-by: Deluan <deluan@navidrome.org> * fix: implement dynamic loading for buffered scrobbler plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: add GetAdmins method to retrieve admin users from the plugin Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Portuguese translations for user and library permissions Signed-off-by: Deluan <deluan@navidrome.org> * reorder migrations Signed-off-by: Deluan <deluan@navidrome.org> * fix: remove unnecessary bulkActionButtons prop from PluginList component * feat: add manual plugin rescan functionality and corresponding UI action Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement user/library and plugin management integration with cleanup on deletion Signed-off-by: Deluan <deluan@navidrome.org> * feat: replace core mock services with test-specific implementations to avoid import cycles * feat: add ID fields to Artist and Song structs and enhance track loading logic by prioritizing ID matches Signed-off-by: Deluan <deluan@navidrome.org> * feat: update plugin permissions from allowedHosts to requiredHosts for better clarity and consistency * feat: refactor plugin host permissions to use RequiredHosts directly for improved clarity * fix: don't record metrics for plugin calls that aren't implemented at all Signed-off-by: Deluan <deluan@navidrome.org> * fix: enhance connection management with improved error handling and cleanup logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: introduce ArtistRef struct for better artist representation and update track metadata handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: update user configuration handling to use user key prefix for improved clarity Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance ConfigCard input fields with multiline support and vertical resizing Signed-off-by: Deluan <deluan@navidrome.org> * fix: rust plugin compilation error Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement IsOptionPattern method for better return type handling in Rust PDK generation Signed-off-by: Deluan <deluan@navidrome.org> --------- Signed-off-by: Deluan <deluan@navidrome.org>
2026-01-14 19:22:48 -05:00
}
// ArtistMBIDRequest is the request for GetArtistMBID.
type ArtistMBIDRequest struct {
// ID is the internal Navidrome artist ID.
ID string `json:"id"`
// Name is the artist name.
Name string `json:"name"`
}
// ArtistMBIDResponse is the response for GetArtistMBID.
type ArtistMBIDResponse struct {
// MBID is the MusicBrainz ID for the artist.
MBID string `json:"mbid"`
}
// ArtistRequest is the common request for artist-related functions.
type ArtistRequest struct {
// ID is the internal Navidrome artist ID.
ID string `json:"id"`
// Name is the artist name.
Name string `json:"name"`
// MBID is the MusicBrainz ID for the artist (if known).
MBID string `json:"mbid,omitempty"`
}
// ArtistURLResponse is the response for GetArtistURL.
type ArtistURLResponse struct {
// URL is the external URL for the artist.
URL string `json:"url"`
}
// ArtistBiographyResponse is the response for GetArtistBiography.
type ArtistBiographyResponse struct {
// Biography is the artist biography text.
Biography string `json:"biography"`
}
// SimilarArtistsRequest is the request for GetSimilarArtists.
type SimilarArtistsRequest struct {
// ID is the internal Navidrome artist ID.
ID string `json:"id"`
// Name is the artist name.
Name string `json:"name"`
// MBID is the MusicBrainz ID for the artist (if known).
MBID string `json:"mbid,omitempty"`
// Limit is the maximum number of similar artists to return.
Limit int32 `json:"limit"`
}
feat(plugins): share plugin DTOs via a types package (#5655) * refactor(plugins): remove Python PDK generation from ndpgen * feat(plugins): parse Go type aliases distinctly in ndpgen * feat(plugins): resolve shared-type aliases against a registry in ndpgen * fix(plugins): resolve host-service shared aliases package-wide Mirror the capability approach in ParseDirectoryWithShared: do a first pass over all package files to build a package-wide alias map, then pass it into parseServiceFile so that a shared-type alias declared in a sibling file is visible when resolving types in the service interface file. Add a focused test that writes the alias in one file and the hostservice in another, confirming RED before the fix and GREEN after. Also strengthens the existing Task 3 test with an ArtistRef.Target assertion. * feat(plugins): add ndpgen -shared-types mode for the Go types package * feat(plugins): generate the nd-pdk-types Rust crate from -shared-types * feat(plugins): inject types import and emit deprecated aliases in Go output * feat(plugins): emit deprecated Rust aliases to the shared types crate * feat(plugins): inline shared-type shapes into XTP schemas * feat(plugins): add nd-pdk-types crate and wire dependents * feat(plugins): move shared capability types to plugins/types with deprecated aliases * fix(plugins): point Rust deprecated-alias note at the replacement type * fix(plugins): include shared aliases in KnownStructs so Rust fields keep their type Capability.KnownStructs() and Service.KnownStructs() previously only registered names from .Structs. After the shared-types migration, types like ArtistRef/TrackInfo/SongRef live in .SharedAliases instead, so ToRustTypeWithStructs could not find them and fell back to serde_json::Value for every struct field referencing a shared type. Add the shared-alias names to the knownStructs map in both methods. Regenerate the Rust capability files; track/song/artist fields now render as their named types (TrackInfo, SongRef, ArtistRef, etc.). Add a regression test that verifies a struct field whose type is only in SharedAliases renders as the named type and not serde_json::Value. * docs(plugins): remove stale Python references from ndpgen and plugins READMEs ndpgen no longer has a -python flag; remove it from the usage synopsis, flags table, and defaults note in ndpgen/README.md. Delete the "Python Client Library" section that described its output. plugins/README.md referenced plugins/pdk/python/host/ (deleted) as the source for Python host-service stubs. Remove that paragraph; Python plugins still work via the XTP-schema / extism-py path (see examples/*-py). * refactor(plugins): dedupe ndpgen helpers and tidy shared-type codegen * docs(plugins): restore Python as a supported XTP schema target The ndpgen-generated Python PDK was removed, but the XTP YAML schemas are language-neutral and the XTP CLI still generates Python bindings from them (as the extism-py examples demonstrate). Only the ndpgen Python output was dropped, not Python support itself. * test(plugins): use the shared types package in test plugins The test fixtures referenced the now-deprecated capability aliases (sonicsimilarity.SongRef, metadata.ArtistRef/SongRef). Point them at the canonical types package so our own fixtures don't depend on symbols slated for removal. * refactor(plugins): use the shared types package in host adapters Replace deprecated capabilities.TrackInfo, capabilities.ArtistRef, and capabilities.SongRef aliases with the canonical types.TrackInfo, types.ArtistRef, and types.SongRef from plugins/types. * fix(plugins): reference shared types by canonical path in generated Rust Previously the generator emitted `pub field: SongRef` (the local deprecated alias) for struct fields whose type came from SharedAliases. Refactored ToRustTypeWithStructs into a private toRustType that accepts a shared map, and added ToRustTypeWithShared which resolves shared-alias names to their canonical nd_pdk_types::X path before falling through to the knownStructs check. Both rustCapabilityFuncMap and rustFuncMap now build the shared map from SharedAliases and use it for fieldRustType, so the generated capability files reference nd_pdk_types::SongRef / nd_pdk_types::TrackInfo directly. The deprecated pub type aliases remain in place as the external back-compat surface. Deprecation warning count from cargo build drops to 0. * fix(examples): implement missing Scrobbler.playback_report in Rust examples The Scrobbler trait gained a playback_report method but the two Rust example plugins (webhook-rs and discord-rich-presence-rs) were not updated, causing E0046 compile errors. Added the missing fn playback_report to both: webhook-rs logs and returns Ok(()) mirroring its now_playing handler; discord-rich-presence-rs is a no-op since Discord presence does not need playback reports. make all-rust now exits 0. * refactor(plugins): point Rust deprecation notes at the nd_pdk::types umbrella path Plugin authors depend on the nd-pdk umbrella crate, which re-exports nd_pdk_types as 'types', so the migration target they should type is nd_pdk::types::X. The alias target stays nd_pdk_types::X (the real path inside nd-pdk-capabilities). * fix(plugins): error when a shared-type alias can't be resolved against the registry * refactor(plugins): parse each Go source file once in ndpgen * fix(plugins): correct ndpgen review nits (flag name, unused dep, docs) * refactor(plugins): drop the now-unused path param from parseServiceFile * refactor(plugins): use shared types directly, rename TrackInfo to Track Capability interfaces now reference the shared `types` package by qualified name (types.Track, types.SongRef, types.ArtistRef) instead of the package-local deprecated aliases, and the shared TrackInfo type is renamed to Track to match its role as the plugin-facing projection of a library media file. The deprecated bare aliases (scrobbler.TrackInfo, metadata.ArtistRef, sonicsimilarity.SongRef, etc.) are kept as re-exports so existing plugins keep compiling, with a deprecation warning steering them to the canonical types. To support this, ndpgen now resolves qualified types.X references: it collects them during type discovery, maps each used canonical type back to its declared deprecated alias for re-export, emits nd_pdk_types::X paths in Rust, and names the XTP schema components by their canonical type. Regenerated the Go and Rust PDK and the XTP schemas, and added generator tests covering the qualified-ref path. Also adds clarifying doc comments to the shared types. * refactor(plugins): extract shared types selector into a named const Replace the "types." string literal that detects and strips the shared types package selector with a single sharedTypesPrefix constant across the ndpgen generator (parser, types, generator, xtp_schema), giving the package one source of truth for the selector. Also restore the single reused scratch map (cleared each iteration) in the resolveSharedAliases BFS instead of allocating a fresh map per shared-struct field, matching the prior implementation. Pure cleanup from a /simplify pass: regeneration produces byte-for-byte identical Go, Rust, and XTP output. * refactor(plugins): keep TrackInfo in the capability package for now Move the track type back out of the shared plugins/types package: it is again defined inline as TrackInfo in plugins/capabilities/scrobbler.go and referenced directly by the scrobbler and lyrics capabilities, reverting the rename to types.Track. The host helper is renamed back to mediaFileToTrackInfo and now returns capabilities.TrackInfo. SongRef and ArtistRef stay in the shared types package; TrackInfo keeps using types.ArtistRef for its artist lists. This type is expected to be reshaped in upcoming work, so leaving it in the capability package avoids churning the shared types twice. Regenerated the Go and Rust PDK and the XTP schemas accordingly. * fix(plugins): emit the Go types import for direct shared-type refs ndpgen's Capability/Service.ImportsSharedTypes only reported a shared-types dependency when a deprecated re-export alias (type X = types.X) was declared. A struct field referencing the canonical form directly (e.g. types.SongRef) with no such alias produced an empty SharedAliases slice, so the Go templates skipped the import while still emitting fields/signatures using types.SongRef — leaving generated PDK code for new shared DTOs uncompilable unless an otherwise unnecessary alias was added. ImportsSharedTypes now also returns true when any struct field references the types. package by qualified name, via a new structsReferenceSharedTypes helper that reuses collectReferencedTypes (so []types.X and map[...]types.X are covered too). * fix(plugins): preserve base64 encoding for shared byte fields in Rust The Rust shared-types crate template rendered a []byte field as a plain Vec<u8> without the base64_bytes serde override used by the capability/client templates. Go's encoding/json serializes []byte as a base64 string, so a Rust plugin using nd_pdk::types would have serialized an array of numbers instead of the wire format the Go/server side expects. GenerateSharedTypesRust now registers the base64_bytes partial and passes a HasByteFields flag (new anyFieldIsByteSlice helper); types.rs.tmpl emits the base64_bytes module and a #[serde(with = "base64_bytes")] attribute on []byte fields, mirroring the capability template. * fix(plugins): include directly-referenced shared types in XTP schemas buildSchemas registered shared types into the schema components only by iterating cap.SharedAliases, which records deprecated re-export aliases. A capability that referenced a shared DTO solely as types.Foo (no declared alias) therefore never got Foo into the component set, so the self-contained XTP schema rendered the field as a generic object (or emitted a dangling $ref), breaking the direct shared-type use case enabled by -shared. resolveSharedAliases now also returns the resolved shapes of every used shared type (alias or not); these are carried on the new Capability.SharedTypes field and registered by buildSchemas alongside SharedAliases. Validated end-to-end with the xtp CLI: a direct types.Foo reference now produces a proper component plus a $ref, so xtp generates a typed struct instead of an untyped serde_json::Map. * fix(plugins): resolve renamed shared aliases to canonical schema refs When a deprecated alias renames its canonical type (e.g. type TrackInfo = types.Track) and a capability field is typed with the alias name (TrackInfo), buildProperty emitted a $ref to #/components/schemas/TrackInfo. Components are keyed by the canonical name (Track), so no TrackInfo component was emitted, leaving a dangling reference that crashes the xtp code generator. buildSchemas now builds an alias->canonical map; buildProperty (and the slice item path) resolves $ref targets through it, and a used alias name marks its canonical component used so it is emitted. Validated with the xtp CLI: the renamed-alias schema previously crashed xtp and now generates cleanly. * fix(plugins): detect shared types used directly in method signatures ImportsSharedTypes only inspected struct fields, so a capability method using a shared type directly in its signature (e.g. types.SongRef as input/output rather than inside a local struct) was not detected. The generated Go templates still rendered the provider/export signatures with types.SongRef, so the capability package omitted the types import and failed to compile; the same gap applied to service params/returns. ImportsSharedTypes now also scans capability method input/output types and service method params/returns, via a typeReferencesSharedTypes helper that reuses collectReferencedTypes (covering pointer/slice/map wrappers). * fix(plugins): add base64 dependency to the shared Rust types crate When a shared DTO has a []byte field, ndpgen emits the base64_bytes serde helper and use base64::... imports into nd-pdk-types/src/lib.rs, but the crate manifest declared only serde. In that case make gen produced a crate that failed to compile with 'unresolved module base64'. Add base64 = "0.22" (matching nd-pdk-capabilities) so the generated shared types crate compiles whenever a []byte field is present. Verified by generating a shared crate with a []byte field and confirming cargo check fails before and passes after. * fix(plugins): translate shared method types in generated Rust A capability method using a shared DTO directly as input/output (e.g. types.SongRef) was passed through rustOutputType unchanged, so the Rust template emitted invalid trait and extism_pdk::Json<$crate::pkg::types.SongRef> signatures that do not compile. Method input/output types now resolve through the shared registry: trait signatures use rustTraitType (shared -> nd_pdk_types::X, locals stay bare) and the export macros use rustMethodType (fully qualified: shared -> nd_pdk_types::X, primitives -> Rust, locals -> $crate::<pkg>::X). Verified end-to-end by compiling a generated capability that takes types.SongRef directly against the real nd-pdk-types crate. * fix(plugins): canonicalize XTP export refs for renamed shared aliases buildSchemas canonicalized alias-to-canonical references for struct-field $ref targets, but buildExport built export input/output $refs straight from fieldBaseType. A capability whose export used a renamed deprecated alias directly (e.g. type TrackInfo = types.Track with NowPlaying(TrackInfo)) emitted $ref: #/components/schemas/TrackInfo, while the component is emitted under the canonical name Track — a dangling export reference. Lift the alias-to-canonical map into GenerateSchema (buildAliasToCanonical) and apply it to export refs via canonicalRefName, the same resolution already used for field properties. * fix(plugins): route shared macro types through $crate for plugin builds When a capability method used a shared type directly, the generated export macro named the type as nd_pdk_types::SongRef. The macro expands in the downstream plugin crate, which depends on the umbrella nd-pdk crate and not on nd-pdk-types directly, so that path is unresolvable there and the plugin fails to build. rustMethodType (macro-facing) now emits $crate::types::X, and the generated nd-pdk-capabilities lib.rs re-exports nd_pdk_types as types so $crate resolves it. Trait signatures keep nd_pdk_types::X since they live in nd-pdk-capabilities, which has the direct dependency. Verified end-to-end: a plugin crate depending only on the umbrella that uses a capability with a direct types.X method now compiles via the macro. * fix(plugins): add nd-pdk-types dependency to the Rust host crate When a host service uses a shared type, ndpgen emits nd_pdk_types::X into the generated nd-pdk-host client wrappers, but the host crate's manifest did not depend on nd-pdk-types, so the crate failed to compile with 'unresolved module nd_pdk_types'. Host client wrappers are plain functions resolved in the host crate's own context (not macros expanded downstream), so a direct dependency is the right fix. Add nd-pdk-types = { path = "../nd-pdk-types" } to nd-pdk-host, mirroring nd-pdk-capabilities. Found while auditing all Rust paths against the realistic crate topology after the capability-side $crate fix; verified by generating a host service with a shared-type return and confirming cargo check fails before and passes after. * fix(plugins): resolve shared aliases in Rust host signatures The Rust host client rendered method params and returns through RustTypeWithStructs, which only consults KnownStructs. A host service using a shared alias in a signature (e.g. type Track = types.Track plus MatchSongs(...) ([]Track, error)) therefore emitted a bare Vec<Track>, but the client template emits no Track alias or import, so the generated nd-pdk-host crate did not compile. Only struct fields went through the shared map. rustType/rustParamType now use the shared map too (RustTypeWithShared / RustParamTypeWithShared), so an aliased param/return resolves to its canonical nd_pdk_types::X path, matching field handling. Verified by generating a host service returning a shared alias and confirming cargo check fails before and passes after.
2026-06-29 21:20:33 -04:00
// Deprecated: use types.ArtistRef.
type ArtistRef = types.ArtistRef
feat(plugins): New Plugin System with multi-language PDK support (#4833) * chore(plugins): remove the old plugins system implementation Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement new plugin system with using Extism Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add capability detection for plugins based on exported functions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add auto-reload functionality for plugins with file watcher support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add auto-reload functionality for plugins with file watcher support Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): standardize variable names and remove superfluous wrapper functions Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): improve error handling and logging in plugin manager Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): implement plugin function call helper and refactor MetadataAgent methods Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): race condition in plugin manager * tests(plugins): change BeforeEach to BeforeAll in MetadataAgent tests Signed-off-by: Deluan <deluan@navidrome.org> * tests(plugins): optimize tests Signed-off-by: Deluan <deluan@navidrome.org> * tests(plugins): more optimizations Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): ignore goroutine leaks from notify library in tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Wikimedia plugin for Navidrome to fetch artist metadata Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): enhance plugin logging and set User-Agent header Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement scrobbler plugin with authorization and scrobbling capabilities Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): integrate logs Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): clean up manifest struct and improve plugin loading logic Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add metadata agent and scrobbler schemas for bootstrapping plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat(hostgen): add hostgen tool for generating Extism host function wrappers - Implemented hostgen tool to generate wrappers from annotated Go interfaces. - Added command-line flags for input/output directories and package name. - Introduced parsing and code generation logic for host services. - Created test data for various service interfaces and expected generated code. - Added documentation for host services and annotations for code generation. - Implemented SubsonicAPI service with corresponding generated code. * feat(subsonicapi): update Call method to return JSON string response Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement SubsonicAPI host function integration with permissions Signed-off-by: Deluan <deluan@navidrome.org> * fix(generator): error-only methods in response handling Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate client wrappers for host functions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(generator): remove error handling for response.Error in client templates Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): add Scheduler service interface with host function wrappers for scheduling tasks * feat(plugins): add WASI build constraints to client wrapper templates, to avoid lint errors Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): implement Scheduler service with one-time and recurring scheduling capabilities Signed-off-by: Deluan <deluan@navidrome.org> * refactor(manifest): remove unused ConfigPermission from permissions schema Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): add scheduler callback schema and implementation for plugins Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): streamline scheduling logic and remove unused callback tracking Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): add Close method for resource cleanup on plugin unload Signed-off-by: Deluan <deluan@navidrome.org> * docs(scheduler): clarify SchedulerCallback requirement for scheduling functions Signed-off-by: Deluan <deluan@navidrome.org> * fix: update wasm build rule to include all Go files in the directory Signed-off-by: Deluan <deluan@navidrome.org> * feat: rewrite the wikimedia plugin using the XTP CLI Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): replace uuid with id.NewRandom for schedule ID generation Signed-off-by: Deluan <deluan@navidrome.org> * refactor: capabilities registration Signed-off-by: Deluan <deluan@navidrome.org> * test: add scheduler service isolation test for plugin instances Signed-off-by: Deluan <deluan@navidrome.org> * refactor: update plugin manager initialization and encapsulate logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: add WebSocket service definitions for plugin communication Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement WebSocket service for plugin integration and connection management Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Crypto Ticker example plugin for real-time cryptocurrency price updates via Coinbase WebSocket API Also add the lifecycle capability Signed-off-by: Deluan <deluan@navidrome.org> * fix: use context.Background() in invokeCallback for scheduled tasks Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename plugin.create() to plugin.instance() Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename pluginInstance to plugin for consistency across the codebase Signed-off-by: Deluan <deluan@navidrome.org> * refactor: simplify schedule cloning in Close method and enhance plugin cleanup error handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement Artwork service for generating artwork URLs in Navidrome plugins - WIP Signed-off-by: Deluan <deluan@navidrome.org> * refactor: moved public URL builders to avoid import cycles Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Cache service for in-memory TTL-based caching in plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Discord Rich Presence example plugin for Navidrome integration Signed-off-by: Deluan <deluan@navidrome.org> * refactor: host function wrappers to use structured request and response types - Updated the host function signatures in `nd_host_artwork.go`, `nd_host_scheduler.go`, `nd_host_subsonicapi.go`, and `nd_host_websocket.go` to accept a single parameter for JSON requests. - Introduced structured request and response types for various cache operations in `nd_host_cache.go`. - Modified cache functions to marshal requests to JSON and unmarshal responses, improving error handling and code clarity. - Removed redundant memory allocation for string parameters in favor of JSON marshaling. - Enhanced error handling in WebSocket and cache operations to return structured error responses. * refactor: error handling in various plugins to convert response.Error to Go errors - Updated error handling in `nd_host_scheduler.go`, `nd_host_websocket.go`, `nd_host_artwork.go`, `nd_host_cache.go`, and `nd_host_subsonicapi.go` to convert string errors from responses into Go errors. - Removed redundant error checks in test data plugins for cleaner code. - Ensured consistent error handling across all plugins to improve reliability and maintainability. * refactor: rename fake plugins to test plugins for clarity in integration tests Signed-off-by: Deluan <deluan@navidrome.org> * feat: add help target to Makefile for plugin usage instructions Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Cover Art Archive plugin as an example of Python plugin Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Makefile and README to clarify Go plugin usage Signed-off-by: Deluan <deluan@navidrome.org> * feat: include plugin capabilities in loading log message Signed-off-by: Deluan <deluan@navidrome.org> * feat: add trace logging for plugin availability and error handling in agents Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Now Playing Logger plugin to showcase calling host functions from Python plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: generate Python client wrappers for various host services Signed-off-by: Deluan <deluan@navidrome.org> * feat: add generated host function wrappers for Scheduler and SubsonicAPI services Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Python plugin documentation and usage instructions for host function wrappers Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Webhook Scrobbler plugin in Rust to send HTTP notifications on scrobble events Signed-off-by: Deluan <deluan@navidrome.org> * feat: enable parallel loading of plugins during startup Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to include WebSocket callback schema in plugin documentation Signed-off-by: Deluan <deluan@navidrome.org> * feat: extend plugin watcher with improved logging and debounce duration adjustment Signed-off-by: Deluan <deluan@navidrome.org> * add trace message for plugin recompiles Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement plugin cache purging functionality Signed-off-by: Deluan <deluan@navidrome.org> * test: move purgeCacheBySize unit tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add plugin repository and database support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add plugin management routes and middleware Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): implement plugin synchronization with database for add, update, and remove actions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add PluginList and PluginShow components with plugin management functionality Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): optimize plugin change detection Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins UI): improve PluginList structure Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): enhance PluginShow with author, website, and permissions display Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): refactor to use MUI and RA components Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add error handling for plugin enable/disable actions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): inject PluginManager into native API Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update GetManager to accept DataStore parameter Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add subsonicRouter to Manager and refactor host service registration Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): enhance debug logging for plugin actions and recompile logic Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): break manager.go into smaller, focused files Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): streamline error handling and improve plugin retrieval logic Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update newWebSocketService to use WebSocketPermission for allowed hosts Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): introduce ToggleEnabledSwitch for managing plugin enable/disable state Signed-off-by: Deluan <deluan@navidrome.org> * docs: update READMEs Signed-off-by: Deluan <deluan@navidrome.org> * feat(library): add Library service for metadata access and filesystem integration Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Library Inspector plugin for periodic library inspection and file size logging Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to reflect JSON configuration format for plugins Signed-off-by: Deluan <deluan@navidrome.org> * fix(build): update target to wasm32-wasip1 for improved WASI support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement configuration management UI with key-value pairs support Signed-off-by: Deluan <deluan@navidrome.org> * feat(ui): adjust grid layout in InfoRow component for improved responsiveness Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): rename ErrorIndicator to EnabledOrErrorField and enhance error handling logic Signed-off-by: Deluan <deluan@navidrome.org> * feat(i18n): add Portuguese translations for plugin management and notifications Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add support for .ndp plugin packages and update build process Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README for .ndp plugin packaging and installation instructions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement KVStore service for persistent key-value storage Signed-off-by: Deluan <deluan@navidrome.org> * docs: enhance README with Extism plugin development resources and recommendations Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): integrate event broker into plugin manager Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): update config handling in PluginShow to track last record state Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Rust host function library and example implementation of Discord Rich Presence plugin in Rust Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate Rust lib.rs file to expose host function wrappers Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update JSON field names to camelCase for consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor: reduce cyclomatic complexity by refactoring main function Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): enhance Rust code generation with typed struct support and improved type handling Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Go client library with host function wrappers and documentation Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate Go client stubs for non-WASM platforms Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): update client template file names for consistency Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add initial implementation of the Navidrome Plugin Development Kit code generator - Pahse 1 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 2 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 2 (2) Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 3 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 4 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 5 Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): consistent naming/types across PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): streamline plugin function signatures and error handling Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update scrobbler interface to return errors directly instead of response structs Signed-off-by: Deluan <deluan@navidrome.org> * test: make all test plugins use the PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): reorganize and sort type definitions for consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update error handling for methods to return errors directly Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update function signatures to return values directly instead of response structs Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update request/response types to use private naming conventions Signed-off-by: Deluan <deluan@navidrome.org> * build: mark .wasm files as intermediate for cleanup after building .ndp Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): consolidate PDK module path and update Go version to 1.25 Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement Rust PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): reorganize Rust output structure to follow standard conventions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update Discord Rich Presence and Library Inspector plugins to use nd-pdk for service calls and implement lifecycle management Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update macro names for websocket and metadata registration to improve clarity and consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): rename scheduler callback methods for consistency and clarity Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update export wrappers to use `//go:wasmexport` for WebAssembly compatibility Signed-off-by: Deluan <deluan@navidrome.org> * docs: update plugin registration docs Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): generate host wrappers Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): conditionally run goleak checks based on CI environment Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to reflect changes in plugin import paths Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update plugin instance creation to accept context for cancellation support Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update return types in metadata interfaces to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): enhance type handling for Rust and XTP output in capability generation Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update IsAuthorized method to return boolean instead of response object Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): add unit tests for rustOutputType and isPrimitiveRustType functions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement XTP JSONSchema validation for generated schemas Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update response types in testMetadataAgent methods to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * docs: update Go and Rust plugin developer sections for clarity Signed-off-by: Deluan <deluan@navidrome.org> * docs: correct example link for library inspector in README Signed-off-by: Deluan <deluan@navidrome.org> * docs: clarify artwork URL generation capabilities in service descriptions Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to include Rust PDK crate information for plugin developers Signed-off-by: Deluan <deluan@navidrome.org> * fix: handle URL parsing errors and use atomic upsert in plugin repository Added proper error handling for url.Parse calls in PublicURL and AbsoluteURL functions. When parsing fails, PublicURL now falls back to AbsoluteURL, and AbsoluteURL logs the error and returns an empty string, preventing malformed URLs from being generated. Replaced the non-atomic UPDATE-then-INSERT pattern in plugin repository Put method with a single atomic INSERT ... ON CONFLICT statement. This eliminates potential race conditions and improves consistency with the upsert pattern already used in host_kvstore.go. * feat: implement mock service instances for non-WASM builds using testify/mock Signed-off-by: Deluan <deluan@navidrome.org> * refactor: Discord RPC struct to encapsulate WebSocket logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: add support for experimental WebAssembly threads Signed-off-by: Deluan <deluan@navidrome.org> * feat: add PDK abstraction layer with mock support for non-WASM builds Signed-off-by: Deluan <deluan@navidrome.org> * feat: add unit tests for Discord plugin and RPC functionality Signed-off-by: Deluan <deluan@navidrome.org> * fix: update return types in minimalPlugin and wikimediaPlugin methods to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * fix: context cancellation and implement WebSocket callback timeout for improved error handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: conditionally include error handling in generated client code templates Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement ConfigService for plugin configuration management Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance plugin manager to support metrics recording Signed-off-by: Deluan <deluan@navidrome.org> * refactor: make MockPDK private Signed-off-by: Deluan <deluan@navidrome.org> * refactor: update interface types to use 'any' in plugin repository methods Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename List method to Keys for clarity in configuration management Signed-off-by: Deluan <deluan@navidrome.org> * test: add ndpgen plugin tests in the pipeline and update Makefile Signed-off-by: Deluan <deluan@navidrome.org> * feat: add users permission management to plugin system Signed-off-by: Deluan <deluan@navidrome.org> * refactor: streamline users integration tests and enhance plugin user management Signed-off-by: Deluan <deluan@navidrome.org> * refactor: remove UserID from scrobbler request structure Signed-off-by: Deluan <deluan@navidrome.org> * test: add integration tests for UsersService enable gate behavior Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement user permissions for SubsonicAPI and scrobbler plugins Signed-off-by: Deluan <deluan@navidrome.org> * fix: show proper error in the UI when enabling a plugin fails Signed-off-by: Deluan <deluan@navidrome.org> * feat: add library permission management to plugin system Signed-off-by: Deluan <deluan@navidrome.org> * feat: add user permission for processing scrobbles in Discord Rich Presence plugin Signed-off-by: Deluan <deluan@navidrome.org> * fix: implement dynamic loading for buffered scrobbler plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: add GetAdmins method to retrieve admin users from the plugin Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Portuguese translations for user and library permissions Signed-off-by: Deluan <deluan@navidrome.org> * reorder migrations Signed-off-by: Deluan <deluan@navidrome.org> * fix: remove unnecessary bulkActionButtons prop from PluginList component * feat: add manual plugin rescan functionality and corresponding UI action Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement user/library and plugin management integration with cleanup on deletion Signed-off-by: Deluan <deluan@navidrome.org> * feat: replace core mock services with test-specific implementations to avoid import cycles * feat: add ID fields to Artist and Song structs and enhance track loading logic by prioritizing ID matches Signed-off-by: Deluan <deluan@navidrome.org> * feat: update plugin permissions from allowedHosts to requiredHosts for better clarity and consistency * feat: refactor plugin host permissions to use RequiredHosts directly for improved clarity * fix: don't record metrics for plugin calls that aren't implemented at all Signed-off-by: Deluan <deluan@navidrome.org> * fix: enhance connection management with improved error handling and cleanup logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: introduce ArtistRef struct for better artist representation and update track metadata handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: update user configuration handling to use user key prefix for improved clarity Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance ConfigCard input fields with multiline support and vertical resizing Signed-off-by: Deluan <deluan@navidrome.org> * fix: rust plugin compilation error Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement IsOptionPattern method for better return type handling in Rust PDK generation Signed-off-by: Deluan <deluan@navidrome.org> --------- Signed-off-by: Deluan <deluan@navidrome.org>
2026-01-14 19:22:48 -05:00
// SimilarArtistsResponse is the response for GetSimilarArtists.
type SimilarArtistsResponse struct {
// Artists is the list of similar artists.
feat(plugins): share plugin DTOs via a types package (#5655) * refactor(plugins): remove Python PDK generation from ndpgen * feat(plugins): parse Go type aliases distinctly in ndpgen * feat(plugins): resolve shared-type aliases against a registry in ndpgen * fix(plugins): resolve host-service shared aliases package-wide Mirror the capability approach in ParseDirectoryWithShared: do a first pass over all package files to build a package-wide alias map, then pass it into parseServiceFile so that a shared-type alias declared in a sibling file is visible when resolving types in the service interface file. Add a focused test that writes the alias in one file and the hostservice in another, confirming RED before the fix and GREEN after. Also strengthens the existing Task 3 test with an ArtistRef.Target assertion. * feat(plugins): add ndpgen -shared-types mode for the Go types package * feat(plugins): generate the nd-pdk-types Rust crate from -shared-types * feat(plugins): inject types import and emit deprecated aliases in Go output * feat(plugins): emit deprecated Rust aliases to the shared types crate * feat(plugins): inline shared-type shapes into XTP schemas * feat(plugins): add nd-pdk-types crate and wire dependents * feat(plugins): move shared capability types to plugins/types with deprecated aliases * fix(plugins): point Rust deprecated-alias note at the replacement type * fix(plugins): include shared aliases in KnownStructs so Rust fields keep their type Capability.KnownStructs() and Service.KnownStructs() previously only registered names from .Structs. After the shared-types migration, types like ArtistRef/TrackInfo/SongRef live in .SharedAliases instead, so ToRustTypeWithStructs could not find them and fell back to serde_json::Value for every struct field referencing a shared type. Add the shared-alias names to the knownStructs map in both methods. Regenerate the Rust capability files; track/song/artist fields now render as their named types (TrackInfo, SongRef, ArtistRef, etc.). Add a regression test that verifies a struct field whose type is only in SharedAliases renders as the named type and not serde_json::Value. * docs(plugins): remove stale Python references from ndpgen and plugins READMEs ndpgen no longer has a -python flag; remove it from the usage synopsis, flags table, and defaults note in ndpgen/README.md. Delete the "Python Client Library" section that described its output. plugins/README.md referenced plugins/pdk/python/host/ (deleted) as the source for Python host-service stubs. Remove that paragraph; Python plugins still work via the XTP-schema / extism-py path (see examples/*-py). * refactor(plugins): dedupe ndpgen helpers and tidy shared-type codegen * docs(plugins): restore Python as a supported XTP schema target The ndpgen-generated Python PDK was removed, but the XTP YAML schemas are language-neutral and the XTP CLI still generates Python bindings from them (as the extism-py examples demonstrate). Only the ndpgen Python output was dropped, not Python support itself. * test(plugins): use the shared types package in test plugins The test fixtures referenced the now-deprecated capability aliases (sonicsimilarity.SongRef, metadata.ArtistRef/SongRef). Point them at the canonical types package so our own fixtures don't depend on symbols slated for removal. * refactor(plugins): use the shared types package in host adapters Replace deprecated capabilities.TrackInfo, capabilities.ArtistRef, and capabilities.SongRef aliases with the canonical types.TrackInfo, types.ArtistRef, and types.SongRef from plugins/types. * fix(plugins): reference shared types by canonical path in generated Rust Previously the generator emitted `pub field: SongRef` (the local deprecated alias) for struct fields whose type came from SharedAliases. Refactored ToRustTypeWithStructs into a private toRustType that accepts a shared map, and added ToRustTypeWithShared which resolves shared-alias names to their canonical nd_pdk_types::X path before falling through to the knownStructs check. Both rustCapabilityFuncMap and rustFuncMap now build the shared map from SharedAliases and use it for fieldRustType, so the generated capability files reference nd_pdk_types::SongRef / nd_pdk_types::TrackInfo directly. The deprecated pub type aliases remain in place as the external back-compat surface. Deprecation warning count from cargo build drops to 0. * fix(examples): implement missing Scrobbler.playback_report in Rust examples The Scrobbler trait gained a playback_report method but the two Rust example plugins (webhook-rs and discord-rich-presence-rs) were not updated, causing E0046 compile errors. Added the missing fn playback_report to both: webhook-rs logs and returns Ok(()) mirroring its now_playing handler; discord-rich-presence-rs is a no-op since Discord presence does not need playback reports. make all-rust now exits 0. * refactor(plugins): point Rust deprecation notes at the nd_pdk::types umbrella path Plugin authors depend on the nd-pdk umbrella crate, which re-exports nd_pdk_types as 'types', so the migration target they should type is nd_pdk::types::X. The alias target stays nd_pdk_types::X (the real path inside nd-pdk-capabilities). * fix(plugins): error when a shared-type alias can't be resolved against the registry * refactor(plugins): parse each Go source file once in ndpgen * fix(plugins): correct ndpgen review nits (flag name, unused dep, docs) * refactor(plugins): drop the now-unused path param from parseServiceFile * refactor(plugins): use shared types directly, rename TrackInfo to Track Capability interfaces now reference the shared `types` package by qualified name (types.Track, types.SongRef, types.ArtistRef) instead of the package-local deprecated aliases, and the shared TrackInfo type is renamed to Track to match its role as the plugin-facing projection of a library media file. The deprecated bare aliases (scrobbler.TrackInfo, metadata.ArtistRef, sonicsimilarity.SongRef, etc.) are kept as re-exports so existing plugins keep compiling, with a deprecation warning steering them to the canonical types. To support this, ndpgen now resolves qualified types.X references: it collects them during type discovery, maps each used canonical type back to its declared deprecated alias for re-export, emits nd_pdk_types::X paths in Rust, and names the XTP schema components by their canonical type. Regenerated the Go and Rust PDK and the XTP schemas, and added generator tests covering the qualified-ref path. Also adds clarifying doc comments to the shared types. * refactor(plugins): extract shared types selector into a named const Replace the "types." string literal that detects and strips the shared types package selector with a single sharedTypesPrefix constant across the ndpgen generator (parser, types, generator, xtp_schema), giving the package one source of truth for the selector. Also restore the single reused scratch map (cleared each iteration) in the resolveSharedAliases BFS instead of allocating a fresh map per shared-struct field, matching the prior implementation. Pure cleanup from a /simplify pass: regeneration produces byte-for-byte identical Go, Rust, and XTP output. * refactor(plugins): keep TrackInfo in the capability package for now Move the track type back out of the shared plugins/types package: it is again defined inline as TrackInfo in plugins/capabilities/scrobbler.go and referenced directly by the scrobbler and lyrics capabilities, reverting the rename to types.Track. The host helper is renamed back to mediaFileToTrackInfo and now returns capabilities.TrackInfo. SongRef and ArtistRef stay in the shared types package; TrackInfo keeps using types.ArtistRef for its artist lists. This type is expected to be reshaped in upcoming work, so leaving it in the capability package avoids churning the shared types twice. Regenerated the Go and Rust PDK and the XTP schemas accordingly. * fix(plugins): emit the Go types import for direct shared-type refs ndpgen's Capability/Service.ImportsSharedTypes only reported a shared-types dependency when a deprecated re-export alias (type X = types.X) was declared. A struct field referencing the canonical form directly (e.g. types.SongRef) with no such alias produced an empty SharedAliases slice, so the Go templates skipped the import while still emitting fields/signatures using types.SongRef — leaving generated PDK code for new shared DTOs uncompilable unless an otherwise unnecessary alias was added. ImportsSharedTypes now also returns true when any struct field references the types. package by qualified name, via a new structsReferenceSharedTypes helper that reuses collectReferencedTypes (so []types.X and map[...]types.X are covered too). * fix(plugins): preserve base64 encoding for shared byte fields in Rust The Rust shared-types crate template rendered a []byte field as a plain Vec<u8> without the base64_bytes serde override used by the capability/client templates. Go's encoding/json serializes []byte as a base64 string, so a Rust plugin using nd_pdk::types would have serialized an array of numbers instead of the wire format the Go/server side expects. GenerateSharedTypesRust now registers the base64_bytes partial and passes a HasByteFields flag (new anyFieldIsByteSlice helper); types.rs.tmpl emits the base64_bytes module and a #[serde(with = "base64_bytes")] attribute on []byte fields, mirroring the capability template. * fix(plugins): include directly-referenced shared types in XTP schemas buildSchemas registered shared types into the schema components only by iterating cap.SharedAliases, which records deprecated re-export aliases. A capability that referenced a shared DTO solely as types.Foo (no declared alias) therefore never got Foo into the component set, so the self-contained XTP schema rendered the field as a generic object (or emitted a dangling $ref), breaking the direct shared-type use case enabled by -shared. resolveSharedAliases now also returns the resolved shapes of every used shared type (alias or not); these are carried on the new Capability.SharedTypes field and registered by buildSchemas alongside SharedAliases. Validated end-to-end with the xtp CLI: a direct types.Foo reference now produces a proper component plus a $ref, so xtp generates a typed struct instead of an untyped serde_json::Map. * fix(plugins): resolve renamed shared aliases to canonical schema refs When a deprecated alias renames its canonical type (e.g. type TrackInfo = types.Track) and a capability field is typed with the alias name (TrackInfo), buildProperty emitted a $ref to #/components/schemas/TrackInfo. Components are keyed by the canonical name (Track), so no TrackInfo component was emitted, leaving a dangling reference that crashes the xtp code generator. buildSchemas now builds an alias->canonical map; buildProperty (and the slice item path) resolves $ref targets through it, and a used alias name marks its canonical component used so it is emitted. Validated with the xtp CLI: the renamed-alias schema previously crashed xtp and now generates cleanly. * fix(plugins): detect shared types used directly in method signatures ImportsSharedTypes only inspected struct fields, so a capability method using a shared type directly in its signature (e.g. types.SongRef as input/output rather than inside a local struct) was not detected. The generated Go templates still rendered the provider/export signatures with types.SongRef, so the capability package omitted the types import and failed to compile; the same gap applied to service params/returns. ImportsSharedTypes now also scans capability method input/output types and service method params/returns, via a typeReferencesSharedTypes helper that reuses collectReferencedTypes (covering pointer/slice/map wrappers). * fix(plugins): add base64 dependency to the shared Rust types crate When a shared DTO has a []byte field, ndpgen emits the base64_bytes serde helper and use base64::... imports into nd-pdk-types/src/lib.rs, but the crate manifest declared only serde. In that case make gen produced a crate that failed to compile with 'unresolved module base64'. Add base64 = "0.22" (matching nd-pdk-capabilities) so the generated shared types crate compiles whenever a []byte field is present. Verified by generating a shared crate with a []byte field and confirming cargo check fails before and passes after. * fix(plugins): translate shared method types in generated Rust A capability method using a shared DTO directly as input/output (e.g. types.SongRef) was passed through rustOutputType unchanged, so the Rust template emitted invalid trait and extism_pdk::Json<$crate::pkg::types.SongRef> signatures that do not compile. Method input/output types now resolve through the shared registry: trait signatures use rustTraitType (shared -> nd_pdk_types::X, locals stay bare) and the export macros use rustMethodType (fully qualified: shared -> nd_pdk_types::X, primitives -> Rust, locals -> $crate::<pkg>::X). Verified end-to-end by compiling a generated capability that takes types.SongRef directly against the real nd-pdk-types crate. * fix(plugins): canonicalize XTP export refs for renamed shared aliases buildSchemas canonicalized alias-to-canonical references for struct-field $ref targets, but buildExport built export input/output $refs straight from fieldBaseType. A capability whose export used a renamed deprecated alias directly (e.g. type TrackInfo = types.Track with NowPlaying(TrackInfo)) emitted $ref: #/components/schemas/TrackInfo, while the component is emitted under the canonical name Track — a dangling export reference. Lift the alias-to-canonical map into GenerateSchema (buildAliasToCanonical) and apply it to export refs via canonicalRefName, the same resolution already used for field properties. * fix(plugins): route shared macro types through $crate for plugin builds When a capability method used a shared type directly, the generated export macro named the type as nd_pdk_types::SongRef. The macro expands in the downstream plugin crate, which depends on the umbrella nd-pdk crate and not on nd-pdk-types directly, so that path is unresolvable there and the plugin fails to build. rustMethodType (macro-facing) now emits $crate::types::X, and the generated nd-pdk-capabilities lib.rs re-exports nd_pdk_types as types so $crate resolves it. Trait signatures keep nd_pdk_types::X since they live in nd-pdk-capabilities, which has the direct dependency. Verified end-to-end: a plugin crate depending only on the umbrella that uses a capability with a direct types.X method now compiles via the macro. * fix(plugins): add nd-pdk-types dependency to the Rust host crate When a host service uses a shared type, ndpgen emits nd_pdk_types::X into the generated nd-pdk-host client wrappers, but the host crate's manifest did not depend on nd-pdk-types, so the crate failed to compile with 'unresolved module nd_pdk_types'. Host client wrappers are plain functions resolved in the host crate's own context (not macros expanded downstream), so a direct dependency is the right fix. Add nd-pdk-types = { path = "../nd-pdk-types" } to nd-pdk-host, mirroring nd-pdk-capabilities. Found while auditing all Rust paths against the realistic crate topology after the capability-side $crate fix; verified by generating a host service with a shared-type return and confirming cargo check fails before and passes after. * fix(plugins): resolve shared aliases in Rust host signatures The Rust host client rendered method params and returns through RustTypeWithStructs, which only consults KnownStructs. A host service using a shared alias in a signature (e.g. type Track = types.Track plus MatchSongs(...) ([]Track, error)) therefore emitted a bare Vec<Track>, but the client template emits no Track alias or import, so the generated nd-pdk-host crate did not compile. Only struct fields went through the shared map. rustType/rustParamType now use the shared map too (RustTypeWithShared / RustParamTypeWithShared), so an aliased param/return resolves to its canonical nd_pdk_types::X path, matching field handling. Verified by generating a host service returning a shared alias and confirming cargo check fails before and passes after.
2026-06-29 21:20:33 -04:00
Artists []types.ArtistRef `json:"artists"`
feat(plugins): New Plugin System with multi-language PDK support (#4833) * chore(plugins): remove the old plugins system implementation Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement new plugin system with using Extism Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add capability detection for plugins based on exported functions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add auto-reload functionality for plugins with file watcher support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add auto-reload functionality for plugins with file watcher support Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): standardize variable names and remove superfluous wrapper functions Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): improve error handling and logging in plugin manager Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): implement plugin function call helper and refactor MetadataAgent methods Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): race condition in plugin manager * tests(plugins): change BeforeEach to BeforeAll in MetadataAgent tests Signed-off-by: Deluan <deluan@navidrome.org> * tests(plugins): optimize tests Signed-off-by: Deluan <deluan@navidrome.org> * tests(plugins): more optimizations Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): ignore goroutine leaks from notify library in tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Wikimedia plugin for Navidrome to fetch artist metadata Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): enhance plugin logging and set User-Agent header Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement scrobbler plugin with authorization and scrobbling capabilities Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): integrate logs Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): clean up manifest struct and improve plugin loading logic Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add metadata agent and scrobbler schemas for bootstrapping plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat(hostgen): add hostgen tool for generating Extism host function wrappers - Implemented hostgen tool to generate wrappers from annotated Go interfaces. - Added command-line flags for input/output directories and package name. - Introduced parsing and code generation logic for host services. - Created test data for various service interfaces and expected generated code. - Added documentation for host services and annotations for code generation. - Implemented SubsonicAPI service with corresponding generated code. * feat(subsonicapi): update Call method to return JSON string response Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement SubsonicAPI host function integration with permissions Signed-off-by: Deluan <deluan@navidrome.org> * fix(generator): error-only methods in response handling Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate client wrappers for host functions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(generator): remove error handling for response.Error in client templates Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): add Scheduler service interface with host function wrappers for scheduling tasks * feat(plugins): add WASI build constraints to client wrapper templates, to avoid lint errors Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): implement Scheduler service with one-time and recurring scheduling capabilities Signed-off-by: Deluan <deluan@navidrome.org> * refactor(manifest): remove unused ConfigPermission from permissions schema Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): add scheduler callback schema and implementation for plugins Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): streamline scheduling logic and remove unused callback tracking Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): add Close method for resource cleanup on plugin unload Signed-off-by: Deluan <deluan@navidrome.org> * docs(scheduler): clarify SchedulerCallback requirement for scheduling functions Signed-off-by: Deluan <deluan@navidrome.org> * fix: update wasm build rule to include all Go files in the directory Signed-off-by: Deluan <deluan@navidrome.org> * feat: rewrite the wikimedia plugin using the XTP CLI Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): replace uuid with id.NewRandom for schedule ID generation Signed-off-by: Deluan <deluan@navidrome.org> * refactor: capabilities registration Signed-off-by: Deluan <deluan@navidrome.org> * test: add scheduler service isolation test for plugin instances Signed-off-by: Deluan <deluan@navidrome.org> * refactor: update plugin manager initialization and encapsulate logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: add WebSocket service definitions for plugin communication Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement WebSocket service for plugin integration and connection management Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Crypto Ticker example plugin for real-time cryptocurrency price updates via Coinbase WebSocket API Also add the lifecycle capability Signed-off-by: Deluan <deluan@navidrome.org> * fix: use context.Background() in invokeCallback for scheduled tasks Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename plugin.create() to plugin.instance() Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename pluginInstance to plugin for consistency across the codebase Signed-off-by: Deluan <deluan@navidrome.org> * refactor: simplify schedule cloning in Close method and enhance plugin cleanup error handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement Artwork service for generating artwork URLs in Navidrome plugins - WIP Signed-off-by: Deluan <deluan@navidrome.org> * refactor: moved public URL builders to avoid import cycles Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Cache service for in-memory TTL-based caching in plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Discord Rich Presence example plugin for Navidrome integration Signed-off-by: Deluan <deluan@navidrome.org> * refactor: host function wrappers to use structured request and response types - Updated the host function signatures in `nd_host_artwork.go`, `nd_host_scheduler.go`, `nd_host_subsonicapi.go`, and `nd_host_websocket.go` to accept a single parameter for JSON requests. - Introduced structured request and response types for various cache operations in `nd_host_cache.go`. - Modified cache functions to marshal requests to JSON and unmarshal responses, improving error handling and code clarity. - Removed redundant memory allocation for string parameters in favor of JSON marshaling. - Enhanced error handling in WebSocket and cache operations to return structured error responses. * refactor: error handling in various plugins to convert response.Error to Go errors - Updated error handling in `nd_host_scheduler.go`, `nd_host_websocket.go`, `nd_host_artwork.go`, `nd_host_cache.go`, and `nd_host_subsonicapi.go` to convert string errors from responses into Go errors. - Removed redundant error checks in test data plugins for cleaner code. - Ensured consistent error handling across all plugins to improve reliability and maintainability. * refactor: rename fake plugins to test plugins for clarity in integration tests Signed-off-by: Deluan <deluan@navidrome.org> * feat: add help target to Makefile for plugin usage instructions Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Cover Art Archive plugin as an example of Python plugin Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Makefile and README to clarify Go plugin usage Signed-off-by: Deluan <deluan@navidrome.org> * feat: include plugin capabilities in loading log message Signed-off-by: Deluan <deluan@navidrome.org> * feat: add trace logging for plugin availability and error handling in agents Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Now Playing Logger plugin to showcase calling host functions from Python plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: generate Python client wrappers for various host services Signed-off-by: Deluan <deluan@navidrome.org> * feat: add generated host function wrappers for Scheduler and SubsonicAPI services Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Python plugin documentation and usage instructions for host function wrappers Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Webhook Scrobbler plugin in Rust to send HTTP notifications on scrobble events Signed-off-by: Deluan <deluan@navidrome.org> * feat: enable parallel loading of plugins during startup Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to include WebSocket callback schema in plugin documentation Signed-off-by: Deluan <deluan@navidrome.org> * feat: extend plugin watcher with improved logging and debounce duration adjustment Signed-off-by: Deluan <deluan@navidrome.org> * add trace message for plugin recompiles Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement plugin cache purging functionality Signed-off-by: Deluan <deluan@navidrome.org> * test: move purgeCacheBySize unit tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add plugin repository and database support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add plugin management routes and middleware Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): implement plugin synchronization with database for add, update, and remove actions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add PluginList and PluginShow components with plugin management functionality Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): optimize plugin change detection Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins UI): improve PluginList structure Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): enhance PluginShow with author, website, and permissions display Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): refactor to use MUI and RA components Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add error handling for plugin enable/disable actions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): inject PluginManager into native API Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update GetManager to accept DataStore parameter Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add subsonicRouter to Manager and refactor host service registration Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): enhance debug logging for plugin actions and recompile logic Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): break manager.go into smaller, focused files Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): streamline error handling and improve plugin retrieval logic Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update newWebSocketService to use WebSocketPermission for allowed hosts Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): introduce ToggleEnabledSwitch for managing plugin enable/disable state Signed-off-by: Deluan <deluan@navidrome.org> * docs: update READMEs Signed-off-by: Deluan <deluan@navidrome.org> * feat(library): add Library service for metadata access and filesystem integration Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Library Inspector plugin for periodic library inspection and file size logging Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to reflect JSON configuration format for plugins Signed-off-by: Deluan <deluan@navidrome.org> * fix(build): update target to wasm32-wasip1 for improved WASI support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement configuration management UI with key-value pairs support Signed-off-by: Deluan <deluan@navidrome.org> * feat(ui): adjust grid layout in InfoRow component for improved responsiveness Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): rename ErrorIndicator to EnabledOrErrorField and enhance error handling logic Signed-off-by: Deluan <deluan@navidrome.org> * feat(i18n): add Portuguese translations for plugin management and notifications Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add support for .ndp plugin packages and update build process Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README for .ndp plugin packaging and installation instructions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement KVStore service for persistent key-value storage Signed-off-by: Deluan <deluan@navidrome.org> * docs: enhance README with Extism plugin development resources and recommendations Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): integrate event broker into plugin manager Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): update config handling in PluginShow to track last record state Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Rust host function library and example implementation of Discord Rich Presence plugin in Rust Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate Rust lib.rs file to expose host function wrappers Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update JSON field names to camelCase for consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor: reduce cyclomatic complexity by refactoring main function Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): enhance Rust code generation with typed struct support and improved type handling Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Go client library with host function wrappers and documentation Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate Go client stubs for non-WASM platforms Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): update client template file names for consistency Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add initial implementation of the Navidrome Plugin Development Kit code generator - Pahse 1 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 2 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 2 (2) Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 3 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 4 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 5 Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): consistent naming/types across PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): streamline plugin function signatures and error handling Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update scrobbler interface to return errors directly instead of response structs Signed-off-by: Deluan <deluan@navidrome.org> * test: make all test plugins use the PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): reorganize and sort type definitions for consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update error handling for methods to return errors directly Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update function signatures to return values directly instead of response structs Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update request/response types to use private naming conventions Signed-off-by: Deluan <deluan@navidrome.org> * build: mark .wasm files as intermediate for cleanup after building .ndp Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): consolidate PDK module path and update Go version to 1.25 Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement Rust PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): reorganize Rust output structure to follow standard conventions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update Discord Rich Presence and Library Inspector plugins to use nd-pdk for service calls and implement lifecycle management Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update macro names for websocket and metadata registration to improve clarity and consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): rename scheduler callback methods for consistency and clarity Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update export wrappers to use `//go:wasmexport` for WebAssembly compatibility Signed-off-by: Deluan <deluan@navidrome.org> * docs: update plugin registration docs Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): generate host wrappers Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): conditionally run goleak checks based on CI environment Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to reflect changes in plugin import paths Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update plugin instance creation to accept context for cancellation support Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update return types in metadata interfaces to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): enhance type handling for Rust and XTP output in capability generation Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update IsAuthorized method to return boolean instead of response object Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): add unit tests for rustOutputType and isPrimitiveRustType functions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement XTP JSONSchema validation for generated schemas Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update response types in testMetadataAgent methods to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * docs: update Go and Rust plugin developer sections for clarity Signed-off-by: Deluan <deluan@navidrome.org> * docs: correct example link for library inspector in README Signed-off-by: Deluan <deluan@navidrome.org> * docs: clarify artwork URL generation capabilities in service descriptions Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to include Rust PDK crate information for plugin developers Signed-off-by: Deluan <deluan@navidrome.org> * fix: handle URL parsing errors and use atomic upsert in plugin repository Added proper error handling for url.Parse calls in PublicURL and AbsoluteURL functions. When parsing fails, PublicURL now falls back to AbsoluteURL, and AbsoluteURL logs the error and returns an empty string, preventing malformed URLs from being generated. Replaced the non-atomic UPDATE-then-INSERT pattern in plugin repository Put method with a single atomic INSERT ... ON CONFLICT statement. This eliminates potential race conditions and improves consistency with the upsert pattern already used in host_kvstore.go. * feat: implement mock service instances for non-WASM builds using testify/mock Signed-off-by: Deluan <deluan@navidrome.org> * refactor: Discord RPC struct to encapsulate WebSocket logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: add support for experimental WebAssembly threads Signed-off-by: Deluan <deluan@navidrome.org> * feat: add PDK abstraction layer with mock support for non-WASM builds Signed-off-by: Deluan <deluan@navidrome.org> * feat: add unit tests for Discord plugin and RPC functionality Signed-off-by: Deluan <deluan@navidrome.org> * fix: update return types in minimalPlugin and wikimediaPlugin methods to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * fix: context cancellation and implement WebSocket callback timeout for improved error handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: conditionally include error handling in generated client code templates Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement ConfigService for plugin configuration management Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance plugin manager to support metrics recording Signed-off-by: Deluan <deluan@navidrome.org> * refactor: make MockPDK private Signed-off-by: Deluan <deluan@navidrome.org> * refactor: update interface types to use 'any' in plugin repository methods Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename List method to Keys for clarity in configuration management Signed-off-by: Deluan <deluan@navidrome.org> * test: add ndpgen plugin tests in the pipeline and update Makefile Signed-off-by: Deluan <deluan@navidrome.org> * feat: add users permission management to plugin system Signed-off-by: Deluan <deluan@navidrome.org> * refactor: streamline users integration tests and enhance plugin user management Signed-off-by: Deluan <deluan@navidrome.org> * refactor: remove UserID from scrobbler request structure Signed-off-by: Deluan <deluan@navidrome.org> * test: add integration tests for UsersService enable gate behavior Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement user permissions for SubsonicAPI and scrobbler plugins Signed-off-by: Deluan <deluan@navidrome.org> * fix: show proper error in the UI when enabling a plugin fails Signed-off-by: Deluan <deluan@navidrome.org> * feat: add library permission management to plugin system Signed-off-by: Deluan <deluan@navidrome.org> * feat: add user permission for processing scrobbles in Discord Rich Presence plugin Signed-off-by: Deluan <deluan@navidrome.org> * fix: implement dynamic loading for buffered scrobbler plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: add GetAdmins method to retrieve admin users from the plugin Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Portuguese translations for user and library permissions Signed-off-by: Deluan <deluan@navidrome.org> * reorder migrations Signed-off-by: Deluan <deluan@navidrome.org> * fix: remove unnecessary bulkActionButtons prop from PluginList component * feat: add manual plugin rescan functionality and corresponding UI action Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement user/library and plugin management integration with cleanup on deletion Signed-off-by: Deluan <deluan@navidrome.org> * feat: replace core mock services with test-specific implementations to avoid import cycles * feat: add ID fields to Artist and Song structs and enhance track loading logic by prioritizing ID matches Signed-off-by: Deluan <deluan@navidrome.org> * feat: update plugin permissions from allowedHosts to requiredHosts for better clarity and consistency * feat: refactor plugin host permissions to use RequiredHosts directly for improved clarity * fix: don't record metrics for plugin calls that aren't implemented at all Signed-off-by: Deluan <deluan@navidrome.org> * fix: enhance connection management with improved error handling and cleanup logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: introduce ArtistRef struct for better artist representation and update track metadata handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: update user configuration handling to use user key prefix for improved clarity Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance ConfigCard input fields with multiline support and vertical resizing Signed-off-by: Deluan <deluan@navidrome.org> * fix: rust plugin compilation error Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement IsOptionPattern method for better return type handling in Rust PDK generation Signed-off-by: Deluan <deluan@navidrome.org> --------- Signed-off-by: Deluan <deluan@navidrome.org>
2026-01-14 19:22:48 -05:00
}
// ImageInfo represents an image with URL and size.
type ImageInfo struct {
// URL is the URL of the image.
URL string `json:"url"`
// Size is the size of the image in pixels (width or height).
Size int32 `json:"size"`
}
// ArtistImagesResponse is the response for GetArtistImages.
type ArtistImagesResponse struct {
// Images is the list of artist images.
Images []ImageInfo `json:"images"`
}
// TopSongsRequest is the request for GetArtistTopSongs.
type TopSongsRequest struct {
// ID is the internal Navidrome artist ID.
ID string `json:"id"`
// Name is the artist name.
Name string `json:"name"`
// MBID is the MusicBrainz ID for the artist (if known).
MBID string `json:"mbid,omitempty"`
// Count is the maximum number of top songs to return.
Count int32 `json:"count"`
}
feat(plugins): share plugin DTOs via a types package (#5655) * refactor(plugins): remove Python PDK generation from ndpgen * feat(plugins): parse Go type aliases distinctly in ndpgen * feat(plugins): resolve shared-type aliases against a registry in ndpgen * fix(plugins): resolve host-service shared aliases package-wide Mirror the capability approach in ParseDirectoryWithShared: do a first pass over all package files to build a package-wide alias map, then pass it into parseServiceFile so that a shared-type alias declared in a sibling file is visible when resolving types in the service interface file. Add a focused test that writes the alias in one file and the hostservice in another, confirming RED before the fix and GREEN after. Also strengthens the existing Task 3 test with an ArtistRef.Target assertion. * feat(plugins): add ndpgen -shared-types mode for the Go types package * feat(plugins): generate the nd-pdk-types Rust crate from -shared-types * feat(plugins): inject types import and emit deprecated aliases in Go output * feat(plugins): emit deprecated Rust aliases to the shared types crate * feat(plugins): inline shared-type shapes into XTP schemas * feat(plugins): add nd-pdk-types crate and wire dependents * feat(plugins): move shared capability types to plugins/types with deprecated aliases * fix(plugins): point Rust deprecated-alias note at the replacement type * fix(plugins): include shared aliases in KnownStructs so Rust fields keep their type Capability.KnownStructs() and Service.KnownStructs() previously only registered names from .Structs. After the shared-types migration, types like ArtistRef/TrackInfo/SongRef live in .SharedAliases instead, so ToRustTypeWithStructs could not find them and fell back to serde_json::Value for every struct field referencing a shared type. Add the shared-alias names to the knownStructs map in both methods. Regenerate the Rust capability files; track/song/artist fields now render as their named types (TrackInfo, SongRef, ArtistRef, etc.). Add a regression test that verifies a struct field whose type is only in SharedAliases renders as the named type and not serde_json::Value. * docs(plugins): remove stale Python references from ndpgen and plugins READMEs ndpgen no longer has a -python flag; remove it from the usage synopsis, flags table, and defaults note in ndpgen/README.md. Delete the "Python Client Library" section that described its output. plugins/README.md referenced plugins/pdk/python/host/ (deleted) as the source for Python host-service stubs. Remove that paragraph; Python plugins still work via the XTP-schema / extism-py path (see examples/*-py). * refactor(plugins): dedupe ndpgen helpers and tidy shared-type codegen * docs(plugins): restore Python as a supported XTP schema target The ndpgen-generated Python PDK was removed, but the XTP YAML schemas are language-neutral and the XTP CLI still generates Python bindings from them (as the extism-py examples demonstrate). Only the ndpgen Python output was dropped, not Python support itself. * test(plugins): use the shared types package in test plugins The test fixtures referenced the now-deprecated capability aliases (sonicsimilarity.SongRef, metadata.ArtistRef/SongRef). Point them at the canonical types package so our own fixtures don't depend on symbols slated for removal. * refactor(plugins): use the shared types package in host adapters Replace deprecated capabilities.TrackInfo, capabilities.ArtistRef, and capabilities.SongRef aliases with the canonical types.TrackInfo, types.ArtistRef, and types.SongRef from plugins/types. * fix(plugins): reference shared types by canonical path in generated Rust Previously the generator emitted `pub field: SongRef` (the local deprecated alias) for struct fields whose type came from SharedAliases. Refactored ToRustTypeWithStructs into a private toRustType that accepts a shared map, and added ToRustTypeWithShared which resolves shared-alias names to their canonical nd_pdk_types::X path before falling through to the knownStructs check. Both rustCapabilityFuncMap and rustFuncMap now build the shared map from SharedAliases and use it for fieldRustType, so the generated capability files reference nd_pdk_types::SongRef / nd_pdk_types::TrackInfo directly. The deprecated pub type aliases remain in place as the external back-compat surface. Deprecation warning count from cargo build drops to 0. * fix(examples): implement missing Scrobbler.playback_report in Rust examples The Scrobbler trait gained a playback_report method but the two Rust example plugins (webhook-rs and discord-rich-presence-rs) were not updated, causing E0046 compile errors. Added the missing fn playback_report to both: webhook-rs logs and returns Ok(()) mirroring its now_playing handler; discord-rich-presence-rs is a no-op since Discord presence does not need playback reports. make all-rust now exits 0. * refactor(plugins): point Rust deprecation notes at the nd_pdk::types umbrella path Plugin authors depend on the nd-pdk umbrella crate, which re-exports nd_pdk_types as 'types', so the migration target they should type is nd_pdk::types::X. The alias target stays nd_pdk_types::X (the real path inside nd-pdk-capabilities). * fix(plugins): error when a shared-type alias can't be resolved against the registry * refactor(plugins): parse each Go source file once in ndpgen * fix(plugins): correct ndpgen review nits (flag name, unused dep, docs) * refactor(plugins): drop the now-unused path param from parseServiceFile * refactor(plugins): use shared types directly, rename TrackInfo to Track Capability interfaces now reference the shared `types` package by qualified name (types.Track, types.SongRef, types.ArtistRef) instead of the package-local deprecated aliases, and the shared TrackInfo type is renamed to Track to match its role as the plugin-facing projection of a library media file. The deprecated bare aliases (scrobbler.TrackInfo, metadata.ArtistRef, sonicsimilarity.SongRef, etc.) are kept as re-exports so existing plugins keep compiling, with a deprecation warning steering them to the canonical types. To support this, ndpgen now resolves qualified types.X references: it collects them during type discovery, maps each used canonical type back to its declared deprecated alias for re-export, emits nd_pdk_types::X paths in Rust, and names the XTP schema components by their canonical type. Regenerated the Go and Rust PDK and the XTP schemas, and added generator tests covering the qualified-ref path. Also adds clarifying doc comments to the shared types. * refactor(plugins): extract shared types selector into a named const Replace the "types." string literal that detects and strips the shared types package selector with a single sharedTypesPrefix constant across the ndpgen generator (parser, types, generator, xtp_schema), giving the package one source of truth for the selector. Also restore the single reused scratch map (cleared each iteration) in the resolveSharedAliases BFS instead of allocating a fresh map per shared-struct field, matching the prior implementation. Pure cleanup from a /simplify pass: regeneration produces byte-for-byte identical Go, Rust, and XTP output. * refactor(plugins): keep TrackInfo in the capability package for now Move the track type back out of the shared plugins/types package: it is again defined inline as TrackInfo in plugins/capabilities/scrobbler.go and referenced directly by the scrobbler and lyrics capabilities, reverting the rename to types.Track. The host helper is renamed back to mediaFileToTrackInfo and now returns capabilities.TrackInfo. SongRef and ArtistRef stay in the shared types package; TrackInfo keeps using types.ArtistRef for its artist lists. This type is expected to be reshaped in upcoming work, so leaving it in the capability package avoids churning the shared types twice. Regenerated the Go and Rust PDK and the XTP schemas accordingly. * fix(plugins): emit the Go types import for direct shared-type refs ndpgen's Capability/Service.ImportsSharedTypes only reported a shared-types dependency when a deprecated re-export alias (type X = types.X) was declared. A struct field referencing the canonical form directly (e.g. types.SongRef) with no such alias produced an empty SharedAliases slice, so the Go templates skipped the import while still emitting fields/signatures using types.SongRef — leaving generated PDK code for new shared DTOs uncompilable unless an otherwise unnecessary alias was added. ImportsSharedTypes now also returns true when any struct field references the types. package by qualified name, via a new structsReferenceSharedTypes helper that reuses collectReferencedTypes (so []types.X and map[...]types.X are covered too). * fix(plugins): preserve base64 encoding for shared byte fields in Rust The Rust shared-types crate template rendered a []byte field as a plain Vec<u8> without the base64_bytes serde override used by the capability/client templates. Go's encoding/json serializes []byte as a base64 string, so a Rust plugin using nd_pdk::types would have serialized an array of numbers instead of the wire format the Go/server side expects. GenerateSharedTypesRust now registers the base64_bytes partial and passes a HasByteFields flag (new anyFieldIsByteSlice helper); types.rs.tmpl emits the base64_bytes module and a #[serde(with = "base64_bytes")] attribute on []byte fields, mirroring the capability template. * fix(plugins): include directly-referenced shared types in XTP schemas buildSchemas registered shared types into the schema components only by iterating cap.SharedAliases, which records deprecated re-export aliases. A capability that referenced a shared DTO solely as types.Foo (no declared alias) therefore never got Foo into the component set, so the self-contained XTP schema rendered the field as a generic object (or emitted a dangling $ref), breaking the direct shared-type use case enabled by -shared. resolveSharedAliases now also returns the resolved shapes of every used shared type (alias or not); these are carried on the new Capability.SharedTypes field and registered by buildSchemas alongside SharedAliases. Validated end-to-end with the xtp CLI: a direct types.Foo reference now produces a proper component plus a $ref, so xtp generates a typed struct instead of an untyped serde_json::Map. * fix(plugins): resolve renamed shared aliases to canonical schema refs When a deprecated alias renames its canonical type (e.g. type TrackInfo = types.Track) and a capability field is typed with the alias name (TrackInfo), buildProperty emitted a $ref to #/components/schemas/TrackInfo. Components are keyed by the canonical name (Track), so no TrackInfo component was emitted, leaving a dangling reference that crashes the xtp code generator. buildSchemas now builds an alias->canonical map; buildProperty (and the slice item path) resolves $ref targets through it, and a used alias name marks its canonical component used so it is emitted. Validated with the xtp CLI: the renamed-alias schema previously crashed xtp and now generates cleanly. * fix(plugins): detect shared types used directly in method signatures ImportsSharedTypes only inspected struct fields, so a capability method using a shared type directly in its signature (e.g. types.SongRef as input/output rather than inside a local struct) was not detected. The generated Go templates still rendered the provider/export signatures with types.SongRef, so the capability package omitted the types import and failed to compile; the same gap applied to service params/returns. ImportsSharedTypes now also scans capability method input/output types and service method params/returns, via a typeReferencesSharedTypes helper that reuses collectReferencedTypes (covering pointer/slice/map wrappers). * fix(plugins): add base64 dependency to the shared Rust types crate When a shared DTO has a []byte field, ndpgen emits the base64_bytes serde helper and use base64::... imports into nd-pdk-types/src/lib.rs, but the crate manifest declared only serde. In that case make gen produced a crate that failed to compile with 'unresolved module base64'. Add base64 = "0.22" (matching nd-pdk-capabilities) so the generated shared types crate compiles whenever a []byte field is present. Verified by generating a shared crate with a []byte field and confirming cargo check fails before and passes after. * fix(plugins): translate shared method types in generated Rust A capability method using a shared DTO directly as input/output (e.g. types.SongRef) was passed through rustOutputType unchanged, so the Rust template emitted invalid trait and extism_pdk::Json<$crate::pkg::types.SongRef> signatures that do not compile. Method input/output types now resolve through the shared registry: trait signatures use rustTraitType (shared -> nd_pdk_types::X, locals stay bare) and the export macros use rustMethodType (fully qualified: shared -> nd_pdk_types::X, primitives -> Rust, locals -> $crate::<pkg>::X). Verified end-to-end by compiling a generated capability that takes types.SongRef directly against the real nd-pdk-types crate. * fix(plugins): canonicalize XTP export refs for renamed shared aliases buildSchemas canonicalized alias-to-canonical references for struct-field $ref targets, but buildExport built export input/output $refs straight from fieldBaseType. A capability whose export used a renamed deprecated alias directly (e.g. type TrackInfo = types.Track with NowPlaying(TrackInfo)) emitted $ref: #/components/schemas/TrackInfo, while the component is emitted under the canonical name Track — a dangling export reference. Lift the alias-to-canonical map into GenerateSchema (buildAliasToCanonical) and apply it to export refs via canonicalRefName, the same resolution already used for field properties. * fix(plugins): route shared macro types through $crate for plugin builds When a capability method used a shared type directly, the generated export macro named the type as nd_pdk_types::SongRef. The macro expands in the downstream plugin crate, which depends on the umbrella nd-pdk crate and not on nd-pdk-types directly, so that path is unresolvable there and the plugin fails to build. rustMethodType (macro-facing) now emits $crate::types::X, and the generated nd-pdk-capabilities lib.rs re-exports nd_pdk_types as types so $crate resolves it. Trait signatures keep nd_pdk_types::X since they live in nd-pdk-capabilities, which has the direct dependency. Verified end-to-end: a plugin crate depending only on the umbrella that uses a capability with a direct types.X method now compiles via the macro. * fix(plugins): add nd-pdk-types dependency to the Rust host crate When a host service uses a shared type, ndpgen emits nd_pdk_types::X into the generated nd-pdk-host client wrappers, but the host crate's manifest did not depend on nd-pdk-types, so the crate failed to compile with 'unresolved module nd_pdk_types'. Host client wrappers are plain functions resolved in the host crate's own context (not macros expanded downstream), so a direct dependency is the right fix. Add nd-pdk-types = { path = "../nd-pdk-types" } to nd-pdk-host, mirroring nd-pdk-capabilities. Found while auditing all Rust paths against the realistic crate topology after the capability-side $crate fix; verified by generating a host service with a shared-type return and confirming cargo check fails before and passes after. * fix(plugins): resolve shared aliases in Rust host signatures The Rust host client rendered method params and returns through RustTypeWithStructs, which only consults KnownStructs. A host service using a shared alias in a signature (e.g. type Track = types.Track plus MatchSongs(...) ([]Track, error)) therefore emitted a bare Vec<Track>, but the client template emits no Track alias or import, so the generated nd-pdk-host crate did not compile. Only struct fields went through the shared map. rustType/rustParamType now use the shared map too (RustTypeWithShared / RustParamTypeWithShared), so an aliased param/return resolves to its canonical nd_pdk_types::X path, matching field handling. Verified by generating a host service returning a shared alias and confirming cargo check fails before and passes after.
2026-06-29 21:20:33 -04:00
// Deprecated: use types.SongRef.
type SongRef = types.SongRef
feat(plugins): New Plugin System with multi-language PDK support (#4833) * chore(plugins): remove the old plugins system implementation Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement new plugin system with using Extism Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add capability detection for plugins based on exported functions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add auto-reload functionality for plugins with file watcher support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add auto-reload functionality for plugins with file watcher support Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): standardize variable names and remove superfluous wrapper functions Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): improve error handling and logging in plugin manager Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): implement plugin function call helper and refactor MetadataAgent methods Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): race condition in plugin manager * tests(plugins): change BeforeEach to BeforeAll in MetadataAgent tests Signed-off-by: Deluan <deluan@navidrome.org> * tests(plugins): optimize tests Signed-off-by: Deluan <deluan@navidrome.org> * tests(plugins): more optimizations Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): ignore goroutine leaks from notify library in tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Wikimedia plugin for Navidrome to fetch artist metadata Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): enhance plugin logging and set User-Agent header Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement scrobbler plugin with authorization and scrobbling capabilities Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): integrate logs Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): clean up manifest struct and improve plugin loading logic Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add metadata agent and scrobbler schemas for bootstrapping plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat(hostgen): add hostgen tool for generating Extism host function wrappers - Implemented hostgen tool to generate wrappers from annotated Go interfaces. - Added command-line flags for input/output directories and package name. - Introduced parsing and code generation logic for host services. - Created test data for various service interfaces and expected generated code. - Added documentation for host services and annotations for code generation. - Implemented SubsonicAPI service with corresponding generated code. * feat(subsonicapi): update Call method to return JSON string response Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement SubsonicAPI host function integration with permissions Signed-off-by: Deluan <deluan@navidrome.org> * fix(generator): error-only methods in response handling Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate client wrappers for host functions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(generator): remove error handling for response.Error in client templates Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): add Scheduler service interface with host function wrappers for scheduling tasks * feat(plugins): add WASI build constraints to client wrapper templates, to avoid lint errors Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): implement Scheduler service with one-time and recurring scheduling capabilities Signed-off-by: Deluan <deluan@navidrome.org> * refactor(manifest): remove unused ConfigPermission from permissions schema Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): add scheduler callback schema and implementation for plugins Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): streamline scheduling logic and remove unused callback tracking Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): add Close method for resource cleanup on plugin unload Signed-off-by: Deluan <deluan@navidrome.org> * docs(scheduler): clarify SchedulerCallback requirement for scheduling functions Signed-off-by: Deluan <deluan@navidrome.org> * fix: update wasm build rule to include all Go files in the directory Signed-off-by: Deluan <deluan@navidrome.org> * feat: rewrite the wikimedia plugin using the XTP CLI Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): replace uuid with id.NewRandom for schedule ID generation Signed-off-by: Deluan <deluan@navidrome.org> * refactor: capabilities registration Signed-off-by: Deluan <deluan@navidrome.org> * test: add scheduler service isolation test for plugin instances Signed-off-by: Deluan <deluan@navidrome.org> * refactor: update plugin manager initialization and encapsulate logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: add WebSocket service definitions for plugin communication Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement WebSocket service for plugin integration and connection management Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Crypto Ticker example plugin for real-time cryptocurrency price updates via Coinbase WebSocket API Also add the lifecycle capability Signed-off-by: Deluan <deluan@navidrome.org> * fix: use context.Background() in invokeCallback for scheduled tasks Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename plugin.create() to plugin.instance() Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename pluginInstance to plugin for consistency across the codebase Signed-off-by: Deluan <deluan@navidrome.org> * refactor: simplify schedule cloning in Close method and enhance plugin cleanup error handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement Artwork service for generating artwork URLs in Navidrome plugins - WIP Signed-off-by: Deluan <deluan@navidrome.org> * refactor: moved public URL builders to avoid import cycles Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Cache service for in-memory TTL-based caching in plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Discord Rich Presence example plugin for Navidrome integration Signed-off-by: Deluan <deluan@navidrome.org> * refactor: host function wrappers to use structured request and response types - Updated the host function signatures in `nd_host_artwork.go`, `nd_host_scheduler.go`, `nd_host_subsonicapi.go`, and `nd_host_websocket.go` to accept a single parameter for JSON requests. - Introduced structured request and response types for various cache operations in `nd_host_cache.go`. - Modified cache functions to marshal requests to JSON and unmarshal responses, improving error handling and code clarity. - Removed redundant memory allocation for string parameters in favor of JSON marshaling. - Enhanced error handling in WebSocket and cache operations to return structured error responses. * refactor: error handling in various plugins to convert response.Error to Go errors - Updated error handling in `nd_host_scheduler.go`, `nd_host_websocket.go`, `nd_host_artwork.go`, `nd_host_cache.go`, and `nd_host_subsonicapi.go` to convert string errors from responses into Go errors. - Removed redundant error checks in test data plugins for cleaner code. - Ensured consistent error handling across all plugins to improve reliability and maintainability. * refactor: rename fake plugins to test plugins for clarity in integration tests Signed-off-by: Deluan <deluan@navidrome.org> * feat: add help target to Makefile for plugin usage instructions Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Cover Art Archive plugin as an example of Python plugin Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Makefile and README to clarify Go plugin usage Signed-off-by: Deluan <deluan@navidrome.org> * feat: include plugin capabilities in loading log message Signed-off-by: Deluan <deluan@navidrome.org> * feat: add trace logging for plugin availability and error handling in agents Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Now Playing Logger plugin to showcase calling host functions from Python plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: generate Python client wrappers for various host services Signed-off-by: Deluan <deluan@navidrome.org> * feat: add generated host function wrappers for Scheduler and SubsonicAPI services Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Python plugin documentation and usage instructions for host function wrappers Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Webhook Scrobbler plugin in Rust to send HTTP notifications on scrobble events Signed-off-by: Deluan <deluan@navidrome.org> * feat: enable parallel loading of plugins during startup Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to include WebSocket callback schema in plugin documentation Signed-off-by: Deluan <deluan@navidrome.org> * feat: extend plugin watcher with improved logging and debounce duration adjustment Signed-off-by: Deluan <deluan@navidrome.org> * add trace message for plugin recompiles Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement plugin cache purging functionality Signed-off-by: Deluan <deluan@navidrome.org> * test: move purgeCacheBySize unit tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add plugin repository and database support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add plugin management routes and middleware Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): implement plugin synchronization with database for add, update, and remove actions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add PluginList and PluginShow components with plugin management functionality Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): optimize plugin change detection Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins UI): improve PluginList structure Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): enhance PluginShow with author, website, and permissions display Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): refactor to use MUI and RA components Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add error handling for plugin enable/disable actions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): inject PluginManager into native API Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update GetManager to accept DataStore parameter Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add subsonicRouter to Manager and refactor host service registration Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): enhance debug logging for plugin actions and recompile logic Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): break manager.go into smaller, focused files Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): streamline error handling and improve plugin retrieval logic Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update newWebSocketService to use WebSocketPermission for allowed hosts Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): introduce ToggleEnabledSwitch for managing plugin enable/disable state Signed-off-by: Deluan <deluan@navidrome.org> * docs: update READMEs Signed-off-by: Deluan <deluan@navidrome.org> * feat(library): add Library service for metadata access and filesystem integration Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Library Inspector plugin for periodic library inspection and file size logging Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to reflect JSON configuration format for plugins Signed-off-by: Deluan <deluan@navidrome.org> * fix(build): update target to wasm32-wasip1 for improved WASI support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement configuration management UI with key-value pairs support Signed-off-by: Deluan <deluan@navidrome.org> * feat(ui): adjust grid layout in InfoRow component for improved responsiveness Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): rename ErrorIndicator to EnabledOrErrorField and enhance error handling logic Signed-off-by: Deluan <deluan@navidrome.org> * feat(i18n): add Portuguese translations for plugin management and notifications Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add support for .ndp plugin packages and update build process Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README for .ndp plugin packaging and installation instructions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement KVStore service for persistent key-value storage Signed-off-by: Deluan <deluan@navidrome.org> * docs: enhance README with Extism plugin development resources and recommendations Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): integrate event broker into plugin manager Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): update config handling in PluginShow to track last record state Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Rust host function library and example implementation of Discord Rich Presence plugin in Rust Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate Rust lib.rs file to expose host function wrappers Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update JSON field names to camelCase for consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor: reduce cyclomatic complexity by refactoring main function Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): enhance Rust code generation with typed struct support and improved type handling Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Go client library with host function wrappers and documentation Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate Go client stubs for non-WASM platforms Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): update client template file names for consistency Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add initial implementation of the Navidrome Plugin Development Kit code generator - Pahse 1 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 2 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 2 (2) Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 3 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 4 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 5 Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): consistent naming/types across PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): streamline plugin function signatures and error handling Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update scrobbler interface to return errors directly instead of response structs Signed-off-by: Deluan <deluan@navidrome.org> * test: make all test plugins use the PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): reorganize and sort type definitions for consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update error handling for methods to return errors directly Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update function signatures to return values directly instead of response structs Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update request/response types to use private naming conventions Signed-off-by: Deluan <deluan@navidrome.org> * build: mark .wasm files as intermediate for cleanup after building .ndp Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): consolidate PDK module path and update Go version to 1.25 Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement Rust PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): reorganize Rust output structure to follow standard conventions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update Discord Rich Presence and Library Inspector plugins to use nd-pdk for service calls and implement lifecycle management Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update macro names for websocket and metadata registration to improve clarity and consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): rename scheduler callback methods for consistency and clarity Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update export wrappers to use `//go:wasmexport` for WebAssembly compatibility Signed-off-by: Deluan <deluan@navidrome.org> * docs: update plugin registration docs Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): generate host wrappers Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): conditionally run goleak checks based on CI environment Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to reflect changes in plugin import paths Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update plugin instance creation to accept context for cancellation support Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update return types in metadata interfaces to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): enhance type handling for Rust and XTP output in capability generation Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update IsAuthorized method to return boolean instead of response object Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): add unit tests for rustOutputType and isPrimitiveRustType functions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement XTP JSONSchema validation for generated schemas Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update response types in testMetadataAgent methods to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * docs: update Go and Rust plugin developer sections for clarity Signed-off-by: Deluan <deluan@navidrome.org> * docs: correct example link for library inspector in README Signed-off-by: Deluan <deluan@navidrome.org> * docs: clarify artwork URL generation capabilities in service descriptions Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to include Rust PDK crate information for plugin developers Signed-off-by: Deluan <deluan@navidrome.org> * fix: handle URL parsing errors and use atomic upsert in plugin repository Added proper error handling for url.Parse calls in PublicURL and AbsoluteURL functions. When parsing fails, PublicURL now falls back to AbsoluteURL, and AbsoluteURL logs the error and returns an empty string, preventing malformed URLs from being generated. Replaced the non-atomic UPDATE-then-INSERT pattern in plugin repository Put method with a single atomic INSERT ... ON CONFLICT statement. This eliminates potential race conditions and improves consistency with the upsert pattern already used in host_kvstore.go. * feat: implement mock service instances for non-WASM builds using testify/mock Signed-off-by: Deluan <deluan@navidrome.org> * refactor: Discord RPC struct to encapsulate WebSocket logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: add support for experimental WebAssembly threads Signed-off-by: Deluan <deluan@navidrome.org> * feat: add PDK abstraction layer with mock support for non-WASM builds Signed-off-by: Deluan <deluan@navidrome.org> * feat: add unit tests for Discord plugin and RPC functionality Signed-off-by: Deluan <deluan@navidrome.org> * fix: update return types in minimalPlugin and wikimediaPlugin methods to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * fix: context cancellation and implement WebSocket callback timeout for improved error handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: conditionally include error handling in generated client code templates Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement ConfigService for plugin configuration management Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance plugin manager to support metrics recording Signed-off-by: Deluan <deluan@navidrome.org> * refactor: make MockPDK private Signed-off-by: Deluan <deluan@navidrome.org> * refactor: update interface types to use 'any' in plugin repository methods Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename List method to Keys for clarity in configuration management Signed-off-by: Deluan <deluan@navidrome.org> * test: add ndpgen plugin tests in the pipeline and update Makefile Signed-off-by: Deluan <deluan@navidrome.org> * feat: add users permission management to plugin system Signed-off-by: Deluan <deluan@navidrome.org> * refactor: streamline users integration tests and enhance plugin user management Signed-off-by: Deluan <deluan@navidrome.org> * refactor: remove UserID from scrobbler request structure Signed-off-by: Deluan <deluan@navidrome.org> * test: add integration tests for UsersService enable gate behavior Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement user permissions for SubsonicAPI and scrobbler plugins Signed-off-by: Deluan <deluan@navidrome.org> * fix: show proper error in the UI when enabling a plugin fails Signed-off-by: Deluan <deluan@navidrome.org> * feat: add library permission management to plugin system Signed-off-by: Deluan <deluan@navidrome.org> * feat: add user permission for processing scrobbles in Discord Rich Presence plugin Signed-off-by: Deluan <deluan@navidrome.org> * fix: implement dynamic loading for buffered scrobbler plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: add GetAdmins method to retrieve admin users from the plugin Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Portuguese translations for user and library permissions Signed-off-by: Deluan <deluan@navidrome.org> * reorder migrations Signed-off-by: Deluan <deluan@navidrome.org> * fix: remove unnecessary bulkActionButtons prop from PluginList component * feat: add manual plugin rescan functionality and corresponding UI action Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement user/library and plugin management integration with cleanup on deletion Signed-off-by: Deluan <deluan@navidrome.org> * feat: replace core mock services with test-specific implementations to avoid import cycles * feat: add ID fields to Artist and Song structs and enhance track loading logic by prioritizing ID matches Signed-off-by: Deluan <deluan@navidrome.org> * feat: update plugin permissions from allowedHosts to requiredHosts for better clarity and consistency * feat: refactor plugin host permissions to use RequiredHosts directly for improved clarity * fix: don't record metrics for plugin calls that aren't implemented at all Signed-off-by: Deluan <deluan@navidrome.org> * fix: enhance connection management with improved error handling and cleanup logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: introduce ArtistRef struct for better artist representation and update track metadata handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: update user configuration handling to use user key prefix for improved clarity Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance ConfigCard input fields with multiline support and vertical resizing Signed-off-by: Deluan <deluan@navidrome.org> * fix: rust plugin compilation error Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement IsOptionPattern method for better return type handling in Rust PDK generation Signed-off-by: Deluan <deluan@navidrome.org> --------- Signed-off-by: Deluan <deluan@navidrome.org>
2026-01-14 19:22:48 -05:00
// TopSongsResponse is the response for GetArtistTopSongs.
type TopSongsResponse struct {
// Songs is the list of top songs.
feat(plugins): share plugin DTOs via a types package (#5655) * refactor(plugins): remove Python PDK generation from ndpgen * feat(plugins): parse Go type aliases distinctly in ndpgen * feat(plugins): resolve shared-type aliases against a registry in ndpgen * fix(plugins): resolve host-service shared aliases package-wide Mirror the capability approach in ParseDirectoryWithShared: do a first pass over all package files to build a package-wide alias map, then pass it into parseServiceFile so that a shared-type alias declared in a sibling file is visible when resolving types in the service interface file. Add a focused test that writes the alias in one file and the hostservice in another, confirming RED before the fix and GREEN after. Also strengthens the existing Task 3 test with an ArtistRef.Target assertion. * feat(plugins): add ndpgen -shared-types mode for the Go types package * feat(plugins): generate the nd-pdk-types Rust crate from -shared-types * feat(plugins): inject types import and emit deprecated aliases in Go output * feat(plugins): emit deprecated Rust aliases to the shared types crate * feat(plugins): inline shared-type shapes into XTP schemas * feat(plugins): add nd-pdk-types crate and wire dependents * feat(plugins): move shared capability types to plugins/types with deprecated aliases * fix(plugins): point Rust deprecated-alias note at the replacement type * fix(plugins): include shared aliases in KnownStructs so Rust fields keep their type Capability.KnownStructs() and Service.KnownStructs() previously only registered names from .Structs. After the shared-types migration, types like ArtistRef/TrackInfo/SongRef live in .SharedAliases instead, so ToRustTypeWithStructs could not find them and fell back to serde_json::Value for every struct field referencing a shared type. Add the shared-alias names to the knownStructs map in both methods. Regenerate the Rust capability files; track/song/artist fields now render as their named types (TrackInfo, SongRef, ArtistRef, etc.). Add a regression test that verifies a struct field whose type is only in SharedAliases renders as the named type and not serde_json::Value. * docs(plugins): remove stale Python references from ndpgen and plugins READMEs ndpgen no longer has a -python flag; remove it from the usage synopsis, flags table, and defaults note in ndpgen/README.md. Delete the "Python Client Library" section that described its output. plugins/README.md referenced plugins/pdk/python/host/ (deleted) as the source for Python host-service stubs. Remove that paragraph; Python plugins still work via the XTP-schema / extism-py path (see examples/*-py). * refactor(plugins): dedupe ndpgen helpers and tidy shared-type codegen * docs(plugins): restore Python as a supported XTP schema target The ndpgen-generated Python PDK was removed, but the XTP YAML schemas are language-neutral and the XTP CLI still generates Python bindings from them (as the extism-py examples demonstrate). Only the ndpgen Python output was dropped, not Python support itself. * test(plugins): use the shared types package in test plugins The test fixtures referenced the now-deprecated capability aliases (sonicsimilarity.SongRef, metadata.ArtistRef/SongRef). Point them at the canonical types package so our own fixtures don't depend on symbols slated for removal. * refactor(plugins): use the shared types package in host adapters Replace deprecated capabilities.TrackInfo, capabilities.ArtistRef, and capabilities.SongRef aliases with the canonical types.TrackInfo, types.ArtistRef, and types.SongRef from plugins/types. * fix(plugins): reference shared types by canonical path in generated Rust Previously the generator emitted `pub field: SongRef` (the local deprecated alias) for struct fields whose type came from SharedAliases. Refactored ToRustTypeWithStructs into a private toRustType that accepts a shared map, and added ToRustTypeWithShared which resolves shared-alias names to their canonical nd_pdk_types::X path before falling through to the knownStructs check. Both rustCapabilityFuncMap and rustFuncMap now build the shared map from SharedAliases and use it for fieldRustType, so the generated capability files reference nd_pdk_types::SongRef / nd_pdk_types::TrackInfo directly. The deprecated pub type aliases remain in place as the external back-compat surface. Deprecation warning count from cargo build drops to 0. * fix(examples): implement missing Scrobbler.playback_report in Rust examples The Scrobbler trait gained a playback_report method but the two Rust example plugins (webhook-rs and discord-rich-presence-rs) were not updated, causing E0046 compile errors. Added the missing fn playback_report to both: webhook-rs logs and returns Ok(()) mirroring its now_playing handler; discord-rich-presence-rs is a no-op since Discord presence does not need playback reports. make all-rust now exits 0. * refactor(plugins): point Rust deprecation notes at the nd_pdk::types umbrella path Plugin authors depend on the nd-pdk umbrella crate, which re-exports nd_pdk_types as 'types', so the migration target they should type is nd_pdk::types::X. The alias target stays nd_pdk_types::X (the real path inside nd-pdk-capabilities). * fix(plugins): error when a shared-type alias can't be resolved against the registry * refactor(plugins): parse each Go source file once in ndpgen * fix(plugins): correct ndpgen review nits (flag name, unused dep, docs) * refactor(plugins): drop the now-unused path param from parseServiceFile * refactor(plugins): use shared types directly, rename TrackInfo to Track Capability interfaces now reference the shared `types` package by qualified name (types.Track, types.SongRef, types.ArtistRef) instead of the package-local deprecated aliases, and the shared TrackInfo type is renamed to Track to match its role as the plugin-facing projection of a library media file. The deprecated bare aliases (scrobbler.TrackInfo, metadata.ArtistRef, sonicsimilarity.SongRef, etc.) are kept as re-exports so existing plugins keep compiling, with a deprecation warning steering them to the canonical types. To support this, ndpgen now resolves qualified types.X references: it collects them during type discovery, maps each used canonical type back to its declared deprecated alias for re-export, emits nd_pdk_types::X paths in Rust, and names the XTP schema components by their canonical type. Regenerated the Go and Rust PDK and the XTP schemas, and added generator tests covering the qualified-ref path. Also adds clarifying doc comments to the shared types. * refactor(plugins): extract shared types selector into a named const Replace the "types." string literal that detects and strips the shared types package selector with a single sharedTypesPrefix constant across the ndpgen generator (parser, types, generator, xtp_schema), giving the package one source of truth for the selector. Also restore the single reused scratch map (cleared each iteration) in the resolveSharedAliases BFS instead of allocating a fresh map per shared-struct field, matching the prior implementation. Pure cleanup from a /simplify pass: regeneration produces byte-for-byte identical Go, Rust, and XTP output. * refactor(plugins): keep TrackInfo in the capability package for now Move the track type back out of the shared plugins/types package: it is again defined inline as TrackInfo in plugins/capabilities/scrobbler.go and referenced directly by the scrobbler and lyrics capabilities, reverting the rename to types.Track. The host helper is renamed back to mediaFileToTrackInfo and now returns capabilities.TrackInfo. SongRef and ArtistRef stay in the shared types package; TrackInfo keeps using types.ArtistRef for its artist lists. This type is expected to be reshaped in upcoming work, so leaving it in the capability package avoids churning the shared types twice. Regenerated the Go and Rust PDK and the XTP schemas accordingly. * fix(plugins): emit the Go types import for direct shared-type refs ndpgen's Capability/Service.ImportsSharedTypes only reported a shared-types dependency when a deprecated re-export alias (type X = types.X) was declared. A struct field referencing the canonical form directly (e.g. types.SongRef) with no such alias produced an empty SharedAliases slice, so the Go templates skipped the import while still emitting fields/signatures using types.SongRef — leaving generated PDK code for new shared DTOs uncompilable unless an otherwise unnecessary alias was added. ImportsSharedTypes now also returns true when any struct field references the types. package by qualified name, via a new structsReferenceSharedTypes helper that reuses collectReferencedTypes (so []types.X and map[...]types.X are covered too). * fix(plugins): preserve base64 encoding for shared byte fields in Rust The Rust shared-types crate template rendered a []byte field as a plain Vec<u8> without the base64_bytes serde override used by the capability/client templates. Go's encoding/json serializes []byte as a base64 string, so a Rust plugin using nd_pdk::types would have serialized an array of numbers instead of the wire format the Go/server side expects. GenerateSharedTypesRust now registers the base64_bytes partial and passes a HasByteFields flag (new anyFieldIsByteSlice helper); types.rs.tmpl emits the base64_bytes module and a #[serde(with = "base64_bytes")] attribute on []byte fields, mirroring the capability template. * fix(plugins): include directly-referenced shared types in XTP schemas buildSchemas registered shared types into the schema components only by iterating cap.SharedAliases, which records deprecated re-export aliases. A capability that referenced a shared DTO solely as types.Foo (no declared alias) therefore never got Foo into the component set, so the self-contained XTP schema rendered the field as a generic object (or emitted a dangling $ref), breaking the direct shared-type use case enabled by -shared. resolveSharedAliases now also returns the resolved shapes of every used shared type (alias or not); these are carried on the new Capability.SharedTypes field and registered by buildSchemas alongside SharedAliases. Validated end-to-end with the xtp CLI: a direct types.Foo reference now produces a proper component plus a $ref, so xtp generates a typed struct instead of an untyped serde_json::Map. * fix(plugins): resolve renamed shared aliases to canonical schema refs When a deprecated alias renames its canonical type (e.g. type TrackInfo = types.Track) and a capability field is typed with the alias name (TrackInfo), buildProperty emitted a $ref to #/components/schemas/TrackInfo. Components are keyed by the canonical name (Track), so no TrackInfo component was emitted, leaving a dangling reference that crashes the xtp code generator. buildSchemas now builds an alias->canonical map; buildProperty (and the slice item path) resolves $ref targets through it, and a used alias name marks its canonical component used so it is emitted. Validated with the xtp CLI: the renamed-alias schema previously crashed xtp and now generates cleanly. * fix(plugins): detect shared types used directly in method signatures ImportsSharedTypes only inspected struct fields, so a capability method using a shared type directly in its signature (e.g. types.SongRef as input/output rather than inside a local struct) was not detected. The generated Go templates still rendered the provider/export signatures with types.SongRef, so the capability package omitted the types import and failed to compile; the same gap applied to service params/returns. ImportsSharedTypes now also scans capability method input/output types and service method params/returns, via a typeReferencesSharedTypes helper that reuses collectReferencedTypes (covering pointer/slice/map wrappers). * fix(plugins): add base64 dependency to the shared Rust types crate When a shared DTO has a []byte field, ndpgen emits the base64_bytes serde helper and use base64::... imports into nd-pdk-types/src/lib.rs, but the crate manifest declared only serde. In that case make gen produced a crate that failed to compile with 'unresolved module base64'. Add base64 = "0.22" (matching nd-pdk-capabilities) so the generated shared types crate compiles whenever a []byte field is present. Verified by generating a shared crate with a []byte field and confirming cargo check fails before and passes after. * fix(plugins): translate shared method types in generated Rust A capability method using a shared DTO directly as input/output (e.g. types.SongRef) was passed through rustOutputType unchanged, so the Rust template emitted invalid trait and extism_pdk::Json<$crate::pkg::types.SongRef> signatures that do not compile. Method input/output types now resolve through the shared registry: trait signatures use rustTraitType (shared -> nd_pdk_types::X, locals stay bare) and the export macros use rustMethodType (fully qualified: shared -> nd_pdk_types::X, primitives -> Rust, locals -> $crate::<pkg>::X). Verified end-to-end by compiling a generated capability that takes types.SongRef directly against the real nd-pdk-types crate. * fix(plugins): canonicalize XTP export refs for renamed shared aliases buildSchemas canonicalized alias-to-canonical references for struct-field $ref targets, but buildExport built export input/output $refs straight from fieldBaseType. A capability whose export used a renamed deprecated alias directly (e.g. type TrackInfo = types.Track with NowPlaying(TrackInfo)) emitted $ref: #/components/schemas/TrackInfo, while the component is emitted under the canonical name Track — a dangling export reference. Lift the alias-to-canonical map into GenerateSchema (buildAliasToCanonical) and apply it to export refs via canonicalRefName, the same resolution already used for field properties. * fix(plugins): route shared macro types through $crate for plugin builds When a capability method used a shared type directly, the generated export macro named the type as nd_pdk_types::SongRef. The macro expands in the downstream plugin crate, which depends on the umbrella nd-pdk crate and not on nd-pdk-types directly, so that path is unresolvable there and the plugin fails to build. rustMethodType (macro-facing) now emits $crate::types::X, and the generated nd-pdk-capabilities lib.rs re-exports nd_pdk_types as types so $crate resolves it. Trait signatures keep nd_pdk_types::X since they live in nd-pdk-capabilities, which has the direct dependency. Verified end-to-end: a plugin crate depending only on the umbrella that uses a capability with a direct types.X method now compiles via the macro. * fix(plugins): add nd-pdk-types dependency to the Rust host crate When a host service uses a shared type, ndpgen emits nd_pdk_types::X into the generated nd-pdk-host client wrappers, but the host crate's manifest did not depend on nd-pdk-types, so the crate failed to compile with 'unresolved module nd_pdk_types'. Host client wrappers are plain functions resolved in the host crate's own context (not macros expanded downstream), so a direct dependency is the right fix. Add nd-pdk-types = { path = "../nd-pdk-types" } to nd-pdk-host, mirroring nd-pdk-capabilities. Found while auditing all Rust paths against the realistic crate topology after the capability-side $crate fix; verified by generating a host service with a shared-type return and confirming cargo check fails before and passes after. * fix(plugins): resolve shared aliases in Rust host signatures The Rust host client rendered method params and returns through RustTypeWithStructs, which only consults KnownStructs. A host service using a shared alias in a signature (e.g. type Track = types.Track plus MatchSongs(...) ([]Track, error)) therefore emitted a bare Vec<Track>, but the client template emits no Track alias or import, so the generated nd-pdk-host crate did not compile. Only struct fields went through the shared map. rustType/rustParamType now use the shared map too (RustTypeWithShared / RustParamTypeWithShared), so an aliased param/return resolves to its canonical nd_pdk_types::X path, matching field handling. Verified by generating a host service returning a shared alias and confirming cargo check fails before and passes after.
2026-06-29 21:20:33 -04:00
Songs []types.SongRef `json:"songs"`
feat(plugins): New Plugin System with multi-language PDK support (#4833) * chore(plugins): remove the old plugins system implementation Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement new plugin system with using Extism Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add capability detection for plugins based on exported functions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add auto-reload functionality for plugins with file watcher support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add auto-reload functionality for plugins with file watcher support Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): standardize variable names and remove superfluous wrapper functions Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): improve error handling and logging in plugin manager Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): implement plugin function call helper and refactor MetadataAgent methods Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): race condition in plugin manager * tests(plugins): change BeforeEach to BeforeAll in MetadataAgent tests Signed-off-by: Deluan <deluan@navidrome.org> * tests(plugins): optimize tests Signed-off-by: Deluan <deluan@navidrome.org> * tests(plugins): more optimizations Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): ignore goroutine leaks from notify library in tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Wikimedia plugin for Navidrome to fetch artist metadata Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): enhance plugin logging and set User-Agent header Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement scrobbler plugin with authorization and scrobbling capabilities Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): integrate logs Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): clean up manifest struct and improve plugin loading logic Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add metadata agent and scrobbler schemas for bootstrapping plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat(hostgen): add hostgen tool for generating Extism host function wrappers - Implemented hostgen tool to generate wrappers from annotated Go interfaces. - Added command-line flags for input/output directories and package name. - Introduced parsing and code generation logic for host services. - Created test data for various service interfaces and expected generated code. - Added documentation for host services and annotations for code generation. - Implemented SubsonicAPI service with corresponding generated code. * feat(subsonicapi): update Call method to return JSON string response Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement SubsonicAPI host function integration with permissions Signed-off-by: Deluan <deluan@navidrome.org> * fix(generator): error-only methods in response handling Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate client wrappers for host functions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(generator): remove error handling for response.Error in client templates Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): add Scheduler service interface with host function wrappers for scheduling tasks * feat(plugins): add WASI build constraints to client wrapper templates, to avoid lint errors Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): implement Scheduler service with one-time and recurring scheduling capabilities Signed-off-by: Deluan <deluan@navidrome.org> * refactor(manifest): remove unused ConfigPermission from permissions schema Signed-off-by: Deluan <deluan@navidrome.org> * feat(scheduler): add scheduler callback schema and implementation for plugins Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): streamline scheduling logic and remove unused callback tracking Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): add Close method for resource cleanup on plugin unload Signed-off-by: Deluan <deluan@navidrome.org> * docs(scheduler): clarify SchedulerCallback requirement for scheduling functions Signed-off-by: Deluan <deluan@navidrome.org> * fix: update wasm build rule to include all Go files in the directory Signed-off-by: Deluan <deluan@navidrome.org> * feat: rewrite the wikimedia plugin using the XTP CLI Signed-off-by: Deluan <deluan@navidrome.org> * refactor(scheduler): replace uuid with id.NewRandom for schedule ID generation Signed-off-by: Deluan <deluan@navidrome.org> * refactor: capabilities registration Signed-off-by: Deluan <deluan@navidrome.org> * test: add scheduler service isolation test for plugin instances Signed-off-by: Deluan <deluan@navidrome.org> * refactor: update plugin manager initialization and encapsulate logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: add WebSocket service definitions for plugin communication Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement WebSocket service for plugin integration and connection management Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Crypto Ticker example plugin for real-time cryptocurrency price updates via Coinbase WebSocket API Also add the lifecycle capability Signed-off-by: Deluan <deluan@navidrome.org> * fix: use context.Background() in invokeCallback for scheduled tasks Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename plugin.create() to plugin.instance() Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename pluginInstance to plugin for consistency across the codebase Signed-off-by: Deluan <deluan@navidrome.org> * refactor: simplify schedule cloning in Close method and enhance plugin cleanup error handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement Artwork service for generating artwork URLs in Navidrome plugins - WIP Signed-off-by: Deluan <deluan@navidrome.org> * refactor: moved public URL builders to avoid import cycles Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Cache service for in-memory TTL-based caching in plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Discord Rich Presence example plugin for Navidrome integration Signed-off-by: Deluan <deluan@navidrome.org> * refactor: host function wrappers to use structured request and response types - Updated the host function signatures in `nd_host_artwork.go`, `nd_host_scheduler.go`, `nd_host_subsonicapi.go`, and `nd_host_websocket.go` to accept a single parameter for JSON requests. - Introduced structured request and response types for various cache operations in `nd_host_cache.go`. - Modified cache functions to marshal requests to JSON and unmarshal responses, improving error handling and code clarity. - Removed redundant memory allocation for string parameters in favor of JSON marshaling. - Enhanced error handling in WebSocket and cache operations to return structured error responses. * refactor: error handling in various plugins to convert response.Error to Go errors - Updated error handling in `nd_host_scheduler.go`, `nd_host_websocket.go`, `nd_host_artwork.go`, `nd_host_cache.go`, and `nd_host_subsonicapi.go` to convert string errors from responses into Go errors. - Removed redundant error checks in test data plugins for cleaner code. - Ensured consistent error handling across all plugins to improve reliability and maintainability. * refactor: rename fake plugins to test plugins for clarity in integration tests Signed-off-by: Deluan <deluan@navidrome.org> * feat: add help target to Makefile for plugin usage instructions Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Cover Art Archive plugin as an example of Python plugin Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Makefile and README to clarify Go plugin usage Signed-off-by: Deluan <deluan@navidrome.org> * feat: include plugin capabilities in loading log message Signed-off-by: Deluan <deluan@navidrome.org> * feat: add trace logging for plugin availability and error handling in agents Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Now Playing Logger plugin to showcase calling host functions from Python plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: generate Python client wrappers for various host services Signed-off-by: Deluan <deluan@navidrome.org> * feat: add generated host function wrappers for Scheduler and SubsonicAPI services Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Python plugin documentation and usage instructions for host function wrappers Signed-off-by: Deluan <deluan@navidrome.org> * feat: add Webhook Scrobbler plugin in Rust to send HTTP notifications on scrobble events Signed-off-by: Deluan <deluan@navidrome.org> * feat: enable parallel loading of plugins during startup Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to include WebSocket callback schema in plugin documentation Signed-off-by: Deluan <deluan@navidrome.org> * feat: extend plugin watcher with improved logging and debounce duration adjustment Signed-off-by: Deluan <deluan@navidrome.org> * add trace message for plugin recompiles Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement plugin cache purging functionality Signed-off-by: Deluan <deluan@navidrome.org> * test: move purgeCacheBySize unit tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add plugin repository and database support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add plugin management routes and middleware Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): implement plugin synchronization with database for add, update, and remove actions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add PluginList and PluginShow components with plugin management functionality Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): optimize plugin change detection Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins UI): improve PluginList structure Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): enhance PluginShow with author, website, and permissions display Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): refactor to use MUI and RA components Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins UI): add error handling for plugin enable/disable actions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): inject PluginManager into native API Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update GetManager to accept DataStore parameter Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add subsonicRouter to Manager and refactor host service registration Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): enhance debug logging for plugin actions and recompile logic Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): break manager.go into smaller, focused files Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): streamline error handling and improve plugin retrieval logic Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update newWebSocketService to use WebSocketPermission for allowed hosts Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): introduce ToggleEnabledSwitch for managing plugin enable/disable state Signed-off-by: Deluan <deluan@navidrome.org> * docs: update READMEs Signed-off-by: Deluan <deluan@navidrome.org> * feat(library): add Library service for metadata access and filesystem integration Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Library Inspector plugin for periodic library inspection and file size logging Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to reflect JSON configuration format for plugins Signed-off-by: Deluan <deluan@navidrome.org> * fix(build): update target to wasm32-wasip1 for improved WASI support Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement configuration management UI with key-value pairs support Signed-off-by: Deluan <deluan@navidrome.org> * feat(ui): adjust grid layout in InfoRow component for improved responsiveness Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): rename ErrorIndicator to EnabledOrErrorField and enhance error handling logic Signed-off-by: Deluan <deluan@navidrome.org> * feat(i18n): add Portuguese translations for plugin management and notifications Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add support for .ndp plugin packages and update build process Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README for .ndp plugin packaging and installation instructions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement KVStore service for persistent key-value storage Signed-off-by: Deluan <deluan@navidrome.org> * docs: enhance README with Extism plugin development resources and recommendations Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): integrate event broker into plugin manager Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): update config handling in PluginShow to track last record state Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Rust host function library and example implementation of Discord Rich Presence plugin in Rust Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate Rust lib.rs file to expose host function wrappers Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update JSON field names to camelCase for consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor: reduce cyclomatic complexity by refactoring main function Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): enhance Rust code generation with typed struct support and improved type handling Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add Go client library with host function wrappers and documentation Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): generate Go client stubs for non-WASM platforms Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): update client template file names for consistency Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add initial implementation of the Navidrome Plugin Development Kit code generator - Pahse 1 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 2 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 2 (2) Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 3 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 4 Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implementation of the Navidrome Plugin Development Kit with generated client wrappers and service interfaces - Phase 5 Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): consistent naming/types across PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): streamline plugin function signatures and error handling Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update scrobbler interface to return errors directly instead of response structs Signed-off-by: Deluan <deluan@navidrome.org> * test: make all test plugins use the PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): reorganize and sort type definitions for consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update error handling for methods to return errors directly Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update function signatures to return values directly instead of response structs Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update request/response types to use private naming conventions Signed-off-by: Deluan <deluan@navidrome.org> * build: mark .wasm files as intermediate for cleanup after building .ndp Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): consolidate PDK module path and update Go version to 1.25 Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement Rust PDK Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): reorganize Rust output structure to follow standard conventions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update Discord Rich Presence and Library Inspector plugins to use nd-pdk for service calls and implement lifecycle management Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update macro names for websocket and metadata registration to improve clarity and consistency Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): rename scheduler callback methods for consistency and clarity Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update export wrappers to use `//go:wasmexport` for WebAssembly compatibility Signed-off-by: Deluan <deluan@navidrome.org> * docs: update plugin registration docs Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): generate host wrappers Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): conditionally run goleak checks based on CI environment Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to reflect changes in plugin import paths Signed-off-by: Deluan <deluan@navidrome.org> * refactor(plugins): update plugin instance creation to accept context for cancellation support Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update return types in metadata interfaces to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): enhance type handling for Rust and XTP output in capability generation Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update IsAuthorized method to return boolean instead of response object Signed-off-by: Deluan <deluan@navidrome.org> * test(plugins): add unit tests for rustOutputType and isPrimitiveRustType functions Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): implement XTP JSONSchema validation for generated schemas Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update response types in testMetadataAgent methods to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * docs: update Go and Rust plugin developer sections for clarity Signed-off-by: Deluan <deluan@navidrome.org> * docs: correct example link for library inspector in README Signed-off-by: Deluan <deluan@navidrome.org> * docs: clarify artwork URL generation capabilities in service descriptions Signed-off-by: Deluan <deluan@navidrome.org> * docs: update README to include Rust PDK crate information for plugin developers Signed-off-by: Deluan <deluan@navidrome.org> * fix: handle URL parsing errors and use atomic upsert in plugin repository Added proper error handling for url.Parse calls in PublicURL and AbsoluteURL functions. When parsing fails, PublicURL now falls back to AbsoluteURL, and AbsoluteURL logs the error and returns an empty string, preventing malformed URLs from being generated. Replaced the non-atomic UPDATE-then-INSERT pattern in plugin repository Put method with a single atomic INSERT ... ON CONFLICT statement. This eliminates potential race conditions and improves consistency with the upsert pattern already used in host_kvstore.go. * feat: implement mock service instances for non-WASM builds using testify/mock Signed-off-by: Deluan <deluan@navidrome.org> * refactor: Discord RPC struct to encapsulate WebSocket logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: add support for experimental WebAssembly threads Signed-off-by: Deluan <deluan@navidrome.org> * feat: add PDK abstraction layer with mock support for non-WASM builds Signed-off-by: Deluan <deluan@navidrome.org> * feat: add unit tests for Discord plugin and RPC functionality Signed-off-by: Deluan <deluan@navidrome.org> * fix: update return types in minimalPlugin and wikimediaPlugin methods to use pointers Signed-off-by: Deluan <deluan@navidrome.org> * fix: context cancellation and implement WebSocket callback timeout for improved error handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: conditionally include error handling in generated client code templates Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement ConfigService for plugin configuration management Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance plugin manager to support metrics recording Signed-off-by: Deluan <deluan@navidrome.org> * refactor: make MockPDK private Signed-off-by: Deluan <deluan@navidrome.org> * refactor: update interface types to use 'any' in plugin repository methods Signed-off-by: Deluan <deluan@navidrome.org> * refactor: rename List method to Keys for clarity in configuration management Signed-off-by: Deluan <deluan@navidrome.org> * test: add ndpgen plugin tests in the pipeline and update Makefile Signed-off-by: Deluan <deluan@navidrome.org> * feat: add users permission management to plugin system Signed-off-by: Deluan <deluan@navidrome.org> * refactor: streamline users integration tests and enhance plugin user management Signed-off-by: Deluan <deluan@navidrome.org> * refactor: remove UserID from scrobbler request structure Signed-off-by: Deluan <deluan@navidrome.org> * test: add integration tests for UsersService enable gate behavior Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement user permissions for SubsonicAPI and scrobbler plugins Signed-off-by: Deluan <deluan@navidrome.org> * fix: show proper error in the UI when enabling a plugin fails Signed-off-by: Deluan <deluan@navidrome.org> * feat: add library permission management to plugin system Signed-off-by: Deluan <deluan@navidrome.org> * feat: add user permission for processing scrobbles in Discord Rich Presence plugin Signed-off-by: Deluan <deluan@navidrome.org> * fix: implement dynamic loading for buffered scrobbler plugins Signed-off-by: Deluan <deluan@navidrome.org> * feat: add GetAdmins method to retrieve admin users from the plugin Signed-off-by: Deluan <deluan@navidrome.org> * feat: update Portuguese translations for user and library permissions Signed-off-by: Deluan <deluan@navidrome.org> * reorder migrations Signed-off-by: Deluan <deluan@navidrome.org> * fix: remove unnecessary bulkActionButtons prop from PluginList component * feat: add manual plugin rescan functionality and corresponding UI action Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement user/library and plugin management integration with cleanup on deletion Signed-off-by: Deluan <deluan@navidrome.org> * feat: replace core mock services with test-specific implementations to avoid import cycles * feat: add ID fields to Artist and Song structs and enhance track loading logic by prioritizing ID matches Signed-off-by: Deluan <deluan@navidrome.org> * feat: update plugin permissions from allowedHosts to requiredHosts for better clarity and consistency * feat: refactor plugin host permissions to use RequiredHosts directly for improved clarity * fix: don't record metrics for plugin calls that aren't implemented at all Signed-off-by: Deluan <deluan@navidrome.org> * fix: enhance connection management with improved error handling and cleanup logic Signed-off-by: Deluan <deluan@navidrome.org> * feat: introduce ArtistRef struct for better artist representation and update track metadata handling Signed-off-by: Deluan <deluan@navidrome.org> * feat: update user configuration handling to use user key prefix for improved clarity Signed-off-by: Deluan <deluan@navidrome.org> * feat: enhance ConfigCard input fields with multiline support and vertical resizing Signed-off-by: Deluan <deluan@navidrome.org> * fix: rust plugin compilation error Signed-off-by: Deluan <deluan@navidrome.org> * feat: implement IsOptionPattern method for better return type handling in Rust PDK generation Signed-off-by: Deluan <deluan@navidrome.org> --------- Signed-off-by: Deluan <deluan@navidrome.org>
2026-01-14 19:22:48 -05:00
}
// AlbumRequest is the common request for album-related functions.
type AlbumRequest struct {
// Name is the album name.
Name string `json:"name"`
// Artist is the album artist name.
Artist string `json:"artist"`
// MBID is the MusicBrainz ID for the album (if known).
MBID string `json:"mbid,omitempty"`
}
// AlbumInfoResponse is the response for GetAlbumInfo.
type AlbumInfoResponse struct {
// Name is the album name.
Name string `json:"name"`
// MBID is the MusicBrainz ID for the album.
MBID string `json:"mbid"`
// Description is the album description/notes.
Description string `json:"description"`
// URL is the external URL for the album.
URL string `json:"url"`
}
// AlbumImagesResponse is the response for GetAlbumImages.
type AlbumImagesResponse struct {
// Images is the list of album images.
Images []ImageInfo `json:"images"`
}
// SimilarSongsByTrackRequest is the request for GetSimilarSongsByTrack.
type SimilarSongsByTrackRequest struct {
// ID is the internal Navidrome mediafile ID.
ID string `json:"id"`
// Name is the track title.
Name string `json:"name"`
// Artist is the artist name.
Artist string `json:"artist"`
// MBID is the MusicBrainz recording ID (if known).
MBID string `json:"mbid,omitempty"`
// Count is the maximum number of similar songs to return.
Count int32 `json:"count"`
}
// SimilarSongsByAlbumRequest is the request for GetSimilarSongsByAlbum.
type SimilarSongsByAlbumRequest struct {
// ID is the internal Navidrome album ID.
ID string `json:"id"`
// Name is the album name.
Name string `json:"name"`
// Artist is the album artist name.
Artist string `json:"artist"`
// MBID is the MusicBrainz release ID (if known).
MBID string `json:"mbid,omitempty"`
// Count is the maximum number of similar songs to return.
Count int32 `json:"count"`
}
// SimilarSongsByArtistRequest is the request for GetSimilarSongsByArtist.
type SimilarSongsByArtistRequest struct {
// ID is the internal Navidrome artist ID.
ID string `json:"id"`
// Name is the artist name.
Name string `json:"name"`
// MBID is the MusicBrainz artist ID (if known).
MBID string `json:"mbid,omitempty"`
// Count is the maximum number of similar songs to return.
Count int32 `json:"count"`
}
// SimilarSongsResponse is the response for GetSimilarSongsBy* functions.
type SimilarSongsResponse struct {
// Songs is the list of similar songs.
feat(plugins): share plugin DTOs via a types package (#5655) * refactor(plugins): remove Python PDK generation from ndpgen * feat(plugins): parse Go type aliases distinctly in ndpgen * feat(plugins): resolve shared-type aliases against a registry in ndpgen * fix(plugins): resolve host-service shared aliases package-wide Mirror the capability approach in ParseDirectoryWithShared: do a first pass over all package files to build a package-wide alias map, then pass it into parseServiceFile so that a shared-type alias declared in a sibling file is visible when resolving types in the service interface file. Add a focused test that writes the alias in one file and the hostservice in another, confirming RED before the fix and GREEN after. Also strengthens the existing Task 3 test with an ArtistRef.Target assertion. * feat(plugins): add ndpgen -shared-types mode for the Go types package * feat(plugins): generate the nd-pdk-types Rust crate from -shared-types * feat(plugins): inject types import and emit deprecated aliases in Go output * feat(plugins): emit deprecated Rust aliases to the shared types crate * feat(plugins): inline shared-type shapes into XTP schemas * feat(plugins): add nd-pdk-types crate and wire dependents * feat(plugins): move shared capability types to plugins/types with deprecated aliases * fix(plugins): point Rust deprecated-alias note at the replacement type * fix(plugins): include shared aliases in KnownStructs so Rust fields keep their type Capability.KnownStructs() and Service.KnownStructs() previously only registered names from .Structs. After the shared-types migration, types like ArtistRef/TrackInfo/SongRef live in .SharedAliases instead, so ToRustTypeWithStructs could not find them and fell back to serde_json::Value for every struct field referencing a shared type. Add the shared-alias names to the knownStructs map in both methods. Regenerate the Rust capability files; track/song/artist fields now render as their named types (TrackInfo, SongRef, ArtistRef, etc.). Add a regression test that verifies a struct field whose type is only in SharedAliases renders as the named type and not serde_json::Value. * docs(plugins): remove stale Python references from ndpgen and plugins READMEs ndpgen no longer has a -python flag; remove it from the usage synopsis, flags table, and defaults note in ndpgen/README.md. Delete the "Python Client Library" section that described its output. plugins/README.md referenced plugins/pdk/python/host/ (deleted) as the source for Python host-service stubs. Remove that paragraph; Python plugins still work via the XTP-schema / extism-py path (see examples/*-py). * refactor(plugins): dedupe ndpgen helpers and tidy shared-type codegen * docs(plugins): restore Python as a supported XTP schema target The ndpgen-generated Python PDK was removed, but the XTP YAML schemas are language-neutral and the XTP CLI still generates Python bindings from them (as the extism-py examples demonstrate). Only the ndpgen Python output was dropped, not Python support itself. * test(plugins): use the shared types package in test plugins The test fixtures referenced the now-deprecated capability aliases (sonicsimilarity.SongRef, metadata.ArtistRef/SongRef). Point them at the canonical types package so our own fixtures don't depend on symbols slated for removal. * refactor(plugins): use the shared types package in host adapters Replace deprecated capabilities.TrackInfo, capabilities.ArtistRef, and capabilities.SongRef aliases with the canonical types.TrackInfo, types.ArtistRef, and types.SongRef from plugins/types. * fix(plugins): reference shared types by canonical path in generated Rust Previously the generator emitted `pub field: SongRef` (the local deprecated alias) for struct fields whose type came from SharedAliases. Refactored ToRustTypeWithStructs into a private toRustType that accepts a shared map, and added ToRustTypeWithShared which resolves shared-alias names to their canonical nd_pdk_types::X path before falling through to the knownStructs check. Both rustCapabilityFuncMap and rustFuncMap now build the shared map from SharedAliases and use it for fieldRustType, so the generated capability files reference nd_pdk_types::SongRef / nd_pdk_types::TrackInfo directly. The deprecated pub type aliases remain in place as the external back-compat surface. Deprecation warning count from cargo build drops to 0. * fix(examples): implement missing Scrobbler.playback_report in Rust examples The Scrobbler trait gained a playback_report method but the two Rust example plugins (webhook-rs and discord-rich-presence-rs) were not updated, causing E0046 compile errors. Added the missing fn playback_report to both: webhook-rs logs and returns Ok(()) mirroring its now_playing handler; discord-rich-presence-rs is a no-op since Discord presence does not need playback reports. make all-rust now exits 0. * refactor(plugins): point Rust deprecation notes at the nd_pdk::types umbrella path Plugin authors depend on the nd-pdk umbrella crate, which re-exports nd_pdk_types as 'types', so the migration target they should type is nd_pdk::types::X. The alias target stays nd_pdk_types::X (the real path inside nd-pdk-capabilities). * fix(plugins): error when a shared-type alias can't be resolved against the registry * refactor(plugins): parse each Go source file once in ndpgen * fix(plugins): correct ndpgen review nits (flag name, unused dep, docs) * refactor(plugins): drop the now-unused path param from parseServiceFile * refactor(plugins): use shared types directly, rename TrackInfo to Track Capability interfaces now reference the shared `types` package by qualified name (types.Track, types.SongRef, types.ArtistRef) instead of the package-local deprecated aliases, and the shared TrackInfo type is renamed to Track to match its role as the plugin-facing projection of a library media file. The deprecated bare aliases (scrobbler.TrackInfo, metadata.ArtistRef, sonicsimilarity.SongRef, etc.) are kept as re-exports so existing plugins keep compiling, with a deprecation warning steering them to the canonical types. To support this, ndpgen now resolves qualified types.X references: it collects them during type discovery, maps each used canonical type back to its declared deprecated alias for re-export, emits nd_pdk_types::X paths in Rust, and names the XTP schema components by their canonical type. Regenerated the Go and Rust PDK and the XTP schemas, and added generator tests covering the qualified-ref path. Also adds clarifying doc comments to the shared types. * refactor(plugins): extract shared types selector into a named const Replace the "types." string literal that detects and strips the shared types package selector with a single sharedTypesPrefix constant across the ndpgen generator (parser, types, generator, xtp_schema), giving the package one source of truth for the selector. Also restore the single reused scratch map (cleared each iteration) in the resolveSharedAliases BFS instead of allocating a fresh map per shared-struct field, matching the prior implementation. Pure cleanup from a /simplify pass: regeneration produces byte-for-byte identical Go, Rust, and XTP output. * refactor(plugins): keep TrackInfo in the capability package for now Move the track type back out of the shared plugins/types package: it is again defined inline as TrackInfo in plugins/capabilities/scrobbler.go and referenced directly by the scrobbler and lyrics capabilities, reverting the rename to types.Track. The host helper is renamed back to mediaFileToTrackInfo and now returns capabilities.TrackInfo. SongRef and ArtistRef stay in the shared types package; TrackInfo keeps using types.ArtistRef for its artist lists. This type is expected to be reshaped in upcoming work, so leaving it in the capability package avoids churning the shared types twice. Regenerated the Go and Rust PDK and the XTP schemas accordingly. * fix(plugins): emit the Go types import for direct shared-type refs ndpgen's Capability/Service.ImportsSharedTypes only reported a shared-types dependency when a deprecated re-export alias (type X = types.X) was declared. A struct field referencing the canonical form directly (e.g. types.SongRef) with no such alias produced an empty SharedAliases slice, so the Go templates skipped the import while still emitting fields/signatures using types.SongRef — leaving generated PDK code for new shared DTOs uncompilable unless an otherwise unnecessary alias was added. ImportsSharedTypes now also returns true when any struct field references the types. package by qualified name, via a new structsReferenceSharedTypes helper that reuses collectReferencedTypes (so []types.X and map[...]types.X are covered too). * fix(plugins): preserve base64 encoding for shared byte fields in Rust The Rust shared-types crate template rendered a []byte field as a plain Vec<u8> without the base64_bytes serde override used by the capability/client templates. Go's encoding/json serializes []byte as a base64 string, so a Rust plugin using nd_pdk::types would have serialized an array of numbers instead of the wire format the Go/server side expects. GenerateSharedTypesRust now registers the base64_bytes partial and passes a HasByteFields flag (new anyFieldIsByteSlice helper); types.rs.tmpl emits the base64_bytes module and a #[serde(with = "base64_bytes")] attribute on []byte fields, mirroring the capability template. * fix(plugins): include directly-referenced shared types in XTP schemas buildSchemas registered shared types into the schema components only by iterating cap.SharedAliases, which records deprecated re-export aliases. A capability that referenced a shared DTO solely as types.Foo (no declared alias) therefore never got Foo into the component set, so the self-contained XTP schema rendered the field as a generic object (or emitted a dangling $ref), breaking the direct shared-type use case enabled by -shared. resolveSharedAliases now also returns the resolved shapes of every used shared type (alias or not); these are carried on the new Capability.SharedTypes field and registered by buildSchemas alongside SharedAliases. Validated end-to-end with the xtp CLI: a direct types.Foo reference now produces a proper component plus a $ref, so xtp generates a typed struct instead of an untyped serde_json::Map. * fix(plugins): resolve renamed shared aliases to canonical schema refs When a deprecated alias renames its canonical type (e.g. type TrackInfo = types.Track) and a capability field is typed with the alias name (TrackInfo), buildProperty emitted a $ref to #/components/schemas/TrackInfo. Components are keyed by the canonical name (Track), so no TrackInfo component was emitted, leaving a dangling reference that crashes the xtp code generator. buildSchemas now builds an alias->canonical map; buildProperty (and the slice item path) resolves $ref targets through it, and a used alias name marks its canonical component used so it is emitted. Validated with the xtp CLI: the renamed-alias schema previously crashed xtp and now generates cleanly. * fix(plugins): detect shared types used directly in method signatures ImportsSharedTypes only inspected struct fields, so a capability method using a shared type directly in its signature (e.g. types.SongRef as input/output rather than inside a local struct) was not detected. The generated Go templates still rendered the provider/export signatures with types.SongRef, so the capability package omitted the types import and failed to compile; the same gap applied to service params/returns. ImportsSharedTypes now also scans capability method input/output types and service method params/returns, via a typeReferencesSharedTypes helper that reuses collectReferencedTypes (covering pointer/slice/map wrappers). * fix(plugins): add base64 dependency to the shared Rust types crate When a shared DTO has a []byte field, ndpgen emits the base64_bytes serde helper and use base64::... imports into nd-pdk-types/src/lib.rs, but the crate manifest declared only serde. In that case make gen produced a crate that failed to compile with 'unresolved module base64'. Add base64 = "0.22" (matching nd-pdk-capabilities) so the generated shared types crate compiles whenever a []byte field is present. Verified by generating a shared crate with a []byte field and confirming cargo check fails before and passes after. * fix(plugins): translate shared method types in generated Rust A capability method using a shared DTO directly as input/output (e.g. types.SongRef) was passed through rustOutputType unchanged, so the Rust template emitted invalid trait and extism_pdk::Json<$crate::pkg::types.SongRef> signatures that do not compile. Method input/output types now resolve through the shared registry: trait signatures use rustTraitType (shared -> nd_pdk_types::X, locals stay bare) and the export macros use rustMethodType (fully qualified: shared -> nd_pdk_types::X, primitives -> Rust, locals -> $crate::<pkg>::X). Verified end-to-end by compiling a generated capability that takes types.SongRef directly against the real nd-pdk-types crate. * fix(plugins): canonicalize XTP export refs for renamed shared aliases buildSchemas canonicalized alias-to-canonical references for struct-field $ref targets, but buildExport built export input/output $refs straight from fieldBaseType. A capability whose export used a renamed deprecated alias directly (e.g. type TrackInfo = types.Track with NowPlaying(TrackInfo)) emitted $ref: #/components/schemas/TrackInfo, while the component is emitted under the canonical name Track — a dangling export reference. Lift the alias-to-canonical map into GenerateSchema (buildAliasToCanonical) and apply it to export refs via canonicalRefName, the same resolution already used for field properties. * fix(plugins): route shared macro types through $crate for plugin builds When a capability method used a shared type directly, the generated export macro named the type as nd_pdk_types::SongRef. The macro expands in the downstream plugin crate, which depends on the umbrella nd-pdk crate and not on nd-pdk-types directly, so that path is unresolvable there and the plugin fails to build. rustMethodType (macro-facing) now emits $crate::types::X, and the generated nd-pdk-capabilities lib.rs re-exports nd_pdk_types as types so $crate resolves it. Trait signatures keep nd_pdk_types::X since they live in nd-pdk-capabilities, which has the direct dependency. Verified end-to-end: a plugin crate depending only on the umbrella that uses a capability with a direct types.X method now compiles via the macro. * fix(plugins): add nd-pdk-types dependency to the Rust host crate When a host service uses a shared type, ndpgen emits nd_pdk_types::X into the generated nd-pdk-host client wrappers, but the host crate's manifest did not depend on nd-pdk-types, so the crate failed to compile with 'unresolved module nd_pdk_types'. Host client wrappers are plain functions resolved in the host crate's own context (not macros expanded downstream), so a direct dependency is the right fix. Add nd-pdk-types = { path = "../nd-pdk-types" } to nd-pdk-host, mirroring nd-pdk-capabilities. Found while auditing all Rust paths against the realistic crate topology after the capability-side $crate fix; verified by generating a host service with a shared-type return and confirming cargo check fails before and passes after. * fix(plugins): resolve shared aliases in Rust host signatures The Rust host client rendered method params and returns through RustTypeWithStructs, which only consults KnownStructs. A host service using a shared alias in a signature (e.g. type Track = types.Track plus MatchSongs(...) ([]Track, error)) therefore emitted a bare Vec<Track>, but the client template emits no Track alias or import, so the generated nd-pdk-host crate did not compile. Only struct fields went through the shared map. rustType/rustParamType now use the shared map too (RustTypeWithShared / RustParamTypeWithShared), so an aliased param/return resolves to its canonical nd_pdk_types::X path, matching field handling. Verified by generating a host service returning a shared alias and confirming cargo check fails before and passes after.
2026-06-29 21:20:33 -04:00
Songs []types.SongRef `json:"songs"`
}
fix(scrobbler): back off when a provider asks us to, instead of retrying per play (#6028) * feat(agents): retry-later error type with optional server delay Add agents.ErrRetryLater and agents.RetryLaterError, which carries the delay requested by an external service (e.g. ListenBrainz's X-RateLimit-Reset-In). scrobbler.ErrRetryLater becomes an alias of the new sentinel, so existing errors.Is checks and the plugin error-string protocol keep working unchanged. Groundwork for honoring server-requested retry delays across scrobbling, metadata agents and artwork. Song.Equals tests moved to song_test.go to enable external test package. * fix(scrobbler): honor backoff window and server-requested retry delay ListenBrainz 429s were decoded into a typed error that classified as unrecoverable, silently discarding the scrobble (a JSON-bodied 429 was measured live). The client now maps any 429 to agents.RetryLaterError, carrying X-RateLimit-Reset-In when present (capped at 1h). Last.fm error 29 (rate limit) is now retryable like 11/16. The buffer's drain loop no longer lets wake signals bypass an active backoff window - new plays enqueue but drain only when the window closes - and the wait honors the server delay via max(backoff, retryIn). * feat(agents): skip cooling-down agents in aggregate calls When an agent reports retry-later, remember a per-agent cooldown deadline (the server-requested delay, or 1 minute when unspecified) and skip that agent in all aggregate metadata calls until it passes. A round that found no data but skipped or saw a throttled agent returns ErrRetryLater instead of ErrNotFound, so callers cannot mistake rate limiting for a definitive 'no data' answer. * feat(artwork): honor server-requested retry delay when rescheduling When an external image lookup fails with a retry-later error carrying a delay (e.g. a 429 with X-RateLimit-Reset-In), the chain trace carries the largest such hint back to the worker, which reschedules the item at max(exponential backoff, server delay) instead of backoff alone. * feat(plugins): retry-later with optional delay for scrobbler and agent plugins Scrobbler plugins can now return scrobbler(retry_later:N) to request a retry in N seconds (capped at 1h); the bare token keeps its old meaning. Metadata-agent plugins, which had no error vocabulary at all, gain the parallel agent(retry_later[:N]) token, mapped to agents.RetryLaterError so the aggregate's cooldown and the artwork worker honor plugin throttling the same way as built-in agents. * fix: address whole-branch review findings for retry-later handling Narrow the aggregate's throttled rule to the spec sentence: core.Agents returns ErrRetryLater only when no agent answered at all (all skipped-cooling or retry-later). An agent that does not implement the called method now returns an internal errUnsupported instead of ErrNotFound, so it counts as "did not run" — without that, the always-appended local agent would answer for biography, URL and images and make ErrRetryLater unreachable. Wire the consequence in core/external: a throttled round no longer stamps ExternalInfoUpdatedAt (artist and album), so the empty result is not cached for the TTL, and TopSongs maps ErrRetryLater to the same empty-200 the not-found path already produced instead of a new client-facing error. Move the Last.fm code-29 mapping into the client's central error construction so every metadata path produces RetryLaterError, and map ListenBrainz's body-level code 429 (sent with a non-429 HTTP status) the same way. Clamp server- and plugin-requested delays in seconds before scaling to a Duration, in all three parse sites: a header of 18446744074 wrapped past 2^64 and came out as a 0.29s delay. Also: extract the artwork worker's reschedule computation into retryDelay() and cover both it and the trace RetryIn wiring with tests; collapse the double regex call in mapScrobblerError; drop capabilities.ScrobblerErrorRetryLaterIn (ndpgen never emits funcs, so plugin authors could not reach it); regenerate the PDKs so MetadataAgentError reaches the Go and Rust SDKs; de-flake the cooldown tests (long RetryIn for the skip case, separate expiry spec); and cover the max() retry-delay aggregation across users in the scrobble buffer. * refactor: dedupe retry-later parsing and simplify error collection - Add agents.NewRetryLater and agents.RetryLaterFromSeconds, with a single 1h cap, replacing the parse+clamp+multiply logic and the maxRetryInSeconds constant duplicated across listenbrainz, plugins and the agent adapter. - Move HTTP header parsing to httpclient.RetryAfter, so the transport layer owns it and stays domain-agnostic; drop retryInFromHeaders from the ListenBrainz client. Covered by a new Ginkgo table in that package. - Collapse the two near-identical plugin retry_later regexes into one parseRetryLater(prefix, msg) shared by the agent and scrobbler adapters. - Fold the duplicated noteRetryIn snippet from fetchArtistImage and fetchAlbumImage into recordAgent, which already branched on the same isTransientExternal condition. - Replace the atomic.Bool + note() closure in populateArtistInfo with errgroup's own error collection; the group carries no context, so a returned error does not cancel its siblings. - Reuse recoveringScrobbler for the per-user delay test instead of a third double, and switch fakeScrobbler's mutex-guarded error to the atomic.Pointer idiom already used in the same package. * refactor(listenbrainz): keep rate-limit header parsing in the adapter The X-RateLimit-Reset-In header is ListenBrainz's own convention, not a shared one: Last.fm sends no rate-limit headers at all and reports its limit as a body code, and no other integration in tree sends Retry-After. A parser in utils/httpclient implied a uniformity across services that does not exist, so it moves back next to the only client that can know which header its service sends. * refactor(agents): collapse the retry-later sentinel and error into one type ErrRetryLater is now the zero-delay RetryLaterError rather than a separate errors.New value, so errors.Is and errors.AsType both match the sentinel and every delay-carrying variant. That removes the trap where a bare sentinel silently skipped the AsType path, and lets every consumer read the delay off the error directly: the RetryIn accessor and the two constructors are gone, with the policy cap applied where untrusted input is parsed. * refactor(agents): split the cooldown store from the per-dispatch tally The cooldown map and mutex become a cooldowns value with active/park, holding no knowledge of errors; agentAttempts records one dispatch's outcomes and owns the classification that noteAgentError used to hide behind a bool. The three dispatch loops now touch a single object: skip folds the cooldown check and the throttled flag into one call, so the store never appears in the loops. * refactor(agents): share one dispatch loop between the agent call helpers callAgentMethod and callAgentSliceMethod ran identical loops, differing only in how they test a result for emptiness: a slice cannot be compared against its zero value, so the two could not share a constraint. Both now delegate to callAgent, which takes that test as a parameter. Keeping the loop in one place matters more than the lines saved: it holds the cooldown skip, the attempt recording and the empty-dispatch verdict, and a fix applied to one copy but not the other would be silent. * test: cover the two retry-later paths a mutation could break silently Both gaps were proven, not guessed: making the artwork worker pass 0 instead of the collected hint left all 386 specs green, and replacing the default agent cooldown with 0 left the agents suite green. The worker test drives a throttled image agent through drain and asserts the persisted retry_at, and the cooldown test parks an agent that asked to be retried without naming a delay, which is what Last.fm does on every rate limit. * refactor(artwork): carry the external failure as an error, not a flag plus a trace field The retry delay was riding on ChainTrace, a diagnostic that gets persisted, while the very same signal — an external source faulted — already travelled by value as resolution.extError. That was two mechanisms for one idea, and it put control-flow state inside a serializable trace. resolution.extError and chainState.extErr become the error itself, so a caller checks err != nil for the fault and errors.AsType for the delay the provider asked for. The agent loops return that error last, per convention, and longerRetry keeps whichever failure wants the longer wait. ChainTrace goes back to holding only steps and no longer imports core/agents. * fix(artwork): check the resolve error before reading its resolution Reading res.extError before the err check was safe only because every error path in resolve returns a bare resolution{}; a future path returning a partly-filled one would have been read silently. The failure path now returns no delay explicitly. * test(artwork): assert the delay acquire reports, not just its downstream effect acquire's retry delay was only covered through the worker's persisted retry_at, one layer away from where the value is computed. Both outcomes are now pinned at the processor: a plain failure asks for nothing, a throttled provider's delay is passed through. * refactor: share the retry-seconds parse and drop the backoff deadline arithmetic The clamp-before-scaling invariant lived in two parsers and was independently re-tested in three files with the same magic number; a fix applied to one copy would have left the others wrapping a huge value down to a fraction of a second. It moves to agents.ParseRetryIn. The buffer tracked an absolute retryDeadline only to re-arm a timer that was already armed for the same instant; a backingOff flag says the same thing without the arithmetic. The plugin token regex now carries its capability in the pattern instead of capturing and comparing, so another capability's token in the same message cannot mask it. resolution.extError becomes extErr, matching its chainState counterpart. * fix(agents): keep the longer cooldown when parks overlap Calls to one agent overlap, so a short cooldown could land after a long one started and cut it short. park now keeps whichever deadline is later, matching the rule longerRetry already applies on the artwork side. No in-tree provider can currently produce two different delays for the same agent, so this is hardening rather than a fix for observed behaviour. * fix(agents): parse the retry delay at a fixed width strconv.Atoi parses into the native int, so on the 32-bit targets we ship (linux/386, windows/386, three ARM variants) a delay above MaxInt32 seconds overflowed and became unspecified instead of being capped. No provider sends a 68-year delay, so this is not user-visible, but the overflow tests asserted the cap and would have failed on those architectures, where tests never run. * fix(plugins): anchor the retry_later regex to a word boundary Prevents a superstring like useragent(retry_later) from matching the agent capability token.
2026-08-29 17:28:29 -04:00
// MetadataAgentError represents an error type for metadata agent operations.
type MetadataAgentError string
const (
// MetadataAgentErrorRetryLater indicates the provider is throttling; retry later.
// Append ":<seconds>" inside the parentheses to request a specific delay.
MetadataAgentErrorRetryLater MetadataAgentError = "agent(retry_later)"
)
// Error implements the error interface for MetadataAgentError.
func (e MetadataAgentError) Error() string { return string(e) }