Changelog
This changelog is used to track all major changes to Mopidy.
For older releases, see: Changelog 3.x · Changelog 2.x · Changelog 1.x · Changelog 0.x
v4.0.1 (UNRELEASED)
- Deps: Require cycleopts >= 4.3 (!2257)
v4.0.0 (2026-04-25)
Mopidy 4.0 is a major release bringing very few functional changes, but mostly focusing on modernizations to Mopidy's tech stack to keep it maintainable going into the future. In Mopidy 4.0 we've:
- dropped support for old versions of our dependencies,
- rebuilt data models using Pydantic, so data is validated and errors are caught at the edges of the application instead of on use,
- removed a few long-deprecated APIs,
- rebuilt the app startup sequence,
- made many modules explicitly private,
- added type hints to most of the source code, and
- rebuilt our docs using Zensical.
Dependencies
We require newer versions of all of our dependencies. The versions we require are all available in Debian 13 Trixie, which is the latest Debian stable release at the time of writing.
-
Python >= 3.13
-
Support for 3.7-3.12 has been dropped. Python 3.13-3.14 has been added to the testing matrix.
-
setuptools is no longer a runtime dependency, as we've replaced the use of
pkg_resourceswithimportlib.metadatafrom Python's standard library.
-
-
GStreamer >= 1.26.2
-
PyGObject >= 3.50 is now an explicit Python dependency. Previously we assumed that you would install this together with your GStreamer installation.
-
Added a workaround for GStreamer's
Gst.Structure().get_name()regression for versions v1.26.1 to v1.26.2 inclusive. (!2094) -
We also added a workaround to not crash when using PyGObject < 3.55.3 together with GLib >= 2.88. (!2248)
-
-
Pykka >= 4.1
-
Tornado >= 6.4
-
Pydantic >= 2.10
- This is a new dependency for Mopidy to replace our custom data models.
-
Cyclopts >= 3.12
- This is a new dependency for Mopidy to build the command line interfaces,
replacing our use of
argparse.
- This is a new dependency for Mopidy to build the command line interfaces,
replacing our use of
-
Rich >= 3.9
- This is a new dependency for Mopidy transitively via Cyclopts, which we've started to use directly as well to handle colorized log output.
-
HTTPX >= 0.28.1
- HTTPX has replaced Requests as our HTTP client. (!2249)
CLI
-
Everything in the app startup from starting Mopidy to having a running server has been rebuilt. The code is now hopefully a lot easier to follow and maintain going forwards. (!2234)
-
Breaking change for extensions with their own CLI commands:
The
mopidy.commandsmodule which extended onargparseto let extensions add their own CLI subcommands has been removed. We now use new dependency Cyclopts to build command line interfaces.The extensions maintained by the core team, including
mopidy-localandmopidy-spotify, have been updated to use Cyclopts. The migration is pretty straight forward, but feel free to reach out for help with migrating your extension. (!2234) -
Previously, different commands had different default logging levels. Now all commands, including
mopidyitself, emit logs from warning level and higher by default. To get more detailed log output, add-vto the root command one or more times. The includedmopidy.servicesystemd unit has been updated to include--verbosein its command, to continue logging on info level. (!2239) -
Support for custom log colors via the
logcolorsconfig section has been removed. (!2241) -
Log output to terminals are now colorized using Rich, which hopefully leads to a more pleasant and readable log output. Log colors can still be disabled by changing the
logging/colorsconfig. (!2241) -
The command
mopidy depsno longer repeats transitive dependencies that have already been listed. This reduces the length of the command's output drastically. (!2152)
Data models
Changes to the data models may affect any Mopidy extension or client.
-
The
TrackandPlaylistmodels now require theurifield to always be set. (#2190, !2229) -
The models are now based on Pydantic data classes, which means:
-
All model fields and the
replace()method should work as before, so unless your extension modifies or adds models, this should not affect you. -
Models are now type-checked at runtime. This should help catch bugs early, where the models are instantiated with data, instead of when the data is used. This means that if your extension is instantiating models with incorrect data, you should see errors sooner than before.
-
-
Since we now use Pydantic to convert data models to and from JSON, the old model machinery has been removed. This includes the following:
mopidy.models.ImmutableObjectmopidy.models.ValidatedImmutableObjectmopidy.models.ModelJSONEncodermopidy.models.model_json_decoder()mopidy.models.fields.Collectionmopidy.models.fields.Datemopidy.models.fields.Fieldmopidy.models.fields.Identifiermopidy.models.fields.Integermopidy.models.fields.Stringmopidy.models.fields.URI
Core API
Changes to the Core API affect Mopidy frontends and clients.
Import paths
-
The core API is no longer exported from submodules, just from
mopidy.core. Update your imports accordingly. (!2221)The removed modules are:
mopidy.core.actormopidy.core.historymopidy.core.librarymopidy.core.listenermopidy.core.mixermopidy.core.playbackmopidy.core.playlistsmopidy.core.tracklist
-
Moved
mopidy.core.PlaybackStatetomopidy.types.PlaybackState, where it has been unified withmopidy.audio.PlaybackState.
Root object
- The
Coreclass now requires theconfigargument to be present. As this argument is provided by Mopidy itself at runtime, this should only affect the setup of extension's test suites.
Playback controller
-
PlaybackController.play()no longer acceptsTlTrackobjects, which has been deprecated since Mopidy 3.0. Use tracklist IDs (tlid) instead. (#1855, !2150)There are a few APIs left that still support
TlTrackobjects where a simple tracklist ID should be used instead, but these have long been deprecated and will be removed in later releases, once our test suites have been updated to not rely so heavily on the use ofTlTrackobjects in API calls.
Backend API
Changes to the Backend API affect Mopidy backend extensions.
-
Document that
Backendis instantiated with two keyword arguments,configandaudio. (!2253) -
Remove the attribute
Backend.audiofrom the documentation. Mopidy core does not rely on a backend exposing this attribute, and the default backend constructor does not set this attribute. How theconfigandaudioarguments are used is up to the backend implementation, but they are provided for backends to use if they need them. (!2253) -
Added
LibraryProvider.lookup_many()to take a list of URIs and return a mapping of URIs to tracks. If this method is not implemented then repeated calls toLibraryProvider.lookup()will be used as a fallback. (!2145) -
Deprecated
LibraryProvider.lookup(). Extensions should implementLibraryProvider.lookup_many()instead. (!2145)
Audio API
Changes to the Audio API only affect the few Mopidy backend extensions that interface with the audio layer themselves.
-
The audio API is no longer exported from submodules, just from
mopidy.audio. Update your imports accordingly. The removed modules are:mopidy.audio.actormopidy.audio.listenermopidy.audio.utils
-
The old audio actor has been split into a
Audiointerface with the API used by core and backends, and aGstAudioimplementation using GStreamer. The API is still very specific to GStreamer, but this split makes it a bit easier to mock out the audio layer in tests as it is clearer what is part of the required interface. (!2224) -
Moved
mopidy.audio.PlaybackStatetomopidy.types.PlaybackState, where it has been unified withmopidy.core.PlaybackState. -
The
convert_tags_to_track()function now requires the trackurias an argument, so that it can construct validTrackobjects. -
Removed APIs only used by Mopidy-Spotify's bespoke audio delivery mechanism, which has not been used since Spotify shut down their libspotify APIs in May 2022. The removed functions/methods are:
mopidy.audio.Audio.emit_data()mopidy.audio.Audio.set_appsrc()mopidy.audio.Audio.set_metadata()mopidy.audio.calculate_duration()mopidy.audio.create_buffer()mopidy.audio.millisecond_to_clocktime()
Type hints
-
Added type hints to most of the source code to make it safer to do changes, to catch bugs earlier, and to make it faster to navigate the code base. Pykka actor proxies are fully supported, so you should be able to see what type of object you get back when you call an actor method, and what type of arguments the method expects.
-
We now have a public
mopidy.typesmodule with types that are useful both for Mopidy core and extensions. This module will probably see tweaks going forward, and should not be considered entirely stable yet. However, if you limit your use of this module in your extension to withif TYPE_CHECKING:code blocks, your extension should continue to work at runtime even if this module sees breaking changes. -
Switched from mypy to pyright and ty for type checking. The jury is still out on which we'll use long-term. (!2226)
Private modules
-
Moved bundled extensions to the private package
mopidy._exts. (!2217)The removed modules are:
mopidy.filemopidy.httpmopidy.m3umopidy.softwaremixermopidy.stream
-
Renamed modules to be explicitly private. (!2226)
The removed modules are:
mopidy.config.keyringmopidy.internal.*
Development process
-
We've dropped the split between the
mainanddevelopbranches in our development process. We now usemainfor all development, and have removed thedevelopbranch. -
All docstrings have been ported from reStructuredText to Markdown in Google style. All docs have been ported from reStructuredText to Markdown, and we now use Zensical to build our docs. During the process, there has been quite a bit of improvements to the docs, but there are clearly corners that still need dusting and updating. (!2247)
-
The
mopidy-ext-templateextension template has been heavily modernized over the last year. All of the extensions maintained in the Mopidy GitHub organization have been updated to the latest version of the template, and we strongly recommend maintainers of other extensions to update their extensions to the latest version of the template as well. With the move from cookiecutter to Copier, it is now much easier to use the template to update existing extensions, not just create new ones.