Skip to content

Spotify Stage

The Spotify Stage matches your Play data with the Spotify catalog, using the Spotify Web API. If the match is confident enough then Multi-Scrobbler uses it to correct and fill-in missing information in your Play data.

This Stage is useful for standardizing your Scrobble’s Play data, regardless of the Source it is coming from, and is a good complement (or alternative) to the Musicbrainz Stage if you’d prefer matches to come from Spotify’s own catalog.

This Stage uses Spotify’s API to search/lookup the Spotify catalog. This does not require a user to log in – only an app clientId/clientSecret is needed. Create a Spotify application if you don’t already have one (the same application used for the Spotify Source can be reused here).

config.json
{
// ...your AIO Config
"transformers": [
{
"type": "spotify",
"name": "MySpotify",
"data": {
"clientId": "787c921a2a2ab42320831aba0c8f2fc2",
"clientSecret": "ec42e09d5ae0ee0f0816ca151008412a"
},
}
]
}
Rate Limiting

rate can be used to configure how many requests are made to the Spotify API, defined by max number of requests within timespan of N seconds (default 10 req/1s):

{
"type": "spotify",
"name": "MySpotify",
"data": {
"clientId": "787c921a2a2ab42320831aba0c8f2fc2",
"clientSecret": "ec42e09d5ae0ee0f0816ca151008412a",
"rate": {
"requests": 10,
"perTime": 1
}
},
}

All of the properties found in Matching with Spotify section are configured in Stage Configuration as defaults.

These are all also optional so you can use the Spotify Transform stage without configuring any of this.

config.json
{
// ...your AIO Config
"transformers": [
{
"type": "spotify",
"name": "MySpotify",
"data": {
"clientId": "787c921a2a2ab42320831aba0c8f2fc2",
"clientSecret": "ec42e09d5ae0ee0f0816ca151008412a"
},
"defaults": {
"score": 0.6,
"deprioritizeCompilations": true
}
}
]
}

Add your Stage to a Source or Client by specifying it in a Hook:

subsonic.json
[
{
// ...your Subsonic File Config
"name": "MySubsonic",
"data": { /* ... */},
"options": {
"playTransform": {
"preCompare": [
{
"type": "spotify",
"name": "MySpotify"
}
]
}
}
}
]

Each Stage Rule (title/artists/albumArtists/album/duration/meta/art) works the same way as it does for the Musicbrainz Stage: either a boolean specifying whether the transformed data should be used for this field, or a when condition.

Your Spotify Stage can be configured using ENV Config by specifying client/secret and, optionally, combining different presets.

Client ID and Secret are the only required configuration:

  • Client Id => ENV SPOTIFY_TRANSFORM_CLIENT_ID or ENV SPOTIFY_CLIENT_ID from the Spotify Source
  • Client Secret => ENV SPOTIFY_TRANSFORM_CLIENT_SECRET or ENV SPOTIFY_CLIENT_SECRET from the Spotify Source

To configure stage defaults use ENV SPOTIFY_TRANSFORM_PRESETS= with a comma-delimited list of presets you wish to apply. If SPOTIFY_TRANSFORM_PRESETS is not set the stage will use defaults.

More than one preset can be applied, in which case they combine.

Example
docker-compose.yaml
services:
multi-scrobbler:
# ...
environment:
# ...
- SPOTIFY_CLIENT_ID=787c921a2a2ab42320831aba0c8f2fc2
- SPOTIFY_CLIENT_SECRET=ec42e09d5ae0ee0f0816ca151008412a
- SPOTIFY_TRANSFORM_PRESETS=missingArt,missingFields,searchIsrc1
# ...
  1. 1.only runs transform if album, artists, title, duration, or art are missing in data and only searches by isrc

Finally, use ENV *_TRANSFORMS=spotify on each Source/Client you wish to apply this stage to. This applies the stage in the preCompare Hook with all Rules enabled.

The * stands for the prefix used for each Source/Client’s ENV keys. Refer to the individual Source/Client Configuration sections to find this. Example:

  • All Subsonic Source ENVs look like SUBSONIC_USER=myuser etc…
  • Use SUBSONIC_TRANSFORMS=spotify
Example Full Docker Deploy with ENV Configuration
docker-compose.yml
services:
multi-scrobbler:
image: foxxmd/multi-scrobbler
container_name: multi-scrobbler
environment:
- SPOTIFY_TRANSFORM_PRESETS=missingArt,missingFields,searchIsrc1
- SPOTIFY_CLIENT_ID=787c921a2a2ab42320831aba0c8f2fc2
- SPOTIFY_CLIENT_SECRET=ec42e09d5ae0ee0f0816ca151008412a
- JELLYFIN_URL=192.168.0.110:8096
- JELLYFIN_APIKEY=c9fae8756fbf481ebd9c5bb56b
- JELLYFIN_USER=MyUser
- JELLYFIN_TRANSFORMS=spotify2
- MALOJA_URL=http://192.168.0.100:420103
- MALOJA_API_KEY=myApiKey
volumes:
- "./config:/config"
ports:
- "9078:9078"
restart: unless-stopped
  1. 1.only runs transform if art or fields are missing in data and only searches by isrc
  2. 2.applies spotify Stage to preCompare of Jellyfin source
  3. 3.maloja receives enhanced scrobble from Jellyfin

To use the configured ENV stage in Rules and Hooks with File/AIO config either omit name or specify it as "name": "MSEnv".

Matching your Scrobble’s Play data with a result from Spotify is comprised of two steps:

  • Searching Spotify using parts of your Scrobble as queries
  • Ranking matched results to select the desired match

Before MS begins a search it checks if your Scrobble data already contains:

  • A track title, artists, album, and duration
  • All spotify IDs and an ISRC

If it already has all required data types then the entire Spotify Stage is skipped. If any are missing then a search is performed.

Define which data types are required using:

  • searchWhenMissing - A list containing any of: album,title,artists,duration,isrc, and ids
    • ids is any missing spotify id (track, artists, albumArtists, and album ids)
    • the default when searchWhenMissing is not defined is: album,artists,title, and duration
  • forceSearch (default false) - Force searching even if all required data is present

Multi-scrobbler searches for a Spotify match using, in order, up to two methods. Use searchOrder to control which methods run and in what order:

"defaults": {
"searchOrder": ["isrc", "basic"]
}
ISRC (isrc)

If your Scrobble data contains an ISRC then Multi-scrobbler searches Spotify’s catalog using this ID.

If the ISRC is present on more than one Spotify album/track (which happens often IE singles, re-releases, and compilation appearances of the same recording all share an ISRC) then the results are ranked using fuzzy matching against your scrobble’s existing album/artist data to pick the best candidate.

Album, Artist, and Title Fields (basic)

Searches Spotify using any/all available text fields from your scrobble: Album, primary Artist, and Title. Results are always ranked using fuzzy matching since Spotify’s text search does not otherwise guarantee an accurate/confident result.

Biasing Search Results with market and locale

market (an ISO 3166-1 alpha-2 country code) can be set to bias/limit search results to what is available in a specific market:

Example
{
"type": "spotify",
"name": "MySpotify",
"data": {/* */},
"defaults": {
// ...
"market": "US"
}
}

locale (in ISO-639-1_ISO-3166-1 format, EX en_US, ja_JP) can additionally be set to try to bias which translation of a localized catalog name (artist/album/track) Spotify returns for a match:

Example
{
"type": "spotify",
"name": "MySpotify",
"data": {/* */},
"defaults": {
// ...
"locale": "en_US"
}
}

Spotify’s search results do not carry a confidence/similarity score so Multi-scrobbler fuzzy-matches every candidate against your original scrobble’s title/artist(s)/album to:

  • disambiguate between multiple candidates (EX an ISRC present on more than one album)
  • for basic search results only, determine whether any candidate is a confident enough match to use at all

Each candidate is scored between 0 and 1 based on how similar its title/artist(s)/album are to your original scrobble. Set score to change the minimum score a basic-search candidate must have to be used. Default is 0.6. This has no effect on isrc matches, which are always used.

{
// ...
"score": 0.61
}
  1. 1.matches must score 0.6 or higher to be considered

You can bias which of title/artist/album contributes most to a candidate’s score with titleWeight, artistWeight, and albumWeight (defaults are 0.4/0.3/0.3, respectively):

"defaults": {
"titleWeight": 0.4,
"artistWeight": 0.3,
"albumWeight": 0.3
}

Spotify catalogs many recordings across multiple compilation albums (Greatest Hits, movie soundtracks that reuse a song, etc…) in addition to their “proper” studio album. If you’d prefer matches to avoid compilation albums when a better alternative exists, enable deprioritizeCompilations:

"defaults": {
"deprioritizeCompilations": true
}

This does not exclude compilations – it only lowers their score relative to other candidates, so a compilation can still be used if it’s the only match found.

You should setup metadata caching to reduce API calls, improve transform performance, and reduce memory usage when using this stage.

Use Rules to apply Spotify match data selectively. This is useful if you don’t want your scrobble’s Artist/Title/Album modified but still want Spotify IDs or ISRC defined for Clients or other Stages to leverage them.

Example
subsonic.json
[
{
// ...your Subsonic File Config
"name": "MySubsonic",
"data": { /* ... */},
"options": {
"playTransform": {
"preCompare": [
{
"type": "spotify",
"name": "MySpotify",
"title": false,
"artists": false,
"album": false,
"albumArtists": false,
"meta": true
}
]
}
}
}
]

If Spotify is not returning matches, or the resulting enhanced Scrobble is not what you expected, enable Debug Mode to help diagnose issues with the Spotify API and Scrobble enhancement.

If you have multiple Modification Stages and need to see the diff for your Play between each Stage, enable "log": "all" in the individual Modification Stage, as described for the Musicbrainz Stage.