discogs: cache TRACK_INDEX_RE

This commit is contained in:
Šarūnas Nejus 2025-05-09 14:53:05 +01:00
parent 8e5858254b
commit 9cc7ecaceb
No known key found for this signature in database
GPG key ID: DD28F6704DBE3435
2 changed files with 42 additions and 40 deletions

View file

@ -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):

View file

@ -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)