Skip to content

Commit

Permalink
Added support for APEv2 cover tags
Browse files Browse the repository at this point in the history
  • Loading branch information
kiefermat committed Oct 27, 2014
1 parent da1624d commit f8abb03
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 2 deletions.
67 changes: 66 additions & 1 deletion beets/mediafile.py
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,7 @@ class VorbisImageStorageStyle(ListStorageStyle):
base64-encoded. Values are `Image` objects.
"""
formats = ['OggOpus', 'OggTheora', 'OggSpeex', 'OggVorbis',
'OggFlac', 'APEv2File', 'WavPack', 'Musepack', 'MonkeysAudio']
'OggFlac']

def __init__(self):
super(VorbisImageStorageStyle, self).__init__(
Expand Down Expand Up @@ -950,6 +950,70 @@ def delete(self, mutagen_file):
mutagen_file.clear_pictures()


class APEv2ImageStorageStyle(ListStorageStyle):
"""Store images in APEv2 tags. Values are `Image` objects.
"""
formats = ['APEv2File', 'WavPack', 'Musepack', 'MonkeysAudio', 'OptimFROG']

_APE_COVER_TAG_NAMES = {ImageType.other: 'Cover Art (other)',
ImageType.icon: 'Cover Art (icon)',
ImageType.other_icon: 'Cover Art (other icon)',
ImageType.front: 'Cover Art (front)',
ImageType.back: 'Cover Art (back)',
ImageType.leaflet: 'Cover Art (leaflet)',
ImageType.media: 'Cover Art (media)',
ImageType.lead_artist: 'Cover Art (lead)',
ImageType.artist: 'Cover Art (artist)',
ImageType.conductor: 'Cover Art (conductor)',
ImageType.group: 'Cover Art (band)',
ImageType.composer: 'Cover Art (composer)',
ImageType.lyricist: 'Cover Art (lyricist)',
ImageType.recording_location: 'Cover Art (studio)',
ImageType.recording_session: 'Cover Art (recording)',
ImageType.performance: 'Cover Art (performance)',
ImageType.screen_capture: 'Cover Art (movie scene)',
ImageType.fish: 'Cover Art (colored fish)',
ImageType.illustration: 'Cover Art (illustration)',
ImageType.artist_logo: 'Cover Art (band logo)',
ImageType.publisher_logo: 'Cover Art (publisher logo)'
}

def __init__(self):
super(APEv2ImageStorageStyle, self).__init__(key='')

def fetch(self, mutagen_file):
images = []
for cover_type, cover_tag in APEv2ImageStorageStyle._APE_COVER_TAG_NAMES.iteritems():
try:
frame = mutagen_file[cover_tag]
text_delimiter_index = frame.value.find('\x00')
comment = frame.value[0:text_delimiter_index] if text_delimiter_index > 0 else None
image_data = frame.value[text_delimiter_index+1:]
images.append(Image(data=image_data, type=cover_type, desc=comment))
except KeyError:
pass

return images

def set_list(self, mutagen_file, values):
self.delete(mutagen_file)

for image in values:
image_type = image.type if image.type is not None else ImageType.other
comment = image.desc if image.desc else ''
image_data = comment + "\x00" + image.data
cover_tag = APEv2ImageStorageStyle._APE_COVER_TAG_NAMES[image_type]
mutagen_file[cover_tag] = image_data

def delete(self, mutagen_file):
"""Remove all images from the file.
"""
for cover_tag in APEv2ImageStorageStyle._APE_COVER_TAG_NAMES.itervalues():
try:
del mutagen_file[cover_tag]
except KeyError:
pass

# MediaField is a descriptor that represents a single logical field. It
# aggregates several StorageStyles describing how to access the data for
# each file type.
Expand Down Expand Up @@ -1206,6 +1270,7 @@ def __init__(self):
ASFImageStorageStyle(),
VorbisImageStorageStyle(),
FlacImageStorageStyle(),
APEv2ImageStorageStyle(),
)

def __get__(self, mediafile, _):
Expand Down
Binary file added test/rsrc/image.ape
Binary file not shown.
3 changes: 2 additions & 1 deletion test/test_mediafile.py
Original file line number Diff line number Diff line change
Expand Up @@ -822,7 +822,8 @@ class FlacTest(ReadWriteTestBase, PartialTestMixin,
}


class ApeTest(ReadWriteTestBase, unittest.TestCase):
class ApeTest(ReadWriteTestBase, ExtendedImageStructureTestMixin,
unittest.TestCase):
extension = 'ape'
audio_properties = {
'length': 1.0,
Expand Down

0 comments on commit f8abb03

Please sign in to comment.