All versions since v0.10.8
v0.10.8
Changelog
Features
- (tealfm) Implement service filtering and parse isrc/music service from records
Bug Fixes
- (musicbrainz) Don’t assume duration is missing
- (No Category) Convert MB length to seconds #423 #427
- (rocksky) Fix generated name for ENV configured rocksky client #424 thanks to @SilverKnightKMA
Documentation
- (No Category) Add valkey docker compose connection example
Refactor
- (No Category) Move ISRC to main play data
v0.11.0
What’s New?
Libre.fm Source/Client
Mutli-scrobbler now supports Libre.fm as a Source and Client.
Additionally, if you are running your own Libre.fm instance there are options for defining your own instance as the endpoint.
Musicbrainz Search Improvements
When enhancing your scrobbles with Musicbrainz there are now 1) additional methods for searching and 2) an additional album sorting mechanism to bias matches towards the actual text from your original scrobble.
The order in which searches are executed has also been updated to be fully user-configurable. However, this is a breaking change if you previously had search-related file-config options (fallback*) or are using PRESETS ENV. See Breaking Changes below for how to migrate.
Breaking Changes
Musicbrainz Stage
If you are using the Musicbrainz Stage transformer with any of the following options then you will eventually need to update your config to maintain existing behavior:
- File Config
fallbackAlbumSearchfallbackArtistSearchfallbackFreeTextSearch
- ENV Config
MB_PRESETS
If none of these apply to you no action is required.
If these do apply to you: your existing configuration WILL continue to work but these options have been deprecated and will be removed in a future release. You should still migrate as soon as convenient.
In v0.11.0 Musicbrainz uses a searchOrder list to define the order that search methods are run in. See the Search Methods docs section for more information.
To migrate:
File Config
"fallbackAlbumSearch": truereplace with"searchOrder": ["album"]"fallbackArtistSearch": "native"- replace with
"searchOrder": ["artist"] - and add
"searchArtistMethod": "native"(ornaive)
- replace with
"fallbackFreeTextSearch": "true"replace with"searchOrder": ["freetext"]
The directions above assume you are using each option by itself. If you are using more than one then simply combine replacements.
EX using both fallbackFreeTextSearch and fallbackAlbumSearch, replace with "searchOrder": ["album", "freetext"]
You will likely also want to add isrc and basic search methods to the front of your searchOrder, as this emulates the previous MB search behavior:
"searchOrder": ["isrc","basic","album","freetext"]ENV Config
MB_PRESETS=...adddefaultto beginning of your list IEMB_PRESETS=default,native,sensible
Removed Deprecated Jellyfin Source
The old, webhook-based Jellyfin source has been deprecated since v0.8.4 (September 2024). It has not been in the documentation for well over a year. It has now been fully removed.
Removed Deprecated Plex/Tautulli Sources
The old webhook-based Plex and Tautulli (notification agent) Sources have been deprecated since v0.8.7 (October 2024). They have not been in the documentation for well over a year. They have now been fully removed.
Full Changelog
Features
- (No Category) Librefm Source/Client #401
- (musicbrainz) Improved missing mbid parsing and tests
- (musicbrainz) BREAKING: Replace fallback search with searchOrder for more search control
- (musicbrainz) BREAKING: Improve ENV presets
- (musicbrainz) Add exclusive search by recording MBID #431 thanks @gabeklavans
- (musicbrainz) Implement basic-or-ids search
- (musicbrainz) Implement field scoring
- (plex) Retrieve MusicBrainz IDs #426 thanks @owendaprile
Bug Fixes
- (endpointlz) Mark play as new from source
- (librefm) Log warning for empty account rather than throwing #401
- (mpd) Add missing MPD envs to source building #432
- (musicbrainz) Simplify config parsing and log original ids
- (musicbrainz) Check for release data type before logging #434
- (tealfm) Don’t define brainz artist array if empty
- (No Category) Move preCompare transform hook for clients to the correct place in lifecycle #435
- (No Category) Don’t mutate original error when merging cause stack for logging
- (No Category) Remove cache assignment in child class
- (No Category) Add missing undici dependency #437 thanks @jackwilsdon
Documentation
- (musicbrainz) Update search methods
- (No Category) Improve MB docs
- (No Category) Add read-only install instructions #429 thanks @drake7707
- (No Category) Re-style details and add custom admonition details component
- (No Category) Use AdmonitionDetails
Miscellaneous Tasks
- (No Category) BREAKING: Remove deprecated Jellyfin Source
- (No Category) BREAKING: Remove deprecated Plex/Tautulli Sources
Testing
- (lastfm) Improve artist and mbid parsing
v0.11.1
What’s New?
Sonos Source
Sonos devices are now supported as Sources. MS can monitor all devices in your Sonos network given any device’s IP. MS supports multiple listening sessions based on device/zone.
Big thanks to @bkredell for the financial support that enabled me to purchase a speaker to do development with. Without this, the implementation would not have been possible.
ENV Config for all Sources
Previously, Sources that were also Clients could only be configured using File or AIO config. This was due to an old design decision that hasn’t been relevant for quite some time. 0.11.1 removes this superficial limitation so that all Sources can be configured using ENV config. Explicitly, these Sources now have ENV config available:
Memory Optimizations
Several optimizations to reduce memory usage should result in 30-50% less memory used, after startup is finished.
Prometheus Metrics
Multi-scrobbler now exposes a prometheus metrics endpoint with these data points:
- Count of discovered plays per Source
- Count of Queued/Scrobbled/Deadletter scrobbles per Client
- Number of issues per Source/Client
- If any of these metrics is > 0 it means your Source/Client is not operating normally
Musicbrainz Track Search and Plex
One of the MBIDs Plex can provide is a Track MBID, which is associated with a Recording and specific Release. 0.11.1 implements searching by this MBID (generically) and fixes parsing of this ID from plex. Sources that provide this can ensure that the exact album you want for a scrobble is used.
Note: Musicbrainz will not search by this id by default. You will need to use the basicOrIds search method to leverage the Track MBID.
Full Changelog
Features
- (lastfm) Add response content logging #449
- (musicbrainz) Search by track MBID using basicorids #447
- (sonos) Implement Sonos Source #398
- (sonos) Fix first media seen logging and add debug guidance to docs
- (sonos) Add group/device filtering
- (No Category) Add prometheus metrics #438
- (No Category) Implement ENV config for clients as sources
Bug Fixes
- (musicbrainz) Find correct release when release or track mbid is present
- (prometheus) Set content type to plain text #438
Documentation
- (sonos) Update admonitions
- (tealfm) Update tealfm links
- (No Category) Add missing default preset to quickstart MB instructions
- (No Category) Add prometheus usage
Refactor
- (musicbrainz) Rename recording mbid property to recording
- (sonos) Add missing sonos source dynamic usage
- (No Category) Add type label for prometheus metrics
- (No Category) Use compiled json for schema in production
- (No Category) Use dynamic import for components
Miscellaneous Tasks
- (No Category) Add config types to issue template
v0.11.2
What’s New?
Debugging Plays and Help
Plays found in the Source “Tracks Discovered” and Client “Scrobbled/Failed” lists now include a Debug button that, when clicked, copies a json object with data that can be used for troubleshooting issues with multi-scrobbler.

