mirror of
https://github.com/navidrome/navidrome.git
synced 2026-05-03 06:51:16 +00:00
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:
parent
4908dae21f
commit
dedaf8e64a
120
plugins/capabilities/playlist_generator.yaml
Normal file
120
plugins/capabilities/playlist_generator.yaml
Normal 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
|
||||||
157
plugins/pdk/go/playlistgenerator/playlistgenerator.go
Normal file
157
plugins/pdk/go/playlistgenerator/playlistgenerator.go
Normal 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
|
||||||
|
}
|
||||||
92
plugins/pdk/go/playlistgenerator/playlistgenerator_stub.go
Normal file
92
plugins/pdk/go/playlistgenerator/playlistgenerator_stub.go
Normal 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) {}
|
||||||
@ -8,6 +8,7 @@
|
|||||||
pub mod lifecycle;
|
pub mod lifecycle;
|
||||||
pub mod lyrics;
|
pub mod lyrics;
|
||||||
pub mod metadata;
|
pub mod metadata;
|
||||||
|
pub mod playlistgenerator;
|
||||||
pub mod scheduler;
|
pub mod scheduler;
|
||||||
pub mod scrobbler;
|
pub mod scrobbler;
|
||||||
pub mod taskworker;
|
pub mod taskworker;
|
||||||
|
|||||||
165
plugins/pdk/rust/nd-pdk-capabilities/src/playlistgenerator.rs
Normal file
165
plugins/pdk/rust/nd-pdk-capabilities/src/playlistgenerator.rs
Normal 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))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user