diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py index ad1310712..8fdd515bd 100644 --- a/beetsplug/discogs.py +++ b/beetsplug/discogs.py @@ -61,6 +61,22 @@ CONNECTION_ERRORS = ( ) +TRACK_INDEX_RE = re.compile( + r""" + (.*?) # medium: everything before medium_index. + (\d*?) # medium_index: a number at the end of + # `position`, except if followed by a subtrack index. + # subtrack_index: can only be matched if medium + # or medium_index have been matched, and can be + ( + (?<=\w)\.[\w]+ # a dot followed by a string (A.1, 2.A) + | (?<=\d)[A-Z]+ # a string that follows a number (1A, B2a) + )? + """, + re.VERBOSE, +) + + class ReleaseFormat(TypedDict): name: str qty: int @@ -655,33 +671,21 @@ class DiscogsPlugin(BeetsPlugin): medium_index=medium_index, ) - def get_track_index(self, position): + @staticmethod + def get_track_index( + position: str, + ) -> tuple[str | None, str | None, str | None]: """Returns the medium, medium index and subtrack index for a discogs track position.""" # Match the standard Discogs positions (12.2.9), which can have several # forms (1, 1-1, A1, A1.1, A1a, ...). - match = re.match( - r"^(.*?)" # medium: everything before medium_index. - r"(\d*?)" # medium_index: a number at the end of - # `position`, except if followed by a subtrack - # index. - # subtrack_index: can only be matched if medium - # or medium_index have been matched, and can be - r"((?<=\w)\.[\w]+" # - a dot followed by a string (A.1, 2.A) - r"|(?<=\d)[A-Z]+" # - a string that follows a number (1A, B2a) - r")?" - r"$", - position.upper(), - ) - - if match: + medium = index = subindex = None + if match := TRACK_INDEX_RE.fullmatch(position.upper()): medium, index, subindex = match.groups() if subindex and subindex.startswith("."): subindex = subindex[1:] - else: - self._log.debug("Invalid position: {0}", position) - medium = index = subindex = None + return medium or None, index or None, subindex or None def get_track_length(self, duration): diff --git a/test/plugins/test_discogs.py b/test/plugins/test_discogs.py index eb9a625b1..c31ac7511 100644 --- a/test/plugins/test_discogs.py +++ b/test/plugins/test_discogs.py @@ -171,27 +171,6 @@ class DGAlbumInfoTest(BeetsTestCase): assert t[3].index == 4 assert t[3].medium_total == 1 - def test_parse_position(self): - """Test the conversion of discogs `position` to medium, medium_index - and subtrack_index.""" - # List of tuples (discogs_position, (medium, medium_index, subindex) - positions = [ - ("1", (None, "1", None)), - ("A12", ("A", "12", None)), - ("12-34", ("12-", "34", None)), - ("CD1-1", ("CD1-", "1", None)), - ("1.12", (None, "1", "12")), - ("12.a", (None, "12", "A")), - ("12.34", (None, "12", "34")), - ("1ab", (None, "1", "AB")), - # Non-standard - ("IV", ("IV", None, None)), - ] - - d = DiscogsPlugin() - for position, expected in positions: - assert d.get_track_index(position) == expected - def test_parse_tracklist_without_sides(self): """Test standard Discogs position 12.2.9#1: "without sides".""" release = self._make_release_from_positions(["1", "2", "3"]) @@ -417,3 +396,22 @@ def test_get_media_and_albumtype(formats, expected_media, expected_albumtype): result = DiscogsPlugin.get_media_and_albumtype(formats) assert result == (expected_media, expected_albumtype) + + +@pytest.mark.parametrize( + "position, medium, index, subindex", + [ + ("1", None, "1", None), + ("A12", "A", "12", None), + ("12-34", "12-", "34", None), + ("CD1-1", "CD1-", "1", None), + ("1.12", None, "1", "12"), + ("12.a", None, "12", "A"), + ("12.34", None, "12", "34"), + ("1ab", None, "1", "AB"), + # Non-standard + ("IV", "IV", None, None), + ], +) +def test_get_track_index(position, medium, index, subindex): + assert DiscogsPlugin.get_track_index(position) == (medium, index, subindex)