magmastream-spotify
v0.2.2
Published
A Spotify plugin for Magmastream.
Readme
magmastream-spotify
A MagmaStream plugin that transparently intercepts Spotify track, album, and playlist URLs and resolves them into playable tracks using direct playback sources like Deezer, YouTube, VK Music, Qobuz, Yandex, and Jiosaavn.
It uses a Spotify Resolver API, so no Spotify credentials are required.
Features
- 🎵 Supports Spotify tracks, albums, and playlists
- 🔑 No Spotify Client ID / Secret required
- 🎯 Uses ISRC-first matching when available for better accuracy
- 🔀 Supports fully custom source resolution order
- 🧩 Includes built-in resolution strategy presets
- 🏷️ Injects Spotify metadata into resolved tracks
- ⚡ Resolves playlists and albums in concurrent batches
- 🧠 Includes optional Redis caching
- 🚫 Supports negative caching for failed lookups
- 🔌 Drop-in plugin that patches
Manager#search()automatically - 🛠️ Includes cache invalidation helpers for manual cache control
Installation
npm install magmastream-spotifyPeer dependency
magmastream = 2.10.2-aplha.11Quick Start
JavaScript
// This example shows Discord.js
const { DiscordJSManager } = require("magmastream");
const { MagmastreamSpotifyPlugin, ResolutionStrategyPreset } = require("magmastream-spotify");
const manager = new DiscordJSManager({
enabledPlugins: [
new MagmastreamSpotifyPlugin({
resolutionStrategyPreset: ResolutionStrategyPreset.DeezerFirst,
}),
],
});TypeScript
// This example shows Discord.js
import { DiscordJSManager } from "magmastream";
import { MagmastreamSpotifyPlugin, ResolutionStrategyPreset } from "magmastream-spotify";
const manager = new DiscordJSManager({
enabledPlugins: [
new MagmastreamSpotifyPlugin({
resolutionStrategyPreset: ResolutionStrategyPreset.DeezerFirst,
}),
],
});Your normal calls such as:
manager.search(spotifyUrl, requester);will automatically resolve Spotify URLs through the plugin.
Options
| Option | Type | Default | Description |
| -------------------------- | -------------------------- | -------------------------------------- | ------------------------------------------------------------------------- |
| resolverApiUrl | string | "http://eu.leonodes.xyz:2450" | Base URL of the Spotify Resolver API |
| resolutionStrategyPreset | ResolutionStrategyPreset | ResolutionStrategyPreset.DeezerFirst | Built-in preset used when sourceOrder is not provided |
| sourceOrder | SpotifyResolverSource[] | [] | Custom ordered list of sources to try. Overrides the preset when provided |
| playlistLimit | number | 100 | Maximum number of tracks to resolve from playlists or albums |
| debug | boolean | false | Enables plugin debug logging |
| redis | RedisCacheOptions | undefined | Optional Redis cache configuration |
Redis Cache Options
The plugin can cache resolver responses, track lookups, playlist/album results, and failed lookups.
| Option | Type | Default | Description |
| --------------- | -------- | ----------------------- | ------------------------------------------------------- |
| client | Redis | required | An existing ioredis client instance |
| prefix | string | "ms:spotify-resolver" | Prefix for all Redis keys used by the plugin |
| resolveTtl | number | 21600 | TTL in seconds for resolver API payloads |
| searchTtl | number | 43200 | TTL in seconds for resolved single-track search results |
| collectionTtl | number | 21600 | TTL in seconds for playlist/album search results |
| negativeTtl | number | 900 | TTL in seconds for failed resolver/search lookups |
Redis Example
import Redis from "ioredis";
import { MagmastreamSpotifyPlugin, ResolutionStrategyPreset } from "magmastream-spotify";
const redis = new Redis({
host: "127.0.0.1",
port: 6379,
});
const plugin = new MagmastreamSpotifyPlugin({
resolutionStrategyPreset: ResolutionStrategyPreset.DeezerFirst,
redis: {
client: redis,
prefix: "ms:spotify-resolver",
resolveTtl: 60 * 60 * 6,
searchTtl: 60 * 45,
collectionTtl: 60 * 30,
negativeTtl: 60 * 15,
},
});Resolution Strategy Presets
The plugin includes several built-in presets.
| Preset | Behavior |
| -------------- | ----------------------------------------------------- |
| DeezerFirst | Try Deezer first, then YouTube |
| YouTubeFirst | Try YouTube first, then Deezer |
| DirectOnly | Try all direct playback sources in the built-in order |
| DirectFirst | Try all direct playback sources in the built-in order |
Example
new MagmastreamSpotifyPlugin({
resolutionStrategyPreset: ResolutionStrategyPreset.DirectOnly,
});Note: in the current implementation,
DirectOnlyandDirectFirstresolve to the same built-in source order.
Custom Source Order
You can fully control the resolution order by providing sourceOrder.
When sourceOrder is set, it overrides resolutionStrategyPreset.
Example
import { MagmastreamSpotifyPlugin, SpotifyResolverSource } from "magmastream-spotify";
new MagmastreamSpotifyPlugin({
sourceOrder: [SpotifyResolverSource.Qobuz, SpotifyResolverSource.Deezer, SpotifyResolverSource.YouTube],
});Another Example
new MagmastreamSpotifyPlugin({
sourceOrder: [SpotifyResolverSource.Jiosaavn, SpotifyResolverSource.VKMusic, SpotifyResolverSource.Deezer],
});Duplicate sources are automatically removed while preserving order.
Supported Sources
The plugin supports these source values through the SpotifyResolverSource enum:
| Source | Description |
| ---------- | ---------------------- |
| Deezer | Direct playback source |
| YouTube | Common fallback source |
| VKMusic | Direct playback source |
| Qobuz | Direct playback source |
| Jiosaavn | Direct playback source |
These map internally to the corresponding SearchPlatform values from MagmaStream.
How It Works
- The plugin patches
Manager#search(). - If the query is not a Spotify URL, it passes through untouched.
- If the query is a Spotify URL, the plugin calls:
GET /api/resolve?url=<spotifyUrl>The Resolver API returns normalized metadata such as:
- title
- artist
- ISRC
- artwork
- playlist or album items
The plugin builds a fallback search chain like:
isrc:<code>
Artist - Title
Title Artist
Title- Each query is tried against the configured source order until a playable result is found.
- The resolved track is enriched with Spotify metadata and returned as a normal MagmaStream search result.
For playlists and albums, tracks are resolved in concurrent batches of 5.
Supported URL Formats
Tracks
https://open.spotify.com/track/<id>Albums
https://open.spotify.com/album/<id>Playlists
https://open.spotify.com/playlist/<id>Locale URLs
https://open.spotify.com/intl-fr/track/<id>
https://open.spotify.com/intl-en/album/<id>
https://open.spotify.com/intl-de/playlist/<id>Any non-Spotify query is passed directly to MagmaStream.
Accessing Spotify Metadata
Resolved tracks include a magmastreamPlugin object with Spotify metadata.
manager.on("trackStart", (player, track) => {
const meta = track.magmastreamPlugin;
console.log(`Playing: ${meta.spotifyArtist} - ${meta.spotifyTitle}`);
console.log(`ISRC: ${meta.spotifyIsrc ?? "none"}`);
console.log(`Spotify URL: ${meta.spotifyUrl ?? "none"}`);
console.log(`Resolved by: ${meta.resolvedBy}`);
});Available fields
track.magmastreamPlugin.spotifyTitle;
track.magmastreamPlugin.spotifyArtist;
track.magmastreamPlugin.spotifyIsrc;
track.magmastreamPlugin.spotifyUrl;
track.magmastreamPlugin.resolvedBy;The plugin also sets the track source to:
TrackSourceTypes.Spotify;so the resolved track keeps Spotify context even if playback came from another provider.
Cache Invalidation Helpers
When Redis is enabled, the plugin exposes helper methods for clearing cached entries.
Invalidate a Spotify URL cache entry
Clears resolver and collection cache for a specific Spotify URL.
await plugin.invalidateSpotifyUrlCache("https://open.spotify.com/track/...");Invalidate a single resolved track cache entry
Clears the cached resolved track for normalized metadata.
await plugin.invalidateTrackCache({
title: "Song Title",
artist: "Artist Name",
isrc: "USRC17607839",
spotifyUrl: "https://open.spotify.com/track/...",
});Clear the full plugin cache
Clears all plugin-managed cache entries under the configured Redis prefix.
const deleted = await plugin.clearAllSpotifyResolverCache();
console.log(`Deleted ${deleted} keys`);This uses SCAN when available and falls back to KEYS if needed.
Negative Caching
The plugin stores failed resolver and search lookups using a shorter TTL.
This helps reduce repeated requests for:
- broken Spotify URLs
- empty resolver responses
- tracks that could not be matched
- unsuccessful playlist/album resolutions
By default, failed lookups are cached for 15 minutes.
Debug Logging
Enable debug mode to print plugin messages and emit them through the manager debug event.
new MagmastreamSpotifyPlugin({
debug: true,
});When enabled, the plugin logs things like:
- intercepted Spotify URLs
- resolver API calls
- Redis hits and misses
- source search attempts
- collection resolution progress
- cache invalidation activity
Example Command
async execute(client, interaction) {
const url = interaction.options.getString("url");
const player = client.manager.create({
guildId: interaction.guildId,
voiceChannelId: interaction.member.voice.channelId,
textChannelId: interaction.channelId,
});
const result = await client.manager.search(url, interaction.user);
if (!("tracks" in result) || !result.tracks.length) {
return interaction.reply("No results found.");
}
player.queue.add(result.tracks[0]);
if (!player.playing) {
player.play();
}
return interaction.reply(`Now playing: **${result.tracks[0].title}**`);
}Notes
- This plugin does not access Spotify directly
- All Spotify metadata is obtained through the configured Resolver API
- Your MagmaStream/Lavalink setup must support the playback sources you want to use
- Redis caching is optional but recommended for production use
- Failed lookups are cached separately using a shorter negative TTL
- The plugin transparently falls back to the original
manager.search()when the query is not a Spotify URL
License
Apache
