From c0ac823cc8489d6b4a6d92847eb89316b1817d7d Mon Sep 17 00:00:00 2001 From: Terry Jia Date: Fri, 17 Jul 2026 17:59:22 -0400 Subject: [PATCH] fix(view): EXIF orientation, atomic mask outputs, robust hash split --- app/assets/services/preview.py | 8 +++--- server.py | 47 ++++++++++++++++++---------------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/app/assets/services/preview.py b/app/assets/services/preview.py index ffdf7e24b..8e81059a3 100644 --- a/app/assets/services/preview.py +++ b/app/assets/services/preview.py @@ -20,7 +20,7 @@ def get_or_create_preview_file( source_abs_path: str, max_size: int, quality: int ) -> str: source_hash, source_ref_id = _ensure_source_asset(source_abs_path) - hash_hex = source_hash.partition(":")[2] + hash_hex = source_hash.split(":")[-1] preview_dir = folder_paths.get_system_user_directory("preview_cache") preview_path = os.path.join( preview_dir, f"{hash_hex}_{max_size}_q{quality}.webp" @@ -32,10 +32,10 @@ def get_or_create_preview_file( tmp_path = f"{preview_path}.{uuid.uuid4().hex}.tmp" try: with Image.open(source_abs_path) as img: - preview_img = img - if max(img.size) > max_size: + preview_img = ImageOps.exif_transpose(img) + if max(preview_img.size) > max_size: preview_img = ImageOps.contain( - img, (max_size, max_size), Image.Resampling.LANCZOS + preview_img, (max_size, max_size), Image.Resampling.LANCZOS ) preview_img.save(tmp_path, format="webp", quality=quality) os.replace(tmp_path, preview_path) diff --git a/server.py b/server.py index 64a0f8f8c..a6f5803d8 100644 --- a/server.py +++ b/server.py @@ -515,7 +515,8 @@ class PromptServer(): if hasattr(original_pil,'text'): for key in original_pil.text: metadata.add_text(key, original_pil.text[key]) - original_pil = original_pil.convert('RGBA') + + original_pil = ImageOps.exif_transpose(original_pil.convert('RGBA')) original_pil.putalpha(Image.new('L', original_pil.size, 255)) mask_pil = Image.open(image.file).convert('RGBA') @@ -527,7 +528,7 @@ class PromptServer(): masked_pil = original_pil.copy() masked_pil.putalpha(new_alpha) - masked_pil.save(filepath, compress_level=4, pnginfo=metadata) + outputs = [(filepath, masked_pil, {"pnginfo": metadata})] paint = post.get("paint") if paint is not None and paint.file: @@ -545,26 +546,28 @@ class PromptServer(): ("painted_filename", painted_pil, {"pnginfo": metadata}), ("painted_masked_filename", painted_masked_pil, {"pnginfo": metadata}), ] - staged = [] - try: - for field, pil_image, save_kwargs in sibling_outputs: - name = post.get(field) - if not name: - continue - dest = os.path.join(save_dir, os.path.basename(name)) - tmp = f"{dest}.{uuid.uuid4().hex}.tmp" - pil_image.save(tmp, format="PNG", compress_level=4, **save_kwargs) - staged.append((tmp, dest)) - for tmp, dest in staged: - os.replace(tmp, dest) - except Exception: - for tmp, _ in staged: - if os.path.exists(tmp): - try: - os.remove(tmp) - except OSError: - pass - raise + for field, pil_image, save_kwargs in sibling_outputs: + name = post.get(field) + if not name: + continue + outputs.append((os.path.join(save_dir, os.path.basename(name)), pil_image, save_kwargs)) + + staged = [] + try: + for dest, pil_image, save_kwargs in outputs: + tmp = f"{dest}.{uuid.uuid4().hex}.tmp" + pil_image.save(tmp, format="PNG", compress_level=4, **save_kwargs) + staged.append((tmp, dest)) + for tmp, dest in staged: + os.replace(tmp, dest) + except Exception: + for tmp, _ in staged: + if os.path.exists(tmp): + try: + os.remove(tmp) + except OSError: + pass + raise # Compositing large originals can take seconds; keep it off the event loop return await asyncio.get_running_loop().run_in_executor(