Skip to content

Commit

Permalink
MediaFile support for MusicBrainz track, album, and artist IDs
Browse files Browse the repository at this point in the history
  • Loading branch information
sampsyo committed Jul 10, 2010
1 parent f8c317a commit ac98777
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 14 deletions.
73 changes: 63 additions & 10 deletions beets/mediafile.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ class StorageStyle(object):
- key: The Mutagen key used to access the field's data.
- list_elem: Store item as a single object or as first element
of a list.
- as_type: Which type the value is stored as (unicode, int, or
bool).
- as_type: Which type the value is stored as (unicode, int,
bool, or str).
- packing: If this value is packed in a multiple-value storage
unit, which type of packing (in the packing enum). Otherwise,
None. (Makes as_type irrelevant).
Expand All @@ -78,8 +78,8 @@ class StorageStyle(object):
- ID3 storage only: match against this 'desc' field as well
as the key.
"""
def __init__(self, key, list_elem = True, as_type = unicode, packing = None,
pack_pos = 0, id3_desc = None):
def __init__(self, key, list_elem = True, as_type = unicode,
packing = None, pack_pos = 0, id3_desc = None):
self.key = key
self.list_elem = list_elem
self.as_type = as_type
Expand Down Expand Up @@ -214,10 +214,19 @@ def _fetchdata(self, obj, style):
if entry is None: # no desc match
return None
else:
# Get the metadata frame object.
try:
entry = obj.mgfile[style.key].text
frame = obj.mgfile[style.key]
except KeyError:
return None

# For most frame types, the data is in the 'text' field.
# For UFID (used for the MusicBrainz track ID), it's in
# the 'data' field.
if isinstance(frame, mutagen.id3.UFID):
entry = frame.data
else:
entry = frame.text

else: # Not MP3.
try:
Expand All @@ -244,7 +253,8 @@ def _storedata(self, obj, val, style):
else: out = val

if obj.type == 'mp3':
if style.id3_desc is not None: # match on desc field
# Try to match on "desc" field.
if style.id3_desc is not None:
frames = obj.mgfile.tags.getall(style.key)

# try modifying in place
Expand All @@ -258,10 +268,29 @@ def _storedata(self, obj, val, style):
# need to make a new frame?
if not found:
frame = mutagen.id3.Frames[style.key](
encoding=3, desc=style.id3_desc, text=val)
encoding=3,
desc=style.id3_desc,
text=val
)
obj.mgfile.tags.add(frame)

else: # no match on desc; just replace based on key
# Try to match on "owner" field.
elif style.key.startswith('UFID:'):
owner = style.key.split(':', 1)[1]
frames = obj.mgfile.tags.getall(style.key)

for frame in frames:
# Replace existing frame data.
if frame.owner == owner:
frame.data = out
else:
# New frame.
frame = mutagen.id3.UFID(owner=owner, data=val)
obj.mgfile.tags.setall('UFID', [frame])

# Just replace based on key.
else:

frame = mutagen.id3.Frames[style.key](encoding=3, text=val)
obj.mgfile.tags.setall(style.key, [frame])

Expand Down Expand Up @@ -366,8 +395,8 @@ def __set__(self, obj, val):
out = 0
else:
out = int(out)
elif style.as_type == bool:
out = bool(out)
elif style.as_type in (bool, str):
out = style.as_type(out)

# store the data
self._storedata(obj, out, style)
Expand Down Expand Up @@ -592,6 +621,30 @@ def save(self):
as_type = bool),
etc = StorageStyle('compilation')
)

# MusicBrainz IDs.
mb_trackid = MediaField(
mp3 = StorageStyle('UFID:http://musicbrainz.org',
list_elem = False),
mp4 = StorageStyle(
'----:com.apple.iTunes:MusicBrainz Track Id',
as_type=str),
etc = StorageStyle('musicbrainz_trackid')
)
mb_albumid = MediaField(
mp3 = StorageStyle('TXXX', id3_desc='MusicBrainz Album Id'),
mp4 = StorageStyle(
'----:com.apple.iTunes:MusicBrainz Album Id',
as_type=str),
etc = StorageStyle('musicbrainz_albumid')
)
mb_artistid = MediaField(
mp3 = StorageStyle('TXXX', id3_desc='MusicBrainz Artist Id'),
mp4 = StorageStyle(
'----:com.apple.iTunes:MusicBrainz Artist Id',
as_type=str),
etc = StorageStyle('musicbrainz_artistid')
)

@property
def length(self):
Expand Down
Binary file modified test/rsrc/full.ape
Binary file not shown.
Binary file modified test/rsrc/full.flac
Binary file not shown.
Binary file modified test/rsrc/full.m4a
Binary file not shown.
Binary file modified test/rsrc/full.mp3
Binary file not shown.
Binary file modified test/rsrc/full.ogg
Binary file not shown.
19 changes: 15 additions & 4 deletions test/test_mediafile_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ def setUp(self):
self.value = not correct_dict[field]
elif type(correct_dict[field]) is datetime.date:
self.value = correct_dict[field] + datetime.timedelta(42)
elif type(correct_dict[field]) is str:
self.value = 'TestValue-' + str(field)
else:
raise ValueError('unknown field type ' + \
str(type(correct_dict[field])))
Expand Down Expand Up @@ -141,16 +143,22 @@ def tearDown(self):
'lyrics': u'the lyrics',
'comments': u'the comments',
'bpm': 6,
'comp': True
'comp': True,
'mb_trackid': '8b882575-08a5-4452-a7a7-cbb8a1531f9e',
'mb_albumid': '9e873859-8aa4-4790-b985-5a953e8ef628',
'mb_artistid':'7cf0ea9d-86b9-4dad-ba9e-2355a64899ea',
},

# Additional coverage for common cases when "total" fields are unset.
# Created with iTunes.
# Created with iTunes. (Also tests unset MusicBrainz fields.)
'partial': {
'track': 2,
'tracktotal': 0,
'disc': 4,
'disctotal': 0
'disctotal': 0,
'mb_trackid': '',
'mb_albumid': '',
'mb_artistid':'',
},
'min': {
'track': 0,
Expand Down Expand Up @@ -178,7 +186,10 @@ def tearDown(self):
'lyrics': u'',
'comments': u'',
'bpm': 0,
'comp': False
'comp': False,
'mb_trackid': u'',
'mb_albumid': u'',
'mb_artistid':u'',
},

# Full release date.
Expand Down

0 comments on commit ac98777

Please sign in to comment.