remove built-in Spotify integration (#5197)

* refactor: remove built-in Spotify integration

Remove the Spotify adapter and all related configuration, replacing
the built-in integration with the plugin system. This deletes the
adapters/spotify package, removes Spotify config options (ID/Secret),
updates the default agents list from "deezer,lastfm,spotify" to
"deezer,lastfm", and cleans up all references across configuration,
metrics, logging, artwork caching, and documentation. Users with
Spotify config options will now see a warning that the options are
no longer available.

* feat: add ListenBrainz to list of default agents

Signed-off-by: Deluan <deluan@navidrome.org>

---------

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan Quintão
2026-03-15 13:18:54 -04:00
committed by GitHub
parent 6b8fcc37c6
commit 69e7d163fc
22 changed files with 32 additions and 480 deletions
+1 -1
View File
@@ -63,7 +63,7 @@ Folder = "/path/to/navidrome/plugins"
Add the plugin to your agents list:
```toml
Agents = "lastfm,spotify,wikimedia"
Agents = "lastfm,wikimedia"
```
## Testing with Extism CLI
+2 -2
View File
@@ -149,7 +149,7 @@
},
"requiredHosts": {
"type": "array",
"description": "List of required host patterns for HTTP requests (e.g., 'api.example.com', '*.spotify.com')",
"description": "List of required host patterns for HTTP requests (e.g., 'api.example.com', '*.musicbrainz.org')",
"items": {
"type": "string"
}
@@ -189,7 +189,7 @@
},
"requiredHosts": {
"type": "array",
"description": "List of required host patterns for WebSocket connections (e.g., 'api.example.com', '*.spotify.com')",
"description": "List of required host patterns for WebSocket connections (e.g., 'api.example.com', '*.musicbrainz.org')",
"items": {
"type": "string"
}
+2 -2
View File
@@ -57,7 +57,7 @@ type HTTPPermission struct {
Reason *string `json:"reason,omitempty" yaml:"reason,omitempty" mapstructure:"reason,omitempty"`
// List of required host patterns for HTTP requests (e.g., 'api.example.com',
// '*.spotify.com')
// '*.musicbrainz.org')
RequiredHosts []string `json:"requiredHosts,omitempty" yaml:"requiredHosts,omitempty" mapstructure:"requiredHosts,omitempty"`
}
@@ -251,6 +251,6 @@ type WebSocketPermission struct {
Reason *string `json:"reason,omitempty" yaml:"reason,omitempty" mapstructure:"reason,omitempty"`
// List of required host patterns for WebSocket connections (e.g.,
// 'api.example.com', '*.spotify.com')
// 'api.example.com', '*.musicbrainz.org')
RequiredHosts []string `json:"requiredHosts,omitempty" yaml:"requiredHosts,omitempty" mapstructure:"requiredHosts,omitempty"`
}
+2 -2
View File
@@ -19,7 +19,7 @@ var _ = Describe("Manifest", func() {
"permissions": {
"http": {
"reason": "Fetch metadata",
"requiredHosts": ["api.example.com", "*.spotify.com"]
"requiredHosts": ["api.example.com", "*.musicbrainz.org"]
}
}
}`)
@@ -34,7 +34,7 @@ var _ = Describe("Manifest", func() {
Expect(*m.Website).To(Equal("https://example.com"))
Expect(m.Permissions.Http).ToNot(BeNil())
Expect(*m.Permissions.Http.Reason).To(Equal("Fetch metadata"))
Expect(m.Permissions.Http.RequiredHosts).To(ContainElements("api.example.com", "*.spotify.com"))
Expect(m.Permissions.Http.RequiredHosts).To(ContainElements("api.example.com", "*.musicbrainz.org"))
})
It("parses a minimal manifest", func() {