diff --git a/picard/coverart.py b/picard/coverart.py index 7f1b3a2ec..4d6ba4f7b 100644 --- a/picard/coverart.py +++ b/picard/coverart.py @@ -74,21 +74,28 @@ class CoverArt: ) try: coverartimage.set_data(data) - log.debug("Cover art image downloaded: %r [%s]" % - ( - coverartimage, - coverartimage.imageinfo_as_string() + if coverartimage.can_be_saved_to_metadata: + log.debug("Cover art image downloaded: %r [%s]" % + ( + coverartimage, + coverartimage.imageinfo_as_string() + ) + ) + self.metadata.append_image(coverartimage) + for track in self.album._new_tracks: + track.metadata.append_image(coverartimage) + # If the image already was a front image, + # there might still be some other non-CAA front + # images in the queue - ignore them. + if not self.front_image_found: + self.front_image_found = coverartimage.is_front_image() + else: + log.debug("Thumbnail for cover art image downloaded: %r [%s]" % + ( + coverartimage, + coverartimage.imageinfo_as_string() + ) ) - ) - self.metadata.append_image(coverartimage) - for track in self.album._new_tracks: - track.metadata.append_image(coverartimage) - # If the image already was a front image, - # there might still be some other non-CAA front - # images in the queue - ignore them. - if not self.front_image_found: - self.front_image_found = coverartimage.is_front_image() - except CoverArtImageIOError as e: self.album.error_append(unicode(e)) self.album._finalize_loading(error=True) @@ -100,7 +107,6 @@ class CoverArt: self.download_next_in_queue() - def download_next_in_queue(self): """Downloads next item in queue. If there are none left, loading of album will be finalized. diff --git a/picard/coverartimage.py b/picard/coverartimage.py index c1d5fa0bd..735d450e6 100644 --- a/picard/coverartimage.py +++ b/picard/coverartimage.py @@ -124,6 +124,11 @@ class CoverArtImage: self.types = types self.comment = comment self.datahash = None + # thumbnail is used to link to another CoverArtImage, ie. for PDFs + self.thumbnail = None + self.can_be_saved_to_tags = True + self.can_be_saved_to_disk = True + self.can_be_saved_to_metadata = True if data is not None: self.set_data(data) @@ -150,6 +155,9 @@ class CoverArtImage: - if `support_types` is False, default to True for any image - if `support_types` is True, default to False for any image """ + if not self.can_be_saved_to_metadata: + # ignore thumbnails + return False if self.is_front is not None: return self.is_front if u'front' in self.types: @@ -246,6 +254,8 @@ class CoverArtImage: :counters: A dictionary mapping filenames to the amount of how many images with that filename were already saved in `dirname`. """ + if not self.can_be_saved_to_disk: + return if config.setting["caa_image_type_as_filename"]: filename = self.maintype log.debug("Make cover filename from types: %r -> %r", @@ -294,10 +304,13 @@ class CoverArtImage: @property def data(self): - """Reads the data from the temporary file created for this image. May - raise IOErrors or OSErrors. + """Reads the data from the temporary file created for this image. + May raise CoverArtImageIOError """ - return self.datahash.data + try: + return self.datahash.data + except (OSError, IOError) as e: + raise CoverArtImageIOError(e) @property def tempfile_filename(self): @@ -317,12 +330,35 @@ class CoverArtImage: class CaaCoverArtImage(CoverArtImage): + """Image from Cover Art Archive""" + support_types = True sourceprefix = u"CAA" + def __init__(self, url, types=[], is_front=False, comment='', data=None): + CoverArtImage.__init__(self, url=url, types=types, comment=comment, + data=data) + self.is_front = is_front + + +class CaaThumbnailCoverArtImage(CaaCoverArtImage): + + """Used for thumbnails of CaaCoverArtImage objects, together with thumbnail + property""" + + def __init__(self, url, types=[], is_front=False, comment='', data=None): + CaaCoverArtImage.__init__(self, url=url, types=types, comment=comment, + data=data) + self.is_front = False + self.can_be_saved_to_disk = False + self.can_be_saved_to_tags = False + self.can_be_saved_to_metadata = False + class TagCoverArtImage(CoverArtImage): + """Image from file tags""" + def __init__(self, file, tag=None, types=[], is_front=None, support_types=False, comment='', data=None): CoverArtImage.__init__(self, url=None, types=types, comment=comment, diff --git a/picard/coverartproviders/caa.py b/picard/coverartproviders/caa.py index f258326ea..a3140e2b6 100644 --- a/picard/coverartproviders/caa.py +++ b/picard/coverartproviders/caa.py @@ -27,7 +27,7 @@ import traceback from picard import config, log from picard.const import CAA_HOST, CAA_PORT from picard.coverartproviders import CoverArtProvider -from picard.coverartimage import CaaCoverArtImage +from picard.coverartimage import CaaCoverArtImage, CaaThumbnailCoverArtImage _CAA_THUMBNAIL_SIZE_MAP = { @@ -128,9 +128,16 @@ class CoverArtProviderCaa(CoverArtProvider): except ValueError: self.error("Invalid JSON: %s", http.url().toString()) else: + imagesize = config.setting["caa_image_size"] + thumbsize = _CAA_THUMBNAIL_SIZE_MAP.get(imagesize, None) for image in caa_data["images"]: if config.setting["caa_approved_only"] and not image["approved"]: continue + is_pdf = image["image"].endswith('.pdf') + if is_pdf and not config.setting["save_images_to_files"]: + log.debug("Skipping pdf cover art : %s" % + image["image"]) + continue # if image has no type set, we still want it to match # pseudo type 'unknown' if not image["types"]: @@ -141,23 +148,29 @@ class CoverArtProviderCaa(CoverArtProvider): types = set(image["types"]).intersection( set(self.caa_types)) if types: - self._queue_from_caa(image) + if thumbsize is None or is_pdf: + url = image["image"] + else: + url = image["thumbnails"][thumbsize] + coverartimage = CaaCoverArtImage( + url, + types=image["types"], + is_front=image['front'], + comment=image["comment"], + ) + if is_pdf: + # thumbnail will be used to "display" PDF in info + # dialog + thumbnail = CaaThumbnailCoverArtImage( + url=image["thumbnails"]['small'], + types=image["types"], + is_front=image['front'], + comment=image["comment"], + ) + self.queue_put(thumbnail) + coverartimage.thumbnail = thumbnail + # PDFs cannot be saved to tags (as 2014/05/29) + coverartimage.can_be_saved_to_tags = False + self.queue_put(coverartimage) self.next_in_queue() - - def _queue_from_caa(self, image): - """Queue images depending on the CAA image size settings.""" - imagesize = config.setting["caa_image_size"] - thumbsize = _CAA_THUMBNAIL_SIZE_MAP.get(imagesize, None) - if thumbsize is None: - url = image["image"] - else: - url = image["thumbnails"][thumbsize] - coverartimage = CaaCoverArtImage( - url, - types=image["types"], - comment=image["comment"], - ) - # front image indicator from CAA - coverartimage.is_front = bool(image['front']) - self.queue_put(coverartimage) diff --git a/picard/metadata.py b/picard/metadata.py index 31f1c11be..fb5e880c0 100644 --- a/picard/metadata.py +++ b/picard/metadata.py @@ -55,13 +55,14 @@ class Metadata(dict): def images_to_be_saved_to_tags(self): if not config.setting["save_images_to_tags"]: return () + images = [img for img in self.images if img.can_be_saved_to_tags] if config.setting["save_only_front_images_to_tags"]: # FIXME : rename option at some point # Embed only ONE front image - for img in self.images: + for img in images: if img.is_front_image(): return [img] - return self.images + return images def remove_image(self, index): self.images.pop(index) diff --git a/picard/ui/infodialog.py b/picard/ui/infodialog.py index 4063bfb3a..0c5d24e47 100644 --- a/picard/ui/infodialog.py +++ b/picard/ui/infodialog.py @@ -23,6 +23,7 @@ import traceback from PyQt4 import QtGui, QtCore from picard import log from picard.coverartarchive import translate_caa_type +from picard.coverartimage import CoverArtImageIOError from picard.util import format_time, encode_filename, bytes2human from picard.ui import PicardDialog from picard.ui.ui_infodialog import Ui_InfoDialog @@ -52,27 +53,36 @@ class InfoDialog(PicardDialog): return for image in images: + data = None try: - data = image.data - except (OSError, IOError) as e: + if image.thumbnail: + try: + data = image.thumbnail.data + except CoverArtImageIOError as e: + log.warning(unicode(e)) + pass + else: + data = image.data + except CoverArtImageIOError: log.error(traceback.format_exc()) continue - size = image.datalength item = QtGui.QListWidgetItem() - pixmap = QtGui.QPixmap() - pixmap.loadFromData(data) - icon = QtGui.QIcon(pixmap) - item.setIcon(icon) - s = u"%s (%s)\n%d x %d\n%s" % ( - bytes2human.decimal(size), - bytes2human.binary(size), - pixmap.width(), - pixmap.height(), - image.types_as_string() - ) + if data is not None: + pixmap = QtGui.QPixmap() + pixmap.loadFromData(data) + icon = QtGui.QIcon(pixmap) + item.setIcon(icon) + infos = [] + infos.append(image.types_as_string()) if image.comment: - s += u"\n%s" % image.comment - item.setText(s) + infos.append(image.comment) + infos.append(u"%s (%s)" % + (bytes2human.decimal(image.datalength), + bytes2human.binary(image.datalength))) + if image.width and image.height: + infos.append(u"%d x %d" % (image.width, image.height)) + infos.append(image.mimetype) + item.setText(u"\n".join(infos)) item.setToolTip(image.source) self.ui.artwork_list.addItem(item) diff --git a/picard/util/imageinfo.py b/picard/util/imageinfo.py index df5ad1c89..6b1ab6bf3 100644 --- a/picard/util/imageinfo.py +++ b/picard/util/imageinfo.py @@ -105,6 +105,12 @@ def identify(data): except ValueError: pass + # PDF + elif data[:4] == '%PDF': + h, w = 0, 0 + mime = 'application/pdf' + extension = '.pdf' + else: raise UnrecognizedFormat('Unrecognized image data')