* fix(plugins): add base64 handling for []byte and remove raw=true Go's json.Marshal automatically base64-encodes []byte fields, but Rust's serde_json serializes Vec<u8> as a JSON array and Python's json.dumps raises TypeError on bytes. This fixes both directions of plugin communication by adding proper base64 encoding/decoding in generated client code. For Rust templates (client and capability): adds a base64_bytes serde helper module with #[serde(with = "base64_bytes")] on all Vec<u8> fields, and adds base64 as a dependency. For Python templates: wraps bytes params with base64.b64encode() and responses with base64.b64decode(). Also removes the raw=true binary framing protocol from all templates, the parser, and the Method type. The raw mechanism added complexity that is no longer needed once []byte works properly over JSON. * fix(plugins): update production code and tests for base64 migration Remove raw=true annotation from SubsonicAPI.CallRaw, delete all raw test fixtures, remove raw-related test cases from parser, generator, and integration tests, and add new test cases validating base64 handling for Rust and Python templates. * fix(plugins): update golden files and regenerate production code Update golden test fixtures for codec and comprehensive services to include base64 handling for []byte fields. Regenerate all production PDK code (Go, Rust, Python) and host wrappers to use standard JSON with base64-encoded byte fields instead of binary framing protocol. * refactor: remove base64 helper duplication from rust template Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): add base64 dependency to capabilities' Cargo.toml Signed-off-by: Deluan <deluan@navidrome.org> --------- Signed-off-by: Deluan <deluan@navidrome.org>
Navidrome Host Function Wrappers for Rust
This directory contains auto-generated Rust wrappers for Navidrome's host services. These wrappers provide idiomatic Rust APIs for interacting with Navidrome from WASM plugins.
⚠️ Auto-Generated Code
Do not edit these files manually. They are generated by the ndpgen tool.
To regenerate:
make gen
Usage
Add this crate as a dependency in your plugin's Cargo.toml:
[dependencies]
nd-host = { path = "../../pdk/rust/host" }
Then import the services you need:
use nd_host::{cache, scheduler, library};
use nd_host::library::Library; // Import the typed struct
#[plugin_fn]
pub fn my_callback(input: String) -> FnResult<String> {
// Use the cache service
cache::set("my_key", b"my_value", 3600)?;
// Schedule a recurring task
scheduler::schedule_recurring("@every 5m", "payload", "task_id")?;
// Access library data with typed structs
let libraries: Vec<Library> = library::get_all_libraries()?;
for lib in &libraries {
info!("Library: {} with {} songs", lib.name, lib.total_songs);
}
Ok("done".to_string())
}
Typed Structs
Services that work with domain objects provide typed Rust structs instead of
serde_json::Value. This enables compile-time type checking and IDE
autocompletion.
For example, the library module provides a Library struct:
use nd_host::library::Library;
let libs: Vec<Library> = library::get_all_libraries()?;
println!("First library: {} ({} songs)", libs[0].name, libs[0].total_songs);
All structs derive Debug, Clone, Serialize, and Deserialize for
convenient use with logging and serialization.
Available Services
| Module | Description |
|---|---|
artwork |
Access album and artist artwork |
cache |
Temporary key-value storage with TTL |
kvstore |
Persistent key-value storage |
library |
Access the music library (albums, artists, tracks) |
scheduler |
Schedule one-time and recurring tasks |
subsonicapi |
Make Subsonic API calls |
websocket |
Send real-time messages to clients |
Building Plugins
Rust plugins must be compiled to WebAssembly:
cargo build --target wasm32-wasip1 --release
See the webhook-rs example for a complete plugin implementation.