From 4d4701893f60b6060f386c9ea50d856d0bc4b723 Mon Sep 17 00:00:00 2001 From: lif <1835304752@qq.com> Date: Thu, 18 Dec 2025 00:36:38 +0800 Subject: [PATCH] fix: remove trailing underscore from output filenames Fixes #1389 Removed the unnecessary trailing underscore from output filenames. - Old format: ComfyUI_00001_.png - New format: ComfyUI_00001.png Updated get_save_image_path to support both formats for backward compatibility with existing files. --- folder_paths.py | 19 +++- nodes.py | 4 +- .../folder_paths_test/test_filename_format.py | 91 +++++++++++++++++++ 3 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 tests-unit/folder_paths_test/test_filename_format.py diff --git a/folder_paths.py b/folder_paths.py index 9c96540e3..88760775d 100644 --- a/folder_paths.py +++ b/folder_paths.py @@ -430,7 +430,11 @@ def get_save_image_path(filename_prefix: str, output_dir: str, image_width=0, im prefix_len = len(os.path.basename(filename_prefix)) prefix = filename[:prefix_len + 1] try: - digits = int(filename[prefix_len + 1:].split('_')[0]) + # Extract the part after prefix (e.g., "00001_.png" or "00001.png") + remainder = filename[prefix_len + 1:] + # Try to parse digits - handle both "00001_" and "00001." formats + digits_str = remainder.split('_')[0].split('.')[0] + digits = int(digits_str) except: digits = 0 return digits, prefix @@ -464,7 +468,18 @@ def get_save_image_path(filename_prefix: str, output_dir: str, image_width=0, im raise Exception(err) try: - counter = max(filter(lambda a: os.path.normcase(a[1][:-1]) == os.path.normcase(filename) and a[1][-1] == "_", map(map_filename, os.listdir(full_output_folder))))[0] + 1 + # Support both old format (filename_) and new format (filename) for backward compatibility + def matches_filename(a): + prefix = a[1] + # Match new format: "filename" (exact match) + if os.path.normcase(prefix) == os.path.normcase(filename): + return True + # Match old format: "filename_" (with trailing underscore) + if prefix.endswith("_") and os.path.normcase(prefix[:-1]) == os.path.normcase(filename): + return True + return False + + counter = max(filter(lambda a: matches_filename(a), map(map_filename, os.listdir(full_output_folder))))[0] + 1 except ValueError: counter = 1 except FileNotFoundError: diff --git a/nodes.py b/nodes.py index 3fa543294..d26451bd4 100644 --- a/nodes.py +++ b/nodes.py @@ -488,7 +488,7 @@ class SaveLatent: for x in extra_pnginfo: metadata[x] = json.dumps(extra_pnginfo[x]) - file = f"{filename}_{counter:05}_.latent" + file = f"{filename}_{counter:05}.latent" results: list[FileLocator] = [] results.append({ @@ -1615,7 +1615,7 @@ class SaveImage: metadata.add_text(x, json.dumps(extra_pnginfo[x])) filename_with_batch_num = filename.replace("%batch_num%", str(batch_number)) - file = f"{filename_with_batch_num}_{counter:05}_.png" + file = f"{filename_with_batch_num}_{counter:05}.png" img.save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=self.compress_level) results.append({ "filename": file, diff --git a/tests-unit/folder_paths_test/test_filename_format.py b/tests-unit/folder_paths_test/test_filename_format.py new file mode 100644 index 000000000..cfd92ed59 --- /dev/null +++ b/tests-unit/folder_paths_test/test_filename_format.py @@ -0,0 +1,91 @@ +"""Tests for output filename format. + +Relates to issue #1389: Trailing underscore in output file names +""" + +import os +import tempfile +import pytest + +import folder_paths + + +class TestFilenameFormat: + """Test output filename format without trailing underscore.""" + + def test_new_filename_format_no_trailing_underscore(self): + """New files should not have trailing underscore before extension.""" + # Expected format: "prefix_00001.png" not "prefix_00001_.png" + filename = "ComfyUI" + counter = 1 + new_format = f"{filename}_{counter:05}.png" + + assert new_format == "ComfyUI_00001.png" + assert not new_format.endswith("_.png"), "Filename should not have trailing underscore" + + def test_get_save_image_path_backward_compatible(self): + """get_save_image_path should work with both old and new format files.""" + with tempfile.TemporaryDirectory() as tmpdir: + # Create files with old format (trailing underscore) + old_format_files = [ + "TestPrefix_00001_.png", + "TestPrefix_00002_.png", + ] + for f in old_format_files: + open(os.path.join(tmpdir, f), 'w').close() + + # get_save_image_path should recognize old format and return next counter + full_path, filename, counter, subfolder, _ = folder_paths.get_save_image_path( + "TestPrefix", tmpdir + ) + + # Counter should be 3 (after 00001 and 00002) + assert counter == 3 + + def test_get_save_image_path_new_format(self): + """get_save_image_path should work with new format files (no trailing underscore).""" + with tempfile.TemporaryDirectory() as tmpdir: + # Create files with new format (no trailing underscore) + new_format_files = [ + "NewPrefix_00001.png", + "NewPrefix_00002.png", + "NewPrefix_00003.png", + ] + for f in new_format_files: + open(os.path.join(tmpdir, f), 'w').close() + + # get_save_image_path should recognize new format + full_path, filename, counter, subfolder, _ = folder_paths.get_save_image_path( + "NewPrefix", tmpdir + ) + + # Counter should be 4 (after 00001, 00002, 00003) + assert counter == 4 + + def test_get_save_image_path_mixed_formats(self): + """get_save_image_path should handle mixed old and new format files.""" + with tempfile.TemporaryDirectory() as tmpdir: + # Mix of old and new format files + files = [ + "MixedPrefix_00001_.png", # old format + "MixedPrefix_00002.png", # new format + "MixedPrefix_00003_.png", # old format + ] + for f in files: + open(os.path.join(tmpdir, f), 'w').close() + + full_path, filename, counter, subfolder, _ = folder_paths.get_save_image_path( + "MixedPrefix", tmpdir + ) + + # Counter should be 4 (recognizing both formats) + assert counter == 4 + + def test_get_save_image_path_empty_directory(self): + """get_save_image_path should return counter 1 for empty directory.""" + with tempfile.TemporaryDirectory() as tmpdir: + full_path, filename, counter, subfolder, _ = folder_paths.get_save_image_path( + "EmptyDir", tmpdir + ) + + assert counter == 1