Skip to content

Commit 392962e

Browse files
committed
Migrate arranger -> arrangers
1 parent 1459201 commit 392962e

13 files changed

Lines changed: 95 additions & 68 deletions

File tree

beets/autotag/hooks.py

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -136,17 +136,18 @@ class Info(AttrDict[Any]):
136136

137137
IGNORED_FIELDS: ClassVar[set[str]] = {"data_url"}
138138
MEDIA_FIELD_MAP: ClassVar[dict[str, str]] = {}
139+
LEGACY_TO_LIST_FIELD: ClassVar[dict[str, str]] = {"genre": "genres"}
139140

140141
@cached_classproperty
141142
def nullable_fields(cls) -> set[str]:
142143
"""Return fields that may be cleared when new metadata is applied."""
143144
return set(config["overwrite_null"][cls.type.lower()].as_str_seq())
144145

145146
def __setitem__(self, key: str, value: Any) -> None:
146-
# handle info.genre = "abc" and info["genre"] = "abc"
147-
if key == "genre":
148-
self["genres"] = self._get_list_from_string_value(
149-
"genre", "genres", value, self["genres"]
147+
# handle legacy info.str_field = "abc" and info["str_field"] = "abc"
148+
if list_field := self.LEGACY_TO_LIST_FIELD.get(key):
149+
self[list_field] = self._get_list_from_string_value(
150+
key, list_field, value, self[list_field]
150151
)
151152
else:
152153
super().__setitem__(key, value)
@@ -387,23 +388,13 @@ class TrackInfo(Info):
387388
"track_id": "mb_trackid",
388389
"medium_index": "track",
389390
}
390-
391-
def __setitem__(self, key: str, value: Any) -> None:
392-
# handle info.remixer = "abc" and info["remixer"] = "abc"
393-
if key == "remixer":
394-
self["remixers"] = self._get_list_from_string_value(
395-
"remixer", "remixers", value, self["remixers"]
396-
)
397-
elif key == "lyricist":
398-
self["lyricists"] = self._get_list_from_string_value(
399-
"lyricist", "lyricists", value, self["lyricists"]
400-
)
401-
elif key == "composer":
402-
self["composers"] = self._get_list_from_string_value(
403-
"composer", "composers", value, self["composers"]
404-
)
405-
else:
406-
super().__setitem__(key, value)
391+
LEGACY_TO_LIST_FIELD: ClassVar[dict[str, str]] = {
392+
**Info.LEGACY_TO_LIST_FIELD,
393+
"remixer": "remixers",
394+
"lyricist": "lyricists",
395+
"composer": "composers",
396+
"arranger": "arrangers",
397+
}
407398

408399
@property
409400
def id(self) -> str | None:
@@ -438,7 +429,7 @@ def raw_data(self) -> JSONDict:
438429
def __init__(
439430
self,
440431
*,
441-
arranger: str | None = None,
432+
arrangers: list[str] | None = None,
442433
bpm: str | None = None,
443434
composers: list[str] | None = None,
444435
composer_sort: str | None = None,
@@ -460,7 +451,7 @@ def __init__(
460451
work_disambig: str | None = None,
461452
**kwargs,
462453
) -> None:
463-
self.arranger = arranger
454+
self.arrangers = arrangers
464455
self.bpm = bpm
465456
self.composers = composers
466457
self.composer_sort = composer_sort

beets/library/library.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class Library(dbcore.Database):
2626
(migrations.MultiRemixerFieldMigration, (Item,)),
2727
(migrations.MultiLyricistFieldMigration, (Item,)),
2828
(migrations.MultiComposerFieldMigration, (Item,)),
29+
(migrations.MultiArrangerFieldMigration, (Item,)),
2930
)
3031

3132
def __init__(

beets/library/migrations.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,13 @@ class MultiComposerFieldMigration(MultiValueFieldMigration):
131131
list_field = "composers"
132132

133133

134+
class MultiArrangerFieldMigration(MultiValueFieldMigration):
135+
"""Backfill multi-value arrangers from legacy single-string arranger data."""
136+
137+
str_field = "arranger"
138+
list_field = "arrangers"
139+
140+
134141
class LyricsRow(NamedTuple):
135142
id: int
136143
lyrics: str

beets/library/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,7 @@ class Item(LibModel):
662662
"work": types.STRING,
663663
"mb_workid": types.STRING,
664664
"work_disambig": types.STRING,
665-
"arranger": types.STRING,
665+
"arrangers": types.MULTI_VALUE_DSV,
666666
"grouping": types.STRING,
667667
"year": types.PaddedInt(4),
668668
"month": types.PaddedInt(2),

beets/test/_common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def item(lib=None, **kwargs):
7878
genres=["the genre"],
7979
lyricists=["the lyricist"],
8080
composers=["the composer"],
81-
arranger="the arranger",
81+
arrangers=["the arranger"],
8282
grouping="the grouping",
8383
work="the work title",
8484
mb_workid="the work musicbrainz id",

beetsplug/musicbrainz.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -399,14 +399,14 @@ def track_info(
399399
info.composers = composer
400400
info.composer_sort = ", ".join(composer_sort)
401401

402-
arranger = []
402+
arrangers = []
403403
for artist_relation in recording.get("artist-relations", ()):
404404
if "type" in artist_relation:
405405
type = artist_relation["type"]
406406
if type == "arranger":
407-
arranger.append(artist_relation["artist"]["name"])
408-
if arranger:
409-
info.arranger = ", ".join(arranger)
407+
arrangers.append(artist_relation["artist"]["name"])
408+
if arrangers:
409+
info.arrangers = arrangers
410410

411411
# Supplementary fields provided by plugins
412412
extra_trackdatas = plugins.send("mb_track_extract", data=recording)

docs/changelog.rst

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ New features
2222
deprecate ``overwrite``.
2323
- :doc:`plugins/autobpm`: The "BPM already exists for item" log message can now
2424
be hidden with the ``--quiet`` flag.
25-
- Store track remixers, lyricists, and composers in the multi-value
26-
``remixers``, ``lyricists``, and ``composers`` fields instead of the legacy
27-
single-value ``remixer``, ``lyricist``, and ``composer`` fields. Existing
28-
libraries are migrated automatically, and :doc:`plugins/musicbrainz` now
29-
preserves each MusicBrainz ``remixer``, ``lyricist``, and ``composer``
30-
relation as a separate value.
25+
- Store track remixers, lyricists, composers, and arrangers in the multi-value
26+
``remixers``, ``lyricists``, ``composers``, and ``arrangers`` fields instead
27+
of the legacy single-value ``remixer``, ``lyricist``, ``composer``, and
28+
``arranger`` fields. Existing libraries are migrated automatically, and
29+
:doc:`plugins/musicbrainz` now preserves each MusicBrainz ``remixer``,
30+
``lyricist``, ``composer``, and ``arranger`` relation as a separate value.
3131

3232
Bug fixes
3333
~~~~~~~~~
@@ -43,9 +43,13 @@ Bug fixes
4343
prepending the full combined artist credit as the first element for
4444
multi-artist releases. :bug:`6470`
4545

46-
..
47-
For plugin developers
48-
~~~~~~~~~~~~~~~~~~~~~
46+
For plugin developers
47+
~~~~~~~~~~~~~~~~~~~~~
48+
49+
- If you maintain a metadata source plugin that populates any of ``arranger``,
50+
``composer``, ``lyricist``, ``remixer`` fields, update it to populate the
51+
respective multi-value fields instead (``arrangers``, ``composers``,
52+
``lyricists``, ``remixers``).
4953

5054
..
5155
Other changes

docs/reference/cli.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,8 @@ artist sort name into the artist field for all your tracks, and ``beet modify
268268
title='$track $title'`` will add track numbers to their title metadata.
269269

270270
To adjust a multi-valued field, such as ``genres``, ``remixers``, ``lyricists``,
271-
or ``composers``, separate the values with |semicolon_space|. For example,
272-
``beet modify genres="rock; pop"``.
271+
``composers``, or ``arrangers``, separate the values with |semicolon_space|. For
272+
example, ``beet modify genres="rock; pop"``.
273273

274274
The ``-a`` option changes to querying album fields instead of track fields and
275275
also enables to operate on albums in addition to the individual tracks. Without

docs/reference/pathformat.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ These functions are built in to beets:
8080
then returns the next ``count`` items joined by ``join``.
8181

8282
This is especially useful for multi-valued fields like ``artists``,
83-
``genres``, ``remixers``, ``lyricists``, or ``composers`` where you may only
84-
want the first value or a limited number of values in a path.
83+
``genres``, ``remixers``, ``lyricists``, ``composers``, or ``arrangers`` where
84+
you may only want the first value or a limited number of values in a path.
8585

8686
Defaults:
8787

@@ -236,6 +236,7 @@ Ordinary metadata:
236236
- albumartist_credit
237237
- genre
238238
- genres: The track genres as a multi-valued field.
239+
- arrangers: The track arrangers as a multi-valued field.
239240
- composers: The track composers as a multi-valued field.
240241
- lyricists: The track lyricists as a multi-valued field.
241242
- remixers: The track remixers as a multi-valued field.

docs/reference/query.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ Recall that ``-a`` makes the ``list`` command show albums instead of individual
9696
tracks, so this command shows me all the releases I have from this year.
9797

9898
For multi-valued tags (such as ``artists``, ``albumartists``, ``remixers``,
99-
``lyricists``, or ``composers``), a regular expression search must be used to
100-
search for a single value within the multi-valued tag.
99+
``lyricists``, ``composers``, or ``arrangers``), a regular expression search
100+
must be used to search for a single value within the multi-valued tag.
101101

102102
Note that you can filter albums by querying tracks fields and vice versa:
103103

0 commit comments

Comments
 (0)