mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-20 21:43:36 -07:00
improve image minification logic
This commit is contained in:
parent
ea099a743b
commit
b073758bf7
1 changed files with 48 additions and 32 deletions
|
@ -71,33 +71,41 @@ class ABCMinifier(ABC):
|
||||||
class PillowMinifier(ABCMinifier):
|
class PillowMinifier(ABCMinifier):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _convert_image(
|
def _convert_image(
|
||||||
image_file: Path, image_format: ImageFormat, dest: Path | None = None, quality: int = 100
|
image_file: Path | None = None, image_format: ImageFormat, dest: Path | None = None, quality: int = 100, img: Image.Image | None = None
|
||||||
) -> Path:
|
) -> Path:
|
||||||
"""
|
"""
|
||||||
Converts an image to the specified format in-place. The original image is not
|
Converts an image to the specified format in-place. The original image is not
|
||||||
removed. By default, the quality is set to 100.
|
removed. By default, the quality is set to 100.
|
||||||
"""
|
"""
|
||||||
|
if img is None:
|
||||||
|
if image_file is None:
|
||||||
|
raise ValueError("Must provide either image_file or img.")
|
||||||
|
img = Image.open(image_file)
|
||||||
|
|
||||||
img = Image.open(image_file)
|
|
||||||
img = ImageOps.exif_transpose(img)
|
img = ImageOps.exif_transpose(img)
|
||||||
|
|
||||||
if img.mode not in image_format.modes:
|
if img.mode not in image_format.modes:
|
||||||
img = img.convert(image_format.modes[0])
|
img = img.convert(image_format.modes[0])
|
||||||
|
|
||||||
dest = dest or image_file.with_suffix(image_format.suffix)
|
if dest is None:
|
||||||
|
if image_file is None:
|
||||||
|
raise ValueError("If dest is not provided, image_file must be.")
|
||||||
|
dest = image_file.with_suffix(image_format.suffix)
|
||||||
|
|
||||||
img.save(dest, image_format.format, quality=quality)
|
img.save(dest, image_format.format, quality=quality)
|
||||||
|
|
||||||
return dest
|
return dest
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def to_jpg(image_file: Path, dest: Path | None = None, quality: int = 100) -> Path:
|
def to_jpg(image_file_path: Path | None = None, dest_path: Path | None = None, quality: int = 100, img: Image.Image | None = None) -> Path:
|
||||||
return PillowMinifier._convert_image(image_file, JPG, dest, quality)
|
return PillowMinifier._convert_image(image_file_path, JPG, dest_path, quality, img)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def to_webp(image_file: Path, dest: Path | None = None, quality: int = 100) -> Path:
|
def to_webp(image_file_path: Path | None = None, dest_path: Path | None = None, quality: int = 100, img: Image.Image | None = None) -> Path:
|
||||||
return PillowMinifier._convert_image(image_file, WEBP, dest, quality)
|
return PillowMinifier._convert_image(image_file_path, WEBP, dest_path, quality, img)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def crop_center(pil_img: Image, crop_width=300, crop_height=300):
|
def crop_center(pil_img: Image, crop_width=300, crop_height=300) -> Image.Image:
|
||||||
img_width, img_height = pil_img.size
|
img_width, img_height = pil_img.size
|
||||||
return pil_img.crop(
|
return pil_img.crop(
|
||||||
(
|
(
|
||||||
|
@ -117,36 +125,44 @@ class PillowMinifier(ABCMinifier):
|
||||||
tiny_dest = image_file.parent.joinpath("tiny-original.webp")
|
tiny_dest = image_file.parent.joinpath("tiny-original.webp")
|
||||||
|
|
||||||
if not force and min_dest.exists() and tiny_dest.exists() and org_dest.exists():
|
if not force and min_dest.exists() and tiny_dest.exists() and org_dest.exists():
|
||||||
self._logger.info(f"{image_file.name} already minified")
|
self._logger.info(f"{image_file.name} already exists in all formats")
|
||||||
return
|
return
|
||||||
|
|
||||||
success = False
|
success = False
|
||||||
|
|
||||||
if self._opts.original:
|
try:
|
||||||
if not force and org_dest.exists():
|
with Image.open(image_file) as img:
|
||||||
self._logger.info(f"{image_file.name} already minified")
|
|
||||||
else:
|
|
||||||
PillowMinifier.to_webp(image_file, org_dest, quality=70)
|
|
||||||
success = True
|
|
||||||
|
|
||||||
if self._opts.miniature:
|
if self._opts.original:
|
||||||
if not force and min_dest.exists():
|
if not force and org_dest.exists():
|
||||||
self._logger.info(f"{image_file.name} already minified")
|
self._logger.info(f"{org_dest} already minified")
|
||||||
else:
|
else:
|
||||||
PillowMinifier.to_webp(image_file, min_dest, quality=70)
|
result_path = PillowMinifier.to_webp(dest_path=org_dest, quality=80, img=img.copy())
|
||||||
self._logger.info(f"{image_file.name} minified")
|
self._logger.info(f"{result_path} created")
|
||||||
success = True
|
success = True
|
||||||
|
|
||||||
if self._opts.tiny:
|
if self._opts.miniature:
|
||||||
if not force and tiny_dest.exists():
|
if not force and min_dest.exists():
|
||||||
self._logger.info(f"{image_file.name} already minified")
|
self._logger.info(f"{min_dest} already minified")
|
||||||
else:
|
else:
|
||||||
img = Image.open(image_file)
|
mini = img.copy()
|
||||||
img = ImageOps.exif_transpose(img)
|
mini.thumbnail((1024, 1024), Image.LANCZOS)
|
||||||
tiny_image = PillowMinifier.crop_center(img)
|
result_path = PillowMinifier.to_webp(dest_path=min_dest, quality=70, img=mini)
|
||||||
tiny_image.save(tiny_dest, WEBP.format, quality=70)
|
self._logger.info(f"{result_path} created")
|
||||||
self._logger.info("Tiny image saved")
|
success = True
|
||||||
success = True
|
|
||||||
|
if self._opts.tiny:
|
||||||
|
if not force and tiny_dest.exists():
|
||||||
|
self._logger.info(f"{tiny_dest} already minified")
|
||||||
|
else:
|
||||||
|
tiny_image = PillowMinifier.crop_center(ImageOps.exif_transpose(img.copy()))
|
||||||
|
result_path = PillowMinifier.to_webp(dest_path=tiny_dest, quality=70, img=tiny_image)
|
||||||
|
self._logger.info(f"{result_path} created")
|
||||||
|
success = True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self._logger.error(f"[ERROR] Failed to minify {image_file.name}. Error: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
if self._purge and success:
|
if self._purge and success:
|
||||||
self.purge(image_file)
|
self.purge(image_file)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue