From d0c3aae2c719e7e6e102bbaaa58c6e9e2457d2e9 Mon Sep 17 00:00:00 2001 From: hay-kot Date: Fri, 2 Apr 2021 17:26:24 -0800 Subject: [PATCH] add image minification --- mealie/services/backups/exports.py | 3 +- mealie/services/backups/imports.py | 9 +++- mealie/services/image/__init__.py | 0 mealie/services/image/minify.py | 82 ++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 mealie/services/image/__init__.py create mode 100644 mealie/services/image/minify.py diff --git a/mealie/services/backups/exports.py b/mealie/services/backups/exports.py index 52dffa018..86220ae85 100644 --- a/mealie/services/backups/exports.py +++ b/mealie/services/backups/exports.py @@ -65,8 +65,7 @@ class ExportDatabase: f.write(content) def export_images(self): - for file in app_dirs.IMG_DIR.iterdir(): - shutil.copy(file, self.img_dir.joinpath(file.name)) + shutil.copytree(app_dirs.IMG_DIR, self.img_dir, dirs_exist_ok=True) def export_items(self, items: list[BaseModel], folder_name: str, export_list=True): items = [x.dict() for x in items] diff --git a/mealie/services/backups/imports.py b/mealie/services/backups/imports.py index 77842f860..2b8bf33e5 100644 --- a/mealie/services/backups/imports.py +++ b/mealie/services/backups/imports.py @@ -11,6 +11,7 @@ from mealie.schema.restore import CustomPageImport, GroupImport, RecipeImport, S from mealie.schema.settings import CustomPageOut, SiteSettings from mealie.schema.theme import SiteTheme from mealie.schema.user import UpdateGroup, UserInDB +from mealie.services.image import minify from pydantic.main import BaseModel from sqlalchemy.orm.session import Session @@ -108,7 +109,13 @@ class ImportDatabase: image_dir = self.import_dir.joinpath("images") for image in image_dir.iterdir(): if image.stem in successful_imports: - shutil.copy(image, app_dirs.IMG_DIR) + if image.is_dir(): + dest = app_dirs.IMG_DIR.joinpath(image.stem) + shutil.copytree(image, dest, dirs_exist_ok=True) + if image.is_file(): + shutil.copy(image, app_dirs.IMG_DIR) + + minify.migrate_images() def import_themes(self): themes_file = self.import_dir.joinpath("themes", "themes.json") diff --git a/mealie/services/image/__init__.py b/mealie/services/image/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/mealie/services/image/minify.py b/mealie/services/image/minify.py new file mode 100644 index 000000000..09b7203cc --- /dev/null +++ b/mealie/services/image/minify.py @@ -0,0 +1,82 @@ +from pathlib import Path + +from mealie.core.config import app_dirs +from PIL import Image, UnidentifiedImageError + + +def minify_image(my_path: Path, min_dest: Path, tiny_dest: Path): + """Minifies an image in it's original file format. Quality is lost + + Args: + my_path (Path): Source Files + min_dest (Path): FULL Destination File Path + tiny_dest (Path): FULL Destination File Path + """ + try: + img = Image.open(my_path) + basewidth = 720 + wpercent = basewidth / float(img.size[0]) + hsize = int((float(img.size[1]) * float(wpercent))) + img = img.resize((basewidth, hsize), Image.ANTIALIAS) + img.save(min_dest, quality=70) + + tiny_image = crop_center(img) + tiny_image.save(tiny_dest, quality=70) + + except UnidentifiedImageError: + pass + + +def crop_center(pil_img, crop_width=300, crop_height=300): + img_width, img_height = pil_img.size + return pil_img.crop( + ( + (img_width - crop_width) // 2, + (img_height - crop_height) // 2, + (img_width + crop_width) // 2, + (img_height + crop_height) // 2, + ) + ) + + +def sizeof_fmt(size, decimal_places=2): + for unit in ["B", "kB", "MB", "GB", "TB", "PB"]: + if size < 1024.0 or unit == "PiB": + break + size /= 1024.0 + return f"{size:.{decimal_places}f} {unit}" + + +def move_all_images(): + for image_file in app_dirs.IMG_DIR.iterdir(): + if image_file.is_file(): + new_folder = app_dirs.IMG_DIR.joinpath(image_file.stem) + new_folder.mkdir(parents=True, exist_ok=True) + image_file.rename(new_folder.joinpath(f"original{image_file.suffix}")) + + +def migrate_images(): + print("Checking for Images to Minify...") + + move_all_images() + + # Minify Loop + for image in app_dirs.IMG_DIR.glob("*/original.*"): + min_dest = image.parent.joinpath(f"min-original{image.suffix}") + tiny_dest = image.parent.joinpath(f"tiny-original{image.suffix}") + + if min_dest.exists() and tiny_dest.exists(): + continue + + minify_image(image, min_dest, tiny_dest) + + org_size = sizeof_fmt(image.stat().st_size) + dest_size = sizeof_fmt(min_dest.stat().st_size) + tiny_size = sizeof_fmt(tiny_dest.stat().st_size) + print(f"{image.name} Minified: {org_size} -> {dest_size} -> {tiny_size}") + + print("Finished Minification Check") + + +if __name__ == "__main__": + migrate_images()