mirror of
https://github.com/beetbox/beets.git
synced 2025-12-15 21:14:19 +01:00
Fix , remove derived types, refactor coalesce tracks
This commit is contained in:
parent
07445fdd07
commit
de293489d1
3 changed files with 121 additions and 100 deletions
|
|
@ -27,7 +27,7 @@ import time
|
|||
import traceback
|
||||
from functools import cache
|
||||
from string import ascii_lowercase
|
||||
from typing import TYPE_CHECKING, cast
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import confuse
|
||||
from discogs_client import Client, Master, Release
|
||||
|
|
@ -102,23 +102,7 @@ class Track(TypedDict):
|
|||
duration: str
|
||||
artists: list[Artist]
|
||||
extraartists: NotRequired[list[Artist]]
|
||||
|
||||
|
||||
class TrackWithSubtracks(Track):
|
||||
sub_tracks: list[TrackWithSubtracks]
|
||||
|
||||
|
||||
class IntermediateTrackInfo(TrackInfo):
|
||||
"""Allows work with string mediums from
|
||||
get_track_info"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
medium_str: str | None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
self.medium_str = medium_str
|
||||
super().__init__(**kwargs)
|
||||
sub_tracks: NotRequired[list[Track]]
|
||||
|
||||
|
||||
class DiscogsPlugin(MetadataSourcePlugin):
|
||||
|
|
@ -520,9 +504,19 @@ class DiscogsPlugin(MetadataSourcePlugin):
|
|||
self,
|
||||
clean_tracklist: list[Track],
|
||||
album_artist_data: tuple[str, str, str | None],
|
||||
) -> tuple[list[TrackInfo], dict[int, str], int, list[str], list[str]]:
|
||||
) -> tuple[
|
||||
list[TrackInfo],
|
||||
dict[int, str],
|
||||
int,
|
||||
list[str],
|
||||
list[str],
|
||||
list[str | None],
|
||||
list[str | None],
|
||||
]:
|
||||
# Distinct works and intra-work divisions, as defined by index tracks.
|
||||
tracks: list[TrackInfo] = []
|
||||
mediums: list[str | None] = []
|
||||
medium_indices: list[str | None] = []
|
||||
index_tracks = {}
|
||||
index = 0
|
||||
divisions: list[str] = []
|
||||
|
|
@ -536,11 +530,19 @@ class DiscogsPlugin(MetadataSourcePlugin):
|
|||
# divisions.
|
||||
divisions += next_divisions
|
||||
del next_divisions[:]
|
||||
track_info = self.get_track_info(
|
||||
track_info, medium, medium_index = self.get_track_info(
|
||||
track, index, divisions, album_artist_data
|
||||
)
|
||||
track_info.track_alt = track["position"]
|
||||
tracks.append(track_info)
|
||||
if medium:
|
||||
mediums.append(medium)
|
||||
else:
|
||||
mediums.append(None)
|
||||
if medium_index:
|
||||
medium_indices.append(medium_index)
|
||||
else:
|
||||
medium_indices.append(None)
|
||||
else:
|
||||
next_divisions.append(track["title"])
|
||||
# We expect new levels of division at the beginning of the
|
||||
|
|
@ -550,7 +552,15 @@ class DiscogsPlugin(MetadataSourcePlugin):
|
|||
except IndexError:
|
||||
pass
|
||||
index_tracks[index + 1] = track["title"]
|
||||
return tracks, index_tracks, index, divisions, next_divisions
|
||||
return (
|
||||
tracks,
|
||||
index_tracks,
|
||||
index,
|
||||
divisions,
|
||||
next_divisions,
|
||||
mediums,
|
||||
medium_indices,
|
||||
)
|
||||
|
||||
def get_tracks(
|
||||
self,
|
||||
|
|
@ -559,9 +569,7 @@ class DiscogsPlugin(MetadataSourcePlugin):
|
|||
) -> list[TrackInfo]:
|
||||
"""Returns a list of TrackInfo objects for a discogs tracklist."""
|
||||
try:
|
||||
clean_tracklist: list[Track] = self.coalesce_tracks(
|
||||
cast(list[TrackWithSubtracks], tracklist)
|
||||
)
|
||||
clean_tracklist: list[Track] = self.coalesce_tracks(tracklist)
|
||||
except Exception as exc:
|
||||
# FIXME: this is an extra precaution for making sure there are no
|
||||
# side effects after #2222. It should be removed after further
|
||||
|
|
@ -572,7 +580,15 @@ class DiscogsPlugin(MetadataSourcePlugin):
|
|||
processed = self._process_clean_tracklist(
|
||||
clean_tracklist, album_artist_data
|
||||
)
|
||||
tracks, index_tracks, index, divisions, next_divisions = processed
|
||||
(
|
||||
tracks,
|
||||
index_tracks,
|
||||
index,
|
||||
divisions,
|
||||
next_divisions,
|
||||
mediums,
|
||||
medium_indices,
|
||||
) = processed
|
||||
# Fix up medium and medium_index for each track. Discogs position is
|
||||
# unreliable, but tracks are in order.
|
||||
medium = None
|
||||
|
|
@ -581,32 +597,34 @@ class DiscogsPlugin(MetadataSourcePlugin):
|
|||
|
||||
# If a medium has two sides (ie. vinyl or cassette), each pair of
|
||||
# consecutive sides should belong to the same medium.
|
||||
if all([track.medium_str is not None for track in tracks]):
|
||||
m = sorted({track.medium_str.lower() for track in tracks})
|
||||
if all([medium is not None for medium in mediums]):
|
||||
m = sorted({medium.lower() if medium else "" for medium in mediums})
|
||||
# If all track.medium are single consecutive letters, assume it is
|
||||
# a 2-sided medium.
|
||||
if "".join(m) in ascii_lowercase:
|
||||
sides_per_medium = 2
|
||||
|
||||
for track in tracks:
|
||||
for i, track in enumerate(tracks):
|
||||
# Handle special case where a different medium does not indicate a
|
||||
# new disc, when there is no medium_index and the ordinal of medium
|
||||
# is not sequential. For example, I, II, III, IV, V. Assume these
|
||||
# are the track index, not the medium.
|
||||
# side_count is the number of mediums or medium sides (in the case
|
||||
# of two-sided mediums) that were seen before.
|
||||
medium_str = mediums[i]
|
||||
medium_index = medium_indices[i]
|
||||
medium_is_index = (
|
||||
track.medium_str
|
||||
and not track.medium_index
|
||||
medium_str
|
||||
and not medium_index
|
||||
and (
|
||||
len(track.medium_str) != 1
|
||||
len(medium_str) != 1
|
||||
or
|
||||
# Not within standard incremental medium values (A, B, C, ...).
|
||||
ord(track.medium_str) - 64 != side_count + 1
|
||||
ord(medium_str) - 64 != side_count + 1
|
||||
)
|
||||
)
|
||||
|
||||
if not medium_is_index and medium != track.medium_str:
|
||||
if not medium_is_index and medium != medium_str:
|
||||
side_count += 1
|
||||
if sides_per_medium == 2:
|
||||
if side_count % sides_per_medium:
|
||||
|
|
@ -617,7 +635,7 @@ class DiscogsPlugin(MetadataSourcePlugin):
|
|||
# Medium changed. Reset index_count.
|
||||
medium_count += 1
|
||||
index_count = 0
|
||||
medium = track.medium_str
|
||||
medium = medium_str
|
||||
|
||||
index_count += 1
|
||||
medium_count = 1 if medium_count == 0 else medium_count
|
||||
|
|
@ -633,19 +651,57 @@ class DiscogsPlugin(MetadataSourcePlugin):
|
|||
disctitle = None
|
||||
track.disctitle = disctitle
|
||||
|
||||
return cast(list[TrackInfo], tracks)
|
||||
return tracks
|
||||
|
||||
def coalesce_tracks(
|
||||
self, raw_tracklist: list[TrackWithSubtracks]
|
||||
) -> list[Track]:
|
||||
def coalesce_tracks(self, raw_tracklist: list[Track]) -> list[Track]:
|
||||
"""Pre-process a tracklist, merging subtracks into a single track. The
|
||||
title for the merged track is the one from the previous index track,
|
||||
if present; otherwise it is a combination of the subtracks titles.
|
||||
"""
|
||||
# Pre-process the tracklist, trying to identify subtracks.
|
||||
|
||||
def add_merged_subtracks(
|
||||
tracklist: list[TrackWithSubtracks],
|
||||
subtracks: list[TrackWithSubtracks],
|
||||
subtracks: list[Track] = []
|
||||
tracklist: list[Track] = []
|
||||
prev_subindex = ""
|
||||
for track in raw_tracklist:
|
||||
# Regular subtrack (track with subindex).
|
||||
if track["position"]:
|
||||
_, _, subindex = self.get_track_index(track["position"])
|
||||
if subindex:
|
||||
if subindex.rjust(len(raw_tracklist)) > prev_subindex:
|
||||
# Subtrack still part of the current main track.
|
||||
subtracks.append(track)
|
||||
else:
|
||||
# Subtrack part of a new group (..., 1.3, *2.1*, ...).
|
||||
self._add_merged_subtracks(tracklist, subtracks)
|
||||
subtracks = [track]
|
||||
prev_subindex = subindex.rjust(len(raw_tracklist))
|
||||
continue
|
||||
|
||||
# Index track with nested sub_tracks.
|
||||
if not track["position"] and "sub_tracks" in track:
|
||||
# Append the index track, assuming it contains the track title.
|
||||
tracklist.append(track)
|
||||
self._add_merged_subtracks(tracklist, track["sub_tracks"])
|
||||
continue
|
||||
|
||||
# Regular track or index track without nested sub_tracks.
|
||||
if subtracks:
|
||||
self._add_merged_subtracks(tracklist, subtracks)
|
||||
subtracks = []
|
||||
prev_subindex = ""
|
||||
tracklist.append(track)
|
||||
|
||||
# Merge and add the remaining subtracks, if any.
|
||||
if subtracks:
|
||||
self._add_merged_subtracks(tracklist, subtracks)
|
||||
|
||||
return tracklist
|
||||
|
||||
def _add_merged_subtracks(
|
||||
self,
|
||||
tracklist: list[Track],
|
||||
subtracks: list[Track],
|
||||
) -> None:
|
||||
"""Modify `tracklist` in place, merging a list of `subtracks` into
|
||||
a single track into `tracklist`."""
|
||||
|
|
@ -685,45 +741,6 @@ class DiscogsPlugin(MetadataSourcePlugin):
|
|||
track["title"] = " / ".join([t["title"] for t in subtracks])
|
||||
tracklist.append(track)
|
||||
|
||||
# Pre-process the tracklist, trying to identify subtracks.
|
||||
subtracks: list[TrackWithSubtracks] = []
|
||||
tracklist: list[TrackWithSubtracks] = []
|
||||
prev_subindex = ""
|
||||
for track in raw_tracklist:
|
||||
# Regular subtrack (track with subindex).
|
||||
if track["position"]:
|
||||
_, _, subindex = self.get_track_index(track["position"])
|
||||
if subindex:
|
||||
if subindex.rjust(len(raw_tracklist)) > prev_subindex:
|
||||
# Subtrack still part of the current main track.
|
||||
subtracks.append(track)
|
||||
else:
|
||||
# Subtrack part of a new group (..., 1.3, *2.1*, ...).
|
||||
add_merged_subtracks(tracklist, subtracks)
|
||||
subtracks = [track]
|
||||
prev_subindex = subindex.rjust(len(raw_tracklist))
|
||||
continue
|
||||
|
||||
# Index track with nested sub_tracks.
|
||||
if not track["position"] and "sub_tracks" in track:
|
||||
# Append the index track, assuming it contains the track title.
|
||||
tracklist.append(track)
|
||||
add_merged_subtracks(tracklist, track["sub_tracks"])
|
||||
continue
|
||||
|
||||
# Regular track or index track without nested sub_tracks.
|
||||
if subtracks:
|
||||
add_merged_subtracks(tracklist, subtracks)
|
||||
subtracks = []
|
||||
prev_subindex = ""
|
||||
tracklist.append(track)
|
||||
|
||||
# Merge and add the remaining subtracks, if any.
|
||||
if subtracks:
|
||||
add_merged_subtracks(tracklist, subtracks)
|
||||
|
||||
return cast(list[Track], 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)'
|
||||
|
|
@ -738,7 +755,7 @@ class DiscogsPlugin(MetadataSourcePlugin):
|
|||
index: int,
|
||||
divisions: list[str],
|
||||
album_artist_data: tuple[str, str, str | None],
|
||||
) -> IntermediateTrackInfo:
|
||||
) -> tuple[TrackInfo, str | None, str | None]:
|
||||
"""Returns a TrackInfo object for a discogs track."""
|
||||
|
||||
artist, artist_anv, artist_id = album_artist_data
|
||||
|
|
@ -784,7 +801,8 @@ class DiscogsPlugin(MetadataSourcePlugin):
|
|||
artist_credit += (
|
||||
f" {self.config['featured_string']} {featured_credit}"
|
||||
)
|
||||
return IntermediateTrackInfo(
|
||||
return (
|
||||
TrackInfo(
|
||||
title=title,
|
||||
track_id=track_id,
|
||||
artist_credit=artist_credit,
|
||||
|
|
@ -792,8 +810,9 @@ class DiscogsPlugin(MetadataSourcePlugin):
|
|||
artist_id=artist_id,
|
||||
length=length,
|
||||
index=index,
|
||||
medium_str=medium,
|
||||
medium_index=medium_index,
|
||||
),
|
||||
medium,
|
||||
medium_index,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ Bug fixes:
|
|||
accepted a list of strings). :bug:`5962`
|
||||
- Fix a bug introduced in release 2.4.0 where import from any valid
|
||||
import-log-file always threw a "none of the paths are importable" error.
|
||||
- :doc:`plugins/discogs`: Fixed unexpected flex attr from the Discogs plugin.
|
||||
:bug:`6177`
|
||||
|
||||
For plugin developers:
|
||||
|
||||
|
|
|
|||
|
|
@ -608,7 +608,7 @@ def test_parse_featured_artists(track, expected_artist):
|
|||
"""Tests the plugins ability to parse a featured artist.
|
||||
Initial check with one featured artist, two featured artists,
|
||||
and three. Ignores artists that are not listed as featured."""
|
||||
t = DiscogsPlugin().get_track_info(
|
||||
t, _, _ = DiscogsPlugin().get_track_info(
|
||||
track, 1, 1, ("ARTIST", "ARTIST CREDIT", 2)
|
||||
)
|
||||
assert t.artist == expected_artist
|
||||
|
|
|
|||
Loading…
Reference in a new issue