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 Composer, Lyricist and Arranger tags #2333

Merged
merged 2 commits into from
Dec 21, 2016
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
15 changes: 15 additions & 0 deletions beets/autotag/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ def apply_item_metadata(item, track_info):
item.mb_artistid = track_info.artist_id
if track_info.data_source:
item.data_source = track_info.data_source

if track_info.lyricist is not None:
item.lyricist = track_info.lyricist
if track_info.composer is not None:
item.composer = track_info.composer
if track_info.arranger is not None:
item.arranger = track_info.arranger
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add a blank line between this stanza and the comment below?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.


# At the moment, the other metadata is left intact (including album
# and track number). Perhaps these should be emptied?

Expand Down Expand Up @@ -142,3 +150,10 @@ def apply_metadata(album_info, mapping):

if track_info.media is not None:
item.media = track_info.media

if track_info.lyricist is not None:
item.lyricist = track_info.lyricist
if track_info.composer is not None:
item.composer = track_info.composer
if track_info.arranger is not None:
item.arranger = track_info.arranger
8 changes: 7 additions & 1 deletion beets/autotag/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ class TrackInfo(object):
- ``artist_credit``: Recording-specific artist name
- ``data_source``: The original data source (MusicBrainz, Discogs, etc.)
- ``data_url``: The data source release URL.
- ``lyricist``: individual track lyricist name
- ``composer``: individual track composer name
- ``arranger`: individual track arranger name

Only ``title`` and ``track_id`` are required. The rest of the fields
may be None. The indices ``index``, ``medium``, and ``medium_index``
Expand All @@ -151,7 +154,7 @@ def __init__(self, title, track_id, artist=None, artist_id=None,
length=None, index=None, medium=None, medium_index=None,
medium_total=None, artist_sort=None, disctitle=None,
artist_credit=None, data_source=None, data_url=None,
media=None):
media=None, lyricist=None, composer=None, arranger=None):
self.title = title
self.track_id = track_id
self.artist = artist
Expand All @@ -167,6 +170,9 @@ def __init__(self, title, track_id, artist=None, artist_id=None,
self.artist_credit = artist_credit
self.data_source = data_source
self.data_url = data_url
self.lyricist = lyricist
self.composer = composer
self.arranger = arranger

# As above, work around a bug in python-musicbrainz-ngs.
def decode(self, codec='utf-8'):
Expand Down
33 changes: 32 additions & 1 deletion beets/autotag/mb.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,12 @@ def get_message(self):
log = logging.getLogger('beets')

RELEASE_INCLUDES = ['artists', 'media', 'recordings', 'release-groups',
'labels', 'artist-credits', 'aliases']
'labels', 'artist-credits', 'aliases',
'recording-level-rels', 'work-rels',
'work-level-rels', 'artist-rels']
TRACK_INCLUDES = ['artists', 'aliases']
if 'work-level-rels' in musicbrainzngs.VALID_INCLUDES['recording']:
TRACK_INCLUDES += ['work-level-rels', 'artist-rels']


def track_url(trackid):
Expand Down Expand Up @@ -179,6 +183,33 @@ def track_info(recording, index=None, medium=None, medium_index=None,
if recording.get('length'):
info.length = int(recording['length']) / (1000.0)

lyricist = []
composer = []
for work_relation in recording.get('work-relation-list', ()):
if work_relation['type'] != 'performance':
continue
for artist_relation in work_relation['work'].get(
'artist-relation-list', ()):
if 'type' in artist_relation:
type = artist_relation['type']
if type == 'lyricist':
lyricist.append(artist_relation['artist']['name'])
elif type == 'composer':
composer.append(artist_relation['artist']['name'])
if lyricist:
info.lyricist = u', '.join(lyricist)
if composer:
info.composer = u', '.join(composer)

arranger = []
for artist_relation in recording.get('artist-relation-list', ()):
if 'type' in artist_relation:
type = artist_relation['type']
if type == 'arranger':
arranger.append(artist_relation['artist']['name'])
if arranger:
info.arranger = u', '.join(arranger)

info.decode()
return info

Expand Down
2 changes: 2 additions & 0 deletions beets/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,9 @@ class Item(LibModel):
'albumartist_sort': types.STRING,
'albumartist_credit': types.STRING,
'genre': types.STRING,
'lyricist': types.STRING,
'composer': types.STRING,
'arranger': types.STRING,
'grouping': types.STRING,
'year': types.PaddedInt(4),
'month': types.PaddedInt(2),
Expand Down
52 changes: 52 additions & 0 deletions beets/mediafile.py
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,45 @@ def store(self, mutagen_file, value):
mutagen_file.tags.setall(self.key, [frame])


class MP3PeopleStorageStyle(MP3StorageStyle):
"""Store list of people in ID3 frames.
"""
def __init__(self, key, involvement='', **kwargs):
self.involvement = involvement
super(MP3PeopleStorageStyle, self).__init__(key, **kwargs)

def store(self, mutagen_file, value):
frames = mutagen_file.tags.getall(self.key)
print(frames)

# Try modifying in place.
found = False
for frame in frames:
if frame.encoding == mutagen.id3.Encoding.UTF8:
for pair in frame.people:
if pair[0].lower() == self.involvement.lower():
pair[1] = value
found = True

# Try creating a new frame.
if not found:
frame = mutagen.id3.Frames[self.key](
encoding=mutagen.id3.Encoding.UTF8,
people=[[self.involvement, value]]
)
print(frame)
mutagen_file.tags.add(frame)

def fetch(self, mutagen_file):
for frame in mutagen_file.tags.getall(self.key):
for pair in frame.people:
if pair[0].lower() == self.involvement.lower():
try:
return pair[1]
except IndexError:
return None


class MP3ListStorageStyle(ListStorageStyle, MP3StorageStyle):
"""Store lists of data in multiple ID3 frames.
"""
Expand Down Expand Up @@ -1590,12 +1629,25 @@ def update(self, dict):
)
genre = genres.single_field()

lyricist = MediaField(
MP3StorageStyle('TEXT'),
MP4StorageStyle('----:com.apple.iTunes:LYRICIST'),
StorageStyle('LYRICIST'),
ASFStorageStyle('WM/Writer'),
)
composer = MediaField(
MP3StorageStyle('TCOM'),
MP4StorageStyle('\xa9wrt'),
StorageStyle('COMPOSER'),
ASFStorageStyle('WM/Composer'),
)
arranger = MediaField(
MP3PeopleStorageStyle('TIPL', involvement='arranger'),
MP4StorageStyle('----:com.apple.iTunes:Arranger'),
StorageStyle('ARRANGER'),
ASFStorageStyle('beets/Arranger'),
)

grouping = MediaField(
MP3StorageStyle('TIT1'),
MP4StorageStyle('\xa9grp'),
Expand Down
2 changes: 2 additions & 0 deletions test/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ def item(lib=None):
albumartist=u'the album artist',
album=u'the album',
genre=u'the genre',
lyricist=u'the lyricist',
composer=u'the composer',
arranger=u'the arranger',
grouping=u'the grouping',
year=1,
month=2,
Expand Down
Binary file modified test/rsrc/unicode’d.mp3
Binary file not shown.
2 changes: 2 additions & 0 deletions test/test_mediafile.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,9 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
'artist',
'album',
'genre',
'lyricist',
'composer',
'arranger',
'grouping',
'year',
'month',
Expand Down