github beetbox/beets v2.12.0
Release v2.12.0

6 hours ago

New features

  • Convert Plugin: The --force and --keep-new CLI flags are now also available as config options via force and keep_new.

  • import command: The --nomove / -M CLI flag can now be used to override the move: yes config option during import.

  • Lyrics Plugin: Write synced (LRC) lyrics to the SYLT (synchronized lyrics) ID3 frame and plain text to USLT for ID3-tagged files, instead of storing raw LRC timestamps in USLT. Players that only support USLT continue to see readable plain lyrics. 🐛 (#6541)

  • ListenBrainz Plugin: Add support for importing ListenBrainz listening history from an export file. Use the -f / --export-file flag to specify the path to the ListenBrainz export file.

  • MusicBrainz Plugin: Introduce aliases_as_credits to make aliases-as-artist-credit optional.

  • Bad Files Plugin: Added settings for auto error and warning actions.

  • Tidal Plugin: New flexible attributes are now populated during imports, including tidal_track_id, tidal_album_id, tidal_artist_id, tidal_track_popularity, tidal_album_popularity, and tidal_updated. Added a new beet tidalsync command to refresh popularity data for imported items by default, or albums with --album, with --force to re-fetch and --write to update file tags.

    Migration: Existing Tidal imports can copy the previously stored IDs into the new flexible attributes with modify command: run beet modify data_source:tidal tidal_album_id='$mb_albumid' -a for albums and beet modify data_source:tidal tidal_track_id='$mb_trackid' for items.

Bug fixes

  • Convert Plugin: Tidy the --playlist help text so it no longer has awkward indentation in CLI output.
  • Duplicates Plugin: Fix plugin output: information about duplicate items was not displayed by default. --count option was ignored 🐛 (#6476)
  • FetchArt Plugin: Catch OSError in _set_art so that permission errors (e.g. a file locked by another process) are logged as warnings instead of crashing beets. 🐛 (#6193)
  • Fish Plugin: Fix error on plugin initialization.
  • import command: Fix duplicate album merge during import when running in threaded mode. The merge action no longer creates a duplicate folder or reports could not get filesize errors. 🐛 (#6601)
  • Lyrics Plugin: Add rate limiting and exponential backoff to HTTP requests to prevent 429 Too Many Requests errors from lyrics sources during bulk imports. 🐛 (#6728)
  • Lyrics Plugin: Improve Musica.com lyric scraping so fetched lyrics no longer omit the opening verse or include non-lyric page content.
  • MBSync Plugin / BPSync Plugin: Do not clear items metadata when import.from_scratch is enabled. 🐛 (#6613)
  • modify command: Fix beet modify -a splitting multi-value field strings (like artists, genres) into individual characters when modifying albums. Album field types now fall back to the corresponding item field type definitions. 🐛 (#5690)
  • move command: move command no longer crashes when an item referenced in the database has been deleted from disk. Missing items are now skipped with a warning and the command continues. 🐛 (#6720)
  • MPDStats Plugin: Fix crashes and invalid configuration when passing --host, --port, or --password on the command line. 🐛 (#5404)
  • MusicBrainz Collection Plugin: Handle MusicBrainz 401 Unauthorized errors during mbupdate without crashing, and log a clearer message that points users to musicbrainz.user and musicbrainz.pass configuration. 🐛 (#6651)
  • MusicBrainz Plugin: Fix KeyError: 'aliases' crash when looking up releases with more than 500 tracks.
  • MusicBrainz Pseudo-Release Plugin: Fix crashes when applying a pseudo-release. One in PseudoAlbumInfo.raw_data and a sqlite3.ProgrammingError.
  • Replace Plugin: Fix TypeError when invoking the Replace Plugin command. 🐛 (#6260)
  • Spotify Plugin: Improved Spotify API parsing to handle missing label data 🐛 (#6679)
  • Spotify Plugin: Use single instead of double quotes in spotify queries.
  • Tidal Plugin: add Tidal Plugin dependency extra to make sure requests-oauthlib is installed. 🐛 (#6633)
  • Tidal Plugin: Fix auth URL not printed in environments without a configured browser 🐛 (#6710)
  • AttrDict.__getattribute__ now unmasks AttributeError raised inside a cached_property body. Previously such errors were swallowed by the __getattr__ fallback, producing a misleading message about the property name itself. The wrapped RuntimeError keeps the original traceback so it still points at the real failing line. 🐛 (#6558)
  • ReadError and WriteError now include the file path and the underlying reason in their message instead of a <super: ...> object representation. 🐛 (#6560)
  • Fix a CLI help formatting regression that moved command descriptions to separate lines; descriptions are inline again, with regression test coverage.
  • Path format queries now correctly match multi-value fields such as genres when using exact string matches like genres:=Classical or genres:=~Classical. 🐛 (#6598)

For plugin developers

  • Introduced beets.importer.DuplicateAction to simplify handling of duplicates.
  • Plugin authors can import all autotagger helpers directly from beets.autotag, including match classes, distance helpers, and assign_items, without relying on lower-level autotag modules.

Other changes

  • Lyrics Plugin: Fold rate limiting and 429 retry from the lyrics-specific LyricsSession into the shared beetsplug._utils.requests.TimeoutAndRetrySession so all plugins benefit. The standalone LyricsSession class has been removed.
  • Spotify Plugin: spotifysync now batches its SQLite commit for a sync run, follows the standard beets write-before-store pattern, and logs audio-features API unavailability only once per run.
  • Titlecase Plugin: Correct the path format example and document the %titlecase{text} template function. 🐛 (#6697)
  • Log message prefix formatting (musicbrainz: msg) moved from a filter to LegacyFormatter, making future customization easier.

Don't miss a new beets release

NewReleases is sending notifications on new releases.