update
Some checks failed
Pipeline: Test, Lint, Build / Get version info (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test JS code (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint i18n files (push) Has been cancelled
Pipeline: Test, Lint, Build / Check Docker configuration (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v5) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v6) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v7) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to GHCR (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to Docker Hub (push) Has been cancelled
Pipeline: Test, Lint, Build / Cleanup digest artifacts (push) Has been cancelled
Pipeline: Test, Lint, Build / Build Windows installers (push) Has been cancelled
Pipeline: Test, Lint, Build / Package/Release (push) Has been cancelled
Pipeline: Test, Lint, Build / Upload Linux PKG (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled
POEditor import / update-translations (push) Has been cancelled

This commit is contained in:
2025-12-08 16:16:23 +01:00
commit c251f174ed
1349 changed files with 194301 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
# SubsonicAPI Demo Plugin
This example plugin demonstrates how to use the SubsonicAPI host service to access Navidrome's Subsonic API from within a plugin.
## What it does
The plugin performs the following operations during initialization:
1. **Ping the server**: Calls `/rest/ping` to check if the Subsonic API is responding
2. **Get license info**: Calls `/rest/getLicense` to retrieve server license information
## Key Features
- Shows how to request `subsonicapi` permission in the manifest
- Demonstrates making Subsonic API calls using the `subsonicapi.Call()` method
- Handles both successful responses and errors
- Uses proper lifecycle management with `OnInit`
## Usage
### Manifest Configuration
```json
{
"permissions": {
"subsonicapi": {
"reason": "Demonstrate accessing Navidrome's Subsonic API from within plugins",
"allowAdmins": true
}
}
}
```
### Plugin Implementation
```go
import "github.com/navidrome/navidrome/plugins/host/subsonicapi"
var subsonicService = subsonicapi.NewSubsonicAPIService()
// OnInit is called when the plugin is loaded
func (SubsonicAPIDemoPlugin) OnInit(ctx context.Context, req *api.InitRequest) (*api.InitResponse, error) {
// Make API calls
response, err := subsonicService.Call(ctx, &subsonicapi.CallRequest{
Url: "/rest/ping?u=admin",
})
// Handle response...
}
```
When running Navidrome with this plugin installed, it will automatically call the Subsonic API endpoints during the
server startup, and you can see the results in the logs:
```agsl
INFO[0000] 2022/01/01 00:00:00 SubsonicAPI Demo Plugin initializing...
DEBU[0000] API: New request /ping client=subsonicapi-demo username=admin version=1.16.1
DEBU[0000] API: Successful response endpoint=/ping status=OK
DEBU[0000] API: New request /getLicense client=subsonicapi-demo username=admin version=1.16.1
INFO[0000] 2022/01/01 00:00:00 SubsonicAPI ping response: {"subsonic-response":{"status":"ok","version":"1.16.1","type":"navidrome","serverVersion":"dev","openSubsonic":true}}
DEBU[0000] API: Successful response endpoint=/getLicense status=OK
DEBU[0000] Plugin initialized successfully elapsed=41.9ms plugin=subsonicapi-demo
INFO[0000] 2022/01/01 00:00:00 SubsonicAPI license info: {"subsonic-response":{"status":"ok","version":"1.16.1","type":"navidrome","serverVersion":"dev","openSubsonic":true,"license":{"valid":true}}}
```
## Important Notes
1. **Authentication**: The plugin must provide valid authentication parameters in the URL:
- **Required**: `u` (username) - The service validates this parameter is present
- Example: `"/rest/ping?u=admin"`
2. **URL Format**: Only the path and query parameters from the URL are used - host, protocol, and method are ignored
3. **Automatic Parameters**: The service automatically adds:
- `c`: Plugin name (client identifier)
- `v`: Subsonic API version (1.16.1)
- `f`: Response format (json)
4. **Internal Authentication**: The service sets up internal authentication using the `u` parameter
5. **Lifecycle**: This plugin uses `LifecycleManagement` with only the `OnInit` method
## Building
This plugin uses the `wasip1` build constraint and must be compiled for WebAssembly:
```bash
# Using the project's make target (recommended)
make plugin-examples
# Manual compilation (when using the proper toolchain)
GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o plugin.wasm plugin.go
```

View File

@@ -0,0 +1,16 @@
{
"$schema": "https://raw.githubusercontent.com/navidrome/navidrome/refs/heads/master/plugins/schema/manifest.schema.json",
"name": "subsonicapi-demo",
"author": "Navidrome Team",
"version": "1.0.0",
"description": "Example plugin demonstrating SubsonicAPI host service usage",
"website": "https://github.com/navidrome/navidrome",
"capabilities": ["LifecycleManagement"],
"permissions": {
"subsonicapi": {
"reason": "Demonstrate accessing Navidrome's Subsonic API from within plugins",
"allowAdmins": true,
"allowedUsernames": ["admin"]
}
}
}

View File

@@ -0,0 +1,68 @@
//go:build wasip1
package main
import (
"context"
"log"
"github.com/navidrome/navidrome/plugins/api"
"github.com/navidrome/navidrome/plugins/host/subsonicapi"
)
// SubsonicAPIService instance for making API calls
var subsonicService = subsonicapi.NewSubsonicAPIService()
// SubsonicAPIDemoPlugin implements LifecycleManagement interface
type SubsonicAPIDemoPlugin struct{}
// OnInit is called when the plugin is loaded
func (SubsonicAPIDemoPlugin) OnInit(ctx context.Context, req *api.InitRequest) (*api.InitResponse, error) {
log.Printf("SubsonicAPI Demo Plugin initializing...")
// Example: Call the ping endpoint to check if the server is alive
response, err := subsonicService.Call(ctx, &subsonicapi.CallRequest{
Url: "/rest/ping?u=admin",
})
if err != nil {
log.Printf("SubsonicAPI call failed: %v", err)
return &api.InitResponse{Error: err.Error()}, nil
}
if response.Error != "" {
log.Printf("SubsonicAPI returned error: %s", response.Error)
return &api.InitResponse{Error: response.Error}, nil
}
log.Printf("SubsonicAPI ping response: %s", response.Json)
// Example: Get server info
infoResponse, err := subsonicService.Call(ctx, &subsonicapi.CallRequest{
Url: "/rest/getLicense?u=admin",
})
if err != nil {
log.Printf("SubsonicAPI getLicense call failed: %v", err)
return &api.InitResponse{Error: err.Error()}, nil
}
if infoResponse.Error != "" {
log.Printf("SubsonicAPI getLicense returned error: %s", infoResponse.Error)
return &api.InitResponse{Error: infoResponse.Error}, nil
}
log.Printf("SubsonicAPI license info: %s", infoResponse.Json)
return &api.InitResponse{}, nil
}
func main() {}
func init() {
// Configure logging: No timestamps, no source file/line
log.SetFlags(0)
log.SetPrefix("[Subsonic Plugin] ")
api.RegisterLifecycleManagement(&SubsonicAPIDemoPlugin{})
}