Skip to content

Releases: linuxserver/docker-beets

nightly-2f3efaea-ls271

18 Apr 19:40
6adee69

Choose a tag to compare

Pre-release

CI Report:

https://ci-tests.linuxserver.io/linuxserver/beets/nightly-2f3efaea-ls271/index.html

LinuxServer Changes:

No changes

Remote Changes:

Refactor musicbrainz to reduce cognitive complexity (#6530)

Summary

A broad internal refactor of beetsplug/musicbrainz.py and its test
suite. No user-facing behaviour changes are intended. The goal is to
make parsing logic easier to understand, test, and maintain.

This PR halves the cognitive complexity in musicbrainz plugin:

Before

$ complexipy beetsplug/musicbrainz.py
🧠 Total Cognitive Complexity: 181
1 file analyzed in 0.0116 seconds

After

$ complexipy beetsplug/musicbrainz.py
🧠 Total Cognitive Complexity: 93
1 file analyzed in 0.0097 seconds

Production Code Changes

Parsing Decomposition

The monolithic album_info method is broken into focused
@staticmethod helpers, each returning a typed TypedDict:

Method Responsibility
_parse_artist_credits Replaces _flatten_artist_credit /
_multi_artist_credit
_parse_release_group albumtype, original_year, etc.
_parse_label_infos label, catalognum
_parse_genres Genre list
_parse_external_ids Third-party IDs from URL relations
_parse_work_relations Composer / lyricist credits
_parse_artist_relations Arranger / remixer credits
get_tracks_from_medium Per-medium track iteration

Each helper returns a typed TypedDict, making the data contract
explicit and the code easier to compose with **kwargs unpacking into
AlbumInfo / TrackInfo.

Other Simplifications

  • _set_date_str (mutable, side-effecting) is replaced by _get_date,
    a pure function returning (year, month, day).
  • _preferred_release_event and _preferred_alias are simplified.
  • Three @cached_property entries (ignored_media,
    ignore_data_tracks, ignore_video_tracks) are moved up to sit with
    other class-level properties.

Test Infrastructure Changes

Factory Layer (test/plugins/factories/musicbrainz.py)

factory-boy / pytest-factoryboy are added as test dependencies. A
new factory module introduces composable, deterministic factories:

  • AliasFactory, ArtistFactory, ArtistCreditFactory
  • RecordingFactory, TrackFactory, MediumFactory
  • ReleaseGroupFactory, ReleaseFactory

Factories use a shared _IdFactory base class that generates stable,
predictable UUIDs (00000000-0000-0000-0000-000000001001, etc.) based
on an id_base + index pair. This makes assertions on IDs readable
and deterministic without hard-coded magic strings.

Before:

recording = {
    "title": "foo",
    "id": "bar",
    "length": 42,
    "artist_credit": [{"artist": {"name": "some-artist", "id": "some-id", ...}, ...}],
    ...
}

After:

recording = recording_factory(title="foo", length=42)

Test Consolidation

Many small single-field assertion tests that each constructed an
identical release are collapsed into a single test_parse_release
snapshot assertion, covering all AlbumInfo fields at once. This
reduces redundant fixture setup and makes it immediately obvious what
the full output of album_info looks like for a default release.

The hand-rolled _make_release / _make_recording helpers are removed
entirely, replaced by composable factory calls using factory-boy's __
double-underscore traversal syntax:

# Before
release = self._make_release(date="1987-03-31")

# After
release = release_factory(release_group__first_release_date="1987-03-31")

nightly-06f5e14b-ls271

18 Apr 15:42
6adee69

Choose a tag to compare

Pre-release

CI Report:

https://ci-tests.linuxserver.io/linuxserver/beets/nightly-06f5e14b-ls271/index.html

LinuxServer Changes:

Full Changelog: nightly-342a7c0e-ls270...nightly-06f5e14b-ls271

Remote Changes:

Call remux_mpeglayer3_wav proactively in read_item (#6544)

Follow-up to #6526. This change moves the remux_mpeglayer3_wav call to
before library.Item.from_path in read_item, so beets no longer
depends on mediafile raising FileTypeError for MPEGLAYER3 WAV files.
This is related to beetbox/mediafile#107.

2.9.0-ls325

17 Apr 19:48
da3d5f7

Choose a tag to compare

CI Report:

https://ci-tests.linuxserver.io/linuxserver/beets/2.9.0-ls325/index.html

LinuxServer Changes:

Full Changelog: 2.9.0-ls324...2.9.0-ls325

Remote Changes:

Updating PIP version of beets to 2.9.0

nightly-bacebe26-ls270

17 Apr 19:48
d1a2282

Choose a tag to compare

Pre-release

CI Report:

https://ci-tests.linuxserver.io/linuxserver/beets/nightly-bacebe26-ls270/index.html

LinuxServer Changes:

Full Changelog: nightly-bacebe26-ls269...nightly-bacebe26-ls270

Remote Changes:

Add keep_synced option to lyrics plugin (#6538)

lyrics: Add keep_synced option to protect synced lyrics from

being overwritten

Adds a keep_synced config option and --keep-synced CLI flag to the
lyrics plugin. When enabled, tracks that already have synced lyrics are
skipped during fetching — even if force is set.

Motivation: Libraries with a mix of synced and plain lyrics can use
force to fill gaps without risking overwriting the higher-quality
synced entries.

Fixes: #5249

This is another widely requested issue with many reactions from users.

nightly-bacebe26-ls269

17 Apr 10:15
8209990

Choose a tag to compare

Pre-release

CI Report:

https://ci-tests.linuxserver.io/linuxserver/beets/nightly-bacebe26-ls269/index.html

LinuxServer Changes:

Full Changelog: nightly-1d01689a-ls268...nightly-bacebe26-ls269

Remote Changes:

Add keep_synced option to lyrics plugin (#6538)

lyrics: Add keep_synced option to protect synced lyrics from

being overwritten

Adds a keep_synced config option and --keep-synced CLI flag to the
lyrics plugin. When enabled, tracks that already have synced lyrics are
skipped during fetching — even if force is set.

Motivation: Libraries with a mix of synced and plain lyrics can use
force to fill gaps without risking overwriting the higher-quality
synced entries.

Fixes: #5249

This is another widely requested issue with many reactions from users.

nightly-342a7c0e-ls270

17 Apr 21:39
d1a2282

Choose a tag to compare

Pre-release

CI Report:

https://ci-tests.linuxserver.io/linuxserver/beets/nightly-342a7c0e-ls270/index.html

LinuxServer Changes:

No changes

Remote Changes:

fix(library): pass clutter to move command (#6540)

#This change fixes move and ensures that clutter options are correctly
passed through allowing beets to prune dirs with clutter in them.

Fixes: #5016

nightly-83017c06-ls267

14 Apr 20:11
1e2aac7

Choose a tag to compare

Pre-release

CI Report:

https://ci-tests.linuxserver.io/linuxserver/beets/nightly-83017c06-ls267/index.html

LinuxServer Changes:

Full Changelog: nightly-ff9f93e6-ls266...nightly-83017c06-ls267

Remote Changes:

docs: Added link and description to the beets-aisauce plugin (#6534)

see beetbox/beets#5922
and https://github.com/metasauce/beets-aisauce

nightly-80cd2155-ls268

15 Apr 20:05
f0a0d1a

Choose a tag to compare

Pre-release

CI Report:

https://ci-tests.linuxserver.io/linuxserver/beets/nightly-80cd2155-ls268/index.html

LinuxServer Changes:

No changes

Remote Changes:

inline: make the album/item available directly (#5439)

There have been multiple requests, in the past, for the ability to use
plugin fields in inline fields. This has not previously been available.
From what I can tell, it was intentionally left unavailable due to
performance concerns.

The way the item fields are made available to the inline python code
means that all fields are looked up, whether they're actually used by
the code or not. Doing that for all computed fields would be a
performance concern.

I don't believe there's a good way to postpone the field computation, as
python eval and compile requires that globals be a dictionary, not a
mapping. Instead, we can make available the album or item model object
to the code directly, and let the code access the fields it needs via
that object, resulting in postponing the computation of the fields until
they're actually accessed.

This is a simple approach that makes the computed and plugin fields
available to inline python, which allows for more code reuse, as well as
more options for shifting logic out of templates and into python code.
The object is available as db_obj.

Examples:

item_fields:
  test_file_size: db_obj.filesize

album_fields:
  test_album_path: db_obj.path
  # If the missing plugin is enabled
  test_album_missing: db_obj.missing

nightly-37d60dd2-ls268

16 Apr 10:11
f0a0d1a

Choose a tag to compare

Pre-release

CI Report:

https://ci-tests.linuxserver.io/linuxserver/beets/nightly-37d60dd2-ls268/index.html

LinuxServer Changes:

No changes

Remote Changes:

Populate extra artists for discogs (#6509)

Discogs: Populate multi-value contributor fields from artist roles

Fixes: #6380

This PR extends the Discogs plugin to properly populate beets'
remixers, lyricists, composers, and arrangers fields from
Discogs artist role credits.

What changed

beetsplug/discogs/states.py — Core logic change in ValidArtist
and ArtistState:

  • The is_feat: bool field on ValidArtist is replaced with role: str, preserving the raw Discogs role string rather than discarding it
    after a single boolean check.
  • A new main_artists property filters to artists with no role or a
    featuring role — this replaces the previous filtering that happened at
    parse time, so valid_artists now includes all artists regardless of
    role.
  • artists, artists_credit, and artists_ids are updated to source
    from main_artists instead of valid_artists, ensuring non-primary
    contributors (e.g. composers, arrangers) no longer appear in artist
    identity fields.
  • New properties — lyricists, arrangers, remixers, composers
    derive their values from valid_artists by substring-matching on the
    role field (e.g. "lyrics", "producer", "remix", "written-by").

beetsplug/discogs/types.pyArtistInfo typed dict gains four
optional fields: arrangers, composers, remixers, lyricists.

Test changes

  • test_anv is refactored from a flat parametrised function into a
    TestAnv class with a shared album_info fixture, splitting the
    assertions across focused test methods per field group.
  • test_parse_featured_artists gains assertions for artists_ids and
    composers.
  • A new test_parse_extraartist_roles test covers role-to-field mapping
    end-to-end for all four contributor types.

Impact

Discogs imports now populate contributor fields that were previously
only filled by the MusicBrainz plugin.

nightly-1d01689a-ls268

14 Apr 23:45
f0a0d1a

Choose a tag to compare

Pre-release

CI Report:

https://ci-tests.linuxserver.io/linuxserver/beets/nightly-1d01689a-ls268/index.html

LinuxServer Changes:

Full Changelog: nightly-83017c06-ls267...nightly-1d01689a-ls268

Remote Changes:

chroma: gate candidates on musicbrainz plugin being enabled (#6532)

Fixes #6212.
Fixes #6441.

Split out from #6522 per @semohr's suggestion.

The chroma plugin unconditionally instantiated its own private
MusicBrainzPlugin instance and called album_for_id /
track_for_id on it regardless of the user's plugin configuration.
This meant MusicBrainz-sourced candidates appeared during tagging even
when the user had not enabled the musicbrainz plugin.

This PR replaces the direct instantiation with a lookup through the
metadata-source registry (get_metadata_source("musicbrainz")).
When the musicbrainz plugin is not loaded, both candidates and
item_candidates short-circuit and return empty. Acoustid
fingerprinting itself is unaffected — acoustid_id and
acoustid_fingerprint fields are still populated as before.

Side-effect fix

The previous cached_property mb = MusicBrainzPlugin() pattern
created a second instance outside the plugin registry. This bypassed
any plugin that swaps the musicbrainz instance at runtime (e.g.
mbpseudo). Routing through get_metadata_source fixes this —
chroma now uses the same shared instance as the rest of beets.

Scope

This PR is intentionally scoped to the gating fix only. The richer
cross-reference routing (extracting Spotify/Discogs/etc. IDs from
MusicBrainz url-relations and dispatching to enabled external
plugins) is being developed separately in #6522.