Merge pull request #332 from zas/cover_art_pdf

Improve handling of PDF cover art from CAA
This commit is contained in:
Laurent Monin
2014-05-29 10:10:01 +02:00
6 changed files with 127 additions and 55 deletions

View File

@@ -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.

View File

@@ -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,

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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')