[music] Tracks can have multiple artists
This commit is contained in:
@ -124,7 +124,7 @@ class Artist(TimeStampedModel):
|
||||
def charts(self):
|
||||
from scrobbles.models import ChartRecord
|
||||
|
||||
return ChartRecord.objects.filter(track__artist=self).order_by("-year")
|
||||
return ChartRecord.objects.filter(track__artists=self).order_by("-year")
|
||||
|
||||
def scrape_allmusic(self, force=False) -> None:
|
||||
if not self.allmusic_id or force:
|
||||
@ -422,7 +422,8 @@ class Album(TimeStampedModel):
|
||||
self.artists.add(new_artist)
|
||||
if not new_artist:
|
||||
for t in self.track_set.all():
|
||||
self.artists.add(t.artist)
|
||||
for a in t.artists.all():
|
||||
self.artists.add(a)
|
||||
if not self.cover_image or self.cover_image == "default-image-replace-me":
|
||||
self.fetch_artwork()
|
||||
self.fix_album_artist()
|
||||
@ -573,7 +574,10 @@ class Album(TimeStampedModel):
|
||||
class Track(ScrobblableMixin):
|
||||
COMPLETION_PERCENT = getattr(settings, "MUSIC_COMPLETION_PERCENT", 100)
|
||||
|
||||
artist = models.ForeignKey(Artist, on_delete=models.DO_NOTHING)
|
||||
artist_fk = models.ForeignKey(
|
||||
Artist, on_delete=models.DO_NOTHING, **BNULL, related_name="+"
|
||||
)
|
||||
artists = models.ManyToManyField(Artist)
|
||||
albums = models.ManyToManyField(Album, related_name="tracks")
|
||||
album = models.ForeignKey(Album, on_delete=models.DO_NOTHING, **BNULL)
|
||||
musicbrainz_id = models.CharField(max_length=255, **BNULL)
|
||||
@ -581,6 +585,10 @@ class Track(ScrobblableMixin):
|
||||
class Meta:
|
||||
unique_together = [["album", "musicbrainz_id"]]
|
||||
|
||||
@property
|
||||
def artist(self):
|
||||
return self.artists.first() or self.artist_fk
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.title} by {self.artist}"
|
||||
|
||||
@ -615,8 +623,9 @@ class Track(ScrobblableMixin):
|
||||
@property
|
||||
def primary_image_url(self) -> str:
|
||||
url = ""
|
||||
if self.artist.thumbnail:
|
||||
url = self.artist.thumbnail_medium.url
|
||||
primary_artist = self.artist
|
||||
if primary_artist and primary_artist.thumbnail:
|
||||
url = primary_artist.thumbnail_medium.url
|
||||
if self.primary_album and self.primary_album.cover_image:
|
||||
url = self.primary_album.cover_image_medium.url
|
||||
return url
|
||||
@ -625,6 +634,7 @@ class Track(ScrobblableMixin):
|
||||
def find_or_create(
|
||||
cls,
|
||||
title: str = "",
|
||||
artist_names: list[str] | None = None,
|
||||
artist_name: str = "",
|
||||
album_name: str = "",
|
||||
run_time_seconds: int | None = None,
|
||||
@ -638,25 +648,40 @@ class Track(ScrobblableMixin):
|
||||
name
|
||||
|
||||
Optionally, we can update any found artists with overwrite."""
|
||||
from music.utils import parse_artist_names
|
||||
|
||||
if artist_names is None and artist_name:
|
||||
artist_names = parse_artist_names(artist_name)
|
||||
|
||||
if not artist_names:
|
||||
artist_names = []
|
||||
|
||||
album = None
|
||||
if album_name:
|
||||
logger.info(f"Looking up album for: {album_name}")
|
||||
album = Album.find_or_create(name=album_name, artist_name=artist_name)
|
||||
artist = album.album_artist
|
||||
else:
|
||||
artist = Artist.find_or_create(artist_name, track_name=title)
|
||||
if not artist:
|
||||
artist = Artist.find_or_create(artist_name)
|
||||
first_name = artist_names[0] if artist_names else ""
|
||||
album = Album.find_or_create(name=album_name, artist_name=first_name)
|
||||
|
||||
artist_objs = []
|
||||
for name in artist_names:
|
||||
artist = Artist.find_or_create(name, track_name=title)
|
||||
if artist:
|
||||
artist_objs.append(artist)
|
||||
|
||||
track = None
|
||||
if artist_objs:
|
||||
track = cls.objects.filter(title=title, artists__in=artist_objs).first()
|
||||
|
||||
lookup_keys = {"title": title, "artist": artist}
|
||||
if run_time_seconds:
|
||||
lookup_keys["base_run_time_seconds"] = run_time_seconds
|
||||
logger.info(f"Looking up track using: {lookup_keys}")
|
||||
track = cls.objects.filter(**lookup_keys).first()
|
||||
if not track:
|
||||
track = cls.objects.filter(title=title, artist=artist).first()
|
||||
if not track:
|
||||
track, _ = cls.objects.get_or_create(title=title, artist=artist)
|
||||
track = cls.objects.filter(title=title).first()
|
||||
|
||||
if not track:
|
||||
track = cls(title=title)
|
||||
track.save()
|
||||
track.refresh_from_db()
|
||||
|
||||
if artist_objs:
|
||||
track.artists.add(*artist_objs)
|
||||
|
||||
if album:
|
||||
track.albums.add(album)
|
||||
@ -666,17 +691,18 @@ class Track(ScrobblableMixin):
|
||||
track.base_run_time_seconds = run_time_seconds
|
||||
track.musicbrainz_id = mbid
|
||||
else:
|
||||
artist_name_str = " & ".join(artist_names) if artist_names else ""
|
||||
logger.info(
|
||||
f"Enriching track {track}",
|
||||
extra={
|
||||
"title": title,
|
||||
"artist_name": artist_name,
|
||||
"artist_name": artist_name_str,
|
||||
"track_id": track.id,
|
||||
},
|
||||
)
|
||||
try:
|
||||
mbid, length = get_recording_mbid_exact(
|
||||
title, artist_name, album_name
|
||||
title, artist_name_str, album_name
|
||||
)
|
||||
except Exception:
|
||||
print("No musicbrainz result found, cannot enrich")
|
||||
@ -688,6 +714,4 @@ class Track(ScrobblableMixin):
|
||||
|
||||
return track
|
||||
|
||||
def fix_metadata(self, force_update=False):
|
||||
|
||||
...
|
||||
def fix_metadata(self, force_update=False): ...
|
||||
|
||||
Reference in New Issue
Block a user