Merge pull request #444 from ABCbum/add-alternative-tagging
Add PERFORMER & COMPOSER metadata tags to audio tracks (if available)
This commit is contained in:
@@ -153,18 +153,65 @@ class _Credit(list):
|
|||||||
|
|
||||||
|
|
||||||
def _getWorks(recording):
|
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 = []
|
works = []
|
||||||
valid_work_rel_types = [
|
valid_type_id = 'a3005666-a872-32c3-ad06-98af558e99b0' # "Performance"
|
||||||
'a3005666-a872-32c3-ad06-98af558e99b0', # "Performance"
|
|
||||||
]
|
|
||||||
if 'work-relation-list' in recording:
|
if 'work-relation-list' in recording:
|
||||||
for work in recording['work-relation-list']:
|
for work in recording['work-relation-list']:
|
||||||
if work['type-id'] in valid_work_rel_types:
|
if work['type-id'] == valid_type_id:
|
||||||
works.append(work['work']['id'])
|
works.append(work['work'])
|
||||||
return works
|
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):
|
def _getMetadata(release, discid=None, country=None):
|
||||||
"""
|
"""
|
||||||
:type release: dict
|
:type release: dict
|
||||||
@@ -241,6 +288,8 @@ def _getMetadata(release, discid=None, country=None):
|
|||||||
trackCredit = _Credit(
|
trackCredit = _Credit(
|
||||||
t.get('artist-credit', t['recording']['artist-credit']
|
t.get('artist-credit', t['recording']['artist-credit']
|
||||||
))
|
))
|
||||||
|
recordingCredit = _Credit(t['recording']['artist-credit'])
|
||||||
|
works = _getWorks(t['recording'])
|
||||||
if len(trackCredit) > 1:
|
if len(trackCredit) > 1:
|
||||||
logger.debug('artist-credit more than 1: %r',
|
logger.debug('artist-credit more than 1: %r',
|
||||||
trackCredit)
|
trackCredit)
|
||||||
@@ -250,11 +299,14 @@ def _getMetadata(release, discid=None, country=None):
|
|||||||
track.artist = trackCredit.getName()
|
track.artist = trackCredit.getName()
|
||||||
track.sortName = trackCredit.getSortName()
|
track.sortName = trackCredit.getSortName()
|
||||||
track.mbidArtist = trackCredit.getIds()
|
track.mbidArtist = trackCredit.getIds()
|
||||||
|
track.recordingArtist = recordingCredit.getName()
|
||||||
|
|
||||||
track.title = t.get('title', t['recording']['title'])
|
track.title = t.get('title', t['recording']['title'])
|
||||||
track.mbid = t['id']
|
track.mbid = t['id']
|
||||||
track.mbidRecording = t['recording']['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 ?
|
# FIXME: unit of duration ?
|
||||||
track.duration = int(t['recording'].get('length', 0))
|
track.duration = int(t['recording'].get('length', 0))
|
||||||
@@ -301,7 +353,8 @@ def getReleaseMetadata(release_id, discid=None, country=None, record=False):
|
|||||||
release_id, includes=["artists", "artist-credits",
|
release_id, includes=["artists", "artist-credits",
|
||||||
"recordings", "discids",
|
"recordings", "discids",
|
||||||
"labels", "recording-level-rels",
|
"labels", "recording-level-rels",
|
||||||
"work-rels", "release-groups"])
|
"work-rels", "release-groups",
|
||||||
|
"work-level-rels", "artist-rels"])
|
||||||
_record(record, 'release', release_id, res)
|
_record(record, 'release', release_id, res)
|
||||||
releaseDetail = res['release']
|
releaseDetail = res['release']
|
||||||
formatted = json.dumps(releaseDetail, sort_keys=False, indent=4)
|
formatted = json.dumps(releaseDetail, sort_keys=False, indent=4)
|
||||||
|
|||||||
@@ -418,6 +418,8 @@ class Program:
|
|||||||
mbidTrack = track.mbid
|
mbidTrack = track.mbid
|
||||||
mbidTrackArtist = track.mbidArtist
|
mbidTrackArtist = track.mbidArtist
|
||||||
mbidWorks = track.mbidWorks
|
mbidWorks = track.mbidWorks
|
||||||
|
composers = track.composers
|
||||||
|
performers = track.performers
|
||||||
except IndexError as e:
|
except IndexError as e:
|
||||||
logger.error('no track %d found, %r', number, e)
|
logger.error('no track %d found, %r', number, e)
|
||||||
raise
|
raise
|
||||||
@@ -451,6 +453,10 @@ class Program:
|
|||||||
tags['MUSICBRAINZ_ALBUMARTISTID'] = mbidReleaseArtist
|
tags['MUSICBRAINZ_ALBUMARTISTID'] = mbidReleaseArtist
|
||||||
if len(mbidWorks) > 0:
|
if len(mbidWorks) > 0:
|
||||||
tags['MUSICBRAINZ_WORKID'] = mbidWorks
|
tags['MUSICBRAINZ_WORKID'] = mbidWorks
|
||||||
|
if len(composers) > 0:
|
||||||
|
tags['COMPOSER'] = composers
|
||||||
|
if len(performers) > 0:
|
||||||
|
tags['PERFORMER'] = performers
|
||||||
|
|
||||||
# TODO/FIXME: ISRC tag
|
# TODO/FIXME: ISRC tag
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,26 @@ class MetadataTestCase(unittest.TestCase):
|
|||||||
track1 = metadata.tracks[0]
|
track1 = metadata.tracks[0]
|
||||||
self.assertEqual(track1.title, 'Brownsville Turnaround')
|
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):
|
def test2MeterSessies10(self):
|
||||||
# various artists, multiple artists per track
|
# various artists, multiple artists per track
|
||||||
filename = 'whipper.release.a76714e0-32b1-4ed4-b28e-f86d99642193.json'
|
filename = 'whipper.release.a76714e0-32b1-4ed4-b28e-f86d99642193.json'
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user