At this stage the data is purely for me to help debug user issues. It should make reproduction of issues much easier and will provide insight into stage modifications. In the future the UI will include a Diff tool to help visualize Play changes over time for self-help.
Check the new Help docs for how to use this debug information to most effectively create an issue for Multi-scrobbler.
Full Changelog
Features
- (scrobblers) Refactor scrobbling functions to capture transactional data
- (No Category) Use json diff to track play changes
- (No Category) Add debug copy action to ui
Bug Fixes
- (librefm) Relax response content-type
- (maloja) Fix logging objects
- (musicbrainz) Check for undefined status #458
- (plex) Do not use album artist MBID when existing artist or is VA #450
- (sonos) Gracefully handle failure when fetching device state #455
- (sonos) Check for UpnpClass existing #398
- (No Category) TS typings for brainz meta and missed recording-track rename
- (No Category) Deep clone discovered plays
Documentation
- (No Category) Comment why compilations don’t work for maloja
- (No Category) Add help docs and update github issue template with play debug data
Refactor
- (sonos) Move device/group filters before track type checks to enable easier user workarounds
v0.11.3
What’s New?
Deezer Improvements
Errors in parsing Deezer history have been fixed thanks to @Ectalite graciously sharing Deezer access to restart stalled development.
Additionally, the Deezer Source now supports scrobbling for a child account when the main account is the owner of a Family Plan.
Jellyfin Config Changes
The allowUnknown File Config property has been deprecated and will be removed in a future release.
It has been replaced by the allowMediaTypes list, which enables more granular control over what types of media can be scrobbled. The list should be one or more of the official media types listed in the jellyfin sdk. The undocumented MusicVideo type is also supported.
If you were using "allowUnknown": true, replace it with the below property in your jellyfin.json config:
{ "allowMediaTypes": ["audio","unknown"]}This setting is also now available as an ENV of comma-separated media types.
JELLYFIN_MEDIATYPES_ALLOW=audio,video,musicvideoChangelog
⛰️ Features
- (deezer) Add family account monitoring #344
🐛 Bug Fixes
- (deezer) Fix error list access #344
- (jellyfin) Fix options being required #469
- (jellyfin) Allow all media types when
allowUnknownis true #445 - (No Category) Compatibility for old scrobbles #462
- (No Category) Normalize state param for auth redirect URLs #443
- (No Category) Allow musicvideo media type and use
allowedMediaTypesfor NowPlayingItem check #445
📚 Documentation
- (plex) Add Musicbrainz stage guidance #447
- (No Category) Make compose default tab for installation
- (No Category) Update screenshot
⚙️ Miscellaneous Tasks
- (docker) Add source OCI labels #463 thanks @Kernald
- (No Category) Fix links in issue templates
- (No Category) Remove unused dockerfile
- (No Category) Update git-cliff workflow
- (No Category) Bump lastfm-ts-api version
v0.11.4
What’s New?
- A very small release to fix a bug with parsing Spotify data #470
v0.11.5
What’s New?
Discord Rich Presence
MS now supports using Discord Rich Presence for Now Playing functionality.
This means that any Source that reports Now Playing or a real-time player, can be used to update your Discord status with a “Listening to…” activity :tada:
There are two types of Discord integrations to choose from:
- App integration uses your existing Discord application on your desktop/laptop
- It also supports a containerized version of Discord
- Headless integration communicates directly with Discord’s API gateway so it is not tied to any specific Discord instance
Check out the parent Discord docs page to get an overview of the differences between these integrations.
Upstream Service Status Page
There is now a status page for monitoring the reachability of public services used by some Sources/Clients is available at
https://status.multi-scrobbler.app
This monitor runs on a VPS and checks the uptime of actual APIs for these services, not just landing pages. On a ~minute interval it checks:
- Listenbrainz API
- Musicbrainz API
- CoverArtArchive API
- Last.fm API
- A few MS-hosted support services
The idea is that this monitor can help you check if external service communication issues are internet-wide or if maybe the issue is in your network.
Full Changelog
⛰️ Features
- (discord) Initial implementation #468
- (discord) Artwork fallback and ENV config
- (discord) Add album art functionality
- (discord) Use artwork url if its from a known domain
- (discord) Use release mbid to get coverart if fallback would otherwise be used
- (discord) Implement retry for cover art api
- (musicbrainz) Implement CoverArtArchive API
- (nowplaying) Update now playing based on play presence AND player status
- (scrobblers) Run preCompare transform hook on playing now data
🐛 Bug Fixes
- (scrobblers) Don’t queue now playing data if scrobbler is not ready
- (transform) Use temporally-invariant hash comparison
- (No Category) Fix config validation error source
- (No Category) Fix labels for MS heartbeat task
📚 Documentation
- (discord) Add discord docs
- (discord) Update artwork guidance for cover art archive
- (discord) Add discord container instructions
- (faq) Add connection/dns guidance
- (No Category) Add status page to monitoring section
v0.12.0
What’s New?
Overhauled Historical Scrobble Matching
MS was originally built with only real-time scrobbling as a design goal so the logic behind “check if this track-to-be-scrobbled already exists for Scrobbler X” only extended as far as checking scrobbles MS had already seen, or the first page of scrobbles returned from the scrobbler’s API. MS has long since evolved beyond these (6 year old) goals but the existing scrobble logic had stayed mostly the same which means many users have had problems with MS when it comes to scrobbling old scrobbles that don’t conform to this real-time assumption.
Now, ~3000 changed lines later and thanks to testing from @4rft5 , this logic has been completely overhauled to enable arbitrary time-range checks for any historical scrobbles: allowing MS to scrobble everything with the assurance they won’t be duplicated, regardless of when they were scrobbled.
Note: This is a fully internal, functional change that does not require any changes in configuration or behavior from you, the user.
Some caveats that will be improved in future releases:
- This change does not yet apply to Rocksky or teal.fm due to constraints on searching PDS repositories. This will be addressed in a future release with manual user interaction.
- There may be issues with fetching in some edge cases pertaining to many results returned. EX: you imported scrobbles into a client with no timestamps so “1 hour timerange” contains ~3000 scrobbles – MS may behave unexpectedly in this scenario. Please file an issue if this scenario applies to you.
This change also paves the way for bulk scrobble migrations between clients. Be on the lookout for this in a future release ;)
Improved Support for File/AIO Config in Documentation
File and AIO Configuration for every Source/Client, on the documentation website, has been improved with new tools:
- A schema explorer lets you browse all possible properties of the config to more easily understand its shape and what is required
- A json editor enables you to modify the examples in-place, while validating your changes against the accompanying schema
- You can edit plain json or use the point-and-click for a guided experience if you aren’t comfortable with json yet
These same tools are available on the Config Playground for general AIO editing and exploration.
Breaking Changes
Koito Support in Listenbrainz Client
The Listenbrainz Client previously has partial support for scrobbling to Koito instances due to a partially compatible listenbrainz api. The new historical scrobble logic makes this support impossible so this support has been fully removed.
If you are still using a LZ client for interacting with a Koito instance you need to migrate your configuration to use the official Koito Client configuration.
This does not affect users already using the Koito client.
Spotify Premium
As of March 9th 2026 Spotify no longer allows Spotify Developer Apps to be owned by a non-premium account.
If you have a non-premium account in this scenario you will likely experience 403 errors the next time your authentication needs to be refreshed.
Note that non-premium accounts can still use MS but you need to re-create an app that is owned by a premium account, first. See the Premium Account Required expandable section in the Spotify Source docs for instructions on how to implement this workaround.
Full Changelog
⛰️ Features
- (docs) Implement config example/structure guidance
- (jellyin) Add external URL guidance
- (scrobbler) Implement play-dependent, arbitrary timerange fetching for duplicate checks
- (scrobbler) Re-implement scrobble preload
- (scrobbler) Improved dupe detection
- (ui) Add service status page link to dashboard
- (ui) Default logs to trace
- (No Category) Implement timerange consolidation
- (No Category) Implement better request retry logic
🐛 Bug Fixes
- (discord) Add hidden activity type
- (discord) Don’t throw on unknown activity type
- (lastfm) Check for singular scrobble response
- (lastfm) Fix not returning nowPlaying scrobble
- (lastfm) Default to not including now playing in history response
- (nowPlaying) Scrobbler should report now playing if player state is known nowPlayingMode source
- (plex) Also read MBIDs from
guidfield @owendaprile - (sources) Add a confidence signal to plays generated from authoratiative sources to avoid fuzzy matching repeats
- (transform) Fix missing defaults assignment for stage config parsing
- (transform) Fix user transform default value
- (transform) Fix transform for stage not using name for selection #481
📚 Documentation
- (spotify) Add premium account guidance
- (No Category) Fix broken links and anchors
🚜 Refactor
- (atproto) Replace naive TID impl with @ewanc26/tid
- (docs) Replace docusaurus json-schema-plugin
- (listenbrainz) Remove Koito support
- BREAKING: Remove Koito support
- (source) Reduce logging noise for activity checks
- (No Category) Use trace logging for defaults
- (No Category) Re-implement retry logic for LZ with fixed request init
- (No Category) Reorganized and consolidate source/client types
⚙️ Miscellaneous Tasks
- (No Category) Exclude schema folder from search
- (No Category) Add netlify-specific build command
v0.12.1
What’s New?
Music Assistant Source
Music Assistant 2.8.0 was released with support for custom Listenbrainz scrobbling URL which means the Multi-Scrobbler Listenbrainz Endpoint Source can now be used for scrobbling from Music Assistant! :tada:
See the Music Assistant setup instructions inside the LZ Endpoint docs to get started.
Notable Fixes and Improvements
- Fixed issues with transform re-use and caching #495 #510 #481
- Improved memory usage for caching and queued scrobbles #507 #484
- Everyone should consider setting up caching with valkey
- Fixed and improved dead scrobble retry behavior #488 #489
- Added Grafana dashboard for use with Prometheus metrics, thanks to @4rft5
Upcoming UI Overhaul
Many of the changes in this release are non-visible improvements/refactoring in preparation for a complete rewrite of the UI. If you would like see a preview of the UI or add feedback on what you’d like to see in a new UI, please check out Issue #500
Changelog
⛰️ Features
- (listenbrainz) Use upstream for backlogged
- (lzendpoint) Update json body parsing to support Music Assistant
- (scrobble) Increase default dead letter retries from 1 to 3
- (No Category) Implement play with lifecycle fixture
- (No Category) Add step and flow result to lifecycle
- (No Category) Add seenAt to source/client play meta
- (No Category) Skip fetching backlogged plays entirely if scrobbleBacklog=false
- (No Category) Add cache stats to prometheus metrics
🐛 Bug Fixes
- (musicbrainz) Catch recordings with no releases
- (scrobble) Improve dead scrobble iteration and logging
- (scrobble) Improve ui/api experience for dead scrobble processing
- (test) Fix expected plays for YTM interim plays
- (No Category) Remove schema-plugin types hint from docusaurus config
- (No Category) Fix type-erased meta property for json plays
- (No Category) Fix wrong operation for maxRandomStagger
- (No Category) Add stagger options as injected dependency to make tests faster
- (No Category) Better handling when stage fails but should continue
- (No Category) Refactor play tranforming to dogfood steps and fix caching #495
- (No Category) Fix sleep time between consecutive dead scrobble attempts
- (No Category) Only enable metrics collection for real runs
- (No Category) Use lifecycle invariant play for closest match
📚 Documentation
- (No Category) Add duplicate scrobble detection explanation
- (No Category) Add Music Assistant to lz endpoint docs
- (No Category) Add prometheus usage
- (No Category) Add netlify badge to footer on preview docs
- (No Category) Add more caching guidance
- (No Category) Fix discord link on jellyfin page
- (No Category) Add grafana dashboard
- (No Category) Simplify installation
- (No Category) Add music assistant to readme
🚜 Refactor
- (No Category) Move some utils into core folder
- (No Category) Fix JsonPlayObject to properly type dates
- (No Category) Separating functions so they are usuable by frontend storybook
- (No Category) Move stagger config responsibility to transformer
- (No Category) Refactor cache building to be able to specify both primary and secondary
- (No Category) Remove ttl from mb api to ensure primary/secondary ttl is used correctly
v0.12.2
Changelog
- Fix play initialization not being reset on each API fetch invocation
v0.13.0
What’s New?
Yandex Music Source
Thanks to @Druidblack for implementing a new source for the Yandex Music service! :tada:
Check the docs for setup and configuration.
Improved Source/Client Restart
An overhaul of the functionality responsible for Source polling and Client scrobble processing makes restarts from the dashboard cleaner and more dependable. There shouldn’t be any more instances of unable to restart due to waiting for signal... :tada:
New Docs Domain and Social Previews
Docs are now “officially” located at https://docs.multi-scrobbler.app
Docs have also been improved with nice social graph preview images (og:image) that will display when a page is shared on a social site. There are images generated for each page that include the page title and/or description.
Docker Log Colors
Logs viewed through docker logs now have ~f a n c y~ colors that mirror what is seen in the dashboard.
These are turned on by default but if you prefer plain logs they can be disabled by setting this environmental variable:
COLORED_STD=falseImage Cleanup Space Savings 🧹
Fixing how the frontend is served from the backend enabled almost all frontend packages to be removed from the production build. This, along with a node_modules cleanup script, results in a ~32% decrease in docker image size.
Breaking Changes
Changelog Highlights
⛰️ Features
- (client) Make scrobble processing abortable
- (client) Strict ENV types
- (docker) Use prune-mod to remove unneeded files in image
- (docker) Make colored console output the default
- (docs) Update docusaurus to 3.9.2
- (musicbrainz) Handle rate limiting within api calls
- (source) Make discover abortable
- (sources) Better type enforcement
- (yandex) add yandex music by @Druidblack #523
🐛 Bug Fixes
- (jellyfin) Fix typo on devicesBlock env assignment #522
- (scrobbler) Fix stagger map generation variable usage
- (No Category) Fix circular dependency in error utils
- (No Category) Align dockefile and devcontainer node/npm versions to fix lockfile mismatch
- (No Category) Initialize plays in correct scope
- (No Category) Build vite-express config based on production env to avoid making vite fallback to development
⚙️ Miscellaneous Tasks
- (No Category) Remove unused testing libraries and fix storybook version
- (No Category) Update node/npm engine constraints in package.json
- (No Category) Bump node to 24
- BREAKING: Bump node to 24
📚 Documentation
- (No Category) Add Yandex Music Source docs
- (No Category) Replace instances of foxxmd.github.io
- (No Category) Finalize a working social card strategy
🚜 Refactor
- (No Category) Implement abortable source polling
- (No Category) Reduce docker image by moving frontend packages to devDependencies
- (No Category) Install expressjs@5
- (No Category) Replace awaitjs/express with express usage
🧪 Testing
- (No Category) Make loop stop wait time configurable so tests run faster/more stable
v0.13.1
Changelog
- fix: Add missing next() invocation preventing ingress Sources from completing requests #561 #562
v0.13.2
What’s New?
Discord Improvements
- Fall back to using release group MBID to retrieve album art if the defined release does not have associated art
- Use zero-width padding to allow single-character titles
Fresher Now Playing
For Sources that support multi-platform (plex, jellyfin), MS will more aggressively mark players as stale when there has been no activity but the Source still reports a player is alive. It will also switch Now Playing to the player that has actual activity if the “current” Now Playing player goes inactive (paused, stopped, stale).
Duplicate Scrobble Detection Docs
A new section in the docs has been added that goes into depth on:
- How MS detects duplicate scrobbles
- Configurations that won’t cause dupes
- Configurations that MAY cause dups
- Solutions for avoiding these scenarios
Full Changelog
⛰️ Features
- (discord) Implement fallback to release group art if present #578
- (endpointlz) Add stubbed playing-now route to make some clients happy(er) #579
- (endpointlz) Implement proper username handling for validate and now-playing routes #579
- (lastfm) Add more logging to auth process and clean up host detection
- (mpd) Rewrite with newer MPD client for better payload parsing #574
- (player) Skip stale updates based on state timestamp and prevent dead players from being created from stale updates
- (source) Add timestamps to play state to track player and play last updated, if present
🐛 Bug Fixes
- (cache) Explicitly define auth/metadata as memory when testing file caching
- (discord) Include known spotify cdn for media providers #569
- (discord) Workaround activity minimum character limit #578
- (endpointlz) Fix fallback route not returning a response
- (lastfm) Fix total results reporting for librefm
- (now playing) Prefer fresher players #480
- (player) Initialize player state in the past to prevent false positive on stale update check if update happens at the same time player is initialized
- (transformer) Try-catch cached steps and fallback to non-cache generation
- (webscrobbler) Convert express wildcard path to regex #583
- (No Category) Check undefined on correct property #567
- (No Category) Simplify listenrange usage
- (No Category) Fix missing await on stagger mapper initial sleep
- (No Category) Workaround for old play structure
📚 Documentation
- (mpd) Add permission guidance
- (No Category) Add base site env detection for cloudflare pages
- (No Category) Order BASE_SITE first to allow overriding other envs
- (No Category) Fix missing backticks causing template errors on version equality
- (No Category) Try to replace canonical urls for github.io with new site
- (No Category) Add duplicate scrobble guidance
- (No Category) Remove extra dupe detection section from client overview
🚜 Refactor
- (No Category) Replace fast-deep-equal with fast-equals #569
🧪 Testing
- (player) Fix incorrect variable used for advanced timestamp
⚙️ Miscellaneous Tasks
- (No Category) Remove references to KOITO_ALLOWED_HOSTS (#581) thanks @gabehf
- (No Category) Override rate-limit-threshold to use new version with reduced dependencies
0.13.3
What’s Changed
🐛 Bug Fixes
- (transform) Fix bad usage of cache potentially causing older-timestamped plays to be returned when newer play data was identical
- (spotify) Don’t use platform creation timestamp as
stateLastUpdatedAt, fixes spotify player accidentally going stale while still active
⚙️ Miscellaneous Tasks
- (lastfm) Increase connection test timeout for slow networks and add DNS resolution logging
v0.14.0
What’s New?
I am excited to release Multi-Scrobbler 0.14.0 :tada: This is one of the largest releases for MS in years which significantly rewrites large portions of MS’s architecture to allow it to scale in preparation for even more exciting, near-future functionality like the upcoming ui overhaul and eventual bulk scrobble transfer feature (move all your scrobbles from Client X to Client Y).
Stats for this release:
- 158 commits
- 243 files touched
- +15,400 additions
- -4,200 deletions
- No AI was used during development. This is organic, free-range coding.
I want to acknowledge and say a big thank you to all the users who helped test and give feedback during this release process. Your input, patience, and reporting was invaluable :heart: :
@4rft5 @FestiveByteRider @gabeklavans @psyciknz @DigitalStarSys @owendaprile @timmyhbk @Sioeen @Ectalite @HStep20
Backend Overhaul
This release introduces a huge architectural upgrade for the backend: almost all data is now backed by a sqlite database powered by drizzle.
Some visible change highlights:
- Discovered plays (from sources) are persisted
- Backlogged, already discovered plays are not re-sent to clients on each startup (they are found in database and ignored)
- Scrobbled plays (successfully sent by clients) are persisted
- Reduces dupe scrobbles since identical scrobble data in MS can now be compared across restarts
- Database migrations are automatic and also automatically make a backup of your database before upgrading
- All queues and Plays are now managed in database instead of in-memory
- Reduced memory usage in all usecases
- Drastically reduced memory when there are many sources/client (5+) or many queued scrobbles (100+)
- Source and Client statistics are persisted
- (breaking) Simplified cache configuration to leverage database where feasible
To prepare for database usage in 0.14.0, please read the Upgrade Path docs.
The migration should be simple for the majority of users. For edge cases and power users, the docs cover how to migrate more advanced settings.
Documentation Improvements
The introduction of the database forced a good look at, and cleanup of, the existing docs. Application-wide config should now be easier to read and the kitchensink examples are now more straight-forward and relevant to common usecases.
Other Changes (Changelog Highlight)
⛰️ Features
- (database) Implement database structures needed for historical plays
- (database) Track component last ready and last active at
- (tealfm) Add now playing support with
fm.teal.alpha.actor.statusby @HiiJax :tada: - (tealfm) Update lexicon defs with new mbid uri format teal-fm/teal#102
- (now playing) More robust handling of clients with real time status updates (teal, discord) #608
- (now playing) reduced logging noise
- (lastfm) bypass scrobble rejection for very short tracks (< 30 seconds) by omitting duration #596
- (No Category) refactor artists to directly map/associate MBIDs
⚙️ Miscellaneous Tasks
- (No Category) update all dependencies #594 by @philocalyst :tada:
📚 Documentation
- (lastfm) Document 2 week scrobble window
- (No Category) Overhaul kitchensink
- (No Category) Add
_IDand_NAMEexamples to all instances of ENV configs - (No Category) Add
idto all instances of file/aio configs - (No Category) Add more descriptive error logging for example config file parsing
v0.14.1
Full Changelog
🐛 Bug Fixes
- (ui) Update JSX namespace to fix missing ambient in react 19
- (No Category) Fix argument order for logger labels
- (No Category) Add missing artist credit transforms causing Navidrome Source plays to create errors #613
⚙️ Miscellaneous Tasks
- (No Category) Re-generate lock file
0.14.2 - UI Preview
What’s New?
Historical Scrobble Matching for teal.fm/Rocksky
Multi-Scrobbler checks your scrobble clients to see if an incoming Play was already scrobbled. This process was previously overhauled in v0.12.0 to make it work for any arbitrary time range, but this overhaul did not include teal.fm and Rocksky due to limitations in how querying atproto records works.
In v0.14.2 this has been resolved so that these clients have the same arbitrary timerange functionality matching as all other clients. This is achieved by initially downloading a full copy of your record and converting them into MS scrobbles to store in the new database. Then, the copy is kept in sync with real time queries to the atproto api. MS then queries this copy when matching scrobbles.
New UI Preview
The UI for MS has been fully rewritten and a preview is now available alongside the normal UI. Some highlights:
- mobile-first design
- live, visual updates for all activity
- search and filter all of your scrobbles
- scrobble timeline shows how MS processed your scrobble data from end-to-end
It can be used by navigating to
http(s)://yourMsDomain/nextI need your feedback on bugs, opinions on design choices, and general usage impressions! Refer to the github issue for the preview ui for more details and to provide feedback.
Some screenshots from the preview ui:
Left: Source/Client list (main page) | Right: Source detail page with Plays
Left: Scrobble duplicate check results | Right: musicbrainz transformer “no results” with used queries
visual indicator of live activity (just scrobbled) on main page
0.15.0
What’s New?
Apple Music Source
Thanks to the efforts of @Exerra , Apple Music is now implemented as a Source for Multi-Scrobbler!
Exerra, the primary developer of node-musickit-api, discovered that the API access required for MS to monitor listening activity only needs credentials that can be acquired from the browser of a logged in user. Critically, this bypasses the long-standing assumption that a paid apple developer account was required to access these APIs.
Exerra also contributed the entire Apple Music Source implementation to Multi-Scrobbler :crown: If you end up using this Source please consider sponsoring them as a thank you for all this hard work!
Improved Subsonic Playback Reporting
The OpenSubsonic API recently finalized a new spec for Playback Reporting that, when implemented by both the client and server, enables realtime player position reporting/states. This allows MS to track Subsonic players with the same level of accuracy as Spotfy, Jellyfin, and other first-class Sources!
Huge thanks to @JannisPohle for the initial PR on cleaning up subsonic players ( #640 ) but then stepping up to also implement this playback functionality ( #590 ) :crown:
As emphasized above, this new functionality requires that both the Subsonic server and the client you use to play music from that server both implement Playback Reporting. An informational tooltip in the UI Preview will indicate if enhanced reporting is available.
Data Directories Standardization
Previously, all persistent data was stored in the directory defined by the CONFIG_DIR ENV, or defaulted to {projectDir}/config.
Now, these locations are configurable by data type and fallback to OS-standard locations:
- Configuration (
config.json,jellyfin.json, etc…) usesCONFIG_DIRENV or OS-standard locations like$XDG_CONFIG_HOME/multi-scrobbler - Data (
ms.db, logs folder, stored credentials, etc…) usesDATA_DIRENV or OS-standard locations like$XDG_DATA_HOME/multi-scrobbler
Upgrading
If you are using a Docker installation with default volume mounting to /config then no change is required. For backwards compatibility, the docker image installation defaults both of these ENVs to /config.
For users using the nodejs/local installation, you will likely want to explicitly set both ENVs for your start command. For instance, if you are currently storing everything in the config folder within the cloned repository and the working directory is also the repository folder then add these ENVs to the beginning of your command:
CONFIG_DIR=./config DATA_DIR=./config ... npm run startSee the Config and Data Directories section in the installation docs to find the OS-standard locations MS will use when these ENVs are not set.
Monitoring Behavior
In preparation for wider usage, the feature known as “Should Scrobble Behavior” has been renamed to “Monitoring Behavior”. The configuration naming has been changed to reflect this.
Upgrading
If you are an Icecast or Azuracast user who has systemScrobble in your File/AIO config options, rename this property to autoMonitor. The behavior for this property remains the same.
Example
Old:
[ { "name": "Station 1", "id": "myIceStation1", "data": { "url": "http://myIcecast.com/radio" }, "options": { "systemScrobble": true } }]New:
[ { "name": "Station 1", "id": "myIceStation1", "data": { "url": "http://myIcecast.com/radio" }, "options": { "autoMonitor": true } }]Config Improvements
Internally, config has undergone a complete overhaul and is now entirely built using Zod. This is a huge improvement over the hacky typescript-types-to-json-schema generator that was being used because the config shape now fully dictates validation, rather than there being a reflection middle step. It additionally removes the ajv library and saves almost 30MB in the docker image when uncompressed.
With the overhaul, a simplification of the config reduced development complexity and made the config explorer in docs clearer, at the expensive of a few, potential breaking changes.
Upgrading
User Stage Transforms
If you have User Stage transforms and they do not already have a "type": "user" property, you need to now add this property or validation will throw an error.
Example
Previous User Transform config could be untyped like
"options": { "playTransform": { "preCompare": [ { "title": [ "badTerm", ] } ], }Now, all user transforms must be typed like all other transforms or config parsing will throw an error:
"options": { "playTransform": { "preCompare": [ { "type": "user", "title": [ "badTerm", ] } ], }Removed Deprecated Cache Config
Cache Configuration was simplified in 0.14.0 but using pre-0.14.0 configuration only caused a deprecation warning. Now, these deprecations have been removed and using the old configuration will cause an error.
Node Native Runtime
Multi-Scrobbler has been refactored to remove the dependency on tsx as the runtime. Now, MS uses Nodejs’ type stripping so that it can directly run the source typescript files without needing the typescript runtime or any compilation steps.
This reduces packaging, tooling, and local run complexity:
- environments do not need to rely on
npmscripts for running MS- or needing to specify the tsx binary with node for commands
- Improves process monitoring readability
- Reduces memory usage since typescript does not need to be loaded into memory
- Reduces disk space since typescript does not need to be installed to run in production
- Tooling calls (mocha, vscode extensions) can directly use node without a shim
If you are currently running MS without using npm run start and have questions about how to migrate please open a discussion.
UI Preview Improvements
The New UI Preview introduced in 0.14.2 has seen several rounds of refinement thanks to user feedback. It is still in “read-only” mode for this release but the next release will begin to introduce write functionality. Check out the preview on your instance:
http://yourMsDomain/nextand share your feedback in the github issue!
Other Changelog Highlights
🐛 Bug Fixes
- (client) Correct create/update timestamps for queues and scrobbles
- (client) Correct SOT scrobble api requests to not have the same from/to value
- (listenbrainz) Fix play fetch more indicator
- (musicbrainz) Catch scenario where all search prereqs fail
- (rocksky) Fix missing albumArtist field #632
- (ui) Stabilize play timeline item order
- (mpd) Assign empty array if artists not defined #621
- (No Category) Fix play fetch options logging data for pageless functions
🧪 Testing
- (No Category) Fix db error test using transient db
Changes Since RC1
⛰️ Features
- (No Category) Reduce db write frequency when idle by buffering date updates #653
🐛 Bug Fixes
- (webscrobbler) Fix bad access of PlayerState #650
- (No Category) Fix missing logPath base dir argument
- (No Category) Guard against tracks/track in mb recording medium #649
📚 Documentation
- (No Category) Add missing user type to user transform examples
- (No Category) Clarify when activity is scrobbled
0.16.0
What’s New?
Reduced Memory Usage
Moving to Zod in 0.15.0 provided a big reduction in memory footprint that has been further improved in this release by moving individual Source/Client validation to be dynamically loaded. This means MS will not load validation/types for Source/Clients you do not use.
Users with a minimal number of Sources/Clients should see the largest impact from this.
Benchmarks for MS with no Sources/Clients
Startup
| Version | Runtime | Validation Context | % Memory Usage Change |
|---|---|---|---|
| 0.14.2 | tsx | ts gen + ajv | - |
| 0.15.0 | node | zod + eager validation | -13% |
| 0.16.0 | node | zod + dynamic validation | -14% |
Stabilized (after 4 minutes of running)
| Version | Runtime | Validation Context | % Memory Usage Change |
|---|---|---|---|
| 0.14.2 | tsx | ts gen + ajv | - |
| 0.15.0 | node | zod + eager validation | -22% |
| 0.16.0 | node | zod + dynamic validation | -27% |
ENV Config UX Improvements
The move to Zod in 0.15.0 also opened the door for big improvements to ENV Config experience for the end-user:
Self-Documenting
ENV variables now share, or inherit, schema from file/aio config so ENV descriptions match those found elsewhere. The ENV config for Source/Clients in the docs is now self-documenting (generated from schema) to prevent typos and make common ENV easier to extend.
Improved Validation
ENV validation at startup is now more straightforward and useful. Instead of mapping variables to file (KOITO_TOKEN => data.token) and then printing an error
✖ Invalid input: expected string, received undefined → at data.tokenVariables are now directly validated to give you a clearer idea of what is missing or wrong
✖ Invalid input: expected string, received undefined → at KOITO_TOKENSource/Client ID Enforcement
To further improve DX/UX the ID requirements first introduced in 0.14.0 are now mandatory.
Previously, MS would log a warning and use your Source/Client name as the ID, as noted in the 0.14.0 upgrade guide. Now, it reports a configuration error if no ID is present.
To migrate, fully complete the Configuring IDs section from the 0.14.0 migration guide.
Other Changelog Highlights
⛰️ Features
- (lfm endpoint) Implement better api emulation #656
- (source) Add Ingress SOT type
- (now playing) Buffer valid updates for endpoint sources #659
- (ui) Improve UX for theme switcher
- (ui) Implement basic settings page
- (No Category) Improve location wording for config errors
🐛 Bug Fixes
- (source): Fix player cleanup for ingress sources #652 #591
📚 Documentation
- (No Category) Use italicize-bold text style for required env properties instead of separate column
- (No Category) Update env descriptions for id, name, and enable
- (No Category) Update now playing for clients
- (No Category) Fix config.json example
- (No Category) Add lastfm endpoint alternative guidance
- (No Category) Update configuration types language
0.16.1
Changelog
🐛 Bug Fixes
- (No Category) Add missing
configureAsfor ENV parsed components #661
0.16.2
Changelog
🐛 Bug Fixes
- (source): Fix missing id/uid on cached recent plays #662 #665
0.16.3
Changelog
🐛 Bug Fixes
- Fix incorrect prefix not being used for env key collection #668
- (apple music) include library-songs in recent tracks #667
📚 Documentation
- Improve scenario guidance for lfm/lz endpoints
0.16.4
The jumble of patches released this week in the aftermath of 0.16.0 were indicative of a poor release.
I take pride in making sure Multi-scrobbler is not one of those projects you are hesitant to upgrade because you don’t know if “this will be the one that breaks unexpectedly”. 0.16.0 was not up to that quality and for that, I apologize.
This release should hopefully make things right.
What’s New?
ENV Testing and Stability
The ENV improvements made in 0.16.0 are here to stay but to address the quality issues mentioned above this release focused on building a proper test suite to validate and test all ENV keys with real, valid data:
- tests were added for all sources/clients that
- generate all ENVs for each source/client with real data derived from the ENV schema shape, using zocker
- assign these ENVs to the actual environment and then build the actual source/client, verifying no config errors and that exactly one source/client of that type exist
This now puts ENV testing on the same level of testing as file/aio config and should catch any errors, like those fixed in 0.16.1 - 0.16.3, before they get to the release stage.
Full Changelog
⛰️ Features
- (No Category) Relax configureAs requirements for AIO configs
- (No Category) Strongly-type internal events #666
- (No Category) Add ENV config test suite
- (No Category) Update json schema to draft-2020-12
- (No Category) Normalize domain:port url input
- (No Category) Better validation for normalized config urls
🐛 Bug Fixes
- (discord) Fix discord ENV parsing validation
- (No Category) Check for ENV prefix with startsWith instead of includes #668
- (No Category) Narrow env config key collection to include prefix underscore #668
- (No Category) Hand-rolled env data fixtures for some edge cases
- (No Category) Hand-roll applemusic env fixture data
📚 Documentation
- (No Category) Add more guidance for File config example/editor
- (No Category) Hand-roll discord ENV updates
0.16.5
This is a small release due to needing to get Listenbrainz’s new request requirements out to stable! A larger release with new UI functionality will be coming soon.
What’s New?
Listenbrainz User-Agent
A recent botnet attack has forced Listenbrainz to require all traffic to include a User-Agent and implement retry backoffs.
Multi-Scrobbler already implements backoffs but did not have a proper User-Agent in place. This release introduces a UA for all requests that should restore Listenbrainz connectivity.
teal.fm production lexicon
teal.fm has moved their lexicon to the production namespace and so this release of multi-scrobbler updates the repository records to use the new namespace.
Other Changes
- @halkeye fixed the code for generating “Edit this page” links in the docs :tada:
0.17.0
What’s New?
Preview UI Feature Parity
The new UI has feature parity with the existing UI, plus so many more improvements. :tada:
0.17.0 will be the last release before the new UI replaces the existing one. If you have design feedback or things aren’t working as expected please report them in this issue. Also would be nice if you left a comment letting me know that everything is working as expected and you like the new design! Feedback either way is good.
Listenbrainz User-Agent Changes
Listenbrainz has updated their User-Agent requirements to require a point of contact, similar to how Musicbrainz works.
The 0.16.5 release that addressed Listenbrainz User-Agent updates is no longer sufficient. Use the updated Listenbrainz for multi-scrobbler to update your configs to include a contact.
Use the 0.17.2 release. Your listenbrainz configuration does not need to be modified.
Improved Source Play Processing
The base Source class has been restructured to use a queue workflow, similar to the base Client class, that enables new functionality and improves all existing Play processing. Some highlights:
- Backlog play processing no longer blocks Source startup
- Ingress based Sources can now send batch scrobbles and get an immediate response without needing to wait for all Plays to process
- Source plays that fail to process can now be retried (same way as Client plays)
- Sources can process existing, queued Plays even if polling has failed
Optimized Existing Play Checks
When polling Listening History based Sources MS parses all returned history, every time, to look for changes in the list.
Previously, if you had preCompare transforms these needed to run for all history before MS could check if each Play already existed.
Now, MS stores a hash of the original data, before transforms, and uses this to check for existing Plays before transforming. This optimization can potentially save hundreds of API calls to musicbrainz and wasted CPU cycles transforming Plays that would then be thrown away immediately.
Play Events
The events shown on the Timeline for a Play in the Preview UI were previously hacked together from many different locations in a Play’s data.
This data has now been restructured as a first-class data structure persisted to the database. What this means for you is that the Timeline will now show a more complete, user-friendly list of events and that future event types are trivial to create.
Api Validation and Docs
Thanks to @Mini-Sylar ’s excellent express-typed-router library MS now has fully-typed api routes.
Ingress-based Sources like Webscrobbler and Listenbrainz/Last.fm Endpoints get exhaustive request body/query validation with same kind of descriptive errors configuration reports.
And all api endpoints, including ingress, are now self-documenting and added to the main docs website. If you’ve ever wanted to build on top of the data MS creates, you now can!
Note: Other than ingress-based endpoints, the APIs are documented but not fully stable yet. They will be stabilized once the preview ui is released.
BrainzMash Musicbrainz Server
For users who are using Musicbrainz for enhancing their scrobbles: a new, public Musicbrainz mirror is detailed in the docs under the BrainzMash Musicbrainz Server expandable section.
BrainzMash is a community-run pool of self-hosted, read-only hybrid Musicbrainz/Lidarr metadata servers. Requests are made to one, public endpoint that load-balances traffic out to all participating, private servers.
Supplementing your config with this additional Musicbrainz server can increase your transformer speed and throughput.
Security Documentation
A Security section has been added to the docs that details the risks of public access to your Multi-Scrobbler and what you can do to secure it.
Thank to @gnosticJade for the Caddy example and corrections on protected routes!
Other Changelog Highlights
⛰️ Features
- (lastfm) Use p-retry for request retries
- (librefm) Implement rate limiting
- (No Category) Use better rate limiting implementation for musicbrainz
🐛 Bug Fixes
- (atproto) Guard against null rate limit header parsing #689
- (atproto) Detect dupe rkey in batch before persist #689
- (atproto) Implement proper error handling for xrpc calls #689
- (source) Check for player repeat using closed listening session fallback #691
- (tests) Improve network mocking to compensate for libraries with frozen imports
- (ui) Log settings route schema and middleware #693
- (transform): Catch and log errors during console diff generation #684
📚 Documentation
- (No Category) Add security guidance
- (No Category) Adjust caddy snippet with better guidance
- (No Category) Cleanup atproto admonitions
- (No Category) Add BrainzMash musicbrainz guidance
- (No Category) Add openapi docs
🚜 Refactor
- (lastfm) Simplify api error reporting
🧪 Testing
- (lastfm) Test rate limiting and error handling
0.17.1
Changelog
⛰️ Features
- (api) Bump express-typed-router and add global validation failure logging
🐛 Bug Fixes
- (api) Make optional query params optional in route schemas #697 thanks @MFA-G
- (lastfm) Source missing options when initing client #699
- (webscrobbler) Loosen scrobble schema for real-world usage #700
- (No Category) Fix route mounting for /api/callback #699
📚 Documentation
- (No Category) Update BrainzMash language
0.17.2
What’s New?
Listenbrainz User Agent Correction
A misreading of the new Listenbrainz User-Agent requirements introduced in 0.17.0 has been clarified: contact is not required for regular users, only developers.
The contact (File/AIO) and LZ_CONTACT (ENV) config options have been made completely optional with updated language. You should only use these options if you are using a forked version of multi-scrobbler.
Changelog
⛰️ Features
- (listenbrainz) Remove contact guidance and provide default developer info #701
- (source) Remove noisy logging when dropping backlog queue plays
- (No Category) Increase lfm/lz source min polling time to 30 seconds
🐛 Bug Fixes
- (deezer) Add missing parsedFrom
- (lastfm) Add missing parsedFrom to enforce pre-transform/queue check on history sources #702
- (No Category) Fix component id param for auth url route
0.18.0 - New UI
What’s New?
New UI :tada:
Multi-Scrobbler has a new UI that has been completely rewritten from scratch!
This is the same UI that has been in preview for awhile. It now completely replaces the old UI. In conjunction with replacing the UI, all the old UI code and associated API endpoints have been ripped out, removing 4400 lines of code and 15 dependencies.
The UI refresh has been a long time coming: #500 was opened March 24 and was the impetus for both the database and queue overhaul implementation. The kinds of features I wanted to see in the UI would not have been possible without these prerequisites so it has spurred much of the quality upgrades for MS over the last half year.
A HUGE thank you goes out to everyone who has helped test the new UI and all its features, giving me feedback on design and exposing bugs. Especially @4rft5 :crown:
There is still much to improve with the new UI so look out for new features coming soon!
Musicbrainz User Agent
Like with the Listenbrainz User Agent change from 0.17.2, contact is no longer required for configuring the Musicbrainz API for transformer use. If you are using this API you do not need to make any changes to continue using it, but you can remove contact if you want to.
If you only have the contact field for your aio confg and want to remove it, replace it with "enable": true.
Musicbrainz Rate Limit Improvements
0.17.0 introduced BrainzMash as a recommended, supplementary Musicbrainz server to increase transformer throughput but the API rate limiting functionality in Multi-Scrobbler still enforced it, and other non-official servers, with the same rate limits as the official Musicbrainz server.
This functionality has been overhauled to be both optionally configurable and load balanced based on rate limit capacity.
Configuration
Set the max requests within seconds-based time frame in your MB API config:
"apis": [ { // uses default Musicbrainz server https://musicbrainz.org // hard-limited to 1req/s "enable": true }, { // non-official musicbrainz server "url": "my.mb.server", "rate": { "requests": 6, // max number of requests "perTime": 1 // can be made within X number of seconds } }]- Official Musicbrainz server is hard-limited to max 1req/s
- Brainzmash is hard-limited to max 4req/s
- Other, custom Musicbrainz servers default to 1req/s but have no hard limit
Load Balancing
Based on the rate limits you set, or that are hardcoded based on the above, Mutli-Scrobbler will automatically load balance requests between your servers based on which has the most number of requests left for the time span.
Subpath Deployments
For users deploying Multi-Scrobbler behind a reverse proxy, subpaths are now supported using BASE_URL env.
To deploy to https://mydomain.com/multi-scrobbler:
services: multi-scrobbler: # ... environment: - BASE_URL=https://mydomain.com/multi-scrobblerFurther setup is required if you also need self-hosted docs, see Base URL guidance in the installation docs.
Other Changelog Highlights
⛰️ Features
- (db) Check for existing play by parent hash
- (No Category) Add hash regeneration migration
🐛 Bug Fixes
- (dedup) Match backlog plays against live-tracked duration window #709 @plttn
- (dedup) Anchor the duration/listenedFor window on playDate, not the SOC-adjusted date #709 @plttn
- (listenbrainz) Try to coerce tracknumber to number #705
- (source) Don’t mark oldest of set of duplicate inputs as a duplicate of the newer inputs #707
- (spotify) Backfill missing ISRC from real-time polling endpoints #703 @plttn
- (No Category) Regenerate play hash and mbid columns on play entity update db operaiton
📚 Documentation
- (endpointlfm) Add API docs callout
- (endpointlz) Add api doc callouts and summaries to popular apps
- (endpointz) Add tidal hi-fi guidance
- (webscrobbler) Add api docs callout
- (No Category) Remove contact from listenbrainz example config file
- (No Category) Update express-typed-router for pathExample handling
- (No Category) Update development docs to reflect current architecture and implementation
🚜 Refactor
- (No Category) Consolidate repo hydration and existing play function
- (No Category) Consolidate play queuing into abstract component
- (No Category) Consolidate ingress queue pre/post processing into abstract component
🧪 Testing
- (spotify) Add test case for skipping enirch when ISRC is present #703 @plttn
0.18.1
🐛 Bug Fixes
- (plex) Re-add missing component artwork api route #718
- (ui) Add duped to common play state so its available for source play filtering
0.19.0
This is huge release that brings a ton of new Play/Scrobble modification options to MS!
What’s New?
Simpler Config For Teal/Rocksky Sources
App Password has been removed from required config for Teal.fm and Rocksky Sources. It still required for Clients (scrobblers).
Simpler Config for Musicbrainz Transformer
Due to the lack of required config since the change to the Musicbrainz API in 0.18.0, explicit setup of the Musicbrainz transformer is no longer required. It is now built-in to MS with a default configuration that can be used without needing to specify apis or anything in your AIO/ENV config.
You can still configure rate limiting for the default server, or add additional servers, using the same config. It’s just not required to explicitly set anything anymore.
Example
If you currently have this in your config.json
{ // ... "transformers": [ // your empty musicbrainz transform { "type": "musicbrainz", "name": "MyMB", "data": { "apis": [ { "enable": true } ] }, }, // ...maybe more transforms here ], // ...}You can remove the musicbrainz object entirely. If it’s the only entry in your transforms array then you can remove that too.
Then, in your Source/Client transform hooks change this
"playTransform": { "preCompare": [ { "type": "musicbrainz", "name": "MyMB" } ] }to this
"playTransform": { "preCompare": [ { "type": "musicbrainz", "name": "MSDefault" } ] }or entirely remove "name": "MSDefault" and MS will use whatever is configured first.
Rocksky PDS Client
The Rocksky Client has been rewritten to use the sdk agent for scrobbling rather than scrobbling through the Rocksky server.
This change, along with using the new Rocksky matchSong metadata API, means Multi-Scrobbler can now:
- give you immediate feedback on when a scrobble does not meet Rocksky’s standard of quality and
- directly write scrobbles to your PDS repository
This is a huge improvement in user experience over the old “submit scrobble to rocksky server and find out later if it was accepted” approach. Use this in conjunction with the new Rocksky Transformer to get (essentially) guaranteed scrobbles to Rocksky with verifiable writes.
An additional benefit of the PDS client is that MS now supports Now Playing for Rocksky.
Rocksky Transformer
A new scrobble enhancement stage is available, the Rocksky Transformer.
This stage uses Rocksky’s metadata API to match multiple providers (deezer, spotify, etc…) and returns an enriched metadata result that Multi-Scrobbler can use to add Musicbrainz Recording MBID, ISRC, artists, etc… similar to how the Musicbrainz Stage works.
Rocksky’s metadata API is still a work-in-progress so the resulting transformation is limited for now, but it is extremely fast and provides good results!
Use this in conjunction with the Rocksky PDS Client to guarantee scrobbles for Rocksky or as a fast-and-cheap alternative to Musicbrainz.
Spotify Transformer
A new scrobble enhancement stage is available, the Spotify Transformer. All of the research, config options, and initial implementation was contributed by @DigitalStarSys :tada:
This stage works similarly to Rocksky and Musicbrainz transformers in that it is useful for fixing/enriching your scrobble data.
It is advantageous to use, over Musicbrainz, if you prefer a simpler setup with fast API responses and formatted data that is familiar to a mainstream audience (vs. having to configure Musicbrainz and filter through all the different release types).
Note: This transform requires a premium Spotify account (same as the Spotify Source) to use the Spotify API.
Cover Art Archive Transformer and Art Transforms
A new scrobble enhancement stage is available, the Cover Art Archive Transformer. This is standalone version of the Cover Art Archive functionality found in Discord.
When this Stage is used, if your scrobble data has Musicbrainz’ MBIDs for album or release-group and is missing album art, MS will use the CAA API to fetch an album art URL.
Art In Other Transformers
As part of this improvement, both the Rocksky and Spotify Transformer can also populate album art URLs when they are missing. This is enabled automatically as part of Stage Rules and can be explicitly disabled with a rule "art": false.
Device Info for Listenbrainz-Compatible Scrobblers
Thanks to @rpschube reported device info from Sources can now be safely submitted as scrobble data to listenbrainz-compatible clients (Listenbrainz and Koito) without leaking any PII by using an allowlist of scrubbed identifiers to match with real device ids.
See the Scrobbling Device Data expandable section on Listenbrainz and Koito clients to see how this works.
Reduced Docker Build Size
More cleanup done after 0.18.0 revealed a large number of files in docker file build layers that could be easily pruned. A minor rewrite of the Dockerfile has eliminated these and improved build times as well. The cleaned up docker image is 20% smaller than 0.18.1.
Typescript Strict Mode
The entire project has been refactored to compile with typescript strict mode on. This “enables a wide range of type checking behavior that results in stronger guarantees of program correctness” which in laymen’s terms means that ongoing development of Multi-Scrobbler is less likely to introduce new bugs. Just moving to strict mode fixed at least 30+ undiscovered bugs!
Dynamic Transformer Loading and Usage
The logic that loads code for tranformers has been refactored so that all transformers and their dependencies loaded just before a Source/Client actually uses them instead of on startup. This should result in less memory usage for all users but especially those that don’t use any transformers at all.
Additionally, the names of built-in transformers and the order they are used in has been changed to be deterministic and give you more options for which you would like to use, between built-in, ENV configured, or file/aio configured. See the Breaking Changes section below for these changes.
Breaking Changes
Rocksky Auth API Key Removal and Token Deprecation
- The previously-deprecated API Key authentication for Rocksky has been removed
- Access Token authentication for Rocksky is now deprecated in favor of handle/app password auth that uses the new functionality from above.
- Review the Rocksky Client docs for how to set this up
Discord Cover Art Archive Deprecation
The automatic Cover Art Archive usage currently found in the Discord Scrobbler is now deprecated and should be replaced with a Cover Art Archive Transformer.
Example
For Discord Scrobbler with ENV add this to your environmental variables:
DISCORD_TRANSFORMS=coverartarchiveFor File/AIO add the example playTransform block to your config options (or just add the coverartarchive stage config if you already have hooks configured):
{ "name": "MyDiscord", "data": { /* ... */}, // add this "options": { "playTransform": { "preCompare": [ { "type": "coverartarchive", } ] } } }A new Kitchensink example shows a more complex example, here.
Transform Names and Fallbacks
Transform stages are specified by type and name when used in a Source/Client, however the name is optional.
Example
[ { "name": "MySubsonic", "data": { /* ... */}, "options": { "playTransform": { "preCompare": [ { "type": "musicbrainz", "name": "MyMB" // name is optional } ] } } }]Previously when name was omitted MS used the first non-default transformer, then tried the default.
Now, names and order of selection (when name is omitted in configuration) looks like this:
- MS looks for a non-default, non-ENV config first
- MS falls back to ENV config (name is
MSEnv) if it exists - MS falls back to default config (name is
MSDefault) if it exists
Steps to migrate:
- If you only use
named configs that aren’tMSDefaultthere are no changes necessary - If you only use ENV configuration there are no changes necessary
- If you use the
nameMSDefaultbut do not mix ENV transforms with AIO/File config, no changes are necessary - If you use the
nameMSDefaultand also have ENV transforms configured (musicbrainz)- Rename
nametoMSEnv
- Rename
See more about Stage Names and Defaults in this new section in the docs.
Logs Directory for Non-Docker Users
More attention has gone into the Data Directory Standardization work that began in 0.15.0. Now, the directory logs are written to has been standardized, alongside the existing data and config directories.
This change only affects non-docker users.
The logs directory is now determined by, in order of priority:
LOGS_DIRENVLOGS_DIRECTORYENV (used by systemd)- OS-specific standardized path
You can see OS-specific paths here in the documentation.
If you are running multi-scrobbler locally with nodejs you’ll likely want to set LOGS_DIR=./config/logs
Other Changelog Highlights
- fix(endpointlz): Missing ‘import’ enum for listen_type on submit payload #732
- fix: Update dbcomponent at runtime when name changes #721
0.19.1
Changelog
⛰️ Features
- (musicbrainz) Disallow audiobook and music videos with config options #734
🐛 Bug Fixes
- (transform) Add missing musicbrainz default transform #737
0.19.2
Changelog
🐛 Bug Fixes
- (transform) Don’t try to initialize transformers that are already initializing, reduces noisy logging on startup
- (transform) Fix async transform initialization race condition and type/name key case sensitivity #739
v0.20.0
What’s New?
Play Editing and Search
Plays can now (finally) be edited! If your scrobbles fail you can now edit the data to correct it manually before resending it.
A plain text mode is initially used where you can edit everything related to fields and metadata ids
Additionally a search mode is available to search all of the metadata providers you have setup. Musicbrainz, Spotify, and Rocksky can all be searched concurrently. Selecting a result fills in that field, any associated fields (track can set artists + album), and meta ids.
Musicbrainz and Rocksky providers are automatically setup. All providers automatically inherit from your existing transformers. You can also set up preferred providers to only use some of them, or use with alternative configuration.
See the docs for more information.
Tuneshine Client
Tuneshine, a wifi-connected hardware device with an aesthetic, low-count LED display for showcasing the album art of the music you are currently listening to, is now integrated as a Scrobble Client.
Like Discord, it can be used to display Now Playing activity from any of your Sources.
Mixcloud Source
Mixcloud is now integrated as a Scrobble Source thanks to @anandghegde
This source records the mixes you listen to as scrobbles. It supports both free and Pro Mixcloud accounts.
Metadata Overhaul
Previously, metadata like track identifiers from Spotify/Musicbrainz/etc… and album art, were stored alongside a Play but they were not strongly associated with specific parts of the Play. This lead to situations where some functionality would require assertions like “Artist 1 has metadata artist ID 1” but it was not possible to guarantee this.
Now, Play data has been restructured to tightly associate all of this metadata to the field it belongs to. This change does not affect any end-user functionality but it dramatically simplifies developer experience. The Play Editing functionality above would have been much more difficult without this change, for example.
Changelog Highlights
⛰️ Features
- (endpointlz) Allow disabling artist extraction from ingress payload #750 #751
- (deezer) Add ISRC and optional title variant to parsed data #752 #754
- (koito) Add includePlayPosition to koito scrobbler implementation #741 #742
- (spotify) Periodically backfill songs from spotify history api for missed live playback state #735 thanks to @sudbrack
- (No Category) Consolidate SSE for logs into events to reduce connection pool usage for HTTP/1.1
- (No Category) Consolidate global SSE connections with coordinator
🐛 Bug Fixes
- (docs) Resource static asset link broken by trailingSlash #573
- (listenbrainz) Only use original artist_name in naive play if no artists credits were extracted #750
- (stats) Persist lifetime discovered/scrobbled counts across restarts #745 @dylangovender
- (No Category) Improve detection of repeat plays when listen range is close to end of a position player
📚 Documentation
- (discord) Update kasmweb/discord service example
- (metadata) Add editing and metadata search
- (No Category) Add reverse proxy recommendation and tips
🚜 Refactor
- (No Category) Decouple transform manager init
- (No Category) Circular dependency elimination
⚙️ Miscellaneous Tasks
- (No Category) Add docs and devcontainer modifications to ease discord IPC development with kasmcord
v0.20.1
This release bumps the Nodejs version used in the docker image to 24.22.1 in order to include the october security release fixes in the distributed docker image.
You should upgrade as soon as possible in any case, but users who run MS in a way that exposes it to the public internet (API or UI accessible from a non-authenticated URL) are particularly vulnerable as this fixes an exploit for remote code execution. Do not delay upgrading if you are in this scenario.
v0.20.2
This is another security fix that removes compromised eMudhra certificates from the base docker image. #759
v0.20.3 Latest
What’s New?
Docs Moved to Starlight
Documentation has been migrated from Docusaurus to Astro ✨Starlight✨
Docusaurus has been great but Starlight, and Astro being a general static site build system, is more flexible and lighter. Along with the already-strong community plugin support it has enabled more functionality for the MS docs that was not possible with Docusaurus.
The docs now have a fresh look with clearer/more readable styling. Particularly awesome new features:
- Code blocks get walkthroughs, footnotes, inline linking, stylized focus, dynamic placeholders, and much more thanks to starlight-codeblocks
- Changelogs are now built into the public site
Additionally, the developer experience for docs is greatly simplified. Same build commands but many things, like og image generation and open api doc generation, are all built-in now.
Improved Shutdown Behavior
MS now handles all exit signals (SIGNIT and SIGTERM) instead of just SIGINT. On exit signal it now tries to gracefully stop all Source/Clients by letting all currently processing Plays/scrobbles finish before closing the database and shutting down.