• The calliope.playlist.PlaylistItem class replaces the older calliope.playlist.Item. The old name is still available as an alias. The calliope.playlist.Playlist type is now a usable list subclass instead of a typing.NewType subclass.

  • Replace ‘splitstream’ module with a pure-Python alternative, ‘jsonlines’.

  • Show version number in cpe –help

  • The cpe lastfm histogram and cpe listenbrainz histogram commands now include field names as part of the JSON output. #223

  • Improvements in the tracker module:
    • Use static, precompiled queries with parameters. This replaces string concatenation used previously. This protects against SPARQL injection and avoids re-compiling queries each time they are used.

    • Use full-text search in albums and tracks queries, which makes them significantly faster.

    • The search functionality can now match against album names.

  • Add calliope.cache.Cache.close() method, which helps avoid SQLite locking issues and failures in



  • Stable interfaces across modules! The new calliope.interface module defines two interfaces, ListenHistoryProvider and ContentResolver. These are implemented each in two different modules. This makes it easier to use different backends in the same codebase.

    The special_mix example is updated to demonstrate how this works.

  • Context objects (BandcampContext, LastfmContext, etc.) now take configuration as keyword args. Some previously took a calliope.config.Configuration object, which made it hard to override the config in applications.

    You can load the config yourself with the new calliope.config.Configuration.get_section() method as needed.

  • There is a new policy for config key names in calliope.conf: use underscores to separate words, e.g. client_id. The previous (undocumented) policy was to use hyphens (e.g. client-id).

    The calliope.config module will translate key names on load so that existing configs continue to work.

  • Python examples are now documented using Sphinx + AutoAPI, allowing you to read the docs online.

  • The special_mix example has some bug fixes and documentation improvements.

  • listenbrainz: The listenbrainz-history tracks command now uses artist_name + track_name to decide which listens refer to the same track. The code did use the recording_msid but this seems to change based on album_name.

  • spotify: Content resolution now queries 20 candidates per item rather than 300.

  • cache: Fix a bug where v1 caches didn’t properly migrate to v2.


  • The lastfm module has new documentation and bug fixes. #214

  • Issues with Pip + Meson installs from sdist and Git repo are resolved. #213


  • Examples are now included in the calliope_music distribution and installed as a separate calliope_examples package. The “Special Mix” example can now be executed directly after installation:

    python3 -m calliope_examples.special_mix
  • Meson is now used as the project build system for Python package builds. All setuptools configuration has been removed. There should be no functional difference with this change. #211


  • Multiple artists are now returned when you resolve or annotate via MusicBrainz and Spotify. This is a change in the data format for those subcommands: fields such as artist_id are replaced by a artists list. #205

  • musicbrainz: The annotate --include option now supports all values that the MusicBrainz API supports.

    • See the full list of keys with cpe musicbrainz list-includes

    • Use * to select many keys, e.g. --include=artist.* to select all data related to the artist.


  • Other Musicbrainz improvements:

    • The annotate command no longer calls search() unless required.

    • More detailed artist data is returned. #201

    • Fix crash if calliope.musicbrainz.annotate() is called without the select_fn parameter.


  • Fix some implementation gaps around calliope.cache.CacheLookupResult. #199

  • Remove unneccessary reimplementation of SQLite’s retry-on-busy handling. #202

  • Bugfixes:

    • cli: Quiet ‘unhandled attribute’ warnings from ‘musicbrainzngs’ library

      when -v 3. #199

    • lastfm-history: Fix database error


    • listenbrainz: Fix import error of bundled pylistenbrainz package (it’s bundled awaiting

    • listenbrainz-history: Fix error when artist-msid isn’t returned


    • More stuff not listed here.


  • Fix a bug in cache migration. #197


  • Cache expiry is now supported in calliope.cache module.
  • bandcamp: Add export-band and export-album commands. #192

  • bandcamp: Add wishlist command.

  • example: Fix collectors/ #96

  • tests: Use Tox to run tests in virtualenv. Minor improvements to Gitlab CI setup and PyPI packaging.


  • examples: Add special-mix example.

  • lastfm-history: Add histogram command

  • listenbrainz: Fix a bug in ‘week’ histogram generation


  • tracker: Support querying remote databases over HTTP.

  • listenbrainz: Add cpe listenbrainz-history histogram command.

  • musicbrainz: Documentation improvements and bugfixes.

  • select: Add named keyword arguments to all constraints.

  • Default Git branch renamed to ‘main’.


  • New cpe listenbrainz command to export playlists from Listenbrainz. #178

  • New cpe listenbrainz-history command to cache and query listening history from Listenbrainz. #176

  • spotify: Export and Import tracks, albums or artists from/into the current users’ spotify library By lackhove. #168

  • Bug fixes.



  • spotify: Use the much improved resolver introduced in 2.0.0 and extend playlist import action to use spotify IDs and URIs and update existing playlists. By lackhove. #155.

  • spotify: Remove the --user flag, it did not do what it claimed to do. #158.

  • Document how to get Spotify API keys. #161.

  • lastfm-history: Move progress bar to stderr. #160.

  • Other small fixes and documentation improvements.


  • Replace –debug with –verbosity CLI option. Thanks to lackhove. #149.

  • Skip tests if module requirements aren’t installed. Thanks to lackhove. #151.

  • Update CI image with some follow-up fixes.

  • musicbrainz: Add a much improved resolver. Thanks to lackhove. #148.

  • spotify: Small improvements to resolver, add playlist import. #150.

  • youtube: Fix mass playlist export #85.