feat(plugins): add similar songs retrieval functions and improve duration consistency (#4933)
* feat: add duration filtering for similar songs matching Signed-off-by: Deluan <deluan@navidrome.org> * test: refactor expectations for similar songs in provider matching tests Signed-off-by: Deluan <deluan@navidrome.org> * feat(plugins): add functions to retrieve similar songs by track, album, and artist Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): support uint32 in ndpgen Signed-off-by: Deluan <deluan@navidrome.org> * fix(plugins): update duration field to use seconds as float instead of milliseconds as uint32 Signed-off-by: Deluan <deluan@navidrome.org> * fix: add helper functions for Rust's skip_serializing_if with numeric types Signed-off-by: Deluan <deluan@navidrome.org> * feat(provider): enhance track matching logic to fallback to title match when duration-filtered tracks fail --------- Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
@@ -117,7 +117,53 @@ type SimilarArtistsResponse struct {
|
||||
Artists []ArtistRef `json:"artists"`
|
||||
}
|
||||
|
||||
// SongRef is a reference to a song with name and optional MBID.
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// SimilarSongsResponse is the response for GetSimilarSongsBy* functions.
|
||||
type SimilarSongsResponse struct {
|
||||
// Songs is the list of similar songs.
|
||||
Songs []SongRef `json:"songs"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
@@ -125,6 +171,16 @@ type SongRef struct {
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the song.
|
||||
MBID string `json:"mbid,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"`
|
||||
}
|
||||
|
||||
// TopSongsRequest is the request for GetArtistTopSongs.
|
||||
@@ -193,16 +249,34 @@ type AlbumInfoProvider interface {
|
||||
// AlbumImagesProvider provides the GetAlbumImages function.
|
||||
type AlbumImagesProvider interface {
|
||||
GetAlbumImages(AlbumRequest) (*AlbumImagesResponse, error)
|
||||
}
|
||||
|
||||
// SimilarSongsByTrackProvider provides the GetSimilarSongsByTrack function.
|
||||
type SimilarSongsByTrackProvider interface {
|
||||
GetSimilarSongsByTrack(SimilarSongsByTrackRequest) (*SimilarSongsResponse, error)
|
||||
}
|
||||
|
||||
// SimilarSongsByAlbumProvider provides the GetSimilarSongsByAlbum function.
|
||||
type SimilarSongsByAlbumProvider interface {
|
||||
GetSimilarSongsByAlbum(SimilarSongsByAlbumRequest) (*SimilarSongsResponse, error)
|
||||
}
|
||||
|
||||
// SimilarSongsByArtistProvider provides the GetSimilarSongsByArtist function.
|
||||
type SimilarSongsByArtistProvider interface {
|
||||
GetSimilarSongsByArtist(SimilarSongsByArtistRequest) (*SimilarSongsResponse, error)
|
||||
} // Internal implementation holders
|
||||
var (
|
||||
artistMBIDImpl func(ArtistMBIDRequest) (*ArtistMBIDResponse, error)
|
||||
artistURLImpl func(ArtistRequest) (*ArtistURLResponse, error)
|
||||
artistBiographyImpl func(ArtistRequest) (*ArtistBiographyResponse, error)
|
||||
similarArtistsImpl func(SimilarArtistsRequest) (*SimilarArtistsResponse, error)
|
||||
artistImagesImpl func(ArtistRequest) (*ArtistImagesResponse, error)
|
||||
artistTopSongsImpl func(TopSongsRequest) (*TopSongsResponse, error)
|
||||
albumInfoImpl func(AlbumRequest) (*AlbumInfoResponse, error)
|
||||
albumImagesImpl func(AlbumRequest) (*AlbumImagesResponse, error)
|
||||
artistMBIDImpl func(ArtistMBIDRequest) (*ArtistMBIDResponse, error)
|
||||
artistURLImpl func(ArtistRequest) (*ArtistURLResponse, error)
|
||||
artistBiographyImpl func(ArtistRequest) (*ArtistBiographyResponse, error)
|
||||
similarArtistsImpl func(SimilarArtistsRequest) (*SimilarArtistsResponse, error)
|
||||
artistImagesImpl func(ArtistRequest) (*ArtistImagesResponse, error)
|
||||
artistTopSongsImpl func(TopSongsRequest) (*TopSongsResponse, error)
|
||||
albumInfoImpl func(AlbumRequest) (*AlbumInfoResponse, error)
|
||||
albumImagesImpl func(AlbumRequest) (*AlbumImagesResponse, error)
|
||||
similarSongsByTrackImpl func(SimilarSongsByTrackRequest) (*SimilarSongsResponse, error)
|
||||
similarSongsByAlbumImpl func(SimilarSongsByAlbumRequest) (*SimilarSongsResponse, error)
|
||||
similarSongsByArtistImpl func(SimilarSongsByArtistRequest) (*SimilarSongsResponse, error)
|
||||
)
|
||||
|
||||
// Register registers a metadata implementation.
|
||||
@@ -232,6 +306,15 @@ func Register(impl Metadata) {
|
||||
if p, ok := impl.(AlbumImagesProvider); ok {
|
||||
albumImagesImpl = p.GetAlbumImages
|
||||
}
|
||||
if p, ok := impl.(SimilarSongsByTrackProvider); ok {
|
||||
similarSongsByTrackImpl = p.GetSimilarSongsByTrack
|
||||
}
|
||||
if p, ok := impl.(SimilarSongsByAlbumProvider); ok {
|
||||
similarSongsByAlbumImpl = p.GetSimilarSongsByAlbum
|
||||
}
|
||||
if p, ok := impl.(SimilarSongsByArtistProvider); ok {
|
||||
similarSongsByArtistImpl = p.GetSimilarSongsByArtist
|
||||
}
|
||||
}
|
||||
|
||||
// NotImplementedCode is the standard return code for unimplemented functions.
|
||||
@@ -453,3 +536,84 @@ func _NdGetAlbumImages() int32 {
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
//go:wasmexport nd_get_similar_songs_by_track
|
||||
func _NdGetSimilarSongsByTrack() int32 {
|
||||
if similarSongsByTrackImpl == nil {
|
||||
// Return standard code - host will skip this plugin gracefully
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input SimilarSongsByTrackRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
}
|
||||
|
||||
output, err := similarSongsByTrackImpl(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_get_similar_songs_by_album
|
||||
func _NdGetSimilarSongsByAlbum() int32 {
|
||||
if similarSongsByAlbumImpl == nil {
|
||||
// Return standard code - host will skip this plugin gracefully
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input SimilarSongsByAlbumRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
}
|
||||
|
||||
output, err := similarSongsByAlbumImpl(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_get_similar_songs_by_artist
|
||||
func _NdGetSimilarSongsByArtist() int32 {
|
||||
if similarSongsByArtistImpl == nil {
|
||||
// Return standard code - host will skip this plugin gracefully
|
||||
return NotImplementedCode
|
||||
}
|
||||
|
||||
var input SimilarSongsByArtistRequest
|
||||
if err := pdk.InputJSON(&input); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
}
|
||||
|
||||
output, err := similarSongsByArtistImpl(input)
|
||||
if err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
}
|
||||
|
||||
if err := pdk.OutputJSON(output); err != nil {
|
||||
pdk.SetError(err)
|
||||
return -1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -114,7 +114,53 @@ type SimilarArtistsResponse struct {
|
||||
Artists []ArtistRef `json:"artists"`
|
||||
}
|
||||
|
||||
// SongRef is a reference to a song with name and optional MBID.
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// SimilarSongsResponse is the response for GetSimilarSongsBy* functions.
|
||||
type SimilarSongsResponse struct {
|
||||
// Songs is the list of similar songs.
|
||||
Songs []SongRef `json:"songs"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
@@ -122,6 +168,16 @@ type SongRef struct {
|
||||
Name string `json:"name"`
|
||||
// MBID is the MusicBrainz ID for the song.
|
||||
MBID string `json:"mbid,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"`
|
||||
}
|
||||
|
||||
// TopSongsRequest is the request for GetArtistTopSongs.
|
||||
@@ -192,6 +248,21 @@ type AlbumImagesProvider interface {
|
||||
GetAlbumImages(AlbumRequest) (*AlbumImagesResponse, error)
|
||||
}
|
||||
|
||||
// SimilarSongsByTrackProvider provides the GetSimilarSongsByTrack function.
|
||||
type SimilarSongsByTrackProvider interface {
|
||||
GetSimilarSongsByTrack(SimilarSongsByTrackRequest) (*SimilarSongsResponse, error)
|
||||
}
|
||||
|
||||
// SimilarSongsByAlbumProvider provides the GetSimilarSongsByAlbum function.
|
||||
type SimilarSongsByAlbumProvider interface {
|
||||
GetSimilarSongsByAlbum(SimilarSongsByAlbumRequest) (*SimilarSongsResponse, error)
|
||||
}
|
||||
|
||||
// SimilarSongsByArtistProvider provides the GetSimilarSongsByArtist function.
|
||||
type SimilarSongsByArtistProvider interface {
|
||||
GetSimilarSongsByArtist(SimilarSongsByArtistRequest) (*SimilarSongsResponse, error)
|
||||
}
|
||||
|
||||
// NotImplementedCode is the standard return code for unimplemented functions.
|
||||
const NotImplementedCode int32 = -2
|
||||
|
||||
|
||||
@@ -4,6 +4,20 @@
|
||||
// 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 }
|
||||
/// AlbumImagesResponse is the response for GetAlbumImages.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -150,7 +164,72 @@ pub struct SimilarArtistsResponse {
|
||||
#[serde(default)]
|
||||
pub artists: Vec<ArtistRef>,
|
||||
}
|
||||
/// SongRef is a reference to a song with name and optional MBID.
|
||||
/// SimilarSongsByAlbumRequest is the request for GetSimilarSongsByAlbum.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SimilarSongsByAlbumRequest {
|
||||
/// ID is the internal Navidrome album ID.
|
||||
#[serde(default)]
|
||||
pub id: String,
|
||||
/// Name is the album name.
|
||||
#[serde(default)]
|
||||
pub name: String,
|
||||
/// Artist is the album artist name.
|
||||
#[serde(default)]
|
||||
pub artist: String,
|
||||
/// MBID is the MusicBrainz release ID (if known).
|
||||
#[serde(default, skip_serializing_if = "String::is_empty")]
|
||||
pub mbid: String,
|
||||
/// Count is the maximum number of similar songs to return.
|
||||
#[serde(default)]
|
||||
pub count: i32,
|
||||
}
|
||||
/// SimilarSongsByArtistRequest is the request for GetSimilarSongsByArtist.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SimilarSongsByArtistRequest {
|
||||
/// ID is the internal Navidrome artist ID.
|
||||
#[serde(default)]
|
||||
pub id: String,
|
||||
/// Name is the artist name.
|
||||
#[serde(default)]
|
||||
pub name: String,
|
||||
/// MBID is the MusicBrainz artist ID (if known).
|
||||
#[serde(default, skip_serializing_if = "String::is_empty")]
|
||||
pub mbid: String,
|
||||
/// Count is the maximum number of similar songs to return.
|
||||
#[serde(default)]
|
||||
pub count: i32,
|
||||
}
|
||||
/// SimilarSongsByTrackRequest is the request for GetSimilarSongsByTrack.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SimilarSongsByTrackRequest {
|
||||
/// ID is the internal Navidrome mediafile ID.
|
||||
#[serde(default)]
|
||||
pub id: String,
|
||||
/// Name is the track title.
|
||||
#[serde(default)]
|
||||
pub name: String,
|
||||
/// Artist is the artist name.
|
||||
#[serde(default)]
|
||||
pub artist: String,
|
||||
/// MBID is the MusicBrainz recording ID (if known).
|
||||
#[serde(default, skip_serializing_if = "String::is_empty")]
|
||||
pub mbid: String,
|
||||
/// Count is the maximum number of similar songs to return.
|
||||
#[serde(default)]
|
||||
pub count: i32,
|
||||
}
|
||||
/// SimilarSongsResponse is the response for GetSimilarSongsBy* functions.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SimilarSongsResponse {
|
||||
/// Songs is the list of similar songs.
|
||||
#[serde(default)]
|
||||
pub songs: Vec<SongRef>,
|
||||
}
|
||||
/// SongRef is a reference to a song with metadata for matching.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SongRef {
|
||||
@@ -163,6 +242,21 @@ pub struct SongRef {
|
||||
/// MBID is the MusicBrainz ID for the song.
|
||||
#[serde(default, skip_serializing_if = "String::is_empty")]
|
||||
pub mbid: 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,
|
||||
}
|
||||
/// TopSongsRequest is the request for GetArtistTopSongs.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
@@ -377,3 +471,66 @@ macro_rules! register_metadata_album_images {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// SimilarSongsByTrackProvider provides the GetSimilarSongsByTrack function.
|
||||
pub trait SimilarSongsByTrackProvider {
|
||||
fn get_similar_songs_by_track(&self, req: SimilarSongsByTrackRequest) -> Result<SimilarSongsResponse, Error>;
|
||||
}
|
||||
|
||||
/// Register the get_similar_songs_by_track export.
|
||||
/// This macro generates the WASM export function for this method.
|
||||
#[macro_export]
|
||||
macro_rules! register_metadata_similar_songs_by_track {
|
||||
($plugin_type:ty) => {
|
||||
#[extism_pdk::plugin_fn]
|
||||
pub fn nd_get_similar_songs_by_track(
|
||||
req: extism_pdk::Json<$crate::metadata::SimilarSongsByTrackRequest>
|
||||
) -> extism_pdk::FnResult<extism_pdk::Json<$crate::metadata::SimilarSongsResponse>> {
|
||||
let plugin = <$plugin_type>::default();
|
||||
let result = $crate::metadata::SimilarSongsByTrackProvider::get_similar_songs_by_track(&plugin, req.into_inner())?;
|
||||
Ok(extism_pdk::Json(result))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// SimilarSongsByAlbumProvider provides the GetSimilarSongsByAlbum function.
|
||||
pub trait SimilarSongsByAlbumProvider {
|
||||
fn get_similar_songs_by_album(&self, req: SimilarSongsByAlbumRequest) -> Result<SimilarSongsResponse, Error>;
|
||||
}
|
||||
|
||||
/// Register the get_similar_songs_by_album export.
|
||||
/// This macro generates the WASM export function for this method.
|
||||
#[macro_export]
|
||||
macro_rules! register_metadata_similar_songs_by_album {
|
||||
($plugin_type:ty) => {
|
||||
#[extism_pdk::plugin_fn]
|
||||
pub fn nd_get_similar_songs_by_album(
|
||||
req: extism_pdk::Json<$crate::metadata::SimilarSongsByAlbumRequest>
|
||||
) -> extism_pdk::FnResult<extism_pdk::Json<$crate::metadata::SimilarSongsResponse>> {
|
||||
let plugin = <$plugin_type>::default();
|
||||
let result = $crate::metadata::SimilarSongsByAlbumProvider::get_similar_songs_by_album(&plugin, req.into_inner())?;
|
||||
Ok(extism_pdk::Json(result))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// SimilarSongsByArtistProvider provides the GetSimilarSongsByArtist function.
|
||||
pub trait SimilarSongsByArtistProvider {
|
||||
fn get_similar_songs_by_artist(&self, req: SimilarSongsByArtistRequest) -> Result<SimilarSongsResponse, Error>;
|
||||
}
|
||||
|
||||
/// Register the get_similar_songs_by_artist export.
|
||||
/// This macro generates the WASM export function for this method.
|
||||
#[macro_export]
|
||||
macro_rules! register_metadata_similar_songs_by_artist {
|
||||
($plugin_type:ty) => {
|
||||
#[extism_pdk::plugin_fn]
|
||||
pub fn nd_get_similar_songs_by_artist(
|
||||
req: extism_pdk::Json<$crate::metadata::SimilarSongsByArtistRequest>
|
||||
) -> extism_pdk::FnResult<extism_pdk::Json<$crate::metadata::SimilarSongsResponse>> {
|
||||
let plugin = <$plugin_type>::default();
|
||||
let result = $crate::metadata::SimilarSongsByArtistProvider::get_similar_songs_by_artist(&plugin, req.into_inner())?;
|
||||
Ok(extism_pdk::Json(result))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,6 +4,20 @@
|
||||
// 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 }
|
||||
/// SchedulerCallbackRequest is the request provided when a scheduled task fires.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -4,6 +4,20 @@
|
||||
// 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 }
|
||||
/// ScrobblerError represents an error type for scrobbling operations.
|
||||
pub type ScrobblerError = &'static str;
|
||||
/// ScrobblerErrorNotAuthorized indicates the user is not authorized.
|
||||
|
||||
@@ -4,6 +4,20 @@
|
||||
// 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 }
|
||||
/// OnBinaryMessageRequest is the request provided when a binary message is received.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
Reference in New Issue
Block a user