beets

MediaDesktop

A command-line music library manager and tagger backed by MusicBrainz.

Latest v2.13.1 · · Desktopby Adrian SampsonWebsitebeetbox/beets

Release activity

Release activity — 10 releases across 10 days since Feb 22, 2026. Each cell is one day; darker means more releases that day. Nothing is recorded before Feb 22, 2026. Older weeks are hidden at this screen width.
MayJunJulAug
SundayNo releases on May 3, 2026No releases on May 10, 2026No releases on May 17, 2026No releases on May 24, 2026No releases on May 31, 2026No releases on Jun 7, 2026No releases on Jun 14, 2026No releases on Jun 21, 2026No releases on Jun 28, 2026No releases on Jul 5, 2026No releases on Jul 12, 2026No releases on Jul 19, 2026No releases on Jul 26, 2026No releases on Aug 2, 2026No releases on Aug 9, 2026No releases on Aug 16, 2026
MondayNo releases on May 4, 2026No releases on May 11, 2026No releases on May 18, 2026No releases on May 25, 2026No releases on Jun 1, 2026No releases on Jun 8, 2026No releases on Jun 15, 20261 release on Jun 22, 2026No releases on Jun 29, 2026No releases on Jul 6, 2026No releases on Jul 13, 2026No releases on Jul 20, 20261 release on Jul 27, 2026No releases on Aug 3, 2026No releases on Aug 10, 2026
TuesdayNo releases on May 5, 2026No releases on May 12, 2026No releases on May 19, 2026No releases on May 26, 2026No releases on Jun 2, 2026No releases on Jun 9, 2026No releases on Jun 16, 2026No releases on Jun 23, 2026No releases on Jun 30, 2026No releases on Jul 7, 2026No releases on Jul 14, 2026No releases on Jul 21, 2026No releases on Jul 28, 2026No releases on Aug 4, 2026No releases on Aug 11, 2026
Wednesday1 release on May 6, 2026No releases on May 13, 2026No releases on May 20, 2026No releases on May 27, 2026No releases on Jun 3, 2026No releases on Jun 10, 2026No releases on Jun 17, 2026No releases on Jun 24, 2026No releases on Jul 1, 2026No releases on Jul 8, 2026No releases on Jul 15, 2026No releases on Jul 22, 20261 release on Jul 29, 2026No releases on Aug 5, 2026No releases on Aug 12, 2026
ThursdayNo releases on May 7, 2026No releases on May 14, 2026No releases on May 21, 2026No releases on May 28, 2026No releases on Jun 4, 2026No releases on Jun 11, 2026No releases on Jun 18, 2026No releases on Jun 25, 2026No releases on Jul 2, 2026No releases on Jul 9, 2026No releases on Jul 16, 2026No releases on Jul 23, 2026No releases on Jul 30, 2026No releases on Aug 6, 2026No releases on Aug 13, 2026
FridayNo releases on May 8, 2026No releases on May 15, 2026No releases on May 22, 2026No releases on May 29, 2026No releases on Jun 5, 2026No releases on Jun 12, 2026No releases on Jun 19, 2026No releases on Jun 26, 2026No releases on Jul 3, 2026No releases on Jul 10, 2026No releases on Jul 17, 2026No releases on Jul 24, 2026No releases on Jul 31, 2026No releases on Aug 7, 2026No releases on Aug 14, 2026
SaturdayNo releases on May 9, 2026No releases on May 16, 2026No releases on May 23, 2026No releases on May 30, 2026No releases on Jun 6, 2026No releases on Jun 13, 2026No releases on Jun 20, 2026No releases on Jun 27, 2026No releases on Jul 4, 2026No releases on Jul 11, 2026No releases on Jul 18, 2026No releases on Jul 25, 2026No releases on Aug 1, 2026No releases on Aug 8, 2026No releases on Aug 15, 2026

10 releases since Feb 22, 2026

Changelog

v2.13.1Latest

Release v2.13.1

Fixed 1
  • Source distributions now include the bundled man pages

Bug fixes

  • Fixed source distributions not including the bundled man pages. :bug: (#6882)
View originalPermalink
How v2.13.1 went
v2.13.0

Release v2.13.0

Added 11
  • Convert Plugin: Add new configuration option convert.refresh and command-line option --refresh to force operation when original file is newer than existing converted file
  • Edit Plugin: Interactive import editor now shows album-level fields as configured by albumfields as a YAML header section when editing an album import
  • FetchArt Plugin: Add fetch_for_asis setting that enables fetching album art from online sources even when imported files are not modified by the auto-tagger
  • Lyrics Plugin: Add lrcmux backend which aggregates lyrics from various other sources
  • Lyrics Plugin: Add --no-keep-synced command option to override keep_synced: yes for a single manual lyrics fetch
  • Lyrics Plugin: Add rest_directory configuration option for specifying a reStructuredText output directory
Changed 2
  • Edit Plugin: Fields that appear in both itemfields and albumfields are shown only in the header, not per-track
  • Tidal Plugin: Normalize Tidal album types to lowercase
Fixed 7
  • Convert Plugin: convert -a with copy_album_art enabled no longer crashes when the stored album art path points to a missing file
  • Edit Plugin: Preserve missing album art paths when editing album metadata instead of turning artpath: null into a path ending in None
  • FtInTitle Plugin: Apply featured-artist rewriting to fetched metadata before commands such as MBSync Plugin use it
  • ImportFeeds Plugin: import command no longer aborts the whole run when a symlink cannot be created but logs the failure and continues
  • LastGenre Plugin: Add support for normalizing genre spellings and naming variants with new configuration option aliases, with built-in list of regex patterns and audited whitelist and genre tree
  • Lyrics Plugin: Leave lyrics empty when a source reports an instrumental track and store that state in lyrics_instrumental flexible attribute
  • Missing Plugin: Honor the -f/--format option and the format_album configuration when listing missing albums in album mode

New features

  • Convert Plugin: Add new configuration option convert.refresh and command-line option --refresh, allowing to force Convert Plugin operation when original file is newer than existing converted file.
  • Edit Plugin: The interactive import editor now shows album-level fields (as configured by albumfields) as a YAML header section when editing an album import. Fields that appear in both itemfields and albumfields are shown only in the header, not per-track.
  • FetchArt Plugin: Add fetch_for_asis setting that enables fetching album art from online sources even when imported files are not modified by the auto-tagger. Default is no which means FetchArt Plugin looks for art only in the local filesystem when the user (or quiet_fallback) chooses asis.
  • FtInTitle Plugin: Apply featured-artist rewriting to fetched metadata before commands such as MBSync Plugin use it. :bug: (#1153)
  • LastGenre Plugin: Add support for normalizing genre spellings and naming variants with a new configuration option aliases. The feature is enabled by default and ships with a built-in list of regex patterns. These patterns can be replaced via the user's configuration. The default whitelist and genre tree were audited against the top 1,000 Last.fm tags: canonical names are now consistent across both files, long-standing mismatches between them have been resolved, and entries align with the built-in alias patterns. :bug: (#6466)
  • Lyrics Plugin: Add lrcmux backend, which aggregates lyrics from various other sources.
  • Lyrics Plugin: Added a --no-keep-synced command option to override keep_synced: yes for a single manual lyrics fetch.
  • Lyrics Plugin: Added a rest_directory configuration option for specifying a reStructuredText output directory, semantically equivalent to -r, --write-rest. :bug: (#2806)
  • modify command: Support += and -= operators to add or remove individual values from multi-valued fields without replacing the whole field. :bug: (#6587)
  • ReplayGain Plugin: Add a metaflac backend that computes ReplayGain for FLAC files using the metaflac command-line tool. :bug: (#1203)
  • Spotify Plugin: Recognize native Spotify URIs (e.g. spotify:album:<id> and spotify:track:<id>) when extracting release/track IDs, in addition to full open.spotify.com URLs and bare IDs
  • Tidal Plugin: Add cover art support. Album metadata now includes cover_art_url from Tidal's coverArt relationship, which the FetchArt Plugin plugin can retrieve.
  • A database backup is now automatically created before running schema migrations. Control with the create_backup_before_migrations option (default: yes).

Bug fixes

  • Convert Plugin: convert -a with copy_album_art enabled no longer crashes when the stored album art path points to a missing file (for example a multi-disc album whose cover lives in the album root rather than a per-disc directory); the missing art is skipped instead. :bug: (#4692)
  • Edit Plugin: Preserve missing album art paths when editing album metadata, instead of turning artpath: null into a path ending in None. :bug: (#2438)
  • ImportFeeds Plugin: import command no longer aborts the whole run when a symlink cannot be created (e.g. on Windows or a read-only directory); the failure is logged and the import continues. :bug: (#840)
  • Lyrics Plugin: Leave lyrics empty when a source reports an instrumental track, and store that state in lyrics_instrumental flexible attribute. Existing [Instrumental] lyrics are migrated automatically. :bug: (#6719)
  • Missing Plugin: Honor the -f/--format option (and the format_album configuration) when listing missing albums in album mode. :bug: (#3804)
  • Smart Playlist Plugin: splupdate no longer crashes with TypeError: unhashable type: 'list' when a playlist configuration includes a playlist: query. :bug: (#5354)
  • SubsonicUpdate Plugin: Log a clearer error when the Subsonic server returns a non-JSON response. :bug: (#5635)
  • Tidal Plugin: Normalize Tidal album types to lowercase.
  • A date query containing a stray | (for example added:2000|2001, as might be typed by a user expecting | to mean "or") now raises a clean "a valid date/time string" error instead of crashing with an uncaught KeyError. A | was being accepted as a relative-date unit due to a regular expression character-class typo.
  • Album store no longer copies artpath onto its items as an absolute path, which broke relative-path portability. A database migration removes any such stale artpath attributes left on items by earlier versions. :bug: (#6756)
  • Sorting by a nullable field (for example a flexible attribute with a declared type whose null value is None) that is present on only some items no longer crashes with a TypeError. Missing values are now grouped together, ordered before present ones when sorting ascending and after them when descending. :bug: (#3461)

Other changes

  • Contributing: The project now uses uv for packaging, virtual environment, and dependency management, replacing poetry. The build backend has changed from poetry-core to hatchling. Please see updates in Development Tools and Getting the Source for more information. :bug: (#5783)
  • Installation Add Homebrew to the list of supported package managers in the installation guide.
  • Installation: Note that Windows users should run beets in a terminal emulator (such as Windows Terminal or cmder) for output to display correctly. :bug: (#2848)
  • Installation: Switch isolated tool installation guidance and GitHub workflow setup to uv tool commands.
  • LastGenre Plugin: Add a new "Choosing the Right Tool" documentation section to guide users in picking the right approach across genre fetching, filtering, and normalization.
  • Spotify Plugin: Retry on 503 Service Unavailable responses from the Spotify API instead of immediately aborting, matching the existing 429 rate-limit retry behavior.
View originalPermalink
How v2.13.0 went
v2.12.0

Release v2.12.0

Added 7
  • 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
  • ListenBrainz Plugin: Add support for importing ListenBrainz listening history from an export file with the `-f` / `--export-file` flag
  • MusicBrainz Plugin: Introduce `aliases_as_credits` to make aliases-as-artist-credit optional
  • Bad Files Plugin: Add settings for auto error and warning actions
  • Tidal Plugin: Populate new flexible attributes during imports including `tidal_track_id`, `tidal_album_id`, `tidal_artist_id`, `tidal_track_popularity`, `tidal_album_popularity`, and `tidal_updated`
  • Tidal Plugin: Add 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
Changed 1
  • 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`
Fixed 12
  • Convert Plugin: Tidy the `--playlist` help text so it no longer has awkward indentation in CLI output
  • Duplicates Plugin: Fix plugin output so information about duplicate items is displayed by default and `--count` option is no longer ignored
  • FetchArt Plugin: Catch `OSError` in `_set_art` so that permission errors are logged as warnings instead of crashing beets
  • Fish Plugin: Fix error on plugin initialization
  • import command: Fix duplicate album merge during import when running in threaded mode to prevent duplicate folders and `could not get filesize` errors
  • Lyrics Plugin: Add rate limiting and exponential backoff to HTTP requests to prevent `429 Too Many Requests` errors from lyrics sources during bulk imports

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. :bug: (#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 :bug: (#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. :bug: (#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. :bug: (#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. :bug: (#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. :bug: (#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. :bug: (#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. :bug: (#6720)
  • MPDStats Plugin: Fix crashes and invalid configuration when passing --host, --port, or --password on the command line. :bug: (#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. :bug: (#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. :bug: (#6260)
  • Spotify Plugin: Improved Spotify API parsing to handle missing label data :bug: (#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. :bug: (#6633)
  • Tidal Plugin: Fix auth URL not printed in environments without a configured browser :bug: (#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. :bug: (#6558)
  • ReadError and WriteError now include the file path and the underlying reason in their message instead of a <super: ...> object representation. :bug: (#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. :bug: (#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. :bug: (#6697)
  • Log message prefix formatting (musicbrainz: msg) moved from a filter to LegacyFormatter, making future customization easier.
View originalPermalink
How v2.12.0 went
v2.11.0

Release v2.11.0

Added 3
  • FromFilename Plugin: support track prefix when parsing track number from filename
  • Tidal plugin: new plugin for fetching metadata from Tidal with support for album and track lookups by ID, batch operations, search by query, and ISRC codes and barcode/EAN support
  • Add support for adding or modifying subtitle field (ID3 tag TIT3)
Changed 4
  • Smart Playlist Plugin: restructure splupdate command output to include track count per-playlist summary, show per-track details only with -v flag, make --pretend flag report would be updated count, and add --format option for customizing track line format
  • import command: remove source archive after successful import when move: yes is set
  • import command: multi-disc album detection now recognizes cassette, digital media, and vinyl as disc markers in addition to disc, disk, and cd
  • Spotify Plugin: batch spotifysync track and audio-features API requests and deduplicate repeated Spotify track IDs within a run
Fixed 10
  • Bad Files Plugin: respect quiet mode during import so corrupt-file prompt is suppressed in non-interactive imports
  • Discogs Plugin: prevent duplicate featured artists in track artist fields when same artist is credited in both artists and extraartists
  • import command: fix duplicate album art files being created when re-importing albums with FetchArt Plugin enabled
  • import command: metadata source plugin ID lookups now correctly call each plugin's own lookup method when running in parallel
  • import command: tags with zero distance penalty are no longer shown as differences in match display
  • import command: with original_date: yes, album-level year, month, and day now use original release date
Removed 1
  • Smart Playlist Plugin: remove --pretend-paths option

New features

  • Smart Playlist Plugin: The splupdate command output is restructured. The per-playlist summary now includes a track count. Per-track details are shown only when -v flag is provided (beet -v splupdate). The --pretend flag produces the same output but reports "N playlists would be updated" instead of "N playlists updated". The --format option allows customizing the track line format. The --pretend-paths option was removed (use --format='$path' instead). :bug: (#6183)

  • import command: When importing an archive (zip, tar, rar, or 7z) with move: yes, the source archive is now removed after a successful import. Archives are preserved if any file in the archive was not imported (e.g. skipped as a duplicate, or the import was aborted), and in non-move import modes.

  • FromFilename Plugin: Support track prefix when parsing the track number from the filename (e.g., track01.m4a).

  • Tidal plugin: Introduces a new plugin for fetching metadata from Tidal. It supports album and track lookups by ID, including batch operations via albums_for_ids and tracks_for_ids. It also enables search by query as well as identifier-based retrieval, with support for ISRC codes (tracks) and barcode/EANs (albums).

    This is an initial, relatively minimal implementation, but already fully usable for common metadata workflows. We welcome feedback, improvement ideas, and community contributions to further extend its capabilities.

    See Tidal Plugin for more information.

  • Add support for adding or modifying a subtitle (ID3 tag TIT3) field

Bug fixes

  • Bad Files Plugin: Respect quiet mode (the --quiet flag or import.quiet: yes config) during import so the corrupt-file prompt is suppressed in non-interactive imports. :bug: (#4736)
  • Discogs Plugin: Prevent duplicate featured artists in track artist fields when the same artist is credited both in artists (for example with Feat. join text) and extraartists as Featuring. :bug: (#6166)
  • import command: Fix duplicate album art files (e.g. cover.2.jpg) being created when re-importing albums with the FetchArt Plugin plugin enabled. Old album art is now properly removed when replacing duplicate albums during import. :bug: (#1264) :bug: (#6205)
  • import command: Metadata source plugin ID lookups now correctly call each plugin's own lookup method when running in parallel. :bug: (#6583)
  • import command: Multi-disc album detection now recognizes cassette, digital media, and vinyl as disc markers (e.g. vinyl 1, 12 vinyl 2), in addition to the existing disc, disk, and cd markers.
  • import command: Tags with a zero distance penalty are no longer shown as differences in the match display. Previously, custom distance_weights could cause fields with no actual mismatch to appear in the line.
  • import command: With original_date: yes, album-level year, month, and day now use the original release date. :bug: (#6577)
  • Lyrics Plugin: Fix apostrophe handling in the musixmatch backend slug. :bug: (#4759)
  • MusicBrainz Plugin: Correctly handle release dates where leading or intermediate components are missing, e.g. 2008-??-02
  • Improve DBAccessError messages to help users diagnose database permission issues more easily. The error message now mentions directory missing and file permissions as potential causes. :bug: (#1676)
  • Library path migration now also handles manually edited database rows where item or album-art paths were stored as SQLite TEXT values instead of bytes, so upgrading to the portable-path storage format no longer fails for those libraries. :bug: (#6561)

Other changes

  • Spotify Plugin: Batch spotifysync track and audio-features API requests and deduplicate repeated Spotify track IDs within a run.
View originalPermalink
How v2.11.0 went
v2.10.0

Release v2.10.0

Added 6
  • Store item and album-art paths relative to the library root in the database while remaining absolute in the rest of beets, with automatic migration of existing paths on first run
  • Access to the album or item object as db_obj in inline plugin fields
  • Import Discogs remixer, lyricist, composer, and arranger credits into multi-value remixers, lyricists, composers, and arrangers fields
  • Add keep_synced config option and --keep-synced CLI flag to lyrics plugin to skip re-fetching lyrics for tracks that already have synced lyrics
  • Use aliases for artist credit in MusicBrainz plugin
  • Execute metadata source plugin searches and lookups concurrently to speed up lookups when multiple plugins are enabled
Fixed 4
  • Chromaprint/Acoustid plugin now correctly respects when the MusicBrainz plugin is not enabled and restores compatibility with MusicBrainz Pseudo-Release plugin
  • Automatically remux WAV files containing MP3 streams to proper MP3 files during import with correct metadata
  • Remove clutter from imported album folders
  • Retry ListenBrainz requests for temporary failures
Security 1
  • Fix stored XSS vulnerability in web plugin where unescaped metadata fields could execute arbitrary JavaScript by using escaped template interpolation

New features

  • Beets library is now made portable: item and album-art paths are now stored relative to the library root in the database while remaining absolute in the rest of beets. Path queries continue matching both library-relative paths and absolute paths under the currently configured music directory under the new storage model. The existing paths in the database are migrated automatically the first time you run any beet command after the update. :bug: (#133)

[!WARNING] make sure you run beet version (or any other command) at least once after upgrading to trigger the migration. Only then you can safely move the library to a new location.

  • Inline Plugin: Add access to the album or item object as db_obj in inline fields.
  • Discogs Plugin: Import Discogs remixer, lyricist, composer, and arranger credits into the multi-value remixers, lyricists, composers, and arrangers fields. :bug: (#6380)
  • Lyrics Plugin: Add keep_synced config option and --keep-synced CLI flag to skip re-fetching lyrics for tracks that already have synced lyrics, even when force is enabled. :bug: (#5249)
  • MusicBrainz Plugin: Use aliases for artist credit.
  • Metadata source plugin searches and lookups are now executed concurrently, speeding up lookups when multiple plugins (e.g. MusicBrainz and Spotify) are enabled.

Bug fixes

  • Chromaprint/Acoustid Plugin: Do not produce MusicBrainz-sourced autotagger candidates when the MusicBrainz Plugin plugin is not enabled. The chroma plugin now looks up the musicbrainz plugin through the metadata-source registry instead of unconditionally instantiating its own private instance, which also restores compatibility with MusicBrainz Pseudo-Release Plugin for chroma-triggered lookups. :bug: (#6212) :bug: (#6441)
  • import command Automatically remux WAV files containing MP3 streams (WAVE_FORMAT_MPEGLAYER3) to proper MP3 files during import, instead of silently importing them with incorrect metadata. :bug: (#6455)
  • import command Remove clutter from imported album folders. :bug: (#5016)
  • ListenBrainz Plugin: Retry listenbrainz requests for temporary failures.
  • Web Plugin: Fix a stored XSS vulnerability where unescaped metadata fields (artist, album, title, comments, lyrics) could execute arbitrary JavaScript in the browser. Template tags now use <%- (escaped interpolation) instead of <%= (raw interpolation).

For plugin developers

  • Consumers of beetsplug._utils.musicbrainz.MusicBrainzAPI now receive normalized MusicBrainz payloads with underscore-separated field names (for example artist_credit and release_group) and grouped relation lists such as work_relations, release_relations, and url_relations. The API responses are also now fully typed with concrete TypedDict models for releases, recordings, works, and relations. Update direct access to raw MusicBrainz response keys if needed.
View originalPermalink
How v2.10.0 went
v2.9.0

Release v2.9.0

Added 12
  • Add official support for Python 3.14
  • AutoBPM Plugin: Add force configuration and CLI option
  • AutoBPM Plugin: Hide "BPM already exists for item" log message with --quiet flag
  • Chromaprint/Acoustid Plugin: Add chromasearch command to search the local library by chromaprint fingerprint
  • FetchArt Plugin: Add support for WebP images
  • import command: Use ffprobe to recognize format of any import music file that has no extension
Fixed 7
  • Deezer Plugin: Fix AttributeError crash when selecting a Deezer match with numeric artist IDs
  • Deezer Plugin: Fix Various Artists albums being tagged with localized string instead of configured va_name
  • Discogs Plugin: Store specific Discogs styles in genres field and broader Discogs genres in style field
  • FetchArt Plugin: Fix error when a configured source does not exist or sources configuration is empty
  • import command: Fix albumartists_sort incorrectly prepending combined artist credit as first element for multi-artist releases
  • ListenBrainz Plugin: Fix lbimport crashing when tracks are processed through Last.fm-specific play-count import logic
  • ListenBrainz Plugin: Paginate through all ListenBrainz listens, aggregate individual listen events into correct play counts, and avoid per-listen MusicBrainz API lookups
Deprecated 1
  • AutoBPM Plugin: Deprecate overwrite configuration in favor of force

Beets now officially supports Python 3.14.

New features

  • AutoBPM Plugin: Add force configuration and CLI option and deprecate overwrite.
  • AutoBPM Plugin: The "BPM already exists for item" log message can now be hidden with the --quiet flag.
  • Chromaprint/Acoustid Plugin: Add new command chromasearch to search the local library by chromaprint fingerprint.
  • FetchArt Plugin: Add support for WebP images.
  • import command Use ffprobe to recognize format of any import music file that has no extension. If the file cannot be recognized as a music file, leave it alone. :bug: (#4881)
  • LastGenre Plugin: Add support for a user-configurable ignorelist to exclude unwanted or incorrect Last.fm (or existing) genres, either per artist or globally :bug: (#6449)
  • MusicBrainz Plugin: Store MBIDs for remixers, lyricists, composers, and arrangers in the new multi-valued fields remixers_mbid, lyricists_mbid, composers_mbid, and arrangers_mbid. :bug: (#5698)
  • ReplayGain Plugin: Conflicting replay gain tags are now removed on write. RG* tags are removed when setting R128* and vice versa.
  • Smart Playlist Plugin: The list of available playlists shown when an unknown playlist name is passed as an argument is now sorted alphabetically and printed space-delimited and POSIX shell-quoted when required. This makes it easier to copy and paste multiple playlists for further use in the shell.
  • Query: Add has_cover_art computed field to query items by embedded cover art presence. Users can now search for tracks with or without embedded artwork using beet list has_cover_art:true or beet list has_cover_art:false.
  • Store track remixers, lyricists, composers, and arrangers in the multi-valued remixers, lyricists, composers, and arrangers fields instead of the legacy single-value remixer, lyricist, composer, and arranger fields. Existing libraries are migrated automatically, and MusicBrainz Plugin now preserves each MusicBrainz remixer, lyricist, composer, and arranger relation as a separate value.

Bug fixes

  • Deezer Plugin: Fix a regression in 2.8.0 where selecting a Deezer match during import could crash with AttributeError: 'AlbumInfo' object has no attribute 'raw_data' when Deezer returned numeric artist IDs. :bug: (#6503)
  • Deezer Plugin: Fix Various Artists albums being tagged with a localized string instead of the configured va_name. Detection now uses Deezer's artist ID rather than the artist name string. :bug: (#4956)
  • Discogs Plugin: Store specific Discogs styles in beets genres and broader Discogs genres in the style field. When append_style_genre is enabled, the broader Discogs genres are also appended to the genres list. :bug: (#6390)
  • FetchArt Plugin: Error when a configured source does not exist or sources configuration is empty. :bug: (#6336)
  • import command Fix albumartists_sort (and related fields) incorrectly prepending the full combined artist credit as the first element for multi-artist releases. :bug: (#6470)
  • ListenBrainz Plugin: Fix lbimport crashing when ListenBrainz tracks are processed through Last.fm-specific play-count import logic. Play-count imports now use source-specific fields so ListenBrainz Plugin, LastImport Plugin, and MPDStats Plugin do not clash. :bug: (#6469)
  • ListenBrainz Plugin: Paginate through all ListenBrainz listens instead of fetching only 25, aggregate individual listen events into correct play counts, use recording_mbid from the ListenBrainz mapping when available, and avoid per-listen MusicBrainz API lookups that caused imports to hang on large listen histories. :bug: (#6469)
  • modify command accepts legacy singular field names such as genre, composer, lyricist, remixer, and arranger in assignments, rewrites them to the corresponding multi-valued fields, and warns users to switch to the plural field names. list command, and query expressions, accept the same legacy singular field names and warn users to switch to the plural field names. :bug: (#6483)
  • Rewrite Plugin Advanced Rewrite Plugin: Fix rewriting multi-valued fields such as genres by applying rules to each matching list entry. Additionally, apply rewrite rules in config order, so that multiple rules can be applied to the same field. :bug: (#6515)
  • Correctly handle semicolon-delimited genre values from externally-tagged files. :bug: (#6450)

For plugin developers

  • If you maintain a metadata source plugin that populates any of arranger, composer, lyricist, remixer fields, update it to populate the respective multi-valued fields instead (arrangers, composers, lyricists, remixers).
View originalPermalink
How v2.9.0 went
v2.8.0

Release v2.8.0

Added 5
  • Add extra_tags option to Discogs Plugin to use additional tags in search queries
  • Add auto_ignore configuration option to Lyrics Plugin to skip fetching lyrics for items matching a beets query during auto import
  • Add --release-type flag to Missing Plugin to specify MusicBrainz release types to show in missing album mode
  • Add -R/--randomize flag to Play Plugin to shuffle the playlist order before passing it to the player
  • Add dest_regen configuration option to Smart Playlist Plugin to regenerate items' path in the generated playlist
Changed 4
  • Missing Plugin default behavior changed to show only releases of type album
  • beets.metadata_plugins.album_for_id and beets.metadata_plugins.track_for_id now require a data_source argument and query only that provider
  • Colorisation, diff and layout utility helpers moved from beets.ui to beets.util.color, beets.util.diff, and beets.util.layout
  • lastgenre tunelog helper generalized into beets.logging.BeetsLogger.extra_debug which emits DEBUG messages only at verbosity level 3 or higher
Fixed 10
  • Beatport Plugin now uses va_name config for the album artist on VA releases instead of hardcoded "Various Artists"
  • config command on Windows now uses cmd /c start "" for the default editor fallback so beet config -e works when VISUAL and EDITOR are unset
  • Fix AttributeError in Fish Plugin
  • import command autotagging by explicit release or recording IDs now keeps candidates from all enabled metadata sources instead of dropping matches when different providers share the same ID
  • Simplify import command autotag metadata application for albums and singletons, fixing null-overwrite handling and keeping singular/plural artist metadata fields in sync
  • LastImport Plugin flexible field renamed from play_count to lastfm_play_count to avoid conflicts with MPDStats Plugin

New features

  • Discogs Plugin: Add extra_tags option to use additional tags (such as barcode, catalognum, country, label, media, and year) in Discogs search queries.
  • Lyrics Plugin: Add auto_ignore configuration option to skip fetching lyrics for items matching a beets query during auto import.
  • Missing Plugin: When running in missing album mode, allows users to specify MusicBrainz release types to show using the --release-type flag. The default behavior is also changed to just show releases of type album. :bug: (#2661)
  • Play Plugin: Added -R/--randomize flag to shuffle the playlist order before passing it to the player.
  • Smart Playlist Plugin: Add new configuration option dest_regen to regenerate items' path in the generated playlist instead of using those in the library. This is useful when items have been imported in don't copy-move (-C -M) mode in the library but are later passed through the Convert Plugin plugin which will regenerate new paths according to the Beets path format.

Bug fixes

  • Beatport Plugin: Use va_name config for the album artist on VA releases instead of hardcoded "Various Artists". :bug: (#6316)
  • config command on Windows now uses cmd /c start "" for the default editor fallback so beet config -e works when VISUAL and EDITOR are unset. :bug: (#6436)
  • Fish Plugin: Fix AttributeError. :bug: (#6340)
  • import command Autotagging by explicit release or recording IDs now keeps candidates from all enabled metadata sources instead of dropping matches when different providers share the same ID. :bug: (#6178) :bug: (#6181)
  • import command Simplify autotag metadata application for albums and singletons, fixing null-overwrite handling and keeping singular/plural artist metadata fields in sync during tagging.
  • LastImport Plugin: Rename flexible field play_count to lastfm_play_count to avoid conflicts with MPDStats Plugin. Migration: This cannot be migrated automatically because of the field clash. If you use LastImport Plugin without MPDStats Plugin, migrate manually with beet modify lastfm_play_count='$play_count'.
  • MBSync Plugin and Missing Plugin now use each item's stored data_source for ID lookups, with a fallback to MusicBrainz.
  • Missing Plugin: Fix --album mode incorrectly reporting albums already in the library as missing. The comparison now correctly uses mb_releasegroupid.
  • MusicBrainz Plugin: Use va_name config for albumartist_sort, albumartists_sort, albumartist_credit, albumartists_credit, and albumartists on VA releases instead of hardcoded "Various Artists". :bug: (#6316)
  • replace: Made drive_sep_replace regex logic more precise to prevent edge-case mismatches (e.g., a song titled "1:00 AM" would incorrectly be considered a Windows drive path).

For plugin developers

  • beets.metadata_plugins.album_for_id and beets.metadata_plugins.track_for_id now require a data_source argument and query only that provider.
  • Colorisation, diff and layout utility helpers previously imported from beets.ui now live in beets.util.color, beets.util.diff, and beets.util.layout. Update external imports accordingly.
  • The lastgenre tunelog helper was generalized into beets.logging.BeetsLogger.extra_debug, which emits DEBUG messages only at verbosity level 3 or higher (for example -vvv). Plugin authors can use it via self._log.extra_debug(...).

Other changes

  • Contributing: Update pipx installation guide link
  • Getting Started: Update quick installation section to reflect current installation guide structure.
  • Installation: Remove redundant macOS section from the installation guide. :bug: (#5993)
  • Installation: Update installation guide to document plugin management with pipx and move package manager instructions to the FAQ.
  • Installation: Update pipx installation guide link
  • API-backed metadata source plugins can now use SearchApiMetadataSourcePlugin for shared search orchestration. Implement provider behavior in ~beets.metadata_plugins.SearchApiMetadataSourcePlugin.get_search_query_with_filters and ~beets.metadata_plugins.SearchApiMetadataSourcePlugin.get_search_response.
  • Deprecate the Beatport Plugin and BPSync Plugin plugins. Beatport has retired the API these plugins rely on, making them non-functional. :bug: (#3862)
View originalPermalink
How v2.8.0 went
v2.7.1

Release v2.7.1

Fixed 1
  • Tests that depend on the optional langdetect package are now skipped when the package is not installed

Bug fixes

  • Tests that depend on the optional langdetect package are now skipped when the package is not installed. :bug: (#6421)
View originalPermalink
How v2.7.1 went
v2.7.0

Release v2.7.0

Added 2
  • Add native support for multiple genres per album/track with the genres field storing genres as a list and written to files as multiple individual genre tags
  • LastGenre Plugin: Add cleanup_existing configuration flag to allow whitelist canonicalization of existing genres
Changed 7
  • MusicBrainz Plugin, Beatport Plugin, Discogs Plugin and LastGenre Plugin have been updated to populate the genres field as a list
  • Lyrics Plugin: With synced enabled, existing synced lyrics are no longer replaced by newly fetched plain lyrics even when force is enabled
  • Lyrics Plugin: Remove Source: <lyrics-url> suffix from lyrics and store the backend name in lyrics_backend, URL in lyrics_url, language in lyrics_language and translation language in lyrics_translation_language flexible attributes
  • Edit Plugin: Editing multi-valued fields now behaves more naturally with list values handled directly
  • LastGenre Plugin: Remove the separator configuration option
  • Lyrics Plugin: Synced lyrics from lrclib source are now checked to ensure the final verse falls within the track's duration
  • modify command: Use semicolon followed by space as the separator to delimit multiple field values instead of \␀
Fixed 8
  • Convert Plugin: Fix extension substitution inside path of the exported playlist
  • FetchArt Plugin: Prevent deletion of configured fallback cover art
  • FtInTitle Plugin: Fix handling of multiple featured artists with ampersand
  • Fuzzy Search Plugin: Force slow query evaluation whenever the fuzzy prefix is used so fuzzy matching is applied consistently
  • Fuzzy Search Plugin: Improve fuzzy matching when the query is shorter than the field value so substring-style searches produce more useful results
  • import command: Duplicate detection now works for as-is imports when autotag is disabled
  • import command: When autotagging, initialise empty multi-valued fields with None instead of empty list to avoid overwriting existing metadata
  • Zero Plugin: When the omit_single_disc option is set, disctotal is zeroed alongside disc
Deprecated 1
  • Populating the single genre field in metadata source plugins is deprecated and support will be removed in version 3.0.0; populate the genres list instead

New features

  • LastGenre Plugin: Added cleanup_existing configuration flag to allow whitelist canonicalization of existing genres.

  • Add native support for multiple genres per album/track. The genres field now stores genres as a list and is written to files as multiple individual genre tags (e.g., separate GENRE tags for FLAC/MP3). The MusicBrainz Plugin, Beatport Plugin, Discogs Plugin and LastGenre Plugin plugins have been updated to populate the genres field as a list.

    Migration: Existing libraries with comma-separated, semicolon-separated, or slash-separated genre strings (e.g., "Rock, Alternative, Indie") are automatically migrated to the genres list when you first run beets after upgrading. The migration runs once when the database schema is updated, splitting genre strings and writing the changes to the database. The updated genres values will be written to media files the next time you run a command that writes tags (such as write command or during import). No manual action or MBSync Plugin is required.

    The genre field is split by the first separator found in the string, in the following order of precedence:

    1. LastGenre Plugin separator configuration
    2. Semicolon followed by a space
    3. Comma followed by a space
    4. Slash wrapped by spaces
  • Lyrics Plugin: With synced enabled, existing synced lyrics are no longer replaced by newly fetched plain lyrics, even when force is enabled.

  • Lyrics Plugin: Remove Source: <lyrics-url> suffix from lyrics. Store the backend name in lyrics_backend, URL in lyrics_url, language in lyrics_language and translation language (if translations present) in lyrics_translation_language flexible attributes. Lyrics are automatically migrated on the first beets run. :bug: (#6370)

Bug fixes

  • Convert Plugin: Fix extension substitution inside path of the exported playlist.
  • FetchArt Plugin: Prevent deletion of configured fallback cover art
  • FtInTitle Plugin: Fix handling of multiple featured artists with ampersand.
  • Fuzzy Search Plugin: Force slow query evaluation whenever the fuzzy prefix is used (for example ~foo or %%foo), so fuzzy matching is applied consistently. :bug: (#5638)
  • Fuzzy Search Plugin: Improve fuzzy matching when the query is shorter than the field value so substring-style searches produce more useful results. :bug: (#2043)
  • import command Duplicate detection now works for as-is imports (when autotag is disabled). Previously, duplicate_keys and duplicate_action config options were silently ignored for as-is imports.
  • import command When autotagging, initialise empty multi-valued fields with None instead of empty list, which caused beets to overwrite existing metadata with empty list values instead of leaving them unchanged. :bug: (#6403)
  • Zero Plugin: When the omit_single_disc option is set, disctotal is zeroed alongside disc.

For plugin developers

  • If you maintain a metadata source plugin that populates the genre field, please update it to populate a list of genres instead. You will see a deprecation warning for now, but support for populating the single genre field will be removed in version 3.0.0.

Other changes

  • Edit Plugin: Editing multi-valued fields now behaves more naturally, with list values handled directly to make metadata edits smoother and more predictable.
  • LastGenre Plugin: The separator configuration option is removed. Since genres are now stored as a list in the genres field and written to files as individual genre tags, this option has no effect and has been removed.
  • Lyrics Plugin: To cut down noise from the lrclib lyrics source, synced lyrics are now checked to ensure the final verse falls within the track's duration.
  • modify command: Use the following separator to delimit multiple field values: ; . For example beet modify albumtypes="album; ep". Previously, \␀ was used as a separator. This applies to fields such as artists, AlbumTypes Plugin etc.
  • Improve highlighting of multi-valued fields changes.
  • Updated URLs in the documentation to use HTTPS where possible and updated outdated links.
View originalPermalink
How v2.7.0 went
v2.6.2

Release v2.6.2

Changed 1
  • Lyrics Plugin: Disable tekstowo by default because it blocks the beets User-Agent
Fixed 7
  • Bad Files Plugin: Fix number of found errors in log message
  • MusicBrainz Plugin: Fix crash when release mediums lack the tracks key
  • MusicBrainz Plugin: Fix fetching very large releases that have more than 500 tracks
  • MusicBrainz Plugin: Fix search terms escaping
  • MusicBrainz Plugin: Fix support for alias and tracks extra_tags
  • MusicBrainz Pseudo-Release Plugin: Fix crash due to missing artist_credit field in the MusicBrainz API response
  • ReplayGain Plugin: Avoid magic Windows prefix in calls to command backends, such as mp3gain

Bug fixes

Other changes

  • Lyrics Plugin: Disable tekstowo by default because it blocks the beets User-Agent.
View originalPermalink
How v2.6.2 went
View all

Discussion

If you publish beets, you can claim this product by proving you administer its repository.