diff --git a/README.md b/README.md
index 6325115..2e63656 100644
--- a/README.md
+++ b/README.md
@@ -85,13 +85,13 @@ Use this as a **pipeline node** โ IMAGE in, IMAGE out. No files are saved to d
### ๐ Smart Batch Resize
-
+
Load **all images from a folder**, resize each one by longest side, and save into a subfolder. Build clean dataset filenames from a prefix, the original name, and/or a sequential counter โ combined with an optional resolution suffix.
-Use this for **bulk preprocessing** โ e.g. preparing a LoRA dataset from a folder of high-res images. It works as a **standalone node**: it needs no upstream input and no downstream connection to do its job โ drop it on the canvas, point it at a folder, hit **Run Batch**, and it reads, resizes, and saves everything itself. Connecting the `images`/`count` outputs is entirely optional, e.g. for previewing results.
+Use this for **bulk preprocessing** โ e.g. preparing a LoRA dataset from a folder of high-res images, or a batch of frames/renders for After Effects where transparency needs to survive the resize. It works as a **standalone node**: it needs no upstream input and no downstream connection to do its job โ drop it on the canvas, point it at a folder, hit **Run Batch**, and it reads, resizes, and saves everything itself. Connecting the `images`/`count` outputs is entirely optional, e.g. for previewing results.
-Includes a **Browse folder** button to pick the input folder directly from the node, and quick presets (512 / 768 / 1024 / 1536) for the longest side โ or type any custom value directly into the field.
+Includes a **Browse folder** button to pick the input folder directly from the node, and quick presets (512 / 768 / 1024 / 1536) for the longest side โ or type any custom value directly into the field. Widgets are grouped into labeled sections (Resize, Filename, Output Location, Output Format, Options) to keep the node scannable despite the number of settings.
| Input | Type | Default | Description |
|---|---|---|---|
@@ -99,15 +99,20 @@ Includes a **Browse folder** button to pick the input folder directly from the n
| Longest side (px) | INT | 1024 | Target size for longest side. Quick presets: 512 / 768 / 1024 / 1536 โ or type any custom value directly into the field |
| Round to multiple of | INT | 8 | Snap dimensions to this multiple |
| Interpolation method | ENUM | lanczos | lanczos / bicubic / bilinear / nearest |
+| Upscale if smaller | BOOLEAN | true | Upscale images smaller than the target longest side. Turn off to only ever downscale, never upscale |
| Filename prefix | STRING | โ | Label prepended to filename โ e.g. `headshot` โ `headshot_photo_001_1024.jpg` |
| Keep original filename | BOOLEAN | true | Include the original filename in the output name |
| Add counter | BOOLEAN | false | Add a sequential 3-digit counter to each filename (001, 002, ...) |
| Counter start | INT | 1 | Starting number for the counter |
| Add resolution to filename | BOOLEAN | true | Append resolution to filename โ e.g. `photo_1024.png` |
+| Filename delimiter | STRING | _ | Separator between filename parts |
| Create resolution subfolder | BOOLEAN | false | Append resolution to subfolder name โ e.g. `resized_1024` |
| Custom output subfolder | STRING | resized | Subfolder name inside the input folder |
-| Skip existing files | BOOLEAN | true | Skip files that already exist in the output folder |
-| Filename delimiter | STRING | _ | Separator between filename parts |
+| Output format | ENUM | keep_source | keep_source / png / webp / jpg โ keep_source preserves each file's original extension; jpg cannot store transparency |
+| Alpha mode | ENUM | auto | auto: keep transparency only if the source has it ยท flatten: always composite onto white and output opaque RGB ยท keep: always output RGBA |
+| Quality | INT | 95 | Compression quality for jpg/webp output (1โ100). Ignored for png (always lossless) |
+| Skip existing files | BOOLEAN | true | Skip files that already exist in the output folder **and** still match the current size settings โ a settings change (e.g. a different longest_side) triggers a reprocess instead of silently serving a stale file |
+| Preview limit | INT | 32 | Max images kept in memory for the preview output. All files are still processed and saved to disk regardless โ this only caps what's returned for preview. 0 = no limit |
**Outputs:** Images (list), Count
@@ -148,6 +153,7 @@ Ready-to-load workflows are included in [`example_workflows/`](example_workflows
- ComfyUI's built-in `Resize Images by Longer Edge` [BETA] has no Lanczos and no `multiple_of` snapping
- `JWImageResizeByLongerSide` (comfyui-various) has no Lanczos in the official release
- No existing node combines batch folder loading + longest-side resize + original filename preservation
+- Smart Batch Resize handles transparency deliberately instead of by accident โ choose whether alpha is preserved, always kept, or flattened onto a solid background, and force a specific output format (png/webp/jpg) independent of the source files' own extensions
- No existing node outputs a ready-to-use `resolution` INT for SeedVR2
- No existing node cycles through multiple prompt lists with automatic list switching and an index
- No existing node switches label + width + height together per category in one place
diff --git a/assets/screenshot-smart-batch-resize.png b/assets/screenshot-smart-batch-resize.png
deleted file mode 100644
index 2037a12..0000000
Binary files a/assets/screenshot-smart-batch-resize.png and /dev/null differ
diff --git a/assets/screenshot-smart-batch-resize_v2.png b/assets/screenshot-smart-batch-resize_v2.png
new file mode 100644
index 0000000..8b6d8c6
Binary files /dev/null and b/assets/screenshot-smart-batch-resize_v2.png differ
diff --git a/js/smart_batch_resize.js b/js/smart_batch_resize.js
index f7379d3..3f3aca7 100644
--- a/js/smart_batch_resize.js
+++ b/js/smart_batch_resize.js
@@ -106,6 +106,41 @@ app.registerExtension({
}
}
+ // Section-header dividers โ visual only, not serialized. Standalone node
+ // with a lot of inputs, so grouping them keeps it scannable at a glance.
+ const addSectionDivider = (beforeWidgetName, label) => {
+ const target = node.widgets.find(w => w.name === beforeWidgetName);
+ if (!target) return;
+ const div = node.addWidget("button", `_div_${label}`, null, () => {}, { serialize: false });
+ div.serialize = false;
+ div.draw = function (ctx, node, widgetWidth, y, height) {
+ const lx = 14;
+ ctx.save();
+ ctx.font = "bold 10px sans-serif";
+ ctx.textBaseline = "middle";
+ ctx.textAlign = "left";
+ ctx.fillStyle = "#888";
+ ctx.fillText(label, lx, y + height / 2);
+ const lineX = lx + ctx.measureText(label).width + 8;
+ ctx.beginPath();
+ ctx.moveTo(lineX, y + height / 2);
+ ctx.lineTo(widgetWidth - 14, y + height / 2);
+ ctx.strokeStyle = "#333";
+ ctx.lineWidth = 1;
+ ctx.stroke();
+ ctx.restore();
+ };
+ const targetIdx = node.widgets.indexOf(target);
+ const divIdx = node.widgets.indexOf(div);
+ node.widgets.splice(divIdx, 1);
+ node.widgets.splice(targetIdx, 0, div);
+ };
+ addSectionDivider("longest_side", "RESIZE");
+ addSectionDivider("prefix", "FILENAME");
+ addSectionDivider("folder_resolution", "OUTPUT LOCATION");
+ addSectionDivider("skip_if_exists", "OPTIONS");
+ addSectionDivider("output_format", "OUTPUT FORMAT");
+
// Override serialize so non-serializable JS widgets (Browse, presets, status,
// run batch) are excluded from widgets_values in the saved workflow JSON.
// Without this, LiteGraph saves null slots for these widgets which then shift
diff --git a/pyproject.toml b/pyproject.toml
index bc6e0d8..1f90bdf 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.3"
+version = "1.1.0"
readme = "README.md"
requires-python = ">=3.10"
license = { file = "LICENSE" }
diff --git a/smart_batch_resize.py b/smart_batch_resize.py
index 460f38a..afe432a 100644
--- a/smart_batch_resize.py
+++ b/smart_batch_resize.py
@@ -152,6 +152,11 @@ class SmartBatchResize:
"default": True,
"tooltip": "Append resolution to filename. E.g. photo_1024.png"
}),
+ "delimiter": ("STRING", {
+ "default": "_",
+ "multiline": False,
+ "tooltip": "Separator between filename parts. E.g. _ or -"
+ }),
# OUTPUT LOCATION
"folder_resolution": ("BOOLEAN", {
@@ -164,21 +169,6 @@ class SmartBatchResize:
"tooltip": "Subfolder name. Resolution is appended if 'Create resolution subfolder' is on."
}),
- # OPTIONS
- "skip_if_exists": ("BOOLEAN", {
- "default": True,
- "tooltip": "Skip files that already exist in the output folder AND still match the current size settings. If longest_side/multiple_of/upscale_if_smaller changed since the file was made, it's reprocessed instead of skipped."
- }),
- "delimiter": ("STRING", {
- "default": "_",
- "multiline": False,
- "tooltip": "Separator between filename parts. E.g. _ or -"
- }),
- "preview_limit": ("INT", {
- "default": 32, "min": 0, "max": 10000, "step": 1,
- "tooltip": "Max images to keep in memory for the preview output. All files are still processed and saved to disk; this only limits what's returned/previewed. 0 = no limit."
- }),
-
# OUTPUT FORMAT
"output_format": (["keep_source", "png", "webp", "jpg"], {
"default": "keep_source",
@@ -192,6 +182,16 @@ class SmartBatchResize:
"default": 95, "min": 1, "max": 100, "step": 1,
"tooltip": "Compression quality for jpg/webp output. Ignored for png (always lossless)."
}),
+
+ # OPTIONS
+ "skip_if_exists": ("BOOLEAN", {
+ "default": True,
+ "tooltip": "Skip files that already exist in the output folder AND still match the current size settings. If longest_side/multiple_of/upscale_if_smaller changed since the file was made, it's reprocessed instead of skipped."
+ }),
+ "preview_limit": ("INT", {
+ "default": 32, "min": 0, "max": 10000, "step": 1,
+ "tooltip": "Max images to keep in memory for the preview output. All files are still processed and saved to disk; this only limits what's returned/previewed. 0 = no limit."
+ }),
}
}