Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PERFORMER & COMPOSER metadata tags to audio tracks (if available) #444

Merged
merged 2 commits into from
Jan 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 61 additions & 8 deletions whipper/common/mbngs.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,18 +153,65 @@ def getIds(self):


def _getWorks(recording):
"""Get "performance of" works out of a recording."""
"""
Get 'performance of' works out of a recording.

:param recording: recording entity in MusicBrainz
:type recording: dict
:returns: list of works being a performance of a recording
:rtype: list
"""
works = []
valid_work_rel_types = [
'a3005666-a872-32c3-ad06-98af558e99b0', # "Performance"
]
valid_type_id = 'a3005666-a872-32c3-ad06-98af558e99b0' # "Performance"
if 'work-relation-list' in recording:
for work in recording['work-relation-list']:
if work['type-id'] in valid_work_rel_types:
works.append(work['work']['id'])
if work['type-id'] == valid_type_id:
works.append(work['work'])
return works


def _getComposers(works):
"""
Get composer(s) from works' artist-relation-list.

:param works: list of works being a performance of a recording
:type works: list
:returns: sorted list of composers (without duplicates)
:rtype: list
"""
composers = set()
valid_type_id = 'd59d99ea-23d4-4a80-b066-edca32ee158f' # "Composer"
for work in works:
if 'artist-relation-list' in work:
for artist_relation in work['artist-relation-list']:
if artist_relation['type-id'] == valid_type_id:
composerName = artist_relation['artist']['name']
composers.add(composerName)
return sorted(composers) # convert to list: mutagen doesn't support set


def _getPerformers(recording):
"""
Get performer(s) from recordings' artist-relation-list.

:param recording: recording entity in MusicBrainz
:type recording: dict
:returns: sorted list of performers' names (without duplicates)
:rtype: list
"""
performers = set()
valid_type_id = {
'59054b12-01ac-43ee-a618-285fd397e461', # "Instruments"
'0fdbe3c6-7700-4a31-ae54-b53f06ae1cfa', # "Vocals"
'628a9658-f54c-4142-b0c0-95f031b544da' # "Performers"
}
if 'artist-relation-list' in recording:
for artist_relation in recording['artist-relation-list']:
if artist_relation['type-id'] in valid_type_id:
performers.add(artist_relation['artist']['name'])
return sorted(performers) # convert to list: mutagen doesn't support set


def _getMetadata(release, discid=None, country=None):
"""
:type release: dict
Expand Down Expand Up @@ -241,6 +288,8 @@ def _getMetadata(release, discid=None, country=None):
trackCredit = _Credit(
t.get('artist-credit', t['recording']['artist-credit']
))
recordingCredit = _Credit(t['recording']['artist-credit'])
works = _getWorks(t['recording'])
if len(trackCredit) > 1:
logger.debug('artist-credit more than 1: %r',
trackCredit)
Expand All @@ -250,11 +299,14 @@ def _getMetadata(release, discid=None, country=None):
track.artist = trackCredit.getName()
track.sortName = trackCredit.getSortName()
track.mbidArtist = trackCredit.getIds()
track.recordingArtist = recordingCredit.getName()

track.title = t.get('title', t['recording']['title'])
track.mbid = t['id']
track.mbidRecording = t['recording']['id']
track.mbidWorks = _getWorks(t['recording'])
track.mbidWorks = sorted({work['id'] for work in works})
track.composers = _getComposers(works)
track.performers = _getPerformers(t['recording'])

# FIXME: unit of duration ?
track.duration = int(t['recording'].get('length', 0))
Expand Down Expand Up @@ -301,7 +353,8 @@ def getReleaseMetadata(release_id, discid=None, country=None, record=False):
release_id, includes=["artists", "artist-credits",
"recordings", "discids",
"labels", "recording-level-rels",
"work-rels", "release-groups"])
"work-rels", "release-groups",
"work-level-rels", "artist-rels"])
_record(record, 'release', release_id, res)
releaseDetail = res['release']
formatted = json.dumps(releaseDetail, sort_keys=False, indent=4)
Expand Down
6 changes: 6 additions & 0 deletions whipper/common/program.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,8 @@ def getTagList(self, number, mbdiscid):
mbidTrack = track.mbid
mbidTrackArtist = track.mbidArtist
mbidWorks = track.mbidWorks
composers = track.composers
performers = track.performers
except IndexError as e:
logger.error('no track %d found, %r', number, e)
raise
Expand Down Expand Up @@ -449,6 +451,10 @@ def getTagList(self, number, mbdiscid):
tags['MUSICBRAINZ_ALBUMARTISTID'] = mbidReleaseArtist
if len(mbidWorks) > 0:
tags['MUSICBRAINZ_WORKID'] = mbidWorks
if len(composers) > 0:
tags['COMPOSER'] = composers
if len(performers) > 0:
tags['PERFORMER'] = performers
ABCbum marked this conversation as resolved.
Show resolved Hide resolved

# TODO/FIXME: ISRC tag

Expand Down
20 changes: 20 additions & 0 deletions whipper/test/test_common_mbngs.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,26 @@ def testTrackTitle(self):
track1 = metadata.tracks[0]
self.assertEqual(track1.title, 'Brownsville Turnaround')

def testComposersAndPerformers(self):
"""
Test whether composers and performers are extracted properly.

See: https://github.com/whipper-team/whipper/issues/191
"""
# Using: Mama Said - Lenny Kravitz
# https://musicbrainz.org/release/410f99f8-a876-3416-bd8e-42233a00a477
filename = 'whipper.release.410f99f8-a876-3416-bd8e-42233a00a477.json'
path = os.path.join(os.path.dirname(__file__), filename)
with open(path, "rb") as handle:
response = json.loads(handle.read().decode('utf-8'))

metadata = mbngs._getMetadata(response['release'],
discid='bIOeHwHT0aZJiENIYjAmoNxCPuA-')
track1 = metadata.tracks[0]
self.assertEqual(track1.composers,
['Hal Fredericks', 'Michael Kamen'])
self.assertEqual(track1.performers, ['Lenny Kravitz', 'Slash'])

def test2MeterSessies10(self):
# various artists, multiple artists per track
filename = 'whipper.release.a76714e0-32b1-4ed4-b28e-f86d99642193.json'
Expand Down

Large diffs are not rendered by default.