mirror of
https://github.com/beetbox/beets.git
synced 2025-12-06 16:42:42 +01:00
Merge branch 'master' into metadata_option
This commit is contained in:
commit
29f55f9911
5 changed files with 108 additions and 8 deletions
|
|
@ -271,10 +271,9 @@ class MetadataSourcePlugin(BeetsPlugin, metaclass=abc.ABCMeta):
|
||||||
"""Returns an artist string (all artists) and an artist_id (the main
|
"""Returns an artist string (all artists) and an artist_id (the main
|
||||||
artist) for a list of artist object dicts.
|
artist) for a list of artist object dicts.
|
||||||
|
|
||||||
For each artist, this function moves articles (such as 'a', 'an',
|
For each artist, this function moves articles (such as 'a', 'an', and 'the')
|
||||||
and 'the') to the front and strips trailing disambiguation numbers. It
|
to the front. It returns a tuple containing the comma-separated string
|
||||||
returns a tuple containing the comma-separated string of all
|
of all normalized artists and the ``id`` of the main/first artist.
|
||||||
normalized artists and the ``id`` of the main/first artist.
|
|
||||||
Alternatively a keyword can be used to combine artists together into a
|
Alternatively a keyword can be used to combine artists together into a
|
||||||
single string by passing the join_key argument.
|
single string by passing the join_key argument.
|
||||||
|
|
||||||
|
|
@ -298,8 +297,6 @@ class MetadataSourcePlugin(BeetsPlugin, metaclass=abc.ABCMeta):
|
||||||
if not artist_id:
|
if not artist_id:
|
||||||
artist_id = artist[id_key]
|
artist_id = artist[id_key]
|
||||||
name = artist[name_key]
|
name = artist[name_key]
|
||||||
# Strip disambiguation number.
|
|
||||||
name = re.sub(r" \(\d+\)$", "", name)
|
|
||||||
# Move articles to the front.
|
# Move articles to the front.
|
||||||
name = re.sub(r"^(.*?), (a|an|the)$", r"\2 \1", name, flags=re.I)
|
name = re.sub(r"^(.*?), (a|an|the)$", r"\2 \1", name, flags=re.I)
|
||||||
# Use a join keyword if requested and available.
|
# Use a join keyword if requested and available.
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,8 @@ TRACK_INDEX_RE = re.compile(
|
||||||
re.VERBOSE,
|
re.VERBOSE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
DISAMBIGUATION_RE = re.compile(r" \(\d+\)")
|
||||||
|
|
||||||
|
|
||||||
class ReleaseFormat(TypedDict):
|
class ReleaseFormat(TypedDict):
|
||||||
name: str
|
name: str
|
||||||
|
|
@ -96,6 +98,7 @@ class DiscogsPlugin(MetadataSourcePlugin):
|
||||||
"separator": ", ",
|
"separator": ", ",
|
||||||
"index_tracks": False,
|
"index_tracks": False,
|
||||||
"append_style_genre": False,
|
"append_style_genre": False,
|
||||||
|
"strip_disambiguation": True,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.config["apikey"].redact = True
|
self.config["apikey"].redact = True
|
||||||
|
|
@ -362,15 +365,20 @@ class DiscogsPlugin(MetadataSourcePlugin):
|
||||||
|
|
||||||
label = catalogno = labelid = None
|
label = catalogno = labelid = None
|
||||||
if result.data.get("labels"):
|
if result.data.get("labels"):
|
||||||
label = result.data["labels"][0].get("name")
|
label = self.strip_disambiguation(
|
||||||
|
result.data["labels"][0].get("name")
|
||||||
|
)
|
||||||
catalogno = result.data["labels"][0].get("catno")
|
catalogno = result.data["labels"][0].get("catno")
|
||||||
labelid = result.data["labels"][0].get("id")
|
labelid = result.data["labels"][0].get("id")
|
||||||
|
|
||||||
cover_art_url = self.select_cover_art(result)
|
cover_art_url = self.select_cover_art(result)
|
||||||
|
|
||||||
# Additional cleanups (various artists name, catalog number, media).
|
# Additional cleanups
|
||||||
|
# (various artists name, catalog number, media, disambiguation).
|
||||||
if va:
|
if va:
|
||||||
artist = config["va_name"].as_str()
|
artist = config["va_name"].as_str()
|
||||||
|
else:
|
||||||
|
artist = self.strip_disambiguation(artist)
|
||||||
if catalogno == "none":
|
if catalogno == "none":
|
||||||
catalogno = None
|
catalogno = None
|
||||||
# Explicitly set the `media` for the tracks, since it is expected by
|
# Explicitly set the `media` for the tracks, since it is expected by
|
||||||
|
|
@ -622,6 +630,14 @@ class DiscogsPlugin(MetadataSourcePlugin):
|
||||||
|
|
||||||
return tracklist
|
return tracklist
|
||||||
|
|
||||||
|
def strip_disambiguation(self, text: str) -> str:
|
||||||
|
"""Removes discogs specific disambiguations from a string.
|
||||||
|
Turns 'Label Name (5)' to 'Label Name' or 'Artist (1) & Another Artist (2)'
|
||||||
|
to 'Artist & Another Artist'. Does nothing if strip_disambiguation is False."""
|
||||||
|
if not self.config["strip_disambiguation"]:
|
||||||
|
return text
|
||||||
|
return DISAMBIGUATION_RE.sub("", text)
|
||||||
|
|
||||||
def get_track_info(self, track, index, divisions):
|
def get_track_info(self, track, index, divisions):
|
||||||
"""Returns a TrackInfo object for a discogs track."""
|
"""Returns a TrackInfo object for a discogs track."""
|
||||||
title = track["title"]
|
title = track["title"]
|
||||||
|
|
@ -634,6 +650,7 @@ class DiscogsPlugin(MetadataSourcePlugin):
|
||||||
artist, artist_id = self.get_artist(
|
artist, artist_id = self.get_artist(
|
||||||
track.get("artists", []), join_key="join"
|
track.get("artists", []), join_key="join"
|
||||||
)
|
)
|
||||||
|
artist = self.strip_disambiguation(artist)
|
||||||
length = self.get_track_length(track["duration"])
|
length = self.get_track_length(track["duration"])
|
||||||
return TrackInfo(
|
return TrackInfo(
|
||||||
title=title,
|
title=title,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ New features:
|
||||||
- :doc:`plugins/lastgenre`: Add a ``--pretend`` option to preview genre changes
|
- :doc:`plugins/lastgenre`: Add a ``--pretend`` option to preview genre changes
|
||||||
without storing or writing them.
|
without storing or writing them.
|
||||||
- :doc:`plugins/convert`: Add a config option to disable writing metadata to converted files.
|
- :doc:`plugins/convert`: Add a config option to disable writing metadata to converted files.
|
||||||
|
- :doc:`plugins/discogs`: New config option `strip_disambiguation` to toggle
|
||||||
|
stripping discogs numeric disambiguation on artist and label fields.
|
||||||
|
|
||||||
Bug fixes:
|
Bug fixes:
|
||||||
|
|
||||||
|
|
@ -22,6 +24,8 @@ Bug fixes:
|
||||||
- :doc:`plugins/spotify` Removed old and undocumented config options
|
- :doc:`plugins/spotify` Removed old and undocumented config options
|
||||||
`artist_field`, `album_field` and `track` that were causing issues with track
|
`artist_field`, `album_field` and `track` that were causing issues with track
|
||||||
matching. :bug:`5189`
|
matching. :bug:`5189`
|
||||||
|
- :doc:`plugins/discogs` Fixed inconsistency in stripping disambiguation from
|
||||||
|
artists but not labels. :bug:`5366`
|
||||||
|
|
||||||
For packagers:
|
For packagers:
|
||||||
|
|
||||||
|
|
@ -36,6 +40,8 @@ Other changes:
|
||||||
- Moved ``art.py`` utility module from ``beets`` into ``beetsplug`` namespace as
|
- Moved ``art.py`` utility module from ``beets`` into ``beetsplug`` namespace as
|
||||||
it is not used in the core beets codebase. It can now be found in
|
it is not used in the core beets codebase. It can now be found in
|
||||||
``beetsplug._utils``.
|
``beetsplug._utils``.
|
||||||
|
- :class:`beets.metadata_plugin.MetadataSourcePlugin`: Remove discogs specific
|
||||||
|
disambiguation stripping.
|
||||||
|
|
||||||
2.4.0 (September 13, 2025)
|
2.4.0 (September 13, 2025)
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,9 @@ Other configurations available under ``discogs:`` are:
|
||||||
- **search_limit**: The maximum number of results to return from Discogs. This
|
- **search_limit**: The maximum number of results to return from Discogs. This
|
||||||
is useful if you want to limit the number of results returned to speed up
|
is useful if you want to limit the number of results returned to speed up
|
||||||
searches. Default: ``5``
|
searches. Default: ``5``
|
||||||
|
- **strip_disambiguation**: Discogs uses strings like ``"(4)"`` to mark distinct
|
||||||
|
artists and labels with the same name. If you'd like to use the discogs
|
||||||
|
disambiguation in your tags, you can disable it. Default: ``True``
|
||||||
|
|
||||||
.. _discogs guidelines: https://support.discogs.com/hc/en-us/articles/360005055373-Database-Guidelines-12-Tracklisting#Index_Tracks_And_Headings
|
.. _discogs guidelines: https://support.discogs.com/hc/en-us/articles/360005055373-Database-Guidelines-12-Tracklisting#Index_Tracks_And_Headings
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,83 @@ class DGAlbumInfoTest(BeetsTestCase):
|
||||||
assert d.genre == "GENRE1, GENRE2"
|
assert d.genre == "GENRE1, GENRE2"
|
||||||
assert d.style is None
|
assert d.style is None
|
||||||
|
|
||||||
|
def test_strip_disambiguation(self):
|
||||||
|
"""Test removing disambiguation from all disambiguated fields."""
|
||||||
|
data = {
|
||||||
|
"id": 123,
|
||||||
|
"uri": "https://www.discogs.com/release/123456-something",
|
||||||
|
"tracklist": [
|
||||||
|
{
|
||||||
|
"title": "track",
|
||||||
|
"position": "A",
|
||||||
|
"type_": "track",
|
||||||
|
"duration": "5:44",
|
||||||
|
"artists": [
|
||||||
|
{"name": "TEST ARTIST (5)", "tracks": "", "id": 11146}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"artists": [
|
||||||
|
{"name": "ARTIST NAME (2)", "id": 321, "join": "&"},
|
||||||
|
{"name": "OTHER ARTIST (5)", "id": 321, "join": ""},
|
||||||
|
],
|
||||||
|
"title": "title",
|
||||||
|
"labels": [
|
||||||
|
{
|
||||||
|
"name": "LABEL NAME (5)",
|
||||||
|
"catno": "catalog number",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
release = Bag(
|
||||||
|
data=data,
|
||||||
|
title=data["title"],
|
||||||
|
artists=[Bag(data=d) for d in data["artists"]],
|
||||||
|
)
|
||||||
|
d = DiscogsPlugin().get_album_info(release)
|
||||||
|
assert d.artist == "ARTIST NAME & OTHER ARTIST"
|
||||||
|
assert d.tracks[0].artist == "TEST ARTIST"
|
||||||
|
assert d.label == "LABEL NAME"
|
||||||
|
|
||||||
|
def test_strip_disambiguation_false(self):
|
||||||
|
"""Test disabling disambiguation removal from all disambiguated fields."""
|
||||||
|
config["discogs"]["strip_disambiguation"] = False
|
||||||
|
data = {
|
||||||
|
"id": 123,
|
||||||
|
"uri": "https://www.discogs.com/release/123456-something",
|
||||||
|
"tracklist": [
|
||||||
|
{
|
||||||
|
"title": "track",
|
||||||
|
"position": "A",
|
||||||
|
"type_": "track",
|
||||||
|
"duration": "5:44",
|
||||||
|
"artists": [
|
||||||
|
{"name": "TEST ARTIST (5)", "tracks": "", "id": 11146}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"artists": [
|
||||||
|
{"name": "ARTIST NAME (2)", "id": 321, "join": "&"},
|
||||||
|
{"name": "OTHER ARTIST (5)", "id": 321, "join": ""},
|
||||||
|
],
|
||||||
|
"title": "title",
|
||||||
|
"labels": [
|
||||||
|
{
|
||||||
|
"name": "LABEL NAME (5)",
|
||||||
|
"catno": "catalog number",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
release = Bag(
|
||||||
|
data=data,
|
||||||
|
title=data["title"],
|
||||||
|
artists=[Bag(data=d) for d in data["artists"]],
|
||||||
|
)
|
||||||
|
d = DiscogsPlugin().get_album_info(release)
|
||||||
|
assert d.artist == "ARTIST NAME (2) & OTHER ARTIST (5)"
|
||||||
|
assert d.tracks[0].artist == "TEST ARTIST (5)"
|
||||||
|
assert d.label == "LABEL NAME (5)"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"formats, expected_media, expected_albumtype",
|
"formats, expected_media, expected_albumtype",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue