Skip to content

All versions since v0.11.5

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
  • 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
image

These same tools are available on the Config Playground for general AIO editing and exploration.

image

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 guid field @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
  • 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.

image

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=false

Image 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.status by @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 _ID and _NAME examples to all instances of ENV configs
  • (No Category) Add id to 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/next

I 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

Image   Image

Left: Scrobble duplicate check results | Right: musicbrainz transformer “no results” with used queries

Image Image

visual indicator of live activity (just scrobbled) on main page

Image

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…) uses CONFIG_DIR ENV or OS-standard locations like $XDG_CONFIG_HOME/multi-scrobbler
  • Data (ms.db, logs folder, stored credentials, etc…) uses DATA_DIR ENV 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:

Terminal window
CONFIG_DIR=./config DATA_DIR=./config ... npm run start

See 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 npm scripts 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/next

and 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.token

Variables are now directly validated to give you a clearer idea of what is missing or wrong

✖ Invalid input: expected string, received undefined
→ at KOITO_TOKEN

Source/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 configureAs for 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.

image

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!

image

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-scrobbler

Further 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.

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=coverartarchive

For 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:

  1. MS looks for a non-default, non-ENV config first
  2. MS falls back to ENV config (name is MSEnv) if it exists
  3. MS falls back to default config (name is MSDefault) if it exists

Steps to migrate:

  • If you only use named configs that aren’t MSDefault there are no changes necessary
  • If you only use ENV configuration there are no changes necessary
  • If you use the name MSDefault but do not mix ENV transforms with AIO/File config, no changes are necessary
  • If you use the name MSDefault and also have ENV transforms configured (musicbrainz)
    • Rename name to MSEnv

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_DIR ENV
  • LOGS_DIRECTORY ENV (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?

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

image

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.

image

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.