From c435fd744653b642011afca04add618395474c5d Mon Sep 17 00:00:00 2001 From: Thomas Scholtes Date: Sun, 9 Mar 2014 12:44:47 +0100 Subject: [PATCH] Implement ImageListField for MP4 --- beets/mediafile.py | 67 +++++++++-------------------------------- test/rsrc/image.m4a | Bin 0 -> 5862 bytes test/test_mediafile.py | 10 ++++-- 3 files changed, 22 insertions(+), 55 deletions(-) create mode 100644 test/rsrc/image.m4a diff --git a/beets/mediafile.py b/beets/mediafile.py index ae97e46cb..209ad407d 100644 --- a/beets/mediafile.py +++ b/beets/mediafile.py @@ -495,28 +495,16 @@ class MP4ImageStorageStyle(MP4ListStorageStyle): def __init__(self, **kwargs): super(MP4ImageStorageStyle, self).__init__(key='covr', **kwargs) - self.as_type = str - def store(self, mutagen_file, images): - covers = [self._mp4_cover(image) for image in images] - mutagen_file['covr'] = covers + def get_list(self, mutagen_file): + return [TagImage(data) for data in self.fetch(mutagen_file)] - @classmethod - def _mp4_cover(cls, data): - """Make ``MP4Cover`` tag from image data. - - Returns instance of ``mutagen.mp4.MP4Cover`` with correct cover - format. - """ - kind = imghdr.what(None, h=data) - if kind == 'png': + def serialize(self, image): + if image.mime_type == 'image/png': kind = mutagen.mp4.MP4Cover.FORMAT_PNG - elif kind == 'jpeg': + elif image.mime_type == 'image/jpeg': kind = mutagen.mp4.MP4Cover.FORMAT_JPEG - else: - raise ValueError('MP4 only supports PNG and JPEG images') - - return mutagen.mp4.MP4Cover(data, kind) + return mutagen.mp4.MP4Cover(image.data, kind) class MP3StorageStyle(StorageStyle): @@ -950,45 +938,20 @@ class DateItemField(MediaField): class CoverArtField(MediaField): - """A descriptor providing access to a file's embedded album art. - Holds a bytestring reflecting the image data. The image should - either be a JPEG or a PNG for cross-format compatibility. It's - probably a bad idea to use anything but these two formats. - """ - # TODO make this into shim when ImageField is implemented for all - # formats. - def __init__(self): - super(CoverArtField, self).__init__( - MP3ImageStorageStyle(), - MP4ImageStorageStyle(), - ASFImageStorageStyle(), - VorbisImageStorageStyle(), - FlacImageStorageStyle(), - out_type=str, - ) + pass def __get__(self, mediafile, _): - if mediafile.type in ['mp3', 'flac', 'asf'] + VorbisImageStorageStyle.formats: - try: - return mediafile.images[0].data - except IndexError: - return None - for style in self.styles(mediafile): - return style.get(mediafile.mgfile) + try: + return mediafile.images[0].data + except IndexError: + return None def __set__(self, mediafile, data): - if mediafile.type in ['mp3', 'flac', 'asf'] + VorbisImageStorageStyle.formats: - if data: - mediafile.images = [TagImage(data=data)] - else: - mediafile.images = [] - return - if data is not None: - if not isinstance(data, str): - raise ValueError('value must be a byte string or None') - for style in self.styles(mediafile): - style.set(mediafile.mgfile, data) + if data: + mediafile.images = [TagImage(data=data)] + else: + mediafile.images = [] class ImageListField(MediaField): diff --git a/test/rsrc/image.m4a b/test/rsrc/image.m4a new file mode 100644 index 0000000000000000000000000000000000000000..89704d557bb3ffac704b5309f2c946e3942a3490 GIT binary patch literal 5862 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 TZ{U