From e18ef5a4ac667e96993ed4028de6f90196318557 Mon Sep 17 00:00:00 2001 From: twodoorcoupe Date: Tue, 11 Jun 2024 11:24:27 +0200 Subject: [PATCH] Compress image only once processing is finished --- picard/const/defaults.py | 3 +++ picard/coverart/__init__.py | 2 +- picard/coverart/image.py | 4 ++-- picard/coverart/processing/__init__.py | 9 ++------- picard/coverart/processing/processors.py | 22 ++++++++++++++++++++-- picard/options.py | 14 ++++++++------ picard/util/imageinfo.py | 6 +++++- test/formats/coverart.py | 4 ++-- test/test_coverart_image.py | 2 +- test/test_coverart_processing.py | 9 +++++---- 10 files changed, 49 insertions(+), 26 deletions(-) diff --git a/picard/const/defaults.py b/picard/const/defaults.py index 560eb0524..43955d6b0 100644 --- a/picard/const/defaults.py +++ b/picard/const/defaults.py @@ -150,3 +150,6 @@ DEFAULT_NUMBERED_TITLE_FORMAT = N_("{title} ({count})") DEFAULT_NAMING_PRESET_ID = "Preset 1" DEFAULT_TIME_FORMAT = '%Y-%m-%d %H:%M:%S' + +DEFAULT_COVER_MIN_SIZE = 250 +DEFAULT_COVER_MAX_SIZE = 1000 diff --git a/picard/coverart/__init__.py b/picard/coverart/__init__.py index c048bccfc..21e25e263 100644 --- a/picard/coverart/__init__.py +++ b/picard/coverart/__init__.py @@ -75,7 +75,7 @@ class CoverArt: if coverartimage.can_be_processed: run_image_processors(data, coverartimage) else: - coverartimage.set_data(data) + coverartimage.set_tags_data(data) if coverartimage.can_be_saved_to_metadata: log.debug("Storing to metadata: %r [%s]", coverartimage, coverartimage.imageinfo_as_string()) diff --git a/picard/coverart/image.py b/picard/coverart/image.py index de9cfcb13..ead924a65 100644 --- a/picard/coverart/image.py +++ b/picard/coverart/image.py @@ -188,7 +188,7 @@ class CoverArtImage: if support_multi_types is not None: self.support_multi_types = support_multi_types if data is not None: - self.set_data(data) + self.set_tags_data(data) try: self.id3_type = id3_type except ValueError: @@ -299,7 +299,7 @@ class CoverArtImage: return 0 return hash(self.datahash.hash()) - def set_data(self, data): + def set_tags_data(self, data): """Store image data in a file, if data already exists in such file it will be re-used and no file write occurs """ diff --git a/picard/coverart/processing/__init__.py b/picard/coverart/processing/__init__.py index 2ba723f88..a3a09db5c 100644 --- a/picard/coverart/processing/__init__.py +++ b/picard/coverart/processing/__init__.py @@ -18,8 +18,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from collections import namedtuple - from picard.coverart.processing import ( # noqa: F401 # pylint: disable=unused-import filters, processors, @@ -35,9 +33,6 @@ from picard.extension_points.cover_art_processors import ( from picard.util import imageinfo -ImageInfo = namedtuple('ImageInfo', ['width', 'height', 'mime', 'extension', 'datalen']) - - class CoverArtProcessingError(Exception): pass @@ -58,7 +53,7 @@ def run_image_metadata_filters(metadata): def run_image_processors(data, coverartimage): try: - info = ImageInfo(*imageinfo.identify(data)) + info = imageinfo.identify(data) both, tags, file = get_cover_art_processors() for processor in both: data = processor.run(data, info, ProcessingTarget.BOTH) @@ -68,7 +63,7 @@ def run_image_processors(data, coverartimage): tags_data = processor.run(tags_data, info, ProcessingTarget.TAGS) for processor in file: file_data = processor.run(file_data, info, ProcessingTarget.FILE) - coverartimage.set_data(tags_data) + coverartimage.set_tags_data(tags_data) coverartimage.set_external_file_data(file_data) except imageinfo.IdentificationError as e: raise CoverArtProcessingError(e) diff --git a/picard/coverart/processing/processors.py b/picard/coverart/processing/processors.py index 47120340b..ca103be17 100644 --- a/picard/coverart/processing/processors.py +++ b/picard/coverart/processing/processors.py @@ -35,7 +35,7 @@ from picard.extension_points.cover_art_processors import ( def _get_image_data(image, image_format): buffer = QBuffer() - image.save(buffer, image_format, quality=100) + image.save(buffer, image_format, quality=90) buffer.close() return buffer.data() @@ -75,7 +75,25 @@ class ResizeImage(ImageProcessor): scaled_image.width(), scaled_image.height() ) - return _get_image_data(scaled_image, info.extension[1:]) + return _get_image_data(scaled_image, "bmp") + + +class ConvertImage(ImageProcessor): + + def save_to_file(self): + return True + + def save_to_tags(self): + return True + + def same_processing(self): + return False + + def run(self, data, info, target): + image = QImage.fromData(data) + extension = info.extension[1:] + return _get_image_data(image, extension) register_cover_art_processor(ResizeImage) +register_cover_art_processor(ConvertImage) diff --git a/picard/options.py b/picard/options.py index a3da85cd9..f2969c83c 100644 --- a/picard/options.py +++ b/picard/options.py @@ -40,6 +40,8 @@ from picard.const.defaults import ( DEFAULT_CAA_IMAGE_TYPE_INCLUDE, DEFAULT_CACHE_SIZE_IN_BYTES, DEFAULT_COVER_IMAGE_FILENAME, + DEFAULT_COVER_MAX_SIZE, + DEFAULT_COVER_MIN_SIZE, DEFAULT_CURRENT_BROWSER_PATH, DEFAULT_DRIVES, DEFAULT_FPCALC_THREADS, @@ -171,14 +173,14 @@ BoolOption('setting', 'save_only_one_front_image', False, title=N_("Save only a # picard/ui/options/cover_processing.py # Cover Art Image Processing BoolOption('setting', 'filter_cover_by_size', False) -IntOption('setting', 'cover_minimum_width', 250) -IntOption('setting', 'cover_minimum_height', 250) +IntOption('setting', 'cover_minimum_width', DEFAULT_COVER_MIN_SIZE) +IntOption('setting', 'cover_minimum_height', DEFAULT_COVER_MIN_SIZE) BoolOption('setting', 'resize_images_saved_to_tags', False) -IntOption('setting', 'cover_tags_maximum_width', 1000) -IntOption('setting', 'cover_tags_maximum_height', 1000) +IntOption('setting', 'cover_tags_maximum_width', DEFAULT_COVER_MAX_SIZE) +IntOption('setting', 'cover_tags_maximum_height', DEFAULT_COVER_MAX_SIZE) BoolOption('setting', 'resize_images_saved_to_file', False) -IntOption('setting', 'cover_file_maximum_width', 1000) -IntOption('setting', 'cover_file_maximum_height', 1000) +IntOption('setting', 'cover_file_maximum_width', DEFAULT_COVER_MAX_SIZE) +IntOption('setting', 'cover_file_maximum_height', DEFAULT_COVER_MAX_SIZE) # picard/ui/options/dialog.py # Attached Profiles diff --git a/picard/util/imageinfo.py b/picard/util/imageinfo.py index 0a75d6611..372cd2c70 100644 --- a/picard/util/imageinfo.py +++ b/picard/util/imageinfo.py @@ -21,12 +21,16 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from collections import namedtuple from io import BytesIO import struct from picard.util.bitreader import LSBBitReader +ImageInfo = namedtuple('ImageInfo', ['width', 'height', 'mime', 'extension', 'datalen']) + + class IdentificationError(Exception): pass @@ -60,7 +64,7 @@ class IdentifyImageType: return self._result() def _result(self): - return (int(self.w), int(self.h), self.mime, self.extension, self.datalen) + return ImageInfo(int(self.w), int(self.h), self.mime, self.extension, self.datalen) def match(self): raise NotImplementedError diff --git a/test/formats/coverart.py b/test/formats/coverart.py index 1b8659479..02797662d 100644 --- a/test/formats/coverart.py +++ b/test/formats/coverart.py @@ -56,9 +56,9 @@ class DummyUnsupportedCoverArt(CoverArtImage): self.width = 100 self.height = 100 self.extension = '.cvr' - self.set_data(data) + self.set_tags_data(data) - def set_data(self, data): + def set_tags_data(self, data): self._data = data self.datalength = len(data) diff --git a/test/test_coverart_image.py b/test/test_coverart_image.py index 78695d277..566ba28d9 100644 --- a/test/test_coverart_image.py +++ b/test/test_coverart_image.py @@ -257,7 +257,7 @@ class CoverArtImageTest(PicardTestCase): self.assertEqual(coverartimage.data, imgdata2) # set data again, with another payload - coverartimage.set_data(imgdata) + coverartimage.set_tags_data(imgdata) tmp_file = coverartimage.tempfile_filename filesize = os.path.getsize(tmp_file) diff --git a/test/test_coverart_processing.py b/test/test_coverart_processing.py index 2bc0763de..8e2b359b6 100644 --- a/test/test_coverart_processing.py +++ b/test/test_coverart_processing.py @@ -26,7 +26,6 @@ from test.picardtestcase import PicardTestCase from picard.coverart.image import CoverArtImage from picard.coverart.processing import ( CoverArtProcessingError, - ImageInfo, run_image_processors, ) from picard.coverart.processing.filters import ( @@ -101,9 +100,9 @@ class ImageProcessorsTest(PicardTestCase): ] for size, expected_size in zip(sizes, expected_sizes): image = create_fake_image(size[0], size[1], "jpg") - info = ImageInfo(*imageinfo.identify(image)) - new_image = processor.run(image, info, ProcessingTarget.TAGS) - new_size = imageinfo.identify(new_image)[:2] + info = imageinfo.identify(image) + new_image = QImage.fromData(processor.run(image, info, ProcessingTarget.TAGS)) + new_size = (new_image.width(), new_image.height()) self.assertEqual(new_size, expected_size) def test_image_processors(self): @@ -133,8 +132,10 @@ class ImageProcessorsTest(PicardTestCase): run_image_processors(image, coverartimage) tags_size = (coverartimage.width, coverartimage.height) file_size = (coverartimage.external_file_coverart.width, coverartimage.external_file_coverart.height) + extension = coverartimage.extension[1:] self.assertEqual(tags_size, expected_size[0]) self.assertEqual(file_size, expected_size[1]) + self.assertEqual(extension, "jpg") def test_identification_error(self): image = create_fake_image(0, 0, "jpg")