diff --git a/.gitignore b/.gitignore index d1d8744..f687ae9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Local Obsidian notes project-craftkit.md how-to-publish-comfyui-node.md +fable-review-2026-07-02.md # Local design source files assets/*.ai diff --git a/pyproject.toml b/pyproject.toml index 648d22d..af34a83 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui-craftkit" description = "Smart resize, batch resize, resolution multiplier and prompt controller nodes for ComfyUI" -version = "1.0.2" +version = "1.0.3" license = { file = "LICENSE" } dependencies = [] diff --git a/smart_batch_resize.py b/smart_batch_resize.py index 94cad18..5267e99 100644 --- a/smart_batch_resize.py +++ b/smart_batch_resize.py @@ -1,6 +1,6 @@ import os from pathlib import Path -from PIL import Image as PILImage +from PIL import Image as PILImage, ImageOps import numpy as np import torch @@ -132,6 +132,13 @@ class SmartBatchResize: CATEGORY = "CraftKit" OUTPUT_NODE = True + @classmethod + def IS_CHANGED(cls, input_folder, **kwargs): + # Folder contents aren't a widget input, so ComfyUI can't see when files + # are added/removed. Force re-execution every run; skip_if_exists keeps + # already-processed files cheap. + return float("nan") + def run(self, input_folder, longest_side, multiple_of, interpolation, prefix, use_original_name, use_counter, counter_start, suffix_resolution, folder_resolution, folder_custom, @@ -164,6 +171,7 @@ class SmartBatchResize: interp = INTERP_MAP[interpolation] images_out = [] counter_index = 0 + skipped_count = 0 for f in files: stem = _build_stem( @@ -182,10 +190,16 @@ class SmartBatchResize: if skip_if_exists and out_path.exists(): print(f"[SmartBatchResize] Skipped (exists): {out_name}") + skipped_count += 1 counter_index += 1 + existing = PILImage.open(out_path).convert("RGB") + arr = np.array(existing).astype("float32") / 255.0 + images_out.append(torch.from_numpy(arr).unsqueeze(0)) continue - img = PILImage.open(f).convert("RGB") + img = PILImage.open(f) + img = ImageOps.exif_transpose(img) + img = img.convert("RGB") w, h = img.size new_w, new_h = _calc_new_size(w, h, longest_side, multiple_of) @@ -200,10 +214,11 @@ class SmartBatchResize: images_out.append(torch.from_numpy(arr).unsqueeze(0)) counter_index += 1 - if not images_out: - raise ValueError("[SmartBatchResize] No images processed.") - - summary = f"✓ {len(images_out)} images saved → {subfolder}/" + processed_count = len(images_out) - skipped_count + if skipped_count: + summary = f"✓ {processed_count} new, {skipped_count} already existed → {subfolder}/" + else: + summary = f"✓ {processed_count} images saved → {subfolder}/" return {"ui": {"text": [summary]}, "result": (images_out, len(images_out))} diff --git a/smart_resolution_multiplier.py b/smart_resolution_multiplier.py index 4be7178..781aa4e 100644 --- a/smart_resolution_multiplier.py +++ b/smart_resolution_multiplier.py @@ -2,7 +2,7 @@ class SmartResolutionMultiplier: """ Multiplies image dimensions by a factor. Outputs width, height, and resolution (longest side) as INT. - Use resolution output directly with SeedVR max_resolution. + Use resolution output directly with SeedVR2's resolution input. """ @classmethod @@ -10,7 +10,7 @@ class SmartResolutionMultiplier: return { "required": { "image": ("IMAGE", { - "tooltip": "Source image. Its current width/height are read directly — no separate Get Image Size node needed." + "tooltip": "Source image. Its current width/height are read directly, no separate Get Image Size node needed." }), "multiplier": ("FLOAT", { "default": 2.0,