Rocksky Stage
The Rocksky Stage matches your Play with metadata from Rocksky. If the match score is high enough then Multi-Scrobbler uses the match to correct and fill-in some missing information in your Play data.
This Stage is useful for normalizing plays for the Rocksky scrobbler or adding metadata ids and artwork from a fast metadata provider.
The Rocksky metadata service is still in development and the data behind it is still maturing.
Because of this, this stage only transforms data that has a high confidence level (meta ids like MBID, isrc, artwork) or when your data is entirely missing fields (like a missing track, duration, or album name).
If you are using ENV Config for multi-scrobbler and just want a quick and easy setup, skip to ENV Configuration.
Set up Valkey Caching to cache Rocksky API calls for faster processing.
Configuration
API Setup
No API setup is required at this time. APIs can still be configured in the event you using are using mirrors, a proxy, or want to configure different rate limiting.
To use the default Stage in Rules and Hooks either omit name or specify it as "name": "MSDefault".
Non-Default Servers and Rate Limiting
Other, or additional, Rocksky Servers/Mirrors can be added to the API configuration. If more than one server is defined then multi-scrobbler will load balance requests based on available rate capacity.
Use url to define the base URL of the Rocksky server to use. If url is not defined multi-scrobbler assumes it is the primary Rocksky server, https://api.rocksky.app.
Example of multiple servers:
{
// ...
"transformers": [
{
"type": "rocksky",
"name": "MyRocksky",
"data": {
"apis": [
{
"enable": true
// uses default Rocksky server https://api.rocksky.app
},
// additional server
{
"enable": true,
"url": "https://my.rs.mirror.domain.com"
}
]
},
}
]
}
Rate Limiting
Rocksky servers can optionally be configured with rate limiting.
Rate limiting is defined by max number of requests within timespan of N seconds.
These constraints apply:
- Official Rocksky server (
https://api.rocksky.appor when nourlis defined) is hard-limited to1000 req / 30s - No limits for other, custom servers but a default
1 req/slimit is applied when none is configured
Example of configuring rate limit:
{
// ...
"transformers": [
{
"type": "rocksky",
"name": "MyRocksky",
"data": {
"apis": [
{
"enable": true
// uses default Rocksky server https://api.rocksky.app
// hard-limited to 1000 req / 30s
},
// additional server
{
"enable": true,
"url": "https://my.rs.mirror.domain.com",
"rate": {
// IE 6 req/s
"requests": 6, // maximum of 6 requests
"perTime": 1 // can be made within 1 second
}
}
]
},
}
]
}
Stage Configuration
All of the properties found in Matching with Rocksky section are configured in Stage Configuration as defaults.
Example:
{
// ...
"transformers": [
{
"type": "rocksky",
"name": "MyRocksky",
"defaults": {
"searchWhenMissing": ['duration','mbid']
"allowNoMatch": false
}
}
]
}
Rules and Hooks
Add your Stage to a Source or Client by specifying it in a Hook:
[
{
"name": "Rocky",
"configureAs": "client",
"data": { /* ... */},
"options": {
"playTransform": {
"preCompare": [
{
"type": "rocksky",
"name": "MSDefault"
}
]
}
}
}
]
Each Stage Rule should be either a boolean, specifying if the transformed data should be used for this field, or a when condition:
Example
[
{
"name": "Rocky",
"configureAs": "client",
"data": { /* ... */},
"options": {
"playTransform": {
"preCompare": [
{
"type": "rocksky",
"name": "MSDefault"
// ...
"title": false, // will not apply any changes to Play title
"album": {
"when": {/* ... */}, // will only apply changes to Play artists if "when" is satisfied
/* ... */
},
"meta": true // adds MusicBrainz MBIDs to scrobble data
}
]
}
}
}
]
The defaults you set in Stage Configuration can be overriden/added to (per property) in each Hook.
Example
[
{
"name": "Rocky",
"configureAs": "client",
"data": { /* ... */},
"options": {
"playTransform": {
"preCompare": [
{
"type": "rocksky",
"name": "MSDefault",
"searchWhenMissing": ['duration','mbid'], // override from defaults
}
]
}
}
}
]
ENV Configuration
The general configuration shown above can also be configured from a selection of presets using ENV Config for individual Sources/Clients.
To configure stage defaults use RS_PRESETS with a comma-delimited list of presets you wish to apply. More than one preset can be applied, in which case they combine. You must choose at least one of the presets below:
default- Adds the defaultsearchOrderisrc+basic(same as using no presets that affectsearchOrder)id- AddssearchOrdermethods that priorites searching by ID:isrc+mbid+basicnative- Adds Extracted Artists (artist) innativemode tosearchOrder
Finally, use ENV *_TRANSFORMS=rocksky on each Source/Client you wish to apply this stage to. This applies the stage in the preTransform 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 Rocksy Clients ENVs look like
ROCKSKY_USER=myuseretc... - Use
ROCKSKY_TRANSFORMS=rocksky
Example Full Docker Deploy with ENV Configuration
Using Jellyfin example from Quickstart with Rocksky and the Rocksky Stage:
services:
multi-scrobbler:
image: foxxmd/multi-scrobbler
container_name: multi-scrobbler
environment:
# searches with the searchOrder
# isrc => basic
- RS_PRESETS=default
- JELLYFIN_URL=192.168.0.110:8096
- JELLYFIN_APIKEY=c9fae8756fbf481ebd9c5bb56b
- JELLYFIN_USER=MyUser
- ROCKSKY_ID=Rocky
- ROCKSKY_HANDLE=alice.bsky.social
- ROCKSKY_APP_PW=1234567
# applies Rocksky Stage to preTransform of Rocksky Client
- ROCKSKY_TRANSFORMS=rocksky
volumes:
- "./config:/config"
ports:
- "9078:9078"
restart: unless-stopped
To use the configured ENV stage in Rules and Hooks with File/AIO config either omit name or specify it as "name": "MSEnv".
What Data is Applied?
Rocksky metadata is still maturing so the data MS ultimately applies your scrobbles is artifically restricted at this time.
Using Rules for Play Data we can usually categorically transform any part of our Play (scrobble) using the data produced by a stage:
Example
// all properties *not* explicitly set default to true
{
"type": "musicbrainz",
// ...
"title": false, // will not apply musicbrainz changes to Play title
//"albumArtists": true, // always applies
//"artists": true // always applies
"album": false, // will never apply changes to Play album
//"duration": true, // always applies
//"meta": true, // always applies
}
But for the Rocksky Stage some of these transformations come with conditions and some are completely disabled.
You can still set all Rules normally, like any other Stage, but these prerequisites will always be checked, and the property transformation skipped, if they are not met:
titleandalbumare only applied if the original scrobble is missing themalbumArtistsis never appliedartistsare only applied if the original scrobble- has no artists
- has only 1 artist and Rocksky returned multiple
metais only applied if the original scrobble is missing them (MBIDs) or only has recording MBID
All other parts behave as normal.
Matching with Rocksky
All properties found in this section are optional.
Matching your Scrobble's Play data with a result from Musicbrainz is comprised of two steps:
- Searching Rocksky using parts of your Scrobble as queries
- Refining matched results to select the desired/allowed match
Both steps have separate configuration.
Searching
Should MS Search?
Before MS begins a search it checks if your Scrobble data already contains:
- A track title, artists, album, and duration
- An associated recording MBID
If it already has all required data types then the entire Rocksky Stage is skipped. If any are missing then a search is performed.
Define which data types are required using:
searchWhenMissing(defaults to all) - A list containing any of:artiststrackalbumdurationmbidforceSearch(defaultfalse) - Force searching even if all required data is present
Example
Stage Configuration example:
// ...
"defaults": {
// only search if duration or recording MBID is missing
"searchWithMissing": ["duration", "mbid"]
// uncomment to make the stage always search, even if above data is present
//forceSearch: true
}
If required data is missing then the next step is making search queries to Rocksky's metadata API to try to get potential candidates.
Search Methods
Multi-scrobbler can search for Rocksky matches using multiple methods. You control:
- the search methods that are used
- the order in which search methods are executed
All search methods will be run in the order they were defined until one method returns at least one match.
The methods you choose, and the order they run in, can drastically change how many API calls are needed to get a match, and what types of matches are returned. See Search Considerations for guidance on how to decide on methods based on Source.
To set search methods and their order use the searchOrder option in your Stage Configuration:
// ...
"defaults": {
// only searches using these methods, in the order they are listed here
"searchOrder": ["isrc", "basic", "artist"]
}
If searchOrder is undefined then Multi-scrobbler will default to using isrc then basic method.
Available Methods
ISRC (isrc)
If your Scrobble data contains an ISRC (usually from Spotify) then Multi-scrobbler can search using this ID, title, and artist only.
Stage Configuration example:
// ...
"defaults": {
// will only search using isrc
"searchOrder": ["isrc"]
}
Recording MBID (mbid)
If your Scrobble data contains a Recording MBID then Multi-scrobbler can search using this MBID, title, and artist only.
Stage Configuration example:
// ...
"defaults": {
// will only search using recording mbid
"searchOrder": ["mbidrecording"]
}
Album, Artist, and Title Fields (basic)
Will search Musicbrainz using any/all available text fields from your scrobble: Album, Artists(s), and Title. Does not leverage any existing MBIDs.
Stage Configuration example:
// ...
"defaults": {
// will only search using album, artist(s), and title
"searchOrder": ["basic"]
}
Album/Artist/Title OR MBIDs (basicorids)
This is the same as the above Album, Artist, and Title Fields search except that if the scrobble data contains a recording MBID it will use those as well as the plain text field.
Stage Configuration example:
// ...
"defaults": {
// will only search using album, artist(s), and title
"searchOrder": ["basicOrIds"]
}
Extracted Artists (artist)
If your scrobble data contains only one artist string then Multi-scrobbler can attempt to extract multiple artists from your artist and track string.
Use artist with searchOrder and optionally specify the extraction mode with searchArtistMethod:
Stage Configuration example:
// ...
"defaults": {
// will only search using title/album + extracted artists
"searchOrder": ["artist"],
//searchArtistMethod: 'native' // optional
}
searchArtistMethod can be set to Native or Naive mode:
- Native (Recommended)
- Naive
Native (native) mode uses an aggressive configuration of the Native Stage to extract artists using common delimiters and common "joined" artist patterns from the artist and title string of your Scrobble data.
If you already have a Native Stage configured you should use that instead, running it before the Rocksky stage.
Native Mode Example
{
"title": "Endless Possibility (feat. Wheatus)",
"artists": ["Bowling For Soup & Punk Rock Factory, My Cool Band"]
}
- Extracts
Bowling For SoupPunk Rock FactoryMy Cool Bandfrom artist string - Extracts
Wheatusfrom title string- Removes
(feat. Wheatus)from title string because it found an artist there
- Removes
Resulting data used for Rocksky search:
{
"title": "Endless Possibility",
"artists": ["Bowling For Soup", "Punk Rock Factory", "My Cool Band", "Wheatus"]
}
Stage Configuration example:
// ...
"defaults": {
// will only search using title/album + native extracted artists
"searchOrder": ["artist"],
"searchArtistMethod": "native"
}
Naive (naive) mode looks for the first found common delimiter in the artist string. If it finds one then it uses the preceding value as the only artist in the Rocksky search. It does not try to extract additional artists from the artist string, or extract anything from the title string.
Naive Mode Example
{
"title": "Endless Possibility (feat. Wheatus)",
"artists": ["Bowling For Soup, Punk Rock Factory & Wheatus"]
}
- Finds
&as first common delimiter, extracts "Bowling For Soup"
Resulting data used for Rocksky search:
{
"title": "Endless Possibility (feat. Wheatus)",
"artists": ["Bowling For Soup"]
}
Stage Configuration example:
// ...
"defaults": {
// will only search using title/album + naive extracted artists
"searchOrder": ["artist"],
"searchArtistMethod": "naive"
}
If all defined search methods do not return any results then the stage is marked as failed (onFailure) for Flow Control.
Refining
Score
Each match returned by Rocksky can contain a list of matched external provider entries (Spotify, Deezer, etc...) that contain a numeric score representing how close it was to the search parameters. Set score in configuration to set a minimum score that must be met by any of these entries. Default is 90.
{
// ...
"score": 90 // external provider entries must score 90 or higher to be considered
}
No External Provider Entries
Rocksky may still return a match even if no external provider entries were associated.
You can configure whether a match is still considered valid when no entries are associated by setting allowNoMatch. The default is to allow matches with no associated entries.
{
// ...
"allowNoMatch": false // consider a match invalid if not associated with an external provider
}
Best Practices
Caching
You should setup metadata caching to reduce API calls, improve transform performance, and reduce memory usage when using this stage.
Search Considerations
How complete, and accurate, your scrobble data is from a certain Source should dictate what Search Methods you should use for matching.
Rocksky is generally pretty good at finding the right match given mostly good/consistent data but if you are not confident of the ID-type data in your scrobbles (MBID, ISRC) you should consider omitting these search method types.
Matching Scrobbles from Last.fm
Last.fm is inconsistent when it comes to matching your scrobbles. It will accept (almost) any fields for track/title/album and aggressively attempt to match/correct these, regardless of your intentions.
Unless the client scrobbling to Last.fm is also sending an mbid, it's likely your scrobble will not be consistently matched correctly and so the mbid supplied by last.fm in its scrobble data will be wrong.
Therefore, you should avoid using mbid and basicorids as the primary search methods (or understand the risks of using them). Instead, prefer to use field-based methods before falling back to id-based methods.
Using Partial Match
Use Rules to apply Rocksky match data selectively.
If you know that your music collection is well organized and you do not want to change the artists/title/album etc... sent to a Client, you can still benefit from matches by only applying MBID using meta so that any Client that supports Musicbrainz data (Koito, Tealfm, Listenbrainz, Rocksky) can still get that data.
The result of using these Rules is gated by this Stage unless they meet prerequisites. See What Data Is Applied?
Example
[
{
"name": "Rocky",
"configureAs": "client",
"data": { /* ... */},
"options": {
"playTransform": {
"preCompare": [
{
"type": "rocksky",
"name": "MSDefault",
"title": false,
"artists": false,
"album": false,
"albumArtists": false,
"meta": true
}
]
}
}
}
]
Examples
Minimal
Example
In a Rocksky Client File Config:
[
{
"name": "Rocky",
"configureAs": "client",
"data": { /* ... */},
"options": {
"playTransform": {
"preCompare": [
{
"type": "rocksky",
"name": "MSDefault"
}
]
}
}
}
]
Or using a Rocksky Client with ENV Config:
services:
multi-scrobbler:
image: foxxmd/multi-scrobbler
environment:
# searches with the searchOrder
# isrc => basic
- RS_PRESETS=default
# ... your source ENVs go here
#
- ROCKSKY_ID=Rocky
- ROCKSKY_HANDLE=alice.bsky.social
- ROCKSKY_APP_PW=1234567
# applies Rocksky Stage to preTransform of Rocksky Client
- ROCKSKY_TRANSFORMS=rocksky
volumes:
- "./config:/config"
ports:
- "9078:9078"
restart: unless-stopped
Add Metadata Only (No Scrobble Modification)
If you do not want your scrobble data to be modified (artists/album/title) but still want to benefit from associating the scrobble with a Musicbrainz match (for Scrobble Clients like Tealfm, Rocksky, and Koito), then use Partial Match rules to specify only meta to be updated.
Example
In a Jellyfin File Config:
[
{
"name": "MyJellyfin",
"data": { /* ... */},
"options": {
"playTransform": {
"preCompare": [
{
"type": "rocksky",
"name": "MSDefault",
"title": false,
"artists": false,
"album": false,
"albumArtists": false,
"meta": true // only update scrobble metadata with MBIDs
}
]
}
}
}
]
Different Search Methods Based on Source (Last.fm and Spotify)
Last.fm has unreliable MBIDs and Spotify provides an ISRC and consistent formatting.
- For Spotify, we want to rely on ID searches
- For Last.fm, we want to use only text searches and take whatever correction is given to us
Use Per Component Overrides to specify different search behavior for each Source.
Example
[
{
"name": "MyLFM",
"configureAs": "source"
"data": { /* ... */},
"options": {
"playTransform": {
"preCompare": [
{
"type": "rocksky",
"name": "MSDefault",
"searchArtistMethod": "native"
// does not use any ID searches
"searchOrder": ["basic", "artist"]
}
]
}
}
}
]
[
{
"name": "MySpotify",
"data": { /* ... */},
"options": {
"playTransform": {
"preCompare": [
{
"type": "rocksky",
"name": "MSDefault",
// use ISRC as first search, then tries searching with title/artist/album/mbid
"searchOrder": ["isrc", "basicorids"]
}
]
}
}
}
]
Use Metadata for Discord Album Art
The Discord Client can fetch album art images from Cover Art Archive if the Scrobble data contains a Release MBID. Use the Rocksky Stage on the preCompare hook for your Discord client so that missing Rocksky IDs are populated for this functionality.
Example
- ENV
- File
- AIO
Using the ENV Configuration from above, add these fields to your docker compose environment:
- RS_PRESETS=default
- DISCORD_TRANSFORMS=rocksky
In your Discord File Config:
[
{
"name": "MS",
"data": { /* ... */},
"options": {
"playTransform": {
"preCompare": [
{
"type": "rocksky",
"name": "MSDefault"
}
]
}
}
}
]
Your AIO Config:
{
// ...
"sources": [
{
"name": "MS",
"type": "discord",
"data": { /* ... */},
"options": {
"playTransform": {
"preCompare": [
{
"type": "rocksky",
"name": "MSDefault"
}
]
}
}
}
]
}