All versions since v0.4.5
v0.4.5
Changelog
Features
- multi-scrobbler is now available as a flatpak on flathub! :tada:
- There is now a lil’ official logo
- Added ability to set scrobble time/percentage threshold per source #73
- When available Jellyfin now submits MBID metadata, alongside normal scrobble data, to listenbrainz #72
- the port the MS webserver is served on can now be controlled via
config.json
Refactor
- Replaced git sourced winston fork with user-scoped winston npm package
Documentation
v0.4.6
Changelog
Bug Fixes
- (kodi) Fix hardcoded string #79
- (ntfy) Include authorization object correctly in publish request data #80
- (tautulli) More response formatting safeguards #77
- (No Category) Limit number of plays returned by deezer #67
- (No Category) Respect maloja response status and throw on error #57
- (No Category) Handle null/non-string values when truncating
- (No Category) Re-initialize auth state after receiving auth callback #76
- (No Category) Passing wrong argument to tautulli payload formatter #77
Features
- (No Category) Use pre/post auth hooks for polling start to simplify logic
v0.4.7
Changelog
Bug Fixes
- (lastfm) Only scrobble pimary artist when multiple are present #81 – contributed by @Whhoesj
Docs
- (No Category) Add FAQ entries for multiple artist scrobbling issues
v0.4.8
Changelog
Features
- (tautulli) Support empty user to enable source with all users #84
- (jellyfin) Implement filter logging controls #85
- (docs) Implement docusaurus site https://foxxmd.github.io/multi-scrobbler
Bug Fixes
- (plex) More config data normalization #84
Documentation
- (No Category) Update docker registry options (Added GHCR)
- (No Category) Fix docker heading
- (No Category) Use external links for config examples
v0.5.0
:tada: 0.5.0 contains a significant internal rewrite for the entire front-end and some fundamentals of the back-end.
Refer to the RC release for full details and Breaking Changes
Changes since RC
- Fixed tsconfig compiler options to make mocha tests run correctly
- Fixed
outlocations and tsconfig location for generating schema - Updated flatpak build process to reflect build changes
Breaking Changes since RC
Flatpak
Flatpak installations now default to starting the ui on port 9079.
- New Installations – Automatically generates a default config that resets port to
9078 - Existing Installations – Port can be defined using ENV or FILE-config, if you want to change it back to
9078
v0.5.1
Changelog
Bug Fixes
- (tautulli) fix redirect path
- (plex/jellyfin) fix redirect allowing relative paths that breaks url when source endpoint has a trailing slash #92
v0.5.2
What’s New?
MS now has a scheduled “heartbeat” that runs every 20 minutes. It’s purpose, for now, is to is start (or restart) sources that have stopped due to upstream issues. The use-case for a Source is:
- request/polling retry attempts are low
- source was initially OK (auth OK, initial communication OK)
- and there is a non-auth related upstream communication issue
The heartbeat tasks will automatically try to restart polling for this Source so MS can recover from an upstream API communication error without user intervention.
Changelog
Features
- (No Category) Schedule heartbeat task to restart sources that should be polling #6 #65 #93
Bug Fixes
- (listenbrainz) Artist parsing joiner phrases must begin with non-word character #94
v0.6.0
What’s New?
UI Improvements and Now Playing
When a Source player state is detected a “Now Playing” player will render in the Source card showing how MS tracks plays for scrobbling.
The UI has been reworked to reduce whitespace, compact the layout, use headers more effectively, and improve wording/visualization.
Recently played tracks are now linked to their source (Spotify, Maloja) if MS can parse them.
Backend Tests and Scrobbling Fixes
A suite of tests have been added to test core backend functionality. These tests helped surface multiple bugs that should, now fixed, reduce duplicate scrobbles and improve consistency for all sources/clients.
Other Notable Improvements and Fixes
- Backlog on startup is configurable #100
- In a source config
optionsproperty use"scrobbleBacklog": falseto disable backlogging on startup
- In a source config
- Enable/disable configs via property
- In a source/client config at the top level use
"enable": false"to disable using that config
- In a source/client config at the top level use
- Scrobbling deferred until track finishes playing or is changed
- This ensures an accurate “listened to” time is recorded for the scrobble, rather than scrobbling as soon as “listened to” time is valid
- “listened to” is now sent in Maloja scrobble payload
- Jellyfin endpoint now accepts more header content-type values #101
- Updated youtube source library to work better with non-chromium browsers #98
Breaking Changes
Docker Image Base Updated (Linuxserver.io)
- Updates alpine 3.17 -> 3.18 * Main reason is to potentially fix DNS issues related to musl #88 * Bumps npm 9.1.2 -> 9.6.6
If you are using Portainer to manage containers you should remove all non-MS related ENVs from the container template before pulling the latest image.
Increased Scrobble Threshold Defaults #73
Previously, the default settings for scrobbling a track specified to scrobble if it was played for more than 30 seconds. This has been increased to:
- Played more than 240 seconds (4 minutes) OR
- More than 50% of the track played (for sources that support reporting track duration)
If you have scrobbleThresholds set in config.json or in individual source configs this does not affect you.
Increased Default Polling Interval
Due to the above change the default polling interval for sources have been adjusted like so:
- Default Interval: 30s -> 10s
- Max Interval: 60s -> 30s
This provides more accurate “listened to” recording and, generally, better source tracking resolution. This should not adversely affect your usage of each provider other than increasing bandwidth a little. These default settings can be overridden using the interval and maxInterval properties in individual source configs.
Changelog
Bug Fixes
- (backend) Fix how port is determined and used for localUrl
- (deezer) Fix deezer passport generation
- (docker) Normalize ownership of node_modules files
- (flatpak) Add missing release node
- (jellyfin) Enable parsing requests with more content-types and add logging on invalid types
- (player) Fix some bugs and add param for easier testing
- (player state) Fix stale/dead logic checks
- (scrobble) Fix config destructing default value
- (scrobble) Fix wrong artists used for comparison
- (scrobble) Fix not using reference duration for fuzzy comparison
- (scrobblers) Test auth when client completes auth callback
- (ui) Use normal match parameters
- (ui) Log line parsing missing line terminator capture
- (ui) Fix wrong route for client auth #97
- (ui) Remove duration percent display when duration is zero
- (ui) For non-polling sources use last activity date for status
- (youtube) Use updated yt music library #98
- (No Category) Load variables from .env for backend
- (No Category) Pass localUrl to scrobble clients for use by lastfm
- (No Category) Fix missing destructure default value
Documentation
- (jellyfin) Add request header workaround for jellyfin #101
- (No Category) Update dashboard screenshot
- (No Category) Clarify subsonic api compatibility
Features
- (config) Implement property to control whether a source/client is used
- (jellyfin) Initial testing suite
- (lastfm) Add player support
- (listenbrainz) Add player support #74
- (maloja) Generate web links for scrobbled tracks
- (maloja) Add listened duration to scrobble payload
- (player) Add sourceOfTruth variable to control logging and future use
- (player) Defer play discovery so listenedFor is accurate
- (player) Log when play fails discovery on player change
- (player) Implement seek and repeat detection
- (player state) Preserve position when player is not in stopped status
- (player state) Implement player status and listened duration in UI
- (scrobble) Use new string approach for track
- (scrobble) Use new string approach for artists
- (scrobble) Implement artist dup bonus scoring heuristic
- (scrobble) Initial scrobble queue implementation
- (scrobble) Implement dead letter queue processing
- (scrobble) Implement heartbeat task for scrobblers
- (scrobble) Improve existing scrobble comparison logic
- (source) Control backlogging via config #100
- (ui) Remove player when dead
- (ui) Compact ui
- (ui) Make player dates human friendly
- (ui) Remove unnecessary wording for track stats
- (ui) Expand displayed plays
- (ui) Implement status indicator
- (No Category) Rework source api to include player states
- (No Category) BREAKING Set default scrobble thresholds to standard values #73
- (No Category) BREAKING Decrease default polling interval
- (No Category) Implement Base URL
- (No Category) Implement new string comparison logic
Miscellaneous Tasks
- (docker) BREAKING Bump lsio base image version to 3.18
- (No Category) Use constant for default retry multiplier
- (No Category) Remove unused dashboard backend endpoint
- (No Category) Ensure production mode for webpack without relying on env
Refactor
- (player) Move listen progress/range functionality into own class (SRP)
- (player state) Refactor handling of non-updated players
- (player state) Remove old play logic
- (scrobble) Print score breakdown in different statement
- (scrobble) Adjust dup score weights
- (scrobble) Refactor scrobble delay and scrobble processing sleep behavior
- (ui) Prevent nodemon from restarting on tailwind config change
Testing
- (player) Add player tests
- (scrobble) Implement basic scrobble tests
- (scrobble) Add more tests for similar-but-unique scrobble detection
- (scrobble) Add test for duplicate when exact title/time
- (scrobble) Improve test durability by using mixed duration sources
- (scrobble) Add tests for scrobble processing queue and existing comparisons
v0.6.1
What’s New?
Webscrobbler Source
The Webscrobbler browser extension has been added as a source using its native webhooks behavior.
Display and Controls for Queued/Dead Letter Scrobbles
Quietly implemented in 0.6.0, MS has two new control structures for making scrobbling more resilient to offline clients and network issues:
- Queued Scrobbles – When a Source discovers a new Play it is added to a queue for each Client that should scrobble it. If the Client fails to scrobble due to networking or “showstopping” errors from the downstream API then it stopped processing the queue until the next heartbeat. This ensures all scrobbles are preserved until the downstream service is ready to accept scrobbles again.
- Dead Letter Scrobbles – If the downstream service fails to scrobble for reasons that are expected by MS (EX Last.fm’s “ignored” response) then the Scrobble is added to the Dead Letter Queue, a queue of failed scrobbles.
- The queue is retried a couple times on heartbeat to account for ephemeral issues downstream.
- If scrobbles still fail the MS user can at least see what failed, when, and why. The queue display in the dashboard also allows users to retry scrobbling manually.
Changelog
Features
- (No Category) Implement WebScrobbler source
- (No Category) Implement dead letter endpoints
- (No Category) Add dead letter error to data
- (No Category) Log storage improvements and limit controls
- (No Category) Implement retry/remove all dead letter scrobbles
- (No Category) Implement live updates for dead letter and scrobble counts
- (No Category) Implement live client/source status updates
- (No Category) Implement queued scrobbles display
Bug Fixes
- (No Category) Deadletter endpoint responses
Documentation
- (webscrobbler) Add disclaimer about hostname for firefox
- (No Category) Add queued scrobble feature highlight
Refactor
- (No Category) Reduce noisy logging at INFO level
v0.6.2
What’s New?
Album Artists
MS now tracks Album Artists and correctly scrobbles this info if the client supports it.
Targeted (Smarter) Polling for Play Durations #108
For polling sources that report track duration (Spotify), MS will now decrease polling interval near the beginning and end of the track in order to get more accurate “listened duration” and track start timestamp. NOTE: Spotify’s Automix feature may interfere with this.
Changelog
Features
- (player) Pad scrobble listen duration when confidence is high #108
- (scrobbler) Use listenedFor when checking for existing scrobbles
- (source) Smart interval calculation based on track position and end time #108
- (No Category) Allow disabling console and file logging through ENV
- (No Category) Refactor auth testing to differentiate between network issues and actual auth errors #102
- (No Category) Implement album artist and scrobble client payload function
- (No Category) Implement album artist for all sources #104
- (No Category) Add app version output
Refactor
- (No Category) Simplify network error detection during auth test
- (No Category) Don’t stringify error when using upstream options
Bug Fixes
- (lastfm) Fix api error when listening history is private by sending session key #105
- (maloja) Fix missing return value for auth test
- (player) Correctly output value of play timestamps for player based on undefined or not #107
- (plex) Use album artist if present and remove “Various” artist #104
- (source) Fix polling interval drift
- (spotify) Improve api error handling #103
- (No Category) Test auth correctly on auth flow completion
- (No Category) Catch errors during source backlog processing #103
Documentation
- (spotify) Add note about duration accuracy when using Spotify’s Automix feature
Miscellaneous Tasks
- (flatpak) Update metainfo and sources
- (No Category) Update string-sameness version
v0.6.3
Changelog
Features
- (player) Handle state transfer for single-platform sources #124
- (player) Improved player deletion behavior
- (scrobbler) Change fuzzy diff threshold to be inclusive
- (scrobbler) Use play data to extract non-joined track name when comparing for duplicate check
- (scrobbler) Improve log readability for duplicate checking
- (scrobblers) Implement user-initiated (re)start mechanism #114
- (source) Improve (re)start behavior and ui
- (webscrobbler) Log the best-guess URL for requests using configured slug
- (No Category) Implement play completion context #121
- (No Category) Improve track credits parsing to recognize post-feat suffixes #121
Bug Fixes
- (jellyfin) Split musicbrainz mbid artist identifier semicolon #114
- (lastfm) Fix handling of non-string mbid string values #114
- (listenbrainz) Treat 400 responses as non-showstopping #114
- (mopidy) Fix album artist parsing #110
- (player) Fix listen range default value
- (spotify) Undefined player state data when in private session - thanks @brunohpaiva
- (webscrobbler) Correct ENV names to match those documented #111
Refactor
- (scrobblers) Consolidate scrobbler client startup surface area
- (source) Start consolidating isReady behavior
- (No Category) Improve string comparison robustness
- (No Category) Move Play time comparison code into own file
- (No Category) Temporal comparison result data improvements #121
Documentation
- (webscrobbler) Add where to find connector list #112
- (webscrobbler) Move slug into data #111
- (No Category) Update schema for logging enums
- (No Category) Add contributing guidelines and PR template
- (No Category) Correct json-schema.app links - thanks @Bujiraso
Miscellaneous Tasks
- (tests) Fix TS compile warning on test for bad data
v0.6.4
What’s New?
Google Cast Source
Support for Google Cast devices as Sources. This is particularly exciting because music platforms that do not have an API for exposing user Now Playing info (Pandora, Tidal, MixCloud, Soundcloud) can now be monitored if you play them through a Google Cast device. Additionally, since the Cast platform exposes generic Now Playing information virtually any app that plays music through Casting can now be monitored by MS.
Application tooling restructure and frontend platform migration
This is no functional change for end-users. This is an internal/development refactor but brings with it many benefits:
- Refactored project from cjs to esm (new module standard)
- Moved away from dead frontend platform (CRA) to Vite
- Removed dependency on webpack
- Removed api proxy required by CRA
- Simplified app entry – Vite is served through express middleware instead of having nodemon spawn separate processes for front/backend
- Refactored project to use tsx runtime wrapper – no longer need to compile to js to run dev/prod
- Side-effect of this is stack traces reported in prod now point to actual source files with correct lines numbers
The only visible changes are:
- a slight increase in docker image size due to bundling typescript/source files
- developers do not need to use
API_PORTanymore – usePORTfor both dev/prod now.
Source init improvements
All sources have been refactored to have more granular init stages:
- build data – any data that needs to be parsed (URLs for mopidy), built, or verified (credentials) happens here
- test connection – if the source can be checked for reach-ability it is done here
- test auth – if the source requires auth it is tested here
If any stage fails more descriptive errors are logged for the failure and the stage it failed at is also described, giving users more insight into why a source failed to start as well as improving debugging.
App Crash Logging
App-crashing errors should now be logged before the app crashes. The easiest way to capture these logs is to set file logging to warn log level so they are stored to file. In your file-based config.json`:
{ "logging": { "level": "debug", "file": "warn" } // ...}Deprecations and Future Changes
Maloja
- Support for Maloja versions below 3.0.0 is now deprecated
- Likely no one is using these versions as they are almost 2 years old but MS still supports them with additional logic that could (should) be simplified
- A warning is emitting if Maloja version is below 3.2.0 due to lack of support for scrobbling album information
Spotify Credentials
A future release will include a migration from the current spotify api library to the official spotify web sdk. The information stored for Spotify credentials in releases prior to 0.6.4 is insufficient and will require reauthentication by the user if, for example, the user upgrades directly from 0.6.2 -> 0.6.5+.
0.6.4 will store all required credential information needed for future releases. Simply upgrade to this version and wait a few hours for Spotify credentials to be refreshed.
Full Changelog
Features
- (chrome) Improve mdns discovery for docker
- (chrome) Implement user filtering media by type
- (chrome) Allow manual device connections
- (chrome) Refactor mdns discovery to run on source heartbeat as well as startup
- (chrome) Catch buffering media
- (deezer) Fix credentials check during build data stage
- (maloja) Improve error handling
- (spotify) Prepare for future spotify lib migration by writing completing credential data
- (subsonic) Improve error/response parsing and report errors correctly on startup #129
- (No Category) Improve discovery for network exceptions in error causes
- (No Category) Improve data init stage log wording
- (No Category) Log when heartbeat tasks start
Bug Fixes
- (No Category) Actually throw auth failure so initialization fails correctly
- (No Category) Fix isReady logic
- (No Category) Fix status indicator color based on status text
- (No Category) Fix status text based on initialization OK values
- (No Category) Fix return values for source init startup
- (No Category) Fix when to check connection
- (No Category) Fix where retries are checked
- (No Category) Better handling of uncaught exceptions and rejections
Documentation
- (No Category) Chromecast docs and schema
- (No Category) Add local source reference to flatpak manifest
Miscellaneous Tasks
- (chrome) Switch to scoped chromecast-client to fix flatpak source generation limitation
- (maloja) Deprecate support for versions below 3.0.0
0.6.5
What’s New?
Upstream Recent
Prior to 0.5.0 MS let you see “recently played” directly returned from a Source’s upstream API (for those that have it IE spotify, lastfm, listenbrainz…) This was useful for determining if the Source’s API was “behind” what had actually been played – for instance sometimes Spotify’s “recently played” can be delayed by minutes/hours which could result in backlogging not returning what you “know” was played more recently.
This is entirely a convenience for the user, rather than new functionality for MS, but had been missing as a feature. This release returns this feature to the frontend.
UI Improvements for Source-of-Truth
For some sources (Lastfm, Listenbrainz) MS always uses the recently played data as the source-of-truth for scrobbling to your clients, rather than MS’s “player”. This is due to information returned for “now playing” for these sources being less accurate than recently played data. This has functionally not changed but this discrepancy is now more clearly presented to the user in the UI via a ? tooltip icon.
Google Cast Stability Improvements
I have rewritten parts of the third-party library providing google cast (chromecast) functionality to be more stable and less prone to crashing the entire MS application. Reconnecting events should now be more reliable as well.
Last.fm Timeout Fixes
An issue causing requests to Last.fm to hang forever has been fixed and will now cause the request to timeout in a reasonable amount of time instead of freeze up MS #134
Existing Scrobble checks for non-english characters
A bug causing scrobbles with non-english character-symbols to potentially not match existing scrobbles from client has been fixed. #121
Docker Image Alternatives
An alternative debian-based docker image is now provided for x86/x64 host architectures through a *-debian flavored tag. This image may be used as a workaround if you experience sporadic networking issues with the regular docker images. #126
Full Changelog
Features
- (chrome) Used improved library
- (lastfm) Implement nowPlaying lfm api call
- (source) Implement recent from API
- (sources) Improve Source of Truth usage and presentation to user #134
- (No Category) Add debug logging for user api requests
- (No Category) Improved logging for last.fm api network errors #115
- (No Category) Implement debian-based image #126
Bug Fixes
- (frontend) Fix scss division deprecation warnings
- (lastfm) Patch lfm library to actually implement a request timeout #134
- (scrobbler) Fix erasing non-english characters #121
- (No Category) Add patch-package as postinstall script so packages actually get patched
Documentation
- (No Category) Add workarounds for rollup issues and flatpak disclaimer
- (No Category) Add debian docker image network workaround to docs #126 #134
Miscellaneous Tasks
- (No Category) Remove some unused packages
- (No Category) Ignore apple cert extensions
- (No Category) Make container image build tags agnostic to default branch name
- (No Category) Add matrix for building debian flavor tag
v0.7.0
See the majority of changes in the RC1 release
New Since RC1
- Updated flatpak appstream metadeta to conform with new guidelines
- Fixed flatpak build manifest to work with new ESM module configuration
v0.7.1
What’s New?
Apprise Notification
Support for notifying of scrobbles and errors via Apprise using an apprise-api server. See the docs for configuration.
Disable Web
The API and Dashboard can now be disabled. This can be useful for users who do not want a point of ingress in to MS on insecure networks.
Full Changelog
Documentation
- (No Category) Add config section and config example for disabling web server #150
Features
- (mpris) Less noisy error handling
- (No Category) Implement option for disabling web server #150
- (No Category) Implement notifications via Apprise
Miscellaneous Tasks
- (No Category) Remove some unused packages
- (No Category) Update schema
- (No Category) Bump version for release
Refactor
- (logging) Reduce noise for polling interval logging
- (logging) Balance debug/verbose level logging
- (No Category) Reduce usage of ErrorWithCause
- (No Category) Optimize imports
- (No Category) Use updated logger to reduce log file path logic complexity
- (No Category) Remove pony-cause dependency
v0.8.0
What’s New?
Musikcube Source
Musikcube, a cross-platform terminal-based music player, is now supported as a Source.
Configuration Normalization
In previous versions many of the common, optional options for source/clients, such as maxRequestRetries or maxPollRetries, were mixed into the data property for configs alongside properties that were specific to that source/client. Additionally, there was an existing options property that already had some common properties, but not all…
jellyfin.json
[ { "name": "default", "enable": false, "clients": [], "data": { "maxRequestRetries": 3, "scrobbleThresholds": { "percent": 30, "duration": 30 }, "users": ["MyUser"] }, "options": { "logPayload": false } }]These common properties have been refactored into the options object so that data is now solely for data that is specific to the source/client. This should make configs easier to reason about as well as making programmatic config validity checks easier.
[ { "name": "default", "enable": false, "clients": [], "data": { "users": ["MyUser"] }, "options": { "logPayload": false, "maxRequestRetries": 3, "scrobbleThresholds": { "percent": 30, "duration": 30 } } }]This is potentially a breaking change, behaviorally, but MS will continue to operate without any changes to your config. The options that have been moved are all optional, minor settings that help fine-tune your experience with MS but do not majorly affect how it works or functionally affect how your sources/clients behave.
If you use any of the properties that have been migrated but have not moved them then MS will simply ignore them until they are moved.
For a complete list of moved properties and how to migrate them see this comment.
Docker Image Change
Due to consistent issues with DNS in alpine-based images ( #160 #126 #134) the image base is being changed to Debian. The image size will increase relatively significantly ( 60MB -> 120MB ) but the benefit is a more stable experience for docker users. Thanks to @Ardakilic for helping diagnose docker image issues and extensive testing/usage of the debian image.
This is not a breaking change but is still a significant refactor. If you have issues with the new default image please open an issue. Alternatively, there will still be an alpine-based image published with a -alpine suffix so you can continue to use the original image if you would prefer, though this may go away in the future.
TLDR
Previously…
latestordevelopor0.7.1-> alpine basedlatest-debianordevelop-debian… -> debian-based
Now…
latestordevelopor0.8.0-> debian basedlatest-alpineordevelop-alpine… -> alpine-based and may go away in the future
Youtube Music Improvements
The monitoring behavior for YTM has been improved to prevent duplicate scrobbling as well as provide insight into YTM history issues. #156
Additionally, the authentication method for YTM has been improved to save updated cookies from YTM if/when they are changed which may help with authentication issues in long-running MS instances.
Full Changelog
Bug Fixes
- (chromecast) Add missing avahi-utils package for debian image
- (spotify) Convert URL to string for redirectUri
- (vite) Use default base string if no other path detected
- (webscrobbler) Add preflight route #157
- (webscrobbler) Fix formatting of slug url in logs
- (youtube) Catch auth issues that occur during polling #158
- (No Category) Refactor Base URL normalization to cover more cases #155
Documentation
- (No Category) BREAKING: Update schema
- (No Category) Clarify BASE_URL usage and add more examples #155
- (No Category) Add scripting on deploy for plausible analytics
- (No Category) Update screenshot
Features
- (client) Implement initial refresh count and config control #153
- (docker) BREAKING: Use debian image as default
- (youtube) Surface library history playlist and raw recently played for debugging #156
- (youtube) Sync cookies with those returned from YTM responses #158
- (youtube) Use full-fat list diffing to watch for new plays #156
- (No Category) Update normalize-url to prevent port erasure #155
Miscellaneous Tasks
- (ci) Update setup-node to v4 to squash node deprecation warnings
- (No Category) Npm audit fixes
- (No Category) Update dependencies
Refactor
- (client) Use same init logic as source
- (config) BREAKING: Move scrobble thresholds to options
- (config) BREAKING: Move source retry config to options
- (config) BREAKING: Move remaining common options out of source data and into options
- (config) BREAKING: Move common options out of client config and into new option property
- (No Category) Move all init logic into common abstract base class
- (No Category) Use @foxxmd/get-version to render app version
Ci
- (No Category) Use checked out repo to get git info rather than GH variables
- (No Category) Implement gated automated PR building
- (No Category) Add package cleanup and manual triggers
v0.8.1
What’s New?
This release is entirely for documentation and developer experience improvement.
100% Docusaurus and Self-Hosted Docs
While MS was already using Docusaurus for the docs website the prior approach to writing docs was to keep them as plain markdown as possible so they were still accessible without needing access to an external website. With the “plain” markdown docs you could potentially browse from your own markdown reader in a cloned repository.
The downside to this interoperability is that docs, specifically configuration, had grown to be very long and full of nested/too many headers for each iteration of env/file + regular sections for clients/services. Adding additional examples made the problem even worse.
The solution, then, was to make the Docusaurus documentation part of multi-scrobbler! Now the same docs found at https://foxxmd.github.io/multi-scrobbler can also be found at the /docs URL on your multi-scrobbler instance (http://yourHost:9078/docs)
This enabled me to lean fully into Docusaurus and all of the layout, styling, and organization it offers. Alongside general improvements new docs have been added to help onboarding. Some highlights:
- A Quickstart guide has been added for aiding onboarding
- Improved readability and usefulness of all content in Configuration docs
- ENV, File, and all-in-one File configuration has moved to Tabs under each Source/Client
- Switching config type syncs tabs selection across all sections and in the URL
- Full configuration examples are displayed for file and AIO-file parsed from the examples in the config directory
- ENV, File, and all-in-one File configuration has moved to Tabs under each Source/Client
- Improved styling across all docs
- Copious usage of collapsed sections for examples to make scanning docs easier
- Improved Table of Contents with only important headings
- Admonition elements for increasing readability for tips/notes/warnings…
- Installation docs use Tabs for docker and docker-compose examples
- Actual development docs and tutorial (!)
- Overview of common development elements
- Full tutorial for creating a new Source
- Moved flatpak instructions into docs
And many more…check out the docs site or click on Docs in your MS dashboard to see the updated site.
Flatpak Convenience Setup
A bash script for automating building flatpak from source has been added at flatpak/setup.sh:
- Removes
node_modulesand generates sources for flatpak-builder for both MS and the docsite - Builds the flatpak app in a given directory
Effectively making flatpak builds a one-line process rather than the manual 7-8 commands it was before.
See flatpak build instructions in the new docs.
Full Changelog
Documentation
- (feat) Add architecture overview, common dev structures, and dev guidance for Sources
- (fix) Update package scripts to use npx to invoke docusarus
- (fix) Add missing docsite build steps to alpine docker variant
- (flatpak) Implement bash setup script and move flatpak instructions to docsite
- (No Category) Update docusaurus to v3
- (No Category) Update docusaurs playground schema for MS to latest
- (No Category) Improve TOC for docusaurus and github
- (No Category) Implement self-hosted docs
- (No Category) Replace manual notes with admonitions
- (No Category) Lean 100% into docusaurus documentation
- (No Category) Refactor installation.md for docusaurus
- (No Category) Update kitchensink.md code block titles
- (No Category) Parse and render config examples from repository
- (No Category) Add quick start guide
- (No Category) Use relative paths for volume mounting
v0.8.2
What’s New?
MPD (Music Player Daemon) Source
MPD is now a supported Source.
Scrobble Modification
Multi-scrobbler now supports modifying a scrobble (artists, album, track) in-flight using search-and-replace or regex patterns from configuration. The scrobble can be modified:
- on discovery from a source
- during comparison when checking duplicates
- before sending to scrobble client
Why?
You may need to “clean up” data from a Source or before sending to a scrobble Client due to any number of reasons:
- ID3 tags in your music collection are dirty or have repeating garbage IE
[YourMusicSource.com] My Artist - My Title - A Source’s service often incorrectly adds data to some field IE
My Artist - My Title (Album Version)when the title should just beMy Title - An Artist you listen to often is spelled different between a Source and a Client which causes duplicate scrobbles
In any scenario where a repeating pattern can be found in the data it would be nice to be able to fix it before the data gets downstream or to help prevent duplicate scrobbling. Multi-scrobbler can help you do this. See the documentation for examples and usage.
Deezer Deprecation
Deezer has discontinued support for their API and so the Deezer Source is now deprecated. The Source will continue to function and will not be removed any time soon but it is still recommended to switch to a more stable alternative such as Deezer -> last.fm -> lfm source for multi-scrobbler.
See this issue for more information.
Full Changelog
Bug Fixes
- (tests) Do not re-use scrobbler between tests
Features
- (mpd) Implement MPD Source MVP
- (mpd) Use idle event to wake up from polling early
- (musikcube) Provide more error logging and example config
- (scrobble) Enable forcing existing scrobbles refresh on every scrobble #173
- (ui) Add timestamp to recent pages #167
- (No Category) On polling error retry check source is initialized and reinitialize if not
- (No Category) Implement common play transform functionality
- (No Category) Add album option to play string building function
- (No Category) Improve logging for play transforms
- (No Category) Implement play transforms in source/client logic #173
- (No Category) Implement removing field if field is an empty string after transforming
Documentation
- (deezer) Deprecate due to discontinued API support #175
- (No Category) Add Play Transform docs
- (No Category) Fix compare example
Miscellaneous Tasks
- (ci) Update untagged package schedule to run once a week
Refactor
- (listenbrainz) Simplify scrobble api calls and logging
- (scrobbler) Consolidate refresh logic and add more logging
- (scrobbler) Change refresh force to user-configurable staleness time #173
Testing
- (No Category) Use faker 9.1RC for more plausible artist/album names in generated data
v0.8.3
What’s New?
VLC as Source
VLC has been implemented as a Source. In addition to scrobbling based on reported fields (ID3 tags) of audio tracks played in VLC a user can specify regular expressions to parse artist/title/album information from filenames when no ID3 tag is present.
Recent Scrobbles bug fix
A major bug was fixed that caused a scrobble client to not store upstream scrobbles (used for duplicate checking) on startup.
Youtube Music Woes
Improvements to logging and handling of inconsistent YTM upstream data has been added as well as entries in the FAQ regarding these problems.
Full Changelog
Features
- (vlc) Implement vlc Source #177
- (vlc) Use regex from configuration to extract metadata from filenames
- (youtube) Increase level of auth update logging when configured to output changes #158
- (youtube) Use just-validated history as recently played on new plays #156
Bug Fixes
- (maloja) Improve handling for Maloja warnings-as-errors #180
- (No Category) Actually store recent scrobbles
Documentation
- (vlc) Add VLC docs
- (vlc) Some fixes for VLC docs
- (youtube) Add FAQ entries for YTM auth and scrobble issues
0.8.4
What’s New?
Jellyfin Source API
A new Jellyfin Source that uses the official API library has been implemented. This supersedes and deprecates the existing webhook-plugin-based Jellyfin Source. Benefits of new implementation:
- More accurate information about plays fixes multiple artists reporting
- Eliminates need for exposed MS instance (to Jellyfin) which simplifies networking
- User can authenticate using user/pass OR Api Key
- More attention/development on
@jellyfin/sdkshould mean better support going forward
See the Jellyfin Source docs for migration steps from webhook to API. The webhook Source will be removed in a future version of MS.
Scrobble Modification Conditions
Scrobbles Modifications, introduced in the last release, can now have “conditional” criteria to help you decide when a search-replace operation on a scrobble should occur.
See the Scrobble Modification docs for how to use this and examples.
Development Improvements
Git Workflow
Workflows for MS have been simplified. The develop branch has been removed in favor of using master as the active development branch. All PRs should now target master instead of develop. Additionally, the develop docker image has been renamed edge.
VS Code
VS Code settings have been committed and docs have been added to make MS development with VS Code as close to one-click as possible. Settings include debug configurations, test running, typescript build, and devcontainer environment.
Full Changelog
Bug Fixes
- (jellyfin) Map AlbumArtist Name property
- (plex) Refactor plex webhook formdata parsing to be more defensive
- (No Category) Bump @foxxmd/logging version to fix read-only system issues – thanks to @jackwilsdon for the fix in https://github.com/FoxxMD/logging/pull/2
- (No Category) Strip wrapping quotes from base url
- (No Category) Freeze typescript to fix weird unused error directive problem
Features
- (jellyfin) Update schema
- (jellyfin) Refactor Jellyfin source to use Jellyfin API
- (jellyfin) Implement partial WS client for future use
- (modifications) Implement “when” conditions for scrobble modifications
- (modifications) Refactor hooks to accept an array of parts
- (No Category) Improve upstream scrobble refresh logic and caching controls
- (No Category) Add VSCode native dev settings
- (No Category) Add VSCode devcontainer
Documentation
- (No Category) Fix link – thanks to @jackwilsdon
- (No Category) Fix redirect for local documentation – thanks to @jackwilsdon
- (No Category) Fix youtube music identifier
- (No Category) Update development docs for VSCode usage
- (No Category) Add when condition docs and examples
- (No Category) Add scrobble modification logging docs
Miscellaneous Tasks
- (ci) Disable alpine builds for PR image
- (ci) Add dependabot PR test gh action
- (deps) Bump the npm_and_yarn group across 2 directories with 7 updates
- (vscode) Add default built task
- (No Category) Refactor git/dev workflow to use master as unstable branch
- (No Category) Fix latest/tag for images and disable alpine builds
- (No Category) Update faker to official release
- (No Category) Update dockerfile to ignore devcontainer and vscode settings
Testing
- (No Category) Add missing version prop for test components
Full Changelog: https://github.com/FoxxMD/multi-scrobbler/compare/0.8.3...0.8.4
0.8.5
What’s New?
- Fix documentation mismatch between new Jellyfin API Source config allow/block options #196
- Fix missing lowercase transform for Jellyfin API Source config options
v0.8.6
What’s New?
Jellyfin API Improvements
The new Jellyfin API Source was missing a decent amount of polishing from its initial release (#196 #200 #209). This release fixes many bugs and also introduces new configuration for allow/block scrobbling by library and media type enforcement.
Configuration Validation Improvements
The maintenance-mode typescript-json-schema library that was being used to validate configuration has been replaced with the much improved ts-json-schema-generator. Validation rules are now generated at runtime instead of using static files from the project which will make maintenance easier. This change, along with refactored validation logic, should make validation errors in logs easier to read and more specific.
Album Art
Players in Sources on the dashboard now support album art. This has been implemented for Jellyfin and Spotify so far. Other sources will have album art implemented in the future.
Github Issues
The issues template have been refactored to use forms and additional templates have been added for feature requests and source/client suggestions. The new templates should help make bug issues easier to write for new users and easier for me to read!
Full Changelog
Features
- (jellyfin) Add debug options for media data troubleshooting
- (jellyfin) Support and enforce library collection type
- (ui) Improve player ui when in stopped state
- (No Category) Improve client/source schema validation generation and error handling
- (No Category) Implement platform output when building track info
Bug Fixes
- (jellyfin) Guard against undefined playback data #209
- (jellyfin) Guard against active session without now playing data #209
- (jellyfin) Fix not handling stopped session state preventing last play in playlist from scrobbling #209
- (jellyfin) Enforce media type when determining session validity
- (jellyfin) Detect theme song extras play and discard #209
- (ytmusic) Allow authUser to be number or string
- (player) Fix missing calculated status on player stop
- (No Category) Fix some source schemas to reflect documentation WRT optional properties
- (No Category) Fix example configs so they pass validation tests
Documentation
- (No Category) Update jellyfin instructions in quickstart to use API Source
- (No Category) Update/fix docker-compose usage
- (No Category) Fix link typo for flatpak
- (No Category) Enable parsing json5 for AIO examples
Miscellaneous Tasks
- (vscode) Add markdown extension to devcontainer
- (No Category) Migrate bug report template to form
- (No Category) Add templates for feature request and new source/client
- (No Category) Add issue template config
- (No Category) Use silent logger for source config tests
- (No Category) Missing schema package in regular deps and freeze ts version
- (No Category) Replace typescript-json-schema with ts-json-schema-generator
Testing
- (jellyfin) Update valid activity tests
- (No Category) Add tests for example configs and test parsing/validation
- (No Category) Add long timeout to first config parse due to slow schema generation
Ci
- (No Category) Add ACT run for just backend test
- (No Category) Make test workflow reusable and add run sanity check
- (No Category) Fix sanity test checkout ref and shorten APP_VERSION sha usage for PRs
v0.8.7
What’s New?
Plex API Source
A new Source has been implemented for Plex that uses the official API instead of webhooks. There are many benefits to the new source:
- Does not require Plex Pass (webhooks are Plex Pass only) so all users can use it
- Simplified networking and setup
- Leverage MS’s calculated player to better record listening activity instead of relying on Plex’s scrobble behavior
- Automatic scrobbling user filtering based on authentication
The old webhook-based Plex Source has been deprecated. See the documentation for migration steps.
Tautulli Deprecation
The Tautulli Source was initially implemented as a way for non Plex Pass users to monitor Plex. I am considering deprecating it for maintainability of the project and because the new Plex API Source should replace it. Please provide feedback if you think it should not be deprecated or what else needs to be implemented for the new Plex source to accommodate your use case.
MS Player Improvements
The internal logic for MS’s “player”, which is used for calculating play state and listening activity for Source’s that report Player position, has had a major rework to leverage a “real-time” simulation of the Source rather than relying on timestamps. There should be no functional change for end-users besides more accurate player reporting. If you notice any regression in behavior or bugs please create an issue.
Daylight Savings Time Fix
Thanks to the users in #214 for discovering and isolating an issue with MS’s logging library that caused empty files to be created on dates that transitioned from Standard Time to DST. This release bumps the logging library version that includes a fix for this issue that should save American users the headache European users had to endure :sweat_smile:
Full Changelog
Bug Fixes
- (No Category) Bump @foxxmd/logging to fix DST rolling file issue
Documentation
- (plex) Clean up api/webhook sections and add migration guide
- (tautulli) Add deprecation notice and discussion link
- (No Category) Fix Jellyfin API migration version
Features
- (player) Implement real-time positional state tracking
- (plex) Initial Plex API Source implementation
- (plex) Add track progress tracking to api source
- (plex) Implement album art for UI
- (ui) Show indeterminate state for non-positional players
- (No Category) Reduce drop reason logging noise for jellyfin/plex API sources
- (No Category) Improved filtering for platform players with multiple states
v0.8.8
See 0.8.7 for the recent major changes.
- Fix handling of local (not signed in) users in the new Plex API Source #218
v0.9.0
What’s New?
It’s a big release! Happy 2025!
Youtube Music
Auth Fixes
Migrated YTM source communication from youtube-music-ts-api to youtubei.js which is actively developed and has fixed code for authenticating with cookies. Users using cookies are recommended to refresh their cookies using the newer guidance in the docs to help ensure they are not invalidated.
This new library also supports OAuth authentication which may be a more stable alternative for users experiencing constant authorization failures.
Scrobble Improvements
The code used for detecting YTM history has been overhauled with more sophisticated change detection which should prevent more false positive scrobbles. If you still experience scrobble issues please read through the updated FAQ section for YTM to enable detailed logging and guidance on how to report these issues in a useful way.
ENV Support
YTM source config now has full ENV support.
Plex and Jellyfin Improvements
A number of bugs related to the new Jellyfin and Plex API sources have been fixed which should incidentally improve scrobbling experiences for all polling-type sources.
DEPRECATION: The legacy (webhook-based) sources for Jellyfin, Plex, and Tautulli have all now been officially deprecated. These will be removed in an upcoming release. If you are still using these (from MS < 0.8.4) please consult the respective configuration docs for migration steps and migrate as soon as possible.
Improved Source/Client Start and Restart
Code related to initializing and restarting sources/clients has been overhauled to enable restart from basically any error state. You should no longer need to restart multi-scrobbler in the event a single component fails. The UI now includes a
Restart (Force)
button on each component. Using Force will cause the component to be initialized from scratch.
Incidentally, this overhaul paves the way for eventually reloading config changes into MS without restarting, in a future release. :eyes:
Debug Mode
A new, global Debug Mode option has been implemented to help with troubleshooting multi-scrobbler. Instead of needing a file configuration to enable optional, “debugging” options for logging on a specific component, etc… users can now simply use the ENV DEBUG_MODE=true to enable all options at once. This can also be configured from the AIO config.json. See the new Debug Mode docs.
Config Explorer and Validation Improvements
The new configuration migration from v0.8.7 has enabled improving the built-in configuration explorer found in the docs. The explorer is now always up to date with whatever docker tag or branch it is used on and is automatically generated on docs build. The explorer should also now have more friendly names and a simpler structure making it actually useful.
Changelog
Bug Fixes
- (jellyfin) Fix missing check for undefined play #239
- (plex) Use track artist from response data when album artist is present #241
- (source) Implement player cleanup with play discovery for stale platforms #223
- (spotify) Album artists no longer missing @Fratt
- (ui) Wording for non-polling sources start/restart
- (ytm) Implement bump-only list change detection #248 #227 #228 #226
- (ytm) Use diff type to restrict valid history changes #248 #227 #228 #226
- (ytm) Compare recent changed history to detect outdated responses #248 #227 #228 #226
- (No Category) Fix WARN: FromAsCasing: ‘as’ and ‘FROM’ keywords’ casing do not match @CodeWithMa
- Remove deprecated version from docker-compose.yml @CodeWithMa
- (No Category) Default positional listen range to 0 position if none is provided #245
Documentation
- (ytm) Add new auth docs and update FAQ
- (ytm) Clearer wording for troubleshooting content
- (No Category) Improve schema generation and compatibility #208 #202
- (No Category) Add lnav config
- (No Category) Add debug mode information #244
- (No Category) Add final deprecation notices for Plex/Tautull/Jellyfin legacy sources
- (No Category) Update kitchensink with newer jellyfin/plex examples
Features
- (jellyfin) Bump client major version
- (jellyfin) More robust connection check and logging
- (notifications) Make init more granular and always complete init #216 #215
- (source) Add experimental file logging
- (source) Ensure delay on close-interval for scrobbling is less than interval
- (ui) Disable minifiying frontend identifiers
- (ytm) BREAKING Use oauth-based api library for better stability #236 #195 #229
- (ytm) Add config option to always log diff
- (ytm) Re-implement auth with cookie and oauth #236 #195 #229
- (ytm) Implement ENV config
- (ytm) Conservative skip detection #248 #227 #228 #226
- (ytm) Use durations to generate play dates for interim tracks #248 #227 #228 #226
- (ytm) Debug mode enables log diffs
- (ytm) Add more innertube options to user configuration
- (ytm) Custom redirectUri checks
- (No Category) Add stage Error classes and cause chain for notification
- (No Category) Initialize usage refactored everywhere #221 #220 #231
- (No Category) Implement start/restart/force init from UI regardless of status #216 #215
- (No Category) Fully implement use of DEBUG_MODE #244
- (No Category) Reduce polling log noise
- (No Category) Log player state when using debug mode #244
- (No Category) Include comment in play diff when present
Miscellaneous Tasks
- (deps) Bump the npm_and_yarn group across 2 directories with 3 updates
- (No Category) Add docker-compose dev file and vscode task
- (No Category) Generate schema files during docker build for docsite #208 #202
- (No Category) Bump plexjs version
Refactor
- (ytm) Clean up tests and history parsing functions
- (ytm) Improve auth handling for reauthentication
Testing
- (spotify) Test album artist parsing
Ci
- (No Category) Generate schema files for docsite in GH docs deploy workflow #208 #202
New Contributors
- @CodeWithMa made their first contribution in https://github.com/FoxxMD/multi-scrobbler/pull/235
- @Fratt made their first contribution in https://github.com/FoxxMD/multi-scrobbler/pull/240
Full Changelog: https://github.com/FoxxMD/multi-scrobbler/compare/0.8.8...0.9.0
v0.9.1
What’s New?
Fix Spotify Bug
A bug introduced with 0.9.0 could cause the last Spotify scrobble to fail if the player became stale. 0.9.1 fixes #254
Github Actions and Install Improvements
Project setup (npm install, etc..) now has an optional, parallel track that installs the application and builds the docsite at the same time. This, along with upgrading Docusarus to leverage @docusaurus/faster, has greatly reduced the time it takes to build MS from scratch.
Additionally, MS now uses a refactored Github Actions workflow that leverages a native ARM runner instead of emulating the ARM docker build, as well as parallelizing the build.
The end result is a workflow runtime on GHA that decreased from 20 minutes to 4 minutes :tada:
Changelog Highlights
- docs: Update to Docusaurs 3.6 to improve build times by @FoxxMD in https://github.com/FoxxMD/multi-scrobbler/pull/251
- feat(docker): Paralellize npm install and build processes by @FoxxMD in https://github.com/FoxxMD/multi-scrobbler/pull/252
- Implement multi-runner image build for multi-arch improvements by @FoxxMD in https://github.com/FoxxMD/multi-scrobbler/pull/246
- fix(spotify): Generate played object from cleanup with correct TS by @FoxxMD in https://github.com/FoxxMD/multi-scrobbler/pull/255
Full Changelog: https://github.com/FoxxMD/multi-scrobbler/compare/0.9.0...0.9.1
0.9.2
What’s New?
So many new Sources! With this release Multi-Scrobbler now supports 20+ Sources!
Additionally, many improvements to resuming scrobbles from long-lived Sources have made their way into this release. Users who keep their players open for long periods should see less false positive scrobbles.
Listenbrainz and Last.fm “Scrobble Endpoint” Sources
Multi-scrobbler can now “pretend” to be a custom Listenbrainz or Last.fm server so that your apps that support scrobbling to custom endpoints for these services can now use Multi-scrobbler instead. See Source configuration for Listenbrainz (Endpoint) and Last.fm (Endpoint).
Azuracast Source
Monitor an Azuracast radio station and scrobble tracks when it is live, has X number of listeners, or manually from the UI.
Icecast Source
Monitor an Icecast or Shoutcast radio station and scrobble tracks automatically or manually from the UI.
Yamaha MusicCast
Scrobble from Yamaha receiver/devices that support the MusicCast service. Currently supports any music played from USB/Network/CD inputs.
Changelog Highlights
Bug Fixes
- (player) Check for 0 duration before finalizing listen session with duration-based calculation
- (source) Do not calculate polling interval decrease if duration does not exist
- (ui) Update status indicator for ingress sources
- (ytm) Recover artist/album from YTM when not surfaced by youtube.js #280
- (No Category) Missing optional period on vs. when parsing artist
- (No Category) Filter components by name and check for undefined #273
- (No Category) Use last known position for final range when cleaning up stale RT player
- (No Category) Better handling of 0 duration tracks
Documentation
- (No Category) Fix extra array in title modification example
- (No Category) Add primary artist extraction examples for scrobble modification
Features
- (azuracast) Add azuracast source implementation
- (icecast) Implement Icecast Source
- (lastm) Implement Lastfm endpoint source
- (listenbrainz) Implement LZ endpoint source
- (musiccast) Implement MusicCast Source
- (source) Add stale/orphaned intervals as user configurable
- (source) Clear listening range on stale to prevent erroneous scrobble on resume
- (source) Add relative time since last activity to logging
- (No Category) Use human-friendly timestamp for stale/ophan interva logging
- (No Category) Add ENVs for default scrobble threshold for duration/percent
Miscellaneous Tasks
- (No Category) Add more log guidance on bug report
- (No Category) Bump YTM dependency version
- (ci) Add context to build-push so it uses checked out PR ref
Testing
- (No Category) Add modification example for parsing a primary artist from delimited string
- (No Category) Add tests for scrobble behavior when cleaning up players
Full Changelog: https://github.com/FoxxMD/multi-scrobbler/compare/0.9.1...0.9.2
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.