diff --git a/api/funkwhale_api/music/metadata.py b/api/funkwhale_api/music/metadata.py index 9a61e67e14ee8dd213e2f88d0d986d2e84a7c0c1..e3bf530dfaf7af365494ab4a66f9548b80a1b8d0 100644 --- a/api/funkwhale_api/music/metadata.py +++ b/api/funkwhale_api/music/metadata.py @@ -165,6 +165,9 @@ CONF = { "field": "albumartist", "to_application": split_and_return_first(";"), }, + "performer": {}, + "arranger": {"field": "REMIXER"}, + "composer": {}, "album": {}, "date": {"field": "date", "to_application": get_date}, "musicbrainz_albumid": {}, @@ -189,6 +192,9 @@ CONF = { "field": "albumartist", "to_application": split_and_return_first(";"), }, + "performer": {}, + "arranger": {"field": "REMIXER"}, + "composer": {}, "album": {}, "date": {"field": "date", "to_application": get_date}, "musicbrainz_albumid": {}, @@ -210,6 +216,9 @@ CONF = { "title": {}, "artist": {}, "album_artist": {"field": "albumartist"}, + "performer": {}, + "arranger": {"field": "REMIXER"}, + "composer": {}, "album": {}, "date": {"field": "date", "to_application": get_date}, "musicbrainz_albumid": {"field": "MusicBrainz Album Id"}, @@ -230,6 +239,9 @@ CONF = { "title": {"field": "TIT2"}, "artist": {"field": "TPE1"}, "album_artist": {"field": "TPE2"}, + "performer": {"field": "TPE3"}, + "arranger": {"field": "TPE4"}, + "composer": {"field": "TCOM"}, "album": {"field": "TALB"}, "date": {"field": "TDRC", "to_application": get_date}, "musicbrainz_albumid": {"field": "MusicBrainz Album Id"}, @@ -256,6 +268,9 @@ CONF = { "title": {}, "artist": {}, "album_artist": {"field": "albumartist"}, + "performer": {}, + "arranger": {"field": "REMIXER"}, + "composer": {}, "album": {}, "date": {"field": "date", "to_application": get_date}, "musicbrainz_albumid": {}, diff --git a/api/funkwhale_api/music/migrations/0037_track_credit.py b/api/funkwhale_api/music/migrations/0037_track_credit.py index 96f2ea7819b1ca5bbbf0e311fb7da066585eca00..47e806fc162d6e69a5231d1277565a5ef11ca9e4 100644 --- a/api/funkwhale_api/music/migrations/0037_track_credit.py +++ b/api/funkwhale_api/music/migrations/0037_track_credit.py @@ -32,7 +32,7 @@ class Migration(migrations.Migration): name="TrackCredit", fields=[ ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), - ("type", models.CharField(choices=[("main", "Main"), ("featured", "Featured"), ("composer", "Composer"), ("conductor", "Conductor"), ("compiler", "Compiler"), ("remixer", "Remixer"), ("producer", "Producer")], default="main", max_length=30)), + ("type", models.CharField(choices=[("main", "Main"), ("featured", "Featured"), ("composer", "Composer"), ("performer", "Performer"), ("arranger", "Arranger")], default="main", max_length=30)), ("artist", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="track_credits", to="music.Artist")), ("track", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="track_credits", to="music.Track")), ], diff --git a/api/funkwhale_api/music/models.py b/api/funkwhale_api/music/models.py index 54e1c513ba9aa970f892edcce8fc7a611c7076a4..23000e451d9a598a03b4f5d4bba6d53f565f9dc9 100644 --- a/api/funkwhale_api/music/models.py +++ b/api/funkwhale_api/music/models.py @@ -625,19 +625,19 @@ class Credit: MAIN = "main" FEATURED = "featured" COMPOSER = "composer" - CONDUCTOR = "conductor" - COMPILER = "compiler" - REMIXER = "remixer" - PRODUCER = "producer" + PERFORMER = "performer" # conductor, performer refinement, orchestra, soloist + # COMPILER = "compiler" + ARRANGER = "remixer" # remixer, interpreter, otherwise modified + # PRODUCER = "producer" _CHOICES = ( (MAIN, "Main"), (FEATURED, "Featured"), (COMPOSER, "Composer"), - (CONDUCTOR, "Conductor"), - (COMPILER, "Compiler"), - (REMIXER, "Remixer"), - (PRODUCER, "Producer"), + (PERFORMER, "Performer"), + # (COMPILER, "Compiler"), + (ARRANGER, "Arranger"), + # (PRODUCER, "Producer"), ) diff --git a/api/funkwhale_api/music/tasks.py b/api/funkwhale_api/music/tasks.py index ea3f0f57b98cda61a78a6ec53aa9ef1e1bab2f24..36d84ae4a3ad216f7e8ea3ce3017715fcebada5d 100644 --- a/api/funkwhale_api/music/tasks.py +++ b/api/funkwhale_api/music/tasks.py @@ -368,6 +368,18 @@ def sort_candidates(candidates, important_fields): return [c for c, s in reversed(sorted(candidates_with_scores, key=lambda v: v[1]))] +def get_artist_from_metadata(name, mbid=None, fid=None, **kwargs): + query = Q(name__iexact=name) + if mbid: + query |= Q(mbid=mbid) + if fid: + query |= Q(fid=fid) + defaults = {"name": name, "mbid": mbid, "fid": fid, **kwargs} + return get_best_candidate_or_create( + models.Artist, query, defaults=defaults, sort_fields=["mbid", "fid"] + )[0] + + @transaction.atomic def get_track_from_import_metadata(data): track_uuid = getter(data, "funkwhale", "track", "uuid") @@ -411,51 +423,55 @@ def get_track_from_import_metadata(data): except IndexError: pass - # get / create artist and album artist + # get / create artists artist_mbid = data.get("musicbrainz_artistid", None) artist_fid = data.get("artist_fid", None) artist_name = data["artist"] - query = Q(name__iexact=artist_name) - if artist_mbid: - query |= Q(mbid=artist_mbid) - if artist_fid: - query |= Q(fid=artist_fid) - defaults = { - "name": artist_name, - "mbid": artist_mbid, - "fid": artist_fid, - "from_activity_id": from_activity_id, - } - if data.get("artist_fdate"): - defaults["creation_date"] = data.get("artist_fdate") - artist = get_best_candidate_or_create( - models.Artist, query, defaults=defaults, sort_fields=["mbid", "fid"] - )[0] + artist = get_artist_from_metadata( + artist_name, artist_mbid, artist_fid, from_activity_id=from_activity_id + ) album_artist_name = data.get("album_artist") or artist_name if album_artist_name == artist_name: album_artist = artist else: - query = Q(name__iexact=album_artist_name) album_artist_mbid = data.get("musicbrainz_albumartistid", None) album_artist_fid = data.get("album_artist_fid", None) - if album_artist_mbid: - query |= Q(mbid=album_artist_mbid) - if album_artist_fid: - query |= Q(fid=album_artist_fid) - defaults = { - "name": album_artist_name, - "mbid": album_artist_mbid, - "fid": album_artist_fid, - "from_activity_id": from_activity_id, - } + opts = {"from_activity_id": from_activity_id} if data.get("album_artist_fdate"): - defaults["creation_date"] = data.get("album_artist_fdate") + opts["creation_date"] = data.get("album_artist_fdate") - album_artist = get_best_candidate_or_create( - models.Artist, query, defaults=defaults, sort_fields=["mbid", "fid"] - )[0] + album_artist = get_artist_from_metadata( + album_artist_name, album_artist_mbid, album_artist_fid, **opts + ) + + # TODO: mbid, fid + composer_name = data.get("composer") + composer = None + if composer_name == artist_name: # NOTE: composer and artist can be the same ^ + composer = artist + elif composer_name: + composer = get_artist_from_metadata( + composer_name, from_activity_id=from_activity_id + ) + + # TODO: mbid, fid + performer_name = data.get("performer") + performer = None + # NOTE: ^ while performer cannot + if performer_name: + performer = get_artist_from_metadata( + performer_name, from_activity_id=from_activity_id + ) + + # TODO: mbid, fid + arranger_name = data.get("arranger") + arranger = None + if arranger_name: + arranger = get_artist_from_metadata( + arranger_name, from_activity_id=from_activity_id + ) # get / create album album_title = data["album"] @@ -507,9 +523,38 @@ def get_track_from_import_metadata(data): if data.get("fdate"): defaults["creation_date"] = data.get("fdate") - track = get_best_candidate_or_create( + track, track_created = get_best_candidate_or_create( models.Track, query, defaults=defaults, sort_fields=["mbid", "fid"] - )[0] + ) + + # create all relationships / credits + # TODO: is it better to get_or_create? + if track_created: + credits = [] + if album_artist != artist: + credits.append(models.TrackCredit( + artist=album_artist, track=track, type=models.Credit.MAIN + )) + credits.append(models.TrackCredit( + artist=artist, track=track, type=models.Credit.FEATURED + )) + else: + credits.append(models.TrackCredit( + artist=artist, track=track, type=models.Credit.MAIN + )) + if composer: + credits.append(models.TrackCredit( + artist=composer, track=track, type=models.Credit.COMPOSER + )) + if performer: + credits.append(models.TrackCredit( + artist=performer, track=track, type=models.Credit.PERFORMER + )) + if arranger: + credits.append(models.TrackCredit( + artist=arranger, track=track, type=models.Credit.ARRANGER + )) + models.TrackCredit.objects.bulk_create(credits) return track