Skip to content

Commit a215945

Browse files
tests for multiple fields written, working on implementation
1 parent de29348 commit a215945

File tree

2 files changed

+181
-76
lines changed

2 files changed

+181
-76
lines changed

beetsplug/discogs.py

Lines changed: 142 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,27 @@ class Track(TypedDict):
104104
extraartists: NotRequired[list[Artist]]
105105
sub_tracks: NotRequired[list[Track]]
106106

107+
class ArtistInfo(TypedDict):
108+
artist: str
109+
artists: list[str]
110+
artist_credit: str
111+
artists_credit: list[str]
112+
artist_id: str
113+
artists_ids: list[str]
114+
115+
class AlbumArtistInfo(TypedDict):
116+
artist: str
117+
artists: list[str]
118+
artist_credit: str
119+
artists_credit: list[str]
120+
artist_id: str
121+
artists_ids: list[str]
122+
albumartist: str
123+
albumartists: list[str]
124+
albumartist_credit: str
125+
albumartists_credit: list[str]
126+
albumartist_id: str
127+
albumartists_ids: list[str]
107128

108129
class DiscogsPlugin(MetadataSourcePlugin):
109130
def __init__(self):
@@ -261,7 +282,6 @@ def track_for_id(self, track_id: str) -> TrackInfo | None:
261282
for track in album.tracks:
262283
if track.track_id == track_id:
263284
return track
264-
265285
return None
266286

267287
def get_albums(self, query: str) -> Iterable[AlbumInfo]:
@@ -346,6 +366,106 @@ def get_artist_with_anv(
346366
artist, artist_id = self.get_artist(artist_list, join_key="join")
347367
return self.strip_disambiguation(artist), artist_id
348368

369+
def build_albumartistinfo(self, artists: list[Artist]) -> AlbumArtistInfo:
370+
info = self.build_artistinfo(artists, album_artist=True)
371+
albumartist: AlbumArtistInfo = {
372+
**info,
373+
"albumartist": info["artist"],
374+
"albumartist_id": info["artist_id"],
375+
"albumartists": info["artists"],
376+
"albumartists_ids": info["artists_ids"],
377+
"albumartist_credit": info["artist_credit"],
378+
"albumartists_credit": info["artists_credit"],
379+
}
380+
return albumartist
381+
382+
def build_artistinfo(
383+
self,
384+
given_artists: list[Artist],
385+
info: ArtistInfo = {
386+
"artist": "",
387+
"artist_id": "",
388+
"artists": [],
389+
"artists_ids": [],
390+
"artist_credit": "",
391+
"artists_credit": [],
392+
},
393+
album_artist: bool = False
394+
) -> ArtistInfo:
395+
"""Iterates through a discogs result and builds
396+
up the artist fields. Does not contribute to
397+
artist_sort as Discogs does not define that.
398+
399+
:param artists: A list of Discogs Artist objects
400+
401+
:param album_artist: If building an album artist,
402+
we need to account for the album_artist anv parameter.
403+
:return an ArtistInfo dictionary.
404+
"""
405+
406+
a_anv: bool = self.config["anv"]["album_artist"].get(bool)
407+
ac_anv: bool = self.config["anv"]["artist_credit"].get(bool)
408+
aa_anv: bool = self.config["anv"]["album_artist"].get(bool)
409+
feat_str: str = self.config["featured_string"].get(str)
410+
411+
artist = ""
412+
artist_anv = ""
413+
artists: list[str] = []
414+
artists_anv: list[str] = []
415+
416+
# Iterate through building the artist strings
417+
for a in given_artists:
418+
# Get the artist name
419+
name = self.strip_disambiguation(a["name"])
420+
discogs_id = a["id"]
421+
anv = a.get("anv", name)
422+
join = a.get("join", "")
423+
role = a.get("role", "").lower()
424+
425+
# Check if the artist is Various
426+
if name.lower() == "various":
427+
name = config["va_name"].as_str()
428+
429+
if role and "featuring" in role:
430+
artist = self._join_artist(artist, name, feat_str)
431+
artist_anv = self._join_artist(artist, name, feat_str)
432+
else:
433+
artist = self._join_artist(artist, name, join)
434+
artist_anv = self._join_artist(artist, anv, join)
435+
artists.append(name)
436+
artists_anv.append(anv)
437+
# Only the first ID is set for the singular field
438+
if not info["artist_id"]:
439+
info["artist_id"] = discogs_id
440+
info["artists_ids"].append(discogs_id)
441+
# Assign fields as necessary
442+
if (a_anv and not album_artist) or (aa_anv and album_artist):
443+
info["artist"] = artist_anv
444+
info["artists"] = artists_anv
445+
else:
446+
info["artist"] = artist
447+
info["artists"] = artists
448+
449+
if ac_anv:
450+
info["artist_credit"] = artist_anv
451+
info["artists_credit"] = artists_anv
452+
else:
453+
info["artist_credit"] = artist
454+
info["artists_credit"] = artists
455+
return info
456+
457+
def _join_artist(self, base: str, artist: str, join: str) -> str:
458+
# Expand the artist field
459+
if not base:
460+
base = artist
461+
else:
462+
if join:
463+
base += f" {join} "
464+
else:
465+
base += ", "
466+
base += artist
467+
return base
468+
349469
def get_album_info(self, result: Release) -> AlbumInfo | None:
350470
"""Returns an AlbumInfo object for a discogs Release object."""
351471
# Explicitly reload the `Release` fields, as they might not be yet
@@ -375,31 +495,23 @@ def get_album_info(self, result: Release) -> AlbumInfo | None:
375495
return None
376496

377497
artist_data = [a.data for a in result.artists]
378-
album_artist, album_artist_id = self.get_artist_with_anv(artist_data)
379-
album_artist_anv, _ = self.get_artist_with_anv(
380-
artist_data, use_anv=True
381-
)
382-
artist_credit = album_artist_anv
498+
# Information for the album artist
499+
albumartist: AlbumArtistInfo = self.build_albumartistinfo(artist_data)
500+
501+
# Information from the album artist artist, if it's being used for track artists
502+
track_albumartist: ArtistInfo = self.build_artistinfo(artist_data)
383503

384504
album = re.sub(r" +", " ", result.title)
385505
album_id = result.data["id"]
386506
# Use `.data` to access the tracklist directly instead of the
387507
# convenient `.tracklist` property, which will strip out useful artist
388508
# information and leave us with skeleton `Artist` objects that will
389509
# each make an API call just to get the same data back.
390-
tracks = self.get_tracks(
391-
result.data["tracklist"],
392-
(album_artist, album_artist_anv, album_artist_id),
393-
)
510+
tracks = self.get_tracks(result.data["tracklist"], track_albumartist)
394511

395-
# Assign ANV to the proper fields for tagging
396-
if not self.config["anv"]["artist_credit"]:
397-
artist_credit = album_artist
398-
if self.config["anv"]["album_artist"]:
399-
album_artist = album_artist_anv
400512

401513
# Extract information for the optional AlbumInfo fields, if possible.
402-
va = result.data["artists"][0].get("name", "").lower() == "various"
514+
va = albumartist["albumartist"] == config["va_name"].as_str()
403515
year = result.data.get("year")
404516
mediums = [t.medium for t in tracks]
405517
country = result.data.get("country")
@@ -431,11 +543,7 @@ def get_album_info(self, result: Release) -> AlbumInfo | None:
431543
cover_art_url = self.select_cover_art(result)
432544

433545
# Additional cleanups
434-
# (various artists name, catalog number, media, disambiguation).
435-
if va:
436-
va_name = config["va_name"].as_str()
437-
album_artist = va_name
438-
artist_credit = va_name
546+
# (catalog number, media, disambiguation).
439547
if catalogno == "none":
440548
catalogno = None
441549
# Explicitly set the `media` for the tracks, since it is expected by
@@ -458,9 +566,7 @@ def get_album_info(self, result: Release) -> AlbumInfo | None:
458566
return AlbumInfo(
459567
album=album,
460568
album_id=album_id,
461-
artist=album_artist,
462-
artist_credit=artist_credit,
463-
artist_id=album_artist_id,
569+
**albumartist, # Unpacks values to satisfy the keyword arguments
464570
tracks=tracks,
465571
albumtype=albumtype,
466572
va=va,
@@ -478,7 +584,7 @@ def get_album_info(self, result: Release) -> AlbumInfo | None:
478584
data_url=data_url,
479585
discogs_albumid=discogs_albumid,
480586
discogs_labelid=labelid,
481-
discogs_artistid=album_artist_id,
587+
discogs_artistid=albumartist["albumartist_id"],
482588
cover_art_url=cover_art_url,
483589
)
484590

@@ -503,7 +609,7 @@ def format(self, classification: Iterable[str]) -> str | None:
503609
def _process_clean_tracklist(
504610
self,
505611
clean_tracklist: list[Track],
506-
album_artist_data: tuple[str, str, str | None],
612+
albumartistinfo: ArtistInfo,
507613
) -> tuple[
508614
list[TrackInfo],
509615
dict[int, str],
@@ -531,7 +637,7 @@ def _process_clean_tracklist(
531637
divisions += next_divisions
532638
del next_divisions[:]
533639
track_info, medium, medium_index = self.get_track_info(
534-
track, index, divisions, album_artist_data
640+
track, index, divisions, albumartistinfo
535641
)
536642
track_info.track_alt = track["position"]
537643
tracks.append(track_info)
@@ -565,7 +671,7 @@ def _process_clean_tracklist(
565671
def get_tracks(
566672
self,
567673
tracklist: list[Track],
568-
album_artist_data: tuple[str, str, str | None],
674+
albumartistinfo: ArtistInfo,
569675
) -> list[TrackInfo]:
570676
"""Returns a list of TrackInfo objects for a discogs tracklist."""
571677
try:
@@ -578,7 +684,7 @@ def get_tracks(
578684
self._log.error("uncaught exception in coalesce_tracks: {}", exc)
579685
clean_tracklist = tracklist
580686
processed = self._process_clean_tracklist(
581-
clean_tracklist, album_artist_data
687+
clean_tracklist, albumartistinfo
582688
)
583689
(
584690
tracks,
@@ -754,16 +860,11 @@ def get_track_info(
754860
track: Track,
755861
index: int,
756862
divisions: list[str],
757-
album_artist_data: tuple[str, str, str | None],
863+
albumartistinfo: ArtistInfo
758864
) -> tuple[TrackInfo, str | None, str | None]:
759865
"""Returns a TrackInfo object for a discogs track."""
760866

761-
artist, artist_anv, artist_id = album_artist_data
762-
artist_credit = artist_anv
763-
if not self.config["anv"]["artist_credit"]:
764-
artist_credit = artist
765-
if self.config["anv"]["artist"]:
766-
artist = artist_anv
867+
artistinfo = albumartistinfo.copy()
767868

768869
title = track["title"]
769870
if self.config["index_tracks"]:
@@ -775,39 +876,19 @@ def get_track_info(
775876

776877
# If artists are found on the track, we will use those instead
777878
if artists := track.get("artists", []):
778-
artist, artist_id = self.get_artist_with_anv(
779-
artists, self.config["anv"]["artist"]
780-
)
781-
artist_credit, _ = self.get_artist_with_anv(
782-
artists, self.config["anv"]["artist_credit"]
783-
)
879+
artistinfo = self.build_artistinfo(artists)
880+
784881
length = self.get_track_length(track["duration"])
785882

786883
# Add featured artists
787884
if extraartists := track.get("extraartists", []):
788-
featured_list = [
789-
artist
790-
for artist in extraartists
791-
if "Featuring" in artist["role"]
792-
]
793-
featured, _ = self.get_artist_with_anv(
794-
featured_list, self.config["anv"]["artist"]
795-
)
796-
featured_credit, _ = self.get_artist_with_anv(
797-
featured_list, self.config["anv"]["artist_credit"]
798-
)
799-
if featured:
800-
artist += f" {self.config['featured_string']} {featured}"
801-
artist_credit += (
802-
f" {self.config['featured_string']} {featured_credit}"
803-
)
885+
artistinfo = self.build_artistinfo(extraartists, artistinfo)
886+
804887
return (
805888
TrackInfo(
806889
title=title,
807890
track_id=track_id,
808-
artist_credit=artist_credit,
809-
artist=artist,
810-
artist_id=artist_id,
891+
**artistinfo,
811892
length=length,
812893
index=index,
813894
),

0 commit comments

Comments
 (0)