All versions since v0.9.3
v0.9.3
What’s New?
Removed Flatpak
Flatpak has been a technical burden that has not gotten easier to maintain since its inception. Due to being a tiny percentage of the install base, and a disproportionate developer effort for this percentage, I have decided to drop support for this installation method.
You can read more about the challenges MS has had with Flathub here. If anyone is willing to contribute and maintain a flatpak distribution I would be willing to support it again.
Fixes for Plex
Previous refactoring to introduce real-time player emulation has had a few kinks since implementing the new Plex Source, mainly due to some inconsistencies with how Plex reports duration and playback position. This release includes a few fixes that should resolve these bugs and make the Plex Source more stable.
Full Changelog
Bug Fixes
- (plex) On Plex webhook routes filter plex sources to get webhook source only #291
- (ui) Guard against null listenedDuration #285
- (No Category) Fix NaN and undefined initial states for rt player #292
Documentation
- (No Category) Remove flatpak installation method #287
Miscellaneous Tasks
- (No Category) Update flatpak description
- (No Category) Update devcontainer to node 20 to match docker image
Testing
- (No Category) Fix real time player not triggered during event loop
0.9.4
What’s New?
Initial Koito Implementation
Multi-scrobbler now supports Koito, a new scrobbler application, as both a Client and a Source using the existing Listenbrainz implementation.
Use a Listenbrainz configuration, as a Client or Source, with a custom url pointing to the Koito scrobble endpoint like http://your_koito_server/apis/listenbrainz/1.
A more fully-featured, independent, Koito implementation will be introduced in a future release.
Full Changelog
Features
- (jellyfin) Add musicbrainz metadata #297
- (listenbrainz) Detect /1/ version prefix on base URL
- (listenbrainz) Implement limited support for Koito as Listenbrainz Source/Client #300
Bug Fixes
- (listenbrainz) Normalize URL and join parts correctly
v0.9.5
What’s New?
Deezer ARL Source
Thanks to @Ectalite providing a premium account for development, as well as a proof of concept for accessing Deezer with ARL, the previously deprecated Deezer Source now has a new, working implementation! :tada:
See the documentation for setup and configuration
This new implementation comes with a few caveats to be aware of:
- Only works with a premium account because Deezer only records full plays of a track which is a feature only available with premium
- The method for accessing Deezer’s API (ARL), while widely used by other projects, is unofficial:
- Deezer may break this functionality at any time
- Using it likely violates Deezer’s TOS. Use this method at your own risk.
If you have any issues with Deezer reporting duplicate plays when using third party devices (Sonos), see the docs to tweak detection behavior.
Full Changelog
Features
- (deezer internal) Add fuzzy discovery ignore option
- (No Category) Implement Deezer Internal (ARL) Source #296
- (No Category) Fuzzy diff threshold configurable
- (No Category) Implement better during/range temporal comparisons
Documentation
- (deezer) Rewrite Deezer Source with ARL implementation
v0.9.6
What’s New?
This is a minor update to improve the scrobbling capability of Listenbrainz scrobblers (Koito) when a track has multiple artists (#312). Nothing new otherwise!
0.9.7
What’s New?
Now Playing
Multi-scrobbler can now report the currently playing track (Now Playing) for scrobble services that support this functionality – Last.fm and Listenbrainz.
This functionality is default enabled for all clients but can be configured globally through ENVs or individually per-client. Consult the docs to see how this functionality works and how it can be customized.
Thanks to @msparkles for the PR providing initial support for this feature! :tada:
Full Changelog
Features
- (subsonic) Implement filtering plays by user in https://github.com/FoxxMD/multi-scrobbler/pull/319
- (lastfm) Now Playing by @msparkles in https://github.com/FoxxMD/multi-scrobbler/pull/316 https://github.com/FoxxMD/multi-scrobbler/issues/164
- (listenbrainz) Implement Now Playing functionality https://github.com/FoxxMD/multi-scrobbler/issues/74
- (listenbrainz) Include more ids/fields in submit additional_info https://github.com/FoxxMD/multi-scrobbler/issues/311
- (No Category) Add config source to startup logging https://github.com/FoxxMD/multi-scrobbler/issues/309
Bug Fixes
- Guard against undefined type in config.json https://github.com/FoxxMD/multi-scrobbler/issues/310
Documentation
- Improve example config rendering for ambiguous configs #310
0.9.8
What’s New?
Koito Source/Client
v0.9.4 introduced Koito support using the existing Listenbrainz Source/Client.
Now, this release introduces full Source and Client implementations for Koito which enables better support/functionality for Koito configuration and any future features.
Thanks to all the feedback and testers in #300 !
Migrating
Instructions below based on which type of MS config you are using:
ENV Config
Rename LZ_ prefixed envs to KOITO_
File Config
Rename listenbrainz.json to koito.json. If you have additional non-koito listenbrainz client/sources then make sure to separate them into different files.
AIO Config
Rename "type": "listenbrainz" to "type": "koito"
Koito URL
Your existing URL config will continue to work as-is. However, I recommend that you change it to the Koito base url only as this will be easier for MS to work with. The “base url” is the same URL you would use to access Koito through a browser.
KOITO_URL |
MS Detected Base URL | Example Scrobble Api URL |
|---|---|---|
http://192.168.0.100:4110 |
http://192.168.0.100:4110 |
http://192.168.0.100:4110/apis/listenbrainz/1 |
https://koito.mydomain.com |
https://koito.mydomain.com |
https://koito.mydomain.com/apis/listenbrainz/1 |
http://192.168.0.100:4110/apis/listenbrainz |
http://192.168.0.100:4110 |
http://192.168.0.100:4110/apis/listenbrainz/1 |
http://192.168.0.100:80/koito |
http://192.168.0.100:80/koito |
http://192.168.0.100:80/koito/apis/listenbrainz/1 |
Docs Update
Docs have had a minor refresh. Some highlights:
- Source/Clients sorted alphabetically for better readability
- Removed outdated information in FAQ and on Flatpak sections
- Fixed anchor scrolling
- Added Koito to Quickstart clients
- Added MPRIS guidance for Docker installs and remote socket instructions
Full Changelog
Features
- (koito) Implement Koito client
- (koito) Implement Koito Source
- (listenbrainz) Add submission_client #224
- (listenbrainz) Add additional player/service meta info for LZ scrobbles #224
- (mpris) Better handling of socket error on connection check
- (plex) Add libraries allow/block warning
- (No Category) Add more logging to apprise errors #158
- (No Category) Add RT Player metrics to player logging
Bug Fixes
- (plex) Increase max drift to 35 seconds to account for symfonium lag #271
- (plex) Strict comparison of library names #326
- (No Category) Only return interpolated album value if not undefined
- (No Category) Superagent type error false positive
Documentation
- (mpris) Add remote socket guidance
- (No Category) Add Koito quickstart instructions
- (No Category) Add Koito client/source docs
- (No Category) Update MPRIS instructions for docker
- (No Category) Remove flatpak install guidance
- (No Category) Update docker installation wording
- (No Category) Sort Source/Clients alphabetically
- (No Category) Remove old FAQ entries
- (No Category) Add updating instructions
- (No Category) Fix anchor scrolling by specifying logo height
- (No Category) Hide navbar on scroll
Miscellaneous Tasks
- (listenbrainz) Add guidance in logging to switch to Koito source/client
- (No Category) Update superagent types
v0.9.9
What’s New?
This is a small release mainly for some bug fixes and documentation improvements.
Plex Source Temporary Fix
Plex 1.41.9.9912 introduced changes in their API that have not been accounted for in the underlying library multi-scrobbler uses to communicate with Plex. This causes issues in multi-scrobbler so this release merges a temporary fix for this error. No interaction is required to fix this error from the user side.
Another minor release will be cut once @lukehagar/plexjs merges the upstream fix for the API.
Intentional Scrobble Repeat Detection
Improvements to how multi-scrobbler tracks Sources that provide Player position data now prevents scrobbles from being detected as duplicates when they are intentionally repeated by the user. #328 #318
You can now keep that track on repeat to your heart’s content knowing MS will scrobble all 20+ consecutive plays. I don’t judge :heart:
Full Changelog
Bug Fixes
- (lastfm) Fix lastfm providing empty string for non-defined album #323
- (listenbrainz) Fix sending undefined release_name in scrobble #323
- (plex) Fix regression with allowing non-music when using librariesAllow
- (plex) Temporary fix for library response validation #314
- (No Category) Add last played data and check for repeat on new Play #318
Documentation
- (No Category) Add Source client config guidance #325
- (No Category) Update docusaurus to 3.8
- (No Category) Fix social card image
- (No Category) Add clarifying callout for multi-use components
- (No Category) Remove koito mentions from LZ docs
- (No Category) Update readme Koito entries
- (No Category) Improve wording around Now Playing state
Features
- (No Category) Track if play is a repeat and use in duplicate scrobble detection #318
Refactor
- (No Category) Move Play position closeness and duration comparisons to own functions
v0.9.10
What’s New?
- fix(listenbrainz): Improve MusicBrainz metadata parsing so more data is included in Play metadata
- fix(koito): Send
recording_mbidin scrobble payload to fix missing metadata #333
v0.9.11
What’s New?
Maloja Source
Implemented Maloja as a Source :tada: #295
All Clients can now also be used as Sources so you have full freedom to scrobble to any service with the ability to relay to any other service!
v0.10.0
What’s New?
Persistent Queue/Failed Scrobbles
This release implements persisted data across multi-scrobble restarts. Initial support will be for Queued and Failed Scrobbles.
This means that MS will save any Plays that have not successfully been scrobbled to Clients. They will be restored when MS is restarted.
This functionality is enabled by default so no configuration changes are required. Scrobble data will be saved in the existing CONFIG_DIR directory bound into the docker container.
Data can be persisted to file (default) or Valkey. See the docs for more information and configuration.
This is a non-breaking change but the app version has been bumped to signify major functionality updates.
Youtube Music Auth Issues
Google/YT seems to have permanently broken authentication using the Oauth client method for Youtube Music. However, cookie authentication is still working and a simpler process.
If you are using Oauth and encounter 400 errors please switch to cookies instead.
Full Changelog
Bug Fixes
- (jellyfin) Guard against undefined NowPlaying Path
- (lastfm) Remove VA from album artist #340
- (lastfm) Full destructure recent response and log respon on error #353
- (listenbrainz) Fix missing album/release_name for listenbrainz endpoint #342
- (No Category) Add additional logging for ytm history request #346 #345
- (No Category) Do not use transform until after client is ready #356
- (No Category) Add full coverage for cached queue updating during client lifecycle
- (No Category) Update object hashing function #358
Documentation
- (ytm) Add oauth warning #346 #345
- (No Category) Remove koito from listenbrainz example
- (No Category) Add caching docs
Features
- (lzendpoint) Accept artist and release artist names in submit payload
- (ui) Add Now Playing behavior for ui Player #338
- (webscrobbler) Use Now Playing Player #338
- (ytm) Add debug logging #346 #345
- (ytm) Allow user-defined oauth scopes #346 #345
- (No Category) Change stale/orphan interval to match current play duration for Players that only track Now Playing data #352
- (No Category) More artist parsing improvements
- (No Category) Improve artist string parsing
- (No Category) Add Access Token auth for Ntfy webhook #350
- (No Category) Implement caching backend
- (No Category) Implement cache file streaming with progress logging
Miscellaneous Tasks
- (No Category) Bump logging version
- (No Category) Update iti usage
- (No Category) Bump with-local-tmp-dir version
- (No Category) Remove unused object-hash package
Refactor
- (No Category) Refactor di cache to allow singleton or transient
Testing
- (ytm) Fix failing type assertion
- (No Category) Fix/workaround tests for new transformrule assumptions
- (No Category) Add scrobble client caching test
Build
- (vscode) Update devcontainer to use compose stack with valkey for caching integration
- (No Category) Move net-interceptor to dev packages
v0.10.1
What’s New?
Rocksky Client
Rocksky, a music tracking and discovery platform built on the AT Protocol (Bluesky) has been added as a client to the docs. It uses a listenbrainz-compatible API so multi-scobbler users can set it up as a regular Listenbrainz Client with a custom URL.
A future release will see a Rocksky Source as well, so you can mirror scrobbles from your PDS to another service.
Full Changelog
Features
- (JellyfinApiSource) Add option to override the Jellyfin base url used in the frontend #374 by @wolffshots
- (listenbrainz) Add source as music service name #369 by @h-wb
- (plex) Enable accepting self-signed certs #372 (#373)
Bug Fixes
- (notification) Parse and use the tags in Apprise config #369 by @gabeklavans
- (plex) Use better url parsing method
Documentation
- (No Category) Add Rockysky setup instructions
- (No Category) Update listenbrainz json example to include url
Miscellaneous Tasks
- (JellyfinApiSource) Extract URL replacement to func and add tests
Testing
- (No Category) Add more tests for url parsing
0.10.2
What’s New?
teal.fm Scrobble Client/Source
teal.fm is a social discovery and web viewing service for scrobbles stored/available on the ATProto network/your repository in a PDS (Personal Data Server).
This release includes a Client and Source implementation for teal.fm. #378
The teal.fm website is still in development and does not have a public release yet but you can start scrobbling, now, and your data wil be ready to use when they launch publicly.
Rocksky Client Fixes and Source
A fix for the previously-released Rocksky client #391 is included, as well as a brand-new implementation for both the Client and a new Source.
Notable QoL Improvements
- Now Playing functionality is now more temporally-accurate for downstream scrobbler clients and in the UI https://github.com/FoxxMD/multi-scrobbler/issues/395
- Subsonic Sources now have repeat detection implemented so your tracks are scrobbled mutliple times when you play them over and over again https://github.com/FoxxMD/multi-scrobbler/issues/375
Documentation Overhaul
Some of you may have already noticed but the docs at foxxmd.github.io/multi-scrobbler have been reworked to improve readability and add more information:
- The monolithic configuration page has been split into separate pages/sections:
- Main, general configuration
- Scrobble Clients section with overview and separate pages for each Client
- Scrobble Sources section with overview and separate pages for each Source
- This should additionally make searching easier since clients/sources show up under their own header instead of as excerpts under a huge configuration section
- Scrobble Clients/Sources include a matrix of supported features and clearer breakdowns of what each feature means + configuration
- Components have been refactored into reusable snippets which will make future documentation easier
- The URL pattern for the entire site has been simplified by removing the
docs/prefix- All old pages should redirect to new pages, but if they don’t please open an issue
Full Changelog
Bug Fixes
- (endpointlz) Return expected OK response body #380
- (listenbrainz) Use duration_ms if duration not provided
- (listenbrainz) Fix interfaces for submit/listen and consolidate
- (rocksky) Add scrobble to deadletter when ignored
- (tealfm) Fix scrobble play date context
- (No Category) Fix Source/Client data types
- (No Category) Improve timing and update frequency to match scrobbler behavior #395
Documentation
- (endpointlz) Add navidrome url setup instructions
- (tealfm) Add docs
- (No Category) Move landing page to docs to simply url structure
- (No Category) Add commonly used components to global scope
- (No Category) Move Source configs to own files
- (No Category) Add client own files
- (No Category) Reorganize doc contents
- (No Category) Improved linking on config/landing and configuration type refinement
- (No Category) Change installation to category doc link
- (No Category) Update kitchensink
- (No Category) Improve sources documentation
- (No Category) Add redirects for old pages
- (No Category) Add subsonic guidance for lz scrobbling
Features
- (endpointlz) Add more logging for invalid routes
- (jellyfin) Add frontend url override as env #374
- (listenbrainz) Use end-of-string token and wildcard route to better log malformed LZ endpoint routes
- (listenbrainz) Add request body to logging for debugging
- (rocksky) Implement Rocksky Source/Client
- (subsonic) Add basic repeat detection based on listened duration #375
- (tealfm) Implement app password auth
- (tealfm) Massage handle for users who don’t read docs
- (No Category) Implement working tealfm client
- (No Category) Make ATProto handle transform usable by RockSky and teal.fm
Miscellaneous Tasks
- (frontend) Update sass package and vite config to remove deprecation warnings
- (No Category) Update schema
Refactor
- (frontend) Update tailwind and move generation into vite
- (frontend) Migrate tailwind config to v4
- (listenbrainz) Move parsing functions out of static
- (listenbrainz) Consolidate lz endpoint submit parsing into lz response parsing
- (tealfm) Move common functionality to api client
Testing
- (No Category) Use zizmor to fix and audit image publish action
- (No Category) Harden remaining workflows using zizmor
0.10.3
Bug Fixes
- (rocksky) Fix wrong config value used for Rocksky API auth
- (rocksky) Fix url path for token validation
- (rocksky) Use
duration_msinstead ofdurationsince Rocksky ignoresduration
0.10.4
Bug Fixes
- (rocksky) Round duration to integer to fix 400 on submit #400
- (tealfm) Round duration to integer
- Float duration did not prevent writing record to PDS but seems to improve compatibility with apps using tealfm data
v0.10.5 - Musicbrainz Matching
What’s New?
Scrobble Enhancements Overhaul
Scrobble Enhancements, previously known as Scrobble Modifications, have gone through a complete rewrite in order to support more types of modifications and future extensibility.
There are now three types of enhancements, now referred to as Stages:
- User Stage is the same functionality as the old Scrobble Modifications – define search-and-replace patterns to correct your scrobble data
- Native Stage use more sophisticated, built-in heuristics to extract multiple artists from strings and clean up track titles
- Musicbrainz Stage uses the Musicbrainz database to match your Scrobbles, correct bad data, enrich missing fields, and supply MBIDs to downstream Scrobble Clients.
If you were previously using Scrobble Modifications your existing configuration will work without any changes.
The overhaul also implements these new features:
- Multiple stages can be created with different defaults, allowing you to define config once and apply it everywhere OR override for specific Sources/Clients
- Stages are highly configurable to ensure your scrobble data ends up exactly how you want it
- Stages can be combined sequentially
- Presets for Native and Musicbrainz Stage allow ENV Config users to take advantage of Stages without requiring File Config
Musicbrainz
The Musicbrainz Stage Configuration provides many ways to configure matching that should cover most usecases.
However, if you find your scrobbles are not matching or an option is not working as expected, please turn on Debug Mode to log more information about what/how multi-scrobbler is trying to match. If you open an issue, please include this log information.
Quickstart Improvements
The Quickstart have been updated to include Source setup for Navidrome, Plex, and Last.fm as a Source.
Full Changelog
Bug Fixes
- (lastfm) Fix network error default value
- (plex) Fix library filter when media has no library #405
- (tealfm) Allow DID identifier
Documentation
- (transforms) Add default stage
- (No Category) Fix kitchensink links
- (No Category) Refactor transformer docs with stages
- (No Category) Add musicbrainz stage
- (No Category) Add native stage
- (No Category) Add user stage
- (No Category) Add more sources in quickstart
Features
- (transform) Implement abstract transformer #365
- (transform) Implement more exhaustive, human-readable transform diff
- (transform) Add proper support for meta and duration transform parts
- (transformer) Allow configuring artist parsing string sources #384
- (transformer) Implement staggered api calls to respect rate limit
- (transformer) Implement filtering and sorting of fetched musicbrainz recordings
- (transformer) Implement country filter/priority for musicbrainz
- (transformer) Add musicbrainz api url logging
- (transformers) Implement applyng limited transform configs via ENV
- (transformers) Implement user transformer
- (transformers) Add native transformer #364 #384
- (transformers) Add flow control to transforms #365
- (transformers) Implement musicbrainz api client
- (transformers) Implement musicbrainz transformer #362 #334
Miscellaneous Tasks
- (No Category) Bump logging for bug fix
Refactor
- (No Category) Move abstract init into own abstract class
Testing
- (musicbrainz) Add basic sanity test
- (tranformer) Add longer timeout and fix logger used in musicbrainz suite
- (No Category) Add edge case musicbrainz tests
v0.10.6
Bug Fixes
- (musicbrainz) Wrong default argument for preRequest
- (transformer) Don’t try to build env transformer when no ENV exists
v0.10.7
This release includes more accurate and granular Musicbrainz search, flow control for scrobble transformations, and secrets interpolation into config files.
What’s New?
Flow Control for Scrobble Enhancements
Stages for enhancing your Scrobbles now support Flow Control, enabling you to configure how Scrobbles are transformed based on the outcome of a Stage. Three properties are added to Stage configuration:
onSuccess(defaultcontinue) - If the Stage successfully finishes processingonFailure(defaultstop) - If the Stage encounters an error while processing, or otherwise fails to achieve the transformation resultonSkip(defaultcontinue) - If the Stage does not process the Play data due to stage-levelwhenor other stage-specific skip conditions
This enables more complex transformation scenarios like: only run Native Stage if Musicbrainz Stage fails to find a match.
More Musicbrainz Search Improvements
- Album+title fallback search is now configurable, rather than automatic, using
fallbackAlbumSearch - Search uses ISRC codes to match, if available. This will greatly improve matching when using the Spotify Source
- Musicbrainz docs have been reworked for clarity and to add more examples.
Secrets Interpolation
Multi-scrobbler can now interpolate ENV values directly into any json config files using bracketed identifiers like [[MY_SECRET]].
This enables sensitive data to be removed from config files which should make using secrets injection platforms easier as well as committing configs to git safer.
Quickstart Musicbrainz
The Quickstart docs have been updated to include an optional step for setting up Musicbrainz via ENV.
Full Changelog
Features
- (musicbrainz) Make fallback album search configurable #414
- (musicbrainz) Search by ISRC before anything else #418
- (transformers) Add onSkip flow control #414
- (transformers) Log more transform stage detail for components
- (No Category) Add additional metadata from spotify and for listenbrainz submissions #418
- (No Category) Implement secret interpolation from ENV #420
- (No Category) Log as WARN if there are unmatched interpolation keys
Bug Fixes
- (musicbrainz) Throw errors on pre/post fetch so onFailure works as expected #414
- (musicbrainz) Add missing duration logic
- (spotify) Don’t report player state if spotify api doesn’t return an item #412
- (transformer) Improve stage error logging
- (transforms) Update types to allow json validation without type for user #267
Documentation
- (transformers) Add Flow Control
- (No Category) Add musicbrainz step to quickstart
- (No Category) Better language for mb quickstart section
- (No Category) Add config interpolation docs
- (No Category) Better warnings for ENV interpolation
- (No Category) Improve general transform docs
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.