Consolidate multiple granular test methods in ApplyTest into a single
comprehensive test that validates all applied metadata at once. This
improves test maintainability and clarity by:
- Replacing ~20 individual test methods with one data-driven test
- Using expected data dictionaries to validate all fields together
- Removing ApplyCompilationTest class (covered by va=True in main test)
- Keeping focused tests for edge cases (artist_credit, date handling)
- Switching from BeetsTestCase to standard TestCase for speed
- Adding operator import for efficient data extraction
The new approach makes it easier to validate all applied metadata at once.
Fixes a bug in the lastgenre plugin, where test state bled into the
following fixtures.
Each plugin has a view to the global persisted beets.config field. As a
result, config variables that aren't explicitly overwritten are
persisted in that global config view.
This commit exposes the lastgenre default config as a static method and
uses that default config to reset the state in between fixture calls.
There were 3 tests that depended on `count: 10` being set on previous
test fixtures, which I adjusted accordingly.
Discovered and discussed in #6317 , see
https://github.com/beetbox/beets/pull/6317#issuecomment-3935462408
When `import.delete` or `import.move` is enabled, the `assign_art`
method calls `task.prune(candidate.path)` unconditionally. This
incorrectly deletes the configured `fetchart.fallback` file. Add
explicit check to skip pruning when the candidate path matches the
configured fallback.
When `import.delete` or `import.move` is enabled, the `assign_art` method calls `task.prune(candidate.path)` unconditionally.
This incorrectly deletes the configured `fetchart.fallback` file.
Add explicit check to skip pruning when the candidate path matches the configured fallback.
When `omit_single_disc` is set, `disctotal` is now also zeroed alongside
`disc`. These tags work together ("Disc 2 of 3") so keeping one without
the other is inconsistent.
Previously, only the `disc` tag was zeroed. This follows from #6015
which made it into v2.5.1 and added the `omit_single_disc` option.
I've added tests and have used this locally for some time.
This was inspired by real life events:
https://musicbrainz.org/release/7c4d7a15-6b30-4bef-8b20-af200186fbdb by
the artist Danny L Harle has a a track with a featuring list that
contains "Danny L Harle, Oklou & MNEK".
Before:
```
artist = Danny L Harle, Oklou
track = Crystallise My Tears feat. MNEK
```
After:
```
artist = Danny L Harle
track = Crystallise My Tears feat. Oklou & MNEK
```
👋🏻 I was trying to set up a local dev environment and noticed a couple
of tests were failing. On macOS, temporary files are created under
`/var`, which is itself a symlink to `/private/var`. This PR resolves
the `assert`s against temp file paths in tests.
Add documentation for the `match.distance_weights` configuration option
in the autotagger matching section of the reference docs.
The section includes:
- Description of what distance weights control
- Complete list of all available fields with their default values
- Example showing how to customize a specific weight
- Note that only overridden fields need to be specified
Closes#6081
Add documentation for the distance_weights configuration option in
the autotagger matching section. This includes all available fields
with their default values and an example of how to customize them.
Closes#6081
Signed-off-by: edvatar <88481784+toroleapinc@users.noreply.github.com>
## Add support for a multi-valued `genres` field
- Update metadata source plugins to populates `genres` instead of
`genre`: `musicbrainz`, `beatport`, `discogs`.
- Remove now redundant `separator` configuration from `lastgenre`.
### Context
We previously had multiple issues with maintaining both _singular_ and
_plural_ fields:
1. Since both fields write and read the same field in music files, the
values in both
fields must be carefully synchronised, otherwise we see these fields
being repeatedly
retagged / rewritten using commands such as `beet write`. See [related
issues](https://github.com/beetbox/beets/issues?q=label%3A"multi%20tags%22)
2. Fixes to sync logic required users manually retagging their
libraries, while music
imported _as-is_ could not be fixed. See #5540, for example.
Therefore, this PR replaces a singular `genre` field by plural `genres`
_for good_:
1. We migrate `genre` -> `genres` immediately on the first `beets`
invocation
2. `genre` field is removed and `genres` is added
3. The old `genre` column in the database is left in place - these
values will be ignored
by beets.
- If someone migrates and later decides to switch back to using an older
version of
beets, their `genre` values are still in place.
### Migration
- This PR creates a new DB table `migrations(name TEXT, table TEXT)`
- We add an entry when a migration has been fully performed on a
specific table
- Thus we only perform the migration if we don't have an entry for that
table
- Entry is only added when the migration has been performed **fully**:
if someone hits
CTRL-C during the migration, the migration will continue on the next
beets invocation,
see:
```py
def migrate_table(self, table: str, *args, **kwargs) -> None:
"""Migrate a specific table."""
if not self.db.migration_exists(self.name, table):
self._migrate_data(table, *args, **kwargs)
self.db.record_migration(self.name, table)
```
- Implemented using SQL due to:
1. Significant speed difference: migrating my 9000 tracks / 2000 albums
library:
- Using our Python implementation: over 11 minutes
- Using SQL: 2 seconds
2. Beets seeing only `genres` field: `genre` field is only accessible by
querying the
database directly.
Supersedes: #6169
* Move genre-to-genres migration into a dedicated Migration class and
wire it into Library._migrations for items and albums.
* Add batched SQL updates via mutate_many and share the multi-value
delimiter as a constant.
* Cover migration behavior with new tests.
I initially attempted to migrate using our model infrastructure
/ Model.store(), see the comparison below:
Durations migrating my library of ~9000 items and ~2300 albums:
1. Using our Python logic: 11 minutes
2. Using SQL directly: 4 seconds
That's why I've gone ahead with option 2.
Migration now happens automatically when the database schema is
updated (in Library._make_table()), so the manual 'beet migrate'
command is no longer needed.
Addresses PR review comment.
- Add Library._make_table() override to automatically migrate genres when database schema is updated
- Migration splits comma/semicolon/slash-separated genre strings into genres list
- Writes changes to both database and media files with progress reporting
- Remove lazy migration from correct_list_fields() - now handled at database level
- Remove migration-specific tests (migration is now automatic, not lazy)
- Update changelog to reflect automatic migration behavior
Related PR review comment changes:
- Replace _is_valid with _filter_valid method in lastgenre plugin
- Use unique_list and remove genre field from Beatport plugin
- Simplify LastGenre tests - remove separator logic
- Document separator deprecation in lastgenre plugin
- Add deprecation warning for genre parameter in Info.__init__()