Create a centralised pytest fixture to provide controllable stdin and
captured stdout in all tests. Simplify DummyIO/DummyIn and remove the
custom DummyOut implementation and make use of pytest builtin fixtures.
Create a centralised pytest fixture to provide controllable stdin and
captured stdout that can be applied to any tests, regardless whether
they are based on pytest or unittest.
* `io` fixture can be used as a fixture in pytest-based tests
* `IOMixin` can be used to attach `io` attribute to any test class,
including `unittest.TestCase`
Fixes#6355
- Fix `plugins/musicbrainz` handling of very large MusicBrainz releases
by passing the missing `limit` and `includes` kwargs to
`mb_api.browse_recordings`.
Fixes#6347
- Fixed MusicBrainz Lucene query formatting in
`MusicBrainzAPI.format_search_term()` (lowercase + trim + escape Lucene
special chars).
- Fixed `plugins.musicbrainz:extra_tags` support by mapping `alias` and
`tracks` into MusicBrainz search fields.
- Adjusted logging to make MusicBrainz API logs visible under the shared
`beets` logger (and removed an unused per-module logger in
`beetsplug.bpd`).
Fixes#6332
- Promotes `packaging` from a release-only dependency to a required
runtime dependency by moving it into `pyproject.toml`'s main
dependencies.
- Updates `poetry.lock` to pick up patched versions of vulnerable
libraries, notably `brotli` (`1.1.0` → `1.2.0`), `urllib3` (`2.5.0` →
`2.6.3`), `werkzeug` (`3.1.3` → `3.1.5`), and `filelock` (`3.20.2` →
`3.20.3`).
<img width="442" height="169" alt="image"
src="https://github.com/user-attachments/assets/fe70475e-0163-4d45-b1e4-362008669c00"
/>
Fixes#6291
I think it does not hurt if the imported_items() method returned an
empty list instead of an throwing an exception if there is nothing in
it!
When a metadata plugin raises an exception during the auto-tagger
process, the entire operation crashes. This behavior is not desirable,
since metadata lookups can legitimately fail for various reasons (e.g.,
temporary API downtime, network issues, or offline usage).
This PR introduces a safeguard by adding general exception handling
around metadata plugin calls. Instead of causing the whole process to
fail, exceptions from individual plugins are now caught and logged. This
ensures that the auto-tagger continues to function with the remaining
available metadata sources. I used a proxy pattern here as this
seems like an elegant solution to me.
This replaces the efforts from #5910
- Update the MusicBrainz integration to read the release group's
canonical `'primary-type'` field instead of the legacy `'type'` field
when deriving `info.albumtype`.
Noticed that `albumtype` was missing from the metadata when I did some
imports in my library.
Fixes a bug where existing tags were set to None, if they weren't whitelisted, but an whitelisted canonicalized parent existed up the tree.
In all other cases, the original genres are canonicalized and considered for the final genre, except in the keep_existing logic branch.
This PR fixes the issue and results in the expected behavior for this combination of options.
For the bug to trigger several conditions had to be met:
- Canonicalization is enabled and a whitelist is specified.
- `force` and `keep_existing` are set. Meaning, that Lastfm is queried for a genre, but the existing genres are still left around when none are found online.
- A release with a non-whitelisted genre exists, but that genre has a whitelisted genre parent up the tree.
- That very release has no genre on lastfm.
This is rather convoluted, but stay with me :D
What would happen is the following:
- `keep_genres` is set to the existing genres, as `force` and `keep_existing` is set.
- Genres for `track`/`album`/`artist` aren't found for this release, as they don't exist in lastfm.
- Then the `keep_existing` logic is entered.
- The old logic only checks if the existing genres have an **exact** match for the whitelist. In contrast to all other code branches, we don't do the `_try_resolve_stage` in case there's no direct match, resulting in no match.
- We continue to the fallback logic, which returns the fallback (`None` in my case)
This patch results in one last try to resolve the existing genres when `keep_existing` is set, which includes canonicalization (if enabled).
Creating indexes turned out to be relatively straightforward!
While we can’t remove them yet, that doesn’t seem necessary for now.
Interestingly, much of the infrastructure for database additions was
already in place.
- This PR introduces a new type, `Index`, which can be used to create an
index on any defined table. 🎉
- The `items` table now automatically registers an index on `album_id`
Closes#5809
The `beets/random.py` module was only used by the random plugin, so I
moved its functions into `beetsplug/random.py` to keep core modules
cleaner.
Changes:
- Moved beets/random.py functions into beetsplug/random.py
- Added typehints for better readability and tooling support
- Added additional tests for improved coverage
- General tidy up and refactor, keeping the core functionality unchanged
- Handle list-valued fields when grouping for --field/--equal-chance to avoid
"TypeError: unhashable type: 'list'" (e.g., artists).
- Sort items by the grouping key before building groups so equal-chance
permutation preserves the same item set as `beet list`, only randomized.