feat: generate PDK code for PlaylistGenerator capability

Generated via ndpgen: Go PDK wrappers, Rust PDK wrappers,
and XTP schema for non-Go plugins.
This commit is contained in:
Deluan
2026-03-05 08:02:31 -05:00
parent 4908dae21f
commit dedaf8e64a
5 changed files with 535 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
version: v1-draft
exports:
nd_playlist_generator_get_playlists:
description: GetPlaylists returns the list of playlists this plugin provides.
input:
$ref: '#/components/schemas/GetPlaylistsRequest'
contentType: application/json
output:
$ref: '#/components/schemas/GetPlaylistsResponse'
contentType: application/json
nd_playlist_generator_get_playlist:
description: GetPlaylist returns the full data for a single playlist (tracks, metadata).
input:
$ref: '#/components/schemas/GetPlaylistRequest'
contentType: application/json
output:
$ref: '#/components/schemas/GetPlaylistResponse'
contentType: application/json
components:
schemas:
GetPlaylistRequest:
description: GetPlaylistRequest is the request for GetPlaylist.
properties:
id:
type: string
description: ID is the plugin-scoped playlist ID.
required:
- id
GetPlaylistResponse:
description: GetPlaylistResponse is the response for GetPlaylist.
properties:
name:
type: string
description: Name is the display name of the playlist.
description:
type: string
description: Description is an optional description for the playlist.
coverArtUrl:
type: string
description: CoverArtURL is an optional external URL for the playlist cover art.
tracks:
type: array
description: Tracks is the list of songs in the playlist, using SongRef for matching.
items:
$ref: '#/components/schemas/SongRef'
validUntil:
type: integer
format: int64
description: |-
ValidUntil is a unix timestamp indicating when this playlist data expires.
0 means static (never refresh).
required:
- name
- tracks
- validUntil
GetPlaylistsRequest:
description: GetPlaylistsRequest is the request for GetPlaylists.
properties: {}
GetPlaylistsResponse:
description: GetPlaylistsResponse is the response for GetPlaylists.
properties:
playlists:
type: array
description: Playlists is the list of playlists provided by this plugin.
items:
$ref: '#/components/schemas/PlaylistInfo'
refreshInterval:
type: integer
format: int64
description: |-
RefreshInterval is the number of seconds until the next GetPlaylists call.
0 means never re-discover.
required:
- playlists
- refreshInterval
PlaylistInfo:
description: PlaylistInfo identifies a plugin playlist and its target user.
properties:
id:
type: string
description: ID is the plugin-scoped unique identifier for this playlist.
ownerUserId:
type: string
description: OwnerUserID is the Navidrome user ID that owns this playlist.
required:
- id
- ownerUserId
SongRef:
description: SongRef is a reference to a song with metadata for matching.
properties:
id:
type: string
description: ID is the internal Navidrome mediafile ID (if known).
name:
type: string
description: Name is the song name.
mbid:
type: string
description: MBID is the MusicBrainz ID for the song.
isrc:
type: string
description: ISRC is the International Standard Recording Code for the song.
artist:
type: string
description: Artist is the artist name.
artistMbid:
type: string
description: ArtistMBID is the MusicBrainz artist ID.
album:
type: string
description: Album is the album name.
albumMbid:
type: string
description: AlbumMBID is the MusicBrainz release ID.
duration:
type: number
format: float
description: Duration is the song duration in seconds.
required:
- name

View File

@@ -0,0 +1,157 @@
// Code generated by ndpgen. DO NOT EDIT.
//
// This file contains export wrappers for the PlaylistGenerator capability.
// It is intended for use in Navidrome plugins built with TinyGo.
//
//go:build wasip1
package playlistgenerator
import (
"github.com/navidrome/navidrome/plugins/pdk/go/pdk"
)
// GetPlaylistRequest is the request for GetPlaylist.
type GetPlaylistRequest struct {
// ID is the plugin-scoped playlist ID.
ID string `json:"id"`
}
// GetPlaylistResponse is the response for GetPlaylist.
type GetPlaylistResponse struct {
// Name is the display name of the playlist.
Name string `json:"name"`
// Description is an optional description for the playlist.
Description string `json:"description,omitempty"`
// CoverArtURL is an optional external URL for the playlist cover art.
CoverArtURL string `json:"coverArtUrl,omitempty"`
// Tracks is the list of songs in the playlist, using SongRef for matching.
Tracks []SongRef `json:"tracks"`
// ValidUntil is a unix timestamp indicating when this playlist data expires.
// 0 means static (never refresh).
ValidUntil int64 `json:"validUntil"`
}
// GetPlaylistsRequest is the request for GetPlaylists.
type GetPlaylistsRequest struct {
}
// GetPlaylistsResponse is the response for GetPlaylists.
type GetPlaylistsResponse struct {
// Playlists is the list of playlists provided by this plugin.
Playlists []PlaylistInfo `json:"playlists"`
// RefreshInterval is the number of seconds until the next GetPlaylists call.
// 0 means never re-discover.
RefreshInterval int64 `json:"refreshInterval"`
}
// PlaylistInfo identifies a plugin playlist and its target user.
type PlaylistInfo struct {
// ID is the plugin-scoped unique identifier for this playlist.
ID string `json:"id"`
// OwnerUserID is the Navidrome user ID that owns this playlist.
OwnerUserID string `json:"ownerUserId"`
}
// SongRef is a reference to a song with metadata for matching.
type SongRef struct {
// ID is the internal Navidrome mediafile ID (if known).
ID string `json:"id,omitempty"`
// Name is the song name.
Name string `json:"name"`
// MBID is the MusicBrainz ID for the song.
MBID string `json:"mbid,omitempty"`
// ISRC is the International Standard Recording Code for the song.
ISRC string `json:"isrc,omitempty"`
// Artist is the artist name.
Artist string `json:"artist,omitempty"`
// ArtistMBID is the MusicBrainz artist ID.
ArtistMBID string `json:"artistMbid,omitempty"`
// Album is the album name.
Album string `json:"album,omitempty"`
// AlbumMBID is the MusicBrainz release ID.
AlbumMBID string `json:"albumMbid,omitempty"`
// Duration is the song duration in seconds.
Duration float32 `json:"duration,omitempty"`
}
// PlaylistGenerator requires all methods to be implemented.
// PlaylistGenerator provides dynamically-generated playlists (e.g., "Daily Mix",
// personalized recommendations). Plugins implementing this capability expose two
// functions: GetPlaylists for lightweight discovery and GetPlaylist for fetching
// the heavy payload (tracks, metadata).
type PlaylistGenerator interface {
// GetPlaylists - GetPlaylists returns the list of playlists this plugin provides.
GetPlaylists(GetPlaylistsRequest) (GetPlaylistsResponse, error)
// GetPlaylist - GetPlaylist returns the full data for a single playlist (tracks, metadata).
GetPlaylist(GetPlaylistRequest) (GetPlaylistResponse, error)
} // Internal implementation holders
var (
playlistsImpl func(GetPlaylistsRequest) (GetPlaylistsResponse, error)
playlistImpl func(GetPlaylistRequest) (GetPlaylistResponse, error)
)
// Register registers a playlistgenerator implementation.
// All methods are required.
func Register(impl PlaylistGenerator) {
playlistsImpl = impl.GetPlaylists
playlistImpl = impl.GetPlaylist
}
// NotImplementedCode is the standard return code for unimplemented functions.
// The host recognizes this and skips the plugin gracefully.
const NotImplementedCode int32 = -2
//go:wasmexport nd_playlist_generator_get_playlists
func _NdPlaylistGeneratorGetPlaylists() int32 {
if playlistsImpl == nil {
// Return standard code - host will skip this plugin gracefully
return NotImplementedCode
}
var input GetPlaylistsRequest
if err := pdk.InputJSON(&input); err != nil {
pdk.SetError(err)
return -1
}
output, err := playlistsImpl(input)
if err != nil {
pdk.SetError(err)
return -1
}
if err := pdk.OutputJSON(output); err != nil {
pdk.SetError(err)
return -1
}
return 0
}
//go:wasmexport nd_playlist_generator_get_playlist
func _NdPlaylistGeneratorGetPlaylist() int32 {
if playlistImpl == nil {
// Return standard code - host will skip this plugin gracefully
return NotImplementedCode
}
var input GetPlaylistRequest
if err := pdk.InputJSON(&input); err != nil {
pdk.SetError(err)
return -1
}
output, err := playlistImpl(input)
if err != nil {
pdk.SetError(err)
return -1
}
if err := pdk.OutputJSON(output); err != nil {
pdk.SetError(err)
return -1
}
return 0
}

View File

@@ -0,0 +1,92 @@
// Code generated by ndpgen. DO NOT EDIT.
//
// This file provides stub implementations for non-WASM platforms.
// It allows Go plugins to compile and run tests outside of WASM,
// but the actual functionality is only available in WASM builds.
//
//go:build !wasip1
package playlistgenerator
// GetPlaylistRequest is the request for GetPlaylist.
type GetPlaylistRequest struct {
// ID is the plugin-scoped playlist ID.
ID string `json:"id"`
}
// GetPlaylistResponse is the response for GetPlaylist.
type GetPlaylistResponse struct {
// Name is the display name of the playlist.
Name string `json:"name"`
// Description is an optional description for the playlist.
Description string `json:"description,omitempty"`
// CoverArtURL is an optional external URL for the playlist cover art.
CoverArtURL string `json:"coverArtUrl,omitempty"`
// Tracks is the list of songs in the playlist, using SongRef for matching.
Tracks []SongRef `json:"tracks"`
// ValidUntil is a unix timestamp indicating when this playlist data expires.
// 0 means static (never refresh).
ValidUntil int64 `json:"validUntil"`
}
// GetPlaylistsRequest is the request for GetPlaylists.
type GetPlaylistsRequest struct {
}
// GetPlaylistsResponse is the response for GetPlaylists.
type GetPlaylistsResponse struct {
// Playlists is the list of playlists provided by this plugin.
Playlists []PlaylistInfo `json:"playlists"`
// RefreshInterval is the number of seconds until the next GetPlaylists call.
// 0 means never re-discover.
RefreshInterval int64 `json:"refreshInterval"`
}
// PlaylistInfo identifies a plugin playlist and its target user.
type PlaylistInfo struct {
// ID is the plugin-scoped unique identifier for this playlist.
ID string `json:"id"`
// OwnerUserID is the Navidrome user ID that owns this playlist.
OwnerUserID string `json:"ownerUserId"`
}
// SongRef is a reference to a song with metadata for matching.
type SongRef struct {
// ID is the internal Navidrome mediafile ID (if known).
ID string `json:"id,omitempty"`
// Name is the song name.
Name string `json:"name"`
// MBID is the MusicBrainz ID for the song.
MBID string `json:"mbid,omitempty"`
// ISRC is the International Standard Recording Code for the song.
ISRC string `json:"isrc,omitempty"`
// Artist is the artist name.
Artist string `json:"artist,omitempty"`
// ArtistMBID is the MusicBrainz artist ID.
ArtistMBID string `json:"artistMbid,omitempty"`
// Album is the album name.
Album string `json:"album,omitempty"`
// AlbumMBID is the MusicBrainz release ID.
AlbumMBID string `json:"albumMbid,omitempty"`
// Duration is the song duration in seconds.
Duration float32 `json:"duration,omitempty"`
}
// PlaylistGenerator requires all methods to be implemented.
// PlaylistGenerator provides dynamically-generated playlists (e.g., "Daily Mix",
// personalized recommendations). Plugins implementing this capability expose two
// functions: GetPlaylists for lightweight discovery and GetPlaylist for fetching
// the heavy payload (tracks, metadata).
type PlaylistGenerator interface {
// GetPlaylists - GetPlaylists returns the list of playlists this plugin provides.
GetPlaylists(GetPlaylistsRequest) (GetPlaylistsResponse, error)
// GetPlaylist - GetPlaylist returns the full data for a single playlist (tracks, metadata).
GetPlaylist(GetPlaylistRequest) (GetPlaylistResponse, error)
}
// NotImplementedCode is the standard return code for unimplemented functions.
const NotImplementedCode int32 = -2
// Register is a no-op on non-WASM platforms.
// This stub allows code to compile outside of WASM.
func Register(_ PlaylistGenerator) {}

View File

@@ -8,6 +8,7 @@
pub mod lifecycle;
pub mod lyrics;
pub mod metadata;
pub mod playlistgenerator;
pub mod scheduler;
pub mod scrobbler;
pub mod taskworker;

View File

@@ -0,0 +1,165 @@
// Code generated by ndpgen. DO NOT EDIT.
//
// This file contains export wrappers for the PlaylistGenerator capability.
// It is intended for use in Navidrome plugins built with extism-pdk.
use serde::{Deserialize, Serialize};
// Helper functions for skip_serializing_if with numeric types
#[allow(dead_code)]
fn is_zero_i32(value: &i32) -> bool { *value == 0 }
#[allow(dead_code)]
fn is_zero_u32(value: &u32) -> bool { *value == 0 }
#[allow(dead_code)]
fn is_zero_i64(value: &i64) -> bool { *value == 0 }
#[allow(dead_code)]
fn is_zero_u64(value: &u64) -> bool { *value == 0 }
#[allow(dead_code)]
fn is_zero_f32(value: &f32) -> bool { *value == 0.0 }
#[allow(dead_code)]
fn is_zero_f64(value: &f64) -> bool { *value == 0.0 }
/// GetPlaylistRequest is the request for GetPlaylist.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetPlaylistRequest {
/// ID is the plugin-scoped playlist ID.
#[serde(default)]
pub id: String,
}
/// GetPlaylistResponse is the response for GetPlaylist.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetPlaylistResponse {
/// Name is the display name of the playlist.
#[serde(default)]
pub name: String,
/// Description is an optional description for the playlist.
#[serde(default, skip_serializing_if = "String::is_empty")]
pub description: String,
/// CoverArtURL is an optional external URL for the playlist cover art.
#[serde(default, skip_serializing_if = "String::is_empty")]
pub cover_art_url: String,
/// Tracks is the list of songs in the playlist, using SongRef for matching.
#[serde(default)]
pub tracks: Vec<SongRef>,
/// ValidUntil is a unix timestamp indicating when this playlist data expires.
/// 0 means static (never refresh).
#[serde(default)]
pub valid_until: i64,
}
/// GetPlaylistsRequest is the request for GetPlaylists.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetPlaylistsRequest {
}
/// GetPlaylistsResponse is the response for GetPlaylists.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetPlaylistsResponse {
/// Playlists is the list of playlists provided by this plugin.
#[serde(default)]
pub playlists: Vec<PlaylistInfo>,
/// RefreshInterval is the number of seconds until the next GetPlaylists call.
/// 0 means never re-discover.
#[serde(default)]
pub refresh_interval: i64,
}
/// PlaylistInfo identifies a plugin playlist and its target user.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PlaylistInfo {
/// ID is the plugin-scoped unique identifier for this playlist.
#[serde(default)]
pub id: String,
/// OwnerUserID is the Navidrome user ID that owns this playlist.
#[serde(default)]
pub owner_user_id: String,
}
/// SongRef is a reference to a song with metadata for matching.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SongRef {
/// ID is the internal Navidrome mediafile ID (if known).
#[serde(default, skip_serializing_if = "String::is_empty")]
pub id: String,
/// Name is the song name.
#[serde(default)]
pub name: String,
/// MBID is the MusicBrainz ID for the song.
#[serde(default, skip_serializing_if = "String::is_empty")]
pub mbid: String,
/// ISRC is the International Standard Recording Code for the song.
#[serde(default, skip_serializing_if = "String::is_empty")]
pub isrc: String,
/// Artist is the artist name.
#[serde(default, skip_serializing_if = "String::is_empty")]
pub artist: String,
/// ArtistMBID is the MusicBrainz artist ID.
#[serde(default, skip_serializing_if = "String::is_empty")]
pub artist_mbid: String,
/// Album is the album name.
#[serde(default, skip_serializing_if = "String::is_empty")]
pub album: String,
/// AlbumMBID is the MusicBrainz release ID.
#[serde(default, skip_serializing_if = "String::is_empty")]
pub album_mbid: String,
/// Duration is the song duration in seconds.
#[serde(default, skip_serializing_if = "is_zero_f32")]
pub duration: f32,
}
/// Error represents an error from a capability method.
#[derive(Debug)]
pub struct Error {
pub message: String,
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}
impl std::error::Error for Error {}
impl Error {
pub fn new(message: impl Into<String>) -> Self {
Self { message: message.into() }
}
}
/// PlaylistGenerator requires all methods to be implemented.
/// PlaylistGenerator provides dynamically-generated playlists (e.g., "Daily Mix",
/// personalized recommendations). Plugins implementing this capability expose two
/// functions: GetPlaylists for lightweight discovery and GetPlaylist for fetching
/// the heavy payload (tracks, metadata).
pub trait PlaylistGenerator {
/// GetPlaylists - GetPlaylists returns the list of playlists this plugin provides.
fn get_playlists(&self, req: GetPlaylistsRequest) -> Result<GetPlaylistsResponse, Error>;
/// GetPlaylist - GetPlaylist returns the full data for a single playlist (tracks, metadata).
fn get_playlist(&self, req: GetPlaylistRequest) -> Result<GetPlaylistResponse, Error>;
}
/// Register all exports for the PlaylistGenerator capability.
/// This macro generates the WASM export functions for all trait methods.
#[macro_export]
macro_rules! register_playlistgenerator {
($plugin_type:ty) => {
#[extism_pdk::plugin_fn]
pub fn nd_playlist_generator_get_playlists(
req: extism_pdk::Json<$crate::playlistgenerator::GetPlaylistsRequest>
) -> extism_pdk::FnResult<extism_pdk::Json<$crate::playlistgenerator::GetPlaylistsResponse>> {
let plugin = <$plugin_type>::default();
let result = $crate::playlistgenerator::PlaylistGenerator::get_playlists(&plugin, req.into_inner())?;
Ok(extism_pdk::Json(result))
}
#[extism_pdk::plugin_fn]
pub fn nd_playlist_generator_get_playlist(
req: extism_pdk::Json<$crate::playlistgenerator::GetPlaylistRequest>
) -> extism_pdk::FnResult<extism_pdk::Json<$crate::playlistgenerator::GetPlaylistResponse>> {
let plugin = <$plugin_type>::default();
let result = $crate::playlistgenerator::PlaylistGenerator::get_playlist(&plugin, req.into_inner())?;
Ok(extism_pdk::Json(result))
}
};
}