diff --git a/README.md b/README.md index 7ba69ed..7dd9a44 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,11 @@ - Output: serialized `STRING` - Use it to compact or pretty-print JSON for prompts, logs, or file output. +- `Dumas Strip Iteration Suffix` + - Input: `filename` + - Output: cleaned `STRING` + - Removes a trailing `_` iteration suffix before the file extension, for example `asset_003.png -> asset.png`. + - `Dumas JSON Get Value` - Inputs: `json_object`, `path` - Output: `JSON` @@ -160,6 +165,8 @@ decr -> use index - 1 `Save Image Dumas` leaves `folder` empty to use ComfyUI's output directory. `name` feeds `%input%` and `name_2` feeds `%input2%`, so a pattern like `project/%input%_%input2%_%counter%` can combine two upstream strings into the saved filename. +`Dumas Strip Iteration Suffix` only strips a final numeric suffix. Names like `my_asset_final.png` are left untouched, while `my_asset_012.png` becomes `my_asset.png`. + ## Roadmap This repo is intended to grow into a broader set of Dumas-branded generic utility nodes, including JSON helpers and adjacent data-manipulation tools. diff --git a/dumas_json_nodes.py b/dumas_json_nodes.py index 2a1eabc..f8e76e8 100644 --- a/dumas_json_nodes.py +++ b/dumas_json_nodes.py @@ -1,6 +1,7 @@ from functools import lru_cache from itertools import islice import json +import os def _clone_container(value): @@ -243,6 +244,21 @@ def _resolve_iteration_index(index, mode, total_items): return max(0, min(index, total_items - 1)) +def _strip_iteration_suffix(value): + if not isinstance(value, str): + return "" + + root, extension = os.path.splitext(value) + if "_" not in root: + return value + + prefix, suffix = root.rsplit("_", 1) + if not suffix.isdigit(): + return value + + return f"{prefix}{extension}" + + class DumasJSONStringToObjectNode: CATEGORY = "Dumas/JSON" RETURN_TYPES = ("JSON",) @@ -265,6 +281,23 @@ class DumasJSONStringToObjectNode: return (None,) +class DumasStripIterationSuffixNode: + CATEGORY = "Dumas/String" + RETURN_TYPES = ("STRING",) + FUNCTION = "strip_iteration_suffix" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "filename": ("STRING", {"default": "", "multiline": False}), + } + } + + def strip_iteration_suffix(self, filename): + return (_strip_iteration_suffix(filename),) + + class DumasJSONObjectToStringNode: CATEGORY = "Dumas/JSON" RETURN_TYPES = ("STRING",) @@ -596,6 +629,7 @@ class DumasJSONUnflattenNode: NODE_CLASS_MAPPINGS = { "DumasJSONStringToObject": DumasJSONStringToObjectNode, + "DumasStripIterationSuffix": DumasStripIterationSuffixNode, "DumasJSONObjectToString": DumasJSONObjectToStringNode, "DumasJSONGetValue": DumasJSONGetValueNode, "DumasJSONSetValue": DumasJSONSetValueNode, @@ -615,6 +649,7 @@ NODE_CLASS_MAPPINGS = { NODE_DISPLAY_NAME_MAPPINGS = { "DumasJSONStringToObject": "Dumas JSON String to Object", + "DumasStripIterationSuffix": "Dumas Strip Iteration Suffix", "DumasJSONObjectToString": "Dumas JSON Object to String", "DumasJSONGetValue": "Dumas JSON Get Value", "DumasJSONSetValue": "Dumas JSON Set Value", diff --git a/tests/test_dumas_json_nodes.py b/tests/test_dumas_json_nodes.py index de83c63..31410b9 100644 --- a/tests/test_dumas_json_nodes.py +++ b/tests/test_dumas_json_nodes.py @@ -17,6 +17,7 @@ from dumas_json_nodes import ( DumasJSONObjectToStringNode, DumasJSONStringToObjectNode, DumasJSONSetValueNode, + DumasStripIterationSuffixNode, ) class DumasJSONNodeTests(unittest.TestCase): @@ -41,6 +42,27 @@ class DumasJSONNodeTests(unittest.TestCase): self.assertEqual(result, ('{\n "a": 2,\n "b": 1\n}',)) + def test_strip_iteration_suffix_removes_trailing_iteration_before_extension(self): + node = DumasStripIterationSuffixNode() + + result = node.strip_iteration_suffix("asset_build_003.png") + + self.assertEqual(result, ("asset_build.png",)) + + def test_strip_iteration_suffix_removes_trailing_iteration_without_extension(self): + node = DumasStripIterationSuffixNode() + + result = node.strip_iteration_suffix("asset_build_003") + + self.assertEqual(result, ("asset_build",)) + + def test_strip_iteration_suffix_leaves_names_without_numeric_suffix_unchanged(self): + node = DumasStripIterationSuffixNode() + + result = node.strip_iteration_suffix("my_asset_final.png") + + self.assertEqual(result, ("my_asset_final.png",)) + def test_get_value_reads_nested_paths(self): node = DumasJSONGetValueNode()