Commit graph

13609 commits

Author SHA1 Message Date
J0J0 Todos
532b0dabb1 Update my teams page entry 2026-03-02 08:31:44 +01:00
Šarūnas Nejus
53119fc581
docs: Document match.distance_weights in autotagger config (#6398)
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
2026-03-01 12:37:42 +00:00
Serene
aa81232336 Use proper syntax highlighting for code block
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2026-03-01 11:46:44 +00:00
Serene-Arc
dd1bda4bd0 Format docs 2026-03-01 11:46:44 +00:00
edvatar
a40bd7ca3c docs: Document match.distance_weights in autotagger docs
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>
2026-03-01 11:46:44 +00:00
Šarūnas Nejus
573dca687a
Fix add multiple genres (#6401)
Only set new fields when we create a new table.
2026-02-28 10:12:11 +00:00
Šarūnas Nejus
085ff1267b
Only add fields when we create new table 2026-02-28 10:06:31 +00:00
Šarūnas Nejus
16be1df940
Add multiple genres (#6367)
## 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
2026-02-27 18:42:46 +00:00
Šarūnas Nejus
a540a8174a
Clarify tests 2026-02-27 18:36:04 +00:00
Šarūnas Nejus
10d13992e6
Dedupe genres parsing in beatport 2026-02-27 18:36:04 +00:00
Šarūnas Nejus
2c63fe77ce
Remove test case indices from test_lastgenre.py 2026-02-27 18:36:04 +00:00
Šarūnas Nejus
62e232983a
Document ordering of the genre split separator 2026-02-27 18:36:04 +00:00
Šarūnas Nejus
67cf15b0bd
Remove lastgenre separator config 2026-02-27 18:36:04 +00:00
Šarūnas Nejus
6f886682ea
Update changelog note 2026-02-27 18:36:04 +00:00
Šarūnas Nejus
52375472e8
Replace genre: with genres: in docs 2026-02-27 18:34:26 +00:00
Šarūnas Nejus
a8d53f78de
Fix the rest of the tests 2026-02-27 18:34:26 +00:00
Šarūnas Nejus
5d7fb4e158
Remove genre field 2026-02-27 18:34:26 +00:00
Šarūnas Nejus
b8f1b9d174
Stop overwriting this test file name 2026-02-27 18:34:26 +00:00
Šarūnas Nejus
cf36ed0754
Only handle multiple genres in discogs 2026-02-27 18:34:26 +00:00
Šarūnas Nejus
4dda8e3e49
Fix deprecation warning 2026-02-27 18:24:54 +00:00
Šarūnas Nejus
2ecbe59f48
Add migration for multi-value genres field
* 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.
2026-02-27 18:24:54 +00:00
Šarūnas Nejus
8edd0fc966
Add generic Migration implementation 2026-02-27 18:24:54 +00:00
Šarūnas Nejus
eac9e1fd97
Add support for migrations 2026-02-27 18:24:54 +00:00
dunkla
0191ecf576
Fix mypy incompatible return type in lastgenre 2026-02-27 18:24:54 +00:00
dunkla
10d197e242
Update lastgenre docstring and remove misleading comment (ref https://github.com/beetbox/beets/pull/6169#issuecomment-3716893013) 2026-02-27 18:24:54 +00:00
dunkla
9003107ee7
Remove noisy comments from beatport tests (ref https://github.com/beetbox/beets/pull/6169#issuecomment-3716893013) 2026-02-27 18:24:54 +00:00
dunkla
67ce53d2c6
Remove conditional logic from lastgenre tests (ref https://github.com/beetbox/beets/pull/6169#issuecomment-3716893013) 2026-02-27 18:24:54 +00:00
dunkla
9224c9c960
Use compact generator expression in Beatport (ref https://github.com/beetbox/beets/pull/6169#issuecomment-3716893013) 2026-02-27 18:24:54 +00:00
dunkla
21fb5a561d
Fix lastgenre migration separator logic (ref https://github.com/beetbox/beets/pull/6169#issuecomment-3716893013) 2026-02-27 18:24:54 +00:00
dunkla
76c4eeedbb
Remove manual migrate command
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.
2026-02-27 18:24:54 +00:00
dunkla
e99d3ca061
Simplify MusicBrainz genres assignment
Remove intermediate variable and assign directly to info.genres.
Addresses PR review comment.
2026-02-27 18:24:54 +00:00
dunkla
36a30b3c65
Implement automatic database-level genre migration
- 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__()
2026-02-27 18:24:54 +00:00
dunkla
99831906c2
simplify check for fallback in beetsplug/lastgenre/__init__.py
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2026-02-23 05:11:37 +00:00
dunkla
07a3cba262
simplify genre unpacking in beetsplug/lastgenre/__init__.py
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2026-02-23 05:11:37 +00:00
dunkla
9922f8fb99
simplify return logic in beetsplug/lastgenre/__init__.py
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2026-02-23 05:11:37 +00:00
dunkla
4e30c181c6
better function description in beetsplug/lastgenre/__init__.py
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2026-02-23 05:11:37 +00:00
dunkla
c55b6d103c
shorte test description in test/plugins/test_lastgenre.py
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2026-02-23 05:11:37 +00:00
dunkla
1c0ebcf348
remove noisy comment from test/plugins/test_beatport.py
Co-authored-by: Šarūnas Nejus <snejus@protonmail.com>
2026-02-23 05:11:36 +00:00
Johann Fot
70bf57baf6
Add native support for multiple genres per album/track
Simplify multi-genre implementation based on maintainer feedback (PR #6169).

Changes:
- Remove multi_value_genres and genre_separator config options
- Replace complex sync_genre_fields() with ensure_first_value('genre', 'genres')
- Update all plugins (Beatport, MusicBrainz, LastGenre) to always write genres as lists
- Add automatic migration for comma/semicolon/slash-separated genre strings
- Add 'beet migrate genres' command for explicit batch migration with --pretend flag
- Update all tests to reflect simplified approach (44 tests passing)
- Update documentation

Implementation aligns with maintainer vision of always using multi-value genres
internally with automatic backward-compatible sync to the genre field via
ensure_first_value(), eliminating configuration complexity.

Migration strategy avoids problems from #5540:
- Automatic lazy migration on item access (no reimport/mbsync needed)
- Optional batch migration command for user control
- No endless rewrite loops due to proper field synchronization
2026-02-23 05:11:36 +00:00
Šarūnas Nejus
dcef1f4ea4
Create .instructions.md for Copilot
Let's test this
2026-02-23 05:09:03 +00:00
Šarūnas Nejus
fa564a05a1
Add @snejus to docs/team.rst (#6389)
I thought it was time to add an entry for myself to `docs/team.rst`.
2026-02-23 04:01:23 +00:00
Šarūnas Nejus
6fe7a9a7d3
Add @snejus to team 2026-02-23 03:54:20 +00:00
Šarūnas Nejus
2ea7886c0c
Fix handling multi valued fields (#6387)
While working on #6367 I noticed that users are currently required to
use our internal separator `\␀` in order to edit multi-valued fields,
for example `beet modify artists='a\␀b'`.

Similarly, this separator is used in output, for example, reporting of
field changes:

```
$ beet modify path::aaa artists='a\␀b'
Modifying 8 items.
54898 | 2022 / RAVE SLUTZ: Fallen Shrine & dj Christian NXC - DEEEJAAAY
  artists: Fallen Shrine\␀dj Christian NXC -> a\␀b
```

This PR replaces `\␀` separator with `; ` for input and formats changes
in multi-valued fields clearly:

```
$ beet modify path::aaa artists='a; b'
54898 | 2022 / RAVE SLUTZ: Fallen Shrine & dj Christian NXC - DEEEJAAAY
  artists:
    - Fallen Shrine
    - dj Christian NXC
    + a
    + b
```
<img width="539" height="142" alt="image"
src="https://github.com/user-attachments/assets/72299db1-d0f8-4f8f-9f30-65caaac85d9e"
/>


### Architecture-level changes

- `DelimitedString` now separates concerns between:
  - database serialization via `db_delimiter` (`to_sql`)
- user-facing/template formatting via a fixed `'; '` delimiter
(`format`)
  - parsing that accepts both DB and user-facing separators (`parse`)
- Field diff rendering now has a dedicated path for list fields:
  - `_field_diff` detects list values
- `_multi_value_diff` computes set-based added/removed entries and
renders per-item diff lines
- Coloring responsibilities were streamlined:
  - raw ANSI application moved to `_colorize`
  - `colorize` is now only the feature-flag/environment gate
- `colordiff` is reduced to string diff highlighting logic, with
redundant wrapper logic removed

### High-level impact

- Multi-valued fields behave consistently between DB storage and
CLI/template usage (`'; '` for user input/output, DB delimiter
internally).
- Diff output for list fields is much more readable, showing explicit
`+`/`-` item-level changes instead of generic string diffs.
- Docs and tests were updated to reflect the new multi-value behavior,
including `%first` usage and `beet modify` examples.
2026-02-23 01:14:28 +00:00
Šarūnas Nejus
edfe00516f
Handle DelimitedString fields as native lists in edit plugin
Treat DelimitedString as a safe YAML-editable type in the edit plugin,
allowing multi-valued fields to be edited as native lists.
2026-02-23 00:33:24 +00:00
Šarūnas Nejus
4699958f25
Remove redundant coloring logic 2026-02-22 16:13:21 +00:00
Šarūnas Nejus
31f79f14a3
Colorize multi-valued field changes distinctly 2026-02-22 16:13:20 +00:00
Šarūnas Nejus
9d237d10fc
Fix multi-value delimiter handling in templates
- Use '\␀' as the DB delimiter while formatting lists with '; ' for
templates.
- Update DelimitedString parsing to accept both separators:
  * '\␀' for the values from the DB
  * '; ' for the rest of parsed values (for example `beet modify genres="eletronic; jazz"`)
- Refresh %first docs and tests to reflect multi-value field behavior.
2026-02-22 16:12:58 +00:00
snejus
13e978ca0e Increment version to 2.6.2 2026-02-22 16:04:40 +00:00
Šarūnas Nejus
940e94c48c
Fix missing artist-credit field in mbpseudo (#6388)
Fixes #6339

This change moves MusicBrainz lookup defaults into
`beetsplug/_utils/musicbrainz.py` so `MusicBrainzAPI` now owns the
default `includes` for both `get_release` and `get_recording`.

#### Architecture impact

- Added centralized defaults: `RELEASE_INCLUDES` and
`RECORDING_INCLUDES` in `MusicBrainzAPI`.
- Updated `get_release` and `get_recording` to apply these defaults via
`kwargs.setdefault("includes", ...)`.
- Removed duplicated include lists from `beetsplug/musicbrainz.py` and
simplified call sites to `self.mb_api.get_release(...)` /
`self.mb_api.get_recording(...)` without passing includes explicitly.

#### High-level result

- Ensures consistent MusicBrainz payload shape across callers.
- Fixes the `mbpseudo` failure caused by missing `artist_credit` data in
some responses.
- Reduces coupling and duplication by making include policy an API-layer
concern.
2026-02-22 11:55:17 +00:00
Šarūnas Nejus
1930400ab8
Set default release/recording includes in MusicBrainzAPI 2026-02-22 02:09:25 +00:00