Let batch results keep their original filenames
Load Image Folder now also returns the source filenames, one per image, appended to the existing list outputs so they stay index-aligned (an image that fails to load is skipped from all three, otherwise downstream pairing silently shifts). Custom Save Image gained an exact_filename toggle. It always appended a timestamp and an index to the prefix, so feeding it an original name still produced photo_1726824000000_000.png. With the toggle on the prefix is the filename. The name reaching that toggle comes from an input, so it is reduced to a basename before use and can't escape the output directory, and a colliding name gets a _1 suffix rather than overwriting what's there. Closes #24.
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## [未发布 Unreleased]
|
||||
- 批量图片载入节点新增 `filenames` 输出(原文件名),配合自定义图像保存节点新增的 `exact_filename` 开关,即可按原文件名保存批处理结果
|
||||
- Load Image Folder gained a `filenames` output, and Custom Save Image an `exact_filename` toggle, so batch results can be saved under their original names
|
||||
|
||||
## [0.71.0] - 2026-09-20
|
||||
- 自定义图标新增开关,关闭后恢复 ComfyUI 默认图标;并把该功能的措辞统一为「浏览器标签页图标」——它一直只改标签页图标,不会替换界面左上角的 ComfyUI 标志
|
||||
- Added a toggle for the custom icon, which restores ComfyUI's own icon when off, and renamed the feature to "browser tab icon": it only ever changed the tab icon, never the ComfyUI logo in the interface
|
||||
|
||||
@@ -8,6 +8,10 @@ This is a custom node utility package developed for ComfyUI. Initially created f
|
||||
|
||||
## 最新更新 / Last Update
|
||||
|
||||
### [未发布 Unreleased]
|
||||
- 批量图片载入节点新增 `filenames` 输出(原文件名),配合自定义图像保存节点新增的 `exact_filename` 开关,即可按原文件名保存批处理结果
|
||||
- Load Image Folder gained a `filenames` output, and Custom Save Image an `exact_filename` toggle, so batch results can be saved under their original names
|
||||
|
||||
### [0.71.0] - 2026-09-20
|
||||
- 自定义图标新增开关,关闭后恢复 ComfyUI 默认图标;并把该功能的措辞统一为「浏览器标签页图标」——它一直只改标签页图标,不会替换界面左上角的 ComfyUI 标志
|
||||
- Added a toggle for the custom icon, which restores ComfyUI's own icon when off, and renamed the feature to "browser tab icon": it only ever changed the tab icon, never the ComfyUI logo in the interface
|
||||
@@ -158,6 +162,8 @@ https://github.com/user-attachments/assets/b8d1d3f0-04d1-46c5-968a-e433778b73e6
|
||||
Provide batch loading and batch processing of images.
|
||||
- 支持 image 和 mask 的批量输出。
|
||||
Support batch output of images and masks.
|
||||
- 新增 `filenames` 输出,逐张给出图片的原文件名(不含扩展名)。配合 𝙆 Custom Save Image 的 `exact_filename` 开关,即可按原文件名保存处理结果。
|
||||
Adds a `filenames` output with each image's original name (without extension). Pair it with the `exact_filename` toggle on 𝙆 Custom Save Image to save results under their original names.
|
||||
|
||||
## 无线数据传输节点 Set & Get
|
||||
|
||||
@@ -264,6 +270,8 @@ https://github.com/user-attachments/assets/b8d1d3f0-04d1-46c5-968a-e433778b73e6
|
||||
- Provides high-quality JPG saving options with customizable compression quality.
|
||||
- Automatically generates unique filenames to prevent overwriting existing files.
|
||||
- Suitable for workflows requiring precise control over output images.
|
||||
- `exact_filename` 开关:开启后 `filename_prefix` 就是最终文件名,不再追加时间戳。重名会自动加 `_1`、`_2` 后缀,不会覆盖已有文件。
|
||||
`exact_filename`: when on, `filename_prefix` is used as the final filename with no timestamp appended. Existing files are never overwritten — a `_1`, `_2` suffix is added instead.
|
||||
|
||||
### 变量使用说明 Variable Usage Notes
|
||||
|
||||
|
||||
@@ -28,6 +28,11 @@ class CustomSaveImage:
|
||||
"author": ("STRING", {"default": ""}),
|
||||
"copyright_info": ("STRING", {"default": ""}),
|
||||
"save_metadata": ("BOOLEAN", {"default": False}),
|
||||
"exact_filename": ("BOOLEAN", {
|
||||
"default": False,
|
||||
"tooltip": "Use filename_prefix as the exact filename instead of appending a timestamp. "
|
||||
"Connect the 'filenames' output of Load Image Folder to save with the original names."
|
||||
}),
|
||||
},
|
||||
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
||||
}
|
||||
@@ -38,7 +43,8 @@ class CustomSaveImage:
|
||||
CATEGORY = "KayTool"
|
||||
|
||||
def save_images(self, images, preview_only=False, filename_prefix="Custom_Save_Image", save_metadata=True, format="PNG",
|
||||
jpg_quality=95, author="", copyright_info="", color_profile="sRGB IEC61966-2.1", prompt=None, extra_pnginfo=None):
|
||||
jpg_quality=95, author="", copyright_info="", color_profile="sRGB IEC61966-2.1", exact_filename=False,
|
||||
prompt=None, extra_pnginfo=None):
|
||||
temp_dir = folder_paths.get_temp_directory()
|
||||
os.makedirs(temp_dir, exist_ok=True)
|
||||
|
||||
@@ -123,8 +129,11 @@ class CustomSaveImage:
|
||||
})
|
||||
|
||||
if not preview_only:
|
||||
final_filename = f"{base_prefix}_{self.get_unique_filename(idx)}.{format.lower()}"
|
||||
final_full_path = os.path.join(output_dir, final_filename)
|
||||
if exact_filename:
|
||||
final_full_path = self.resolve_exact_path(output_dir, base_prefix, format.lower())
|
||||
else:
|
||||
final_filename = f"{base_prefix}_{self.get_unique_filename(idx)}.{format.lower()}"
|
||||
final_full_path = os.path.join(output_dir, final_filename)
|
||||
shutil.copy2(temp_full_path, final_full_path)
|
||||
|
||||
return {"ui": {"images": results}, "status": "Images saved successfully"}
|
||||
@@ -139,6 +148,19 @@ class CustomSaveImage:
|
||||
with open(path, "rb") as f:
|
||||
return f.read()
|
||||
|
||||
def resolve_exact_path(self, output_dir, name, ext):
|
||||
"""把 base_prefix 当成完整文件名用,但不允许它逃出 output_dir,也不覆盖已有文件。"""
|
||||
# 文件名可能来自上游输入,必须剥掉路径成分,挡住 ../ 之类的写法
|
||||
name = os.path.basename(name.replace("\\", "/").rstrip("/")).strip()
|
||||
name = name.lstrip(".") or "Custom_Save_Image"
|
||||
|
||||
candidate = os.path.join(output_dir, f"{name}.{ext}")
|
||||
counter = 1
|
||||
while os.path.exists(candidate):
|
||||
candidate = os.path.join(output_dir, f"{name}_{counter}.{ext}")
|
||||
counter += 1
|
||||
return candidate
|
||||
|
||||
def get_unique_filename(self, idx):
|
||||
import time
|
||||
return f"{int(time.time() * 1000)}_{idx:03d}"
|
||||
|
||||
@@ -21,9 +21,9 @@ class LoadImageFolder:
|
||||
}
|
||||
|
||||
CATEGORY = "KayTool"
|
||||
RETURN_TYPES = ("IMAGE", "MASK")
|
||||
RETURN_NAMES = ("images", "masks")
|
||||
OUTPUT_IS_LIST = (True, True)
|
||||
RETURN_TYPES = ("IMAGE", "MASK", "STRING")
|
||||
RETURN_NAMES = ("images", "masks", "filenames")
|
||||
OUTPUT_IS_LIST = (True, True, True)
|
||||
FUNCTION = "load_images"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
@@ -57,6 +57,9 @@ class LoadImageFolder:
|
||||
|
||||
images = []
|
||||
masks = []
|
||||
# 与 images 严格同序同长:某张图读失败时三者一起跳过,
|
||||
# 否则下游按索引配对就会张冠李戴。
|
||||
filenames = []
|
||||
|
||||
|
||||
for image_path in image_files:
|
||||
@@ -78,6 +81,8 @@ class LoadImageFolder:
|
||||
|
||||
images.append(image_tensor)
|
||||
masks.append(mask.unsqueeze(0))
|
||||
# 不带扩展名:下游保存节点自己决定存成 PNG 还是 JPG
|
||||
filenames.append(os.path.splitext(os.path.basename(image_path))[0])
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error loading {image_path}: {str(e)}")
|
||||
@@ -87,9 +92,9 @@ class LoadImageFolder:
|
||||
if not images:
|
||||
placeholder_image = torch.zeros((1, 64, 64, 3), dtype=torch.float32)
|
||||
placeholder_mask = torch.zeros((1, 64, 64), dtype=torch.float32)
|
||||
return ([placeholder_image], [placeholder_mask])
|
||||
return ([placeholder_image], [placeholder_mask], ["placeholder"])
|
||||
|
||||
return (images, masks)
|
||||
return (images, masks, filenames)
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(s, path, subfolders):
|
||||
|
||||
Reference in New Issue
Block a user