This commit is contained in:
justumen
2024-09-14 09:29:13 +02:00
parent c3a573565a
commit 478ad073c4
11 changed files with 85 additions and 24 deletions

View File

@@ -1,4 +1,4 @@
# 🔗 Comfyui : Bjornulf_custom_nodes v0.25 🔗
# 🔗 Comfyui : Bjornulf_custom_nodes v0.26 🔗
# ❤️ Coffee : ☕☕☕☕☕ 5/5
@@ -73,6 +73,7 @@ wget --content-disposition -P /workspace/ComfyUI/models/checkpoints "https://civ
- **v0.23**: Add a new node: Pause, resume or stop workflow.
- **v0.24**: Add a new node: Pause, select input, pick one.
- **v0.25**: Two new nodes: Loop Images and Random image.
- **v0.26**: New node : Loop write Text. Also increase nb of inputs allowed for most nodes. (+ update some breaking changes)
# 📝 Nodes descriptions
@@ -111,6 +112,7 @@ General-purpose loop node.
**Description:**
Cycle through a list of text inputs.
## 7 - ♻ Loop Integer
![Loop Integer](screenshots/loop_integer.png)
![Loop Int + Show Text](screenshots/loop_int+show_text.png)
@@ -121,6 +123,10 @@ Iterate through a range of integer values, good for `steps` in ksampler, etc...
❗ Don't forget that you can convert ksampler widgets to input by right-clicking the ksampler node :
![Widget to Input](screenshots/widget-to-input.png)
Here is an example of usage with ksampler (Notice that it isn't optimized, but good enough for testing) :
![Widget to Input](screenshots/example_loop_integer.png)
## 8 - ♻ Loop Float
![Loop Float](screenshots/loop_float.png)
![Loop Float + Show Text](screenshots/loop_float+show_text.png)
@@ -369,4 +375,12 @@ Just take a random image from a list of images.
**Description:**
Loop over a list of images.
Usage example : You have a list of images, and you want to apply the same process to all of them.
Above is an example of the loop images node sending them to an Ipadapter style transfer workflow. (Same seed of course.)
Above is an example of the loop images node sending them to an Ipadapter style transfer workflow. (Same seed of course.)
### 39 - ♻ Loop (✒ Write Text)
![pick input](screenshots/loop_write_text.png)
**Description:**
If you need a quick loop but you don't want something too complex with a loop node, you can use this combined write text + loop.
It will take the same special syntax as the write text node `{blue|red}`, but it will loop over ALL the possibilities instead of taking one at random.

View File

@@ -44,6 +44,8 @@ from .pause_resume_stop import PauseResume
from .pick_input import PickInput
from .loop_images import LoopImages
from .random_image import RandomImage
from .loop_write_text import LoopWriteText
# from .random_checkpoint import RandomCheckpoint
# from .pass_preview_image import PassPreviewImage
# from .check_black_image import CheckBlackImage
@@ -54,6 +56,8 @@ from .random_image import RandomImage
NODE_CLASS_MAPPINGS = {
# "Bjornulf_CustomStringType": CustomStringType,
"Bjornulf_ollamaLoader": ollamaLoader,
# "Bjoenulf_RandomCheckpoint": RandomCheckpoint,
"Bjornulf_LoopWriteText": LoopWriteText,
"Bjornulf_LoopImages": LoopImages,
"Bjornulf_RandomImage": RandomImage,
# "Bjornulf_PassPreviewImage": PassPreviewImage,
@@ -107,6 +111,8 @@ NODE_CLASS_MAPPINGS = {
NODE_DISPLAY_NAME_MAPPINGS = {
# "Bjornulf_CustomStringType": "!!! CUSTOM STRING TYPE !!!",
"Bjornulf_ollamaLoader": "🦙 Ollama (Description)",
# "Bjoenulf_RandomCheckpoint": "🎲 Random Checkpoint",
"Bjornulf_LoopWriteText": "♻ Loop (✒ Write Text)",
"Bjornulf_LoopImages": "♻🖼 Loop (Images)",
"Bjornulf_RandomImage": "🎲🖼 Random Image",
# "Bjornulf_PassPreviewImage": "🖼⮕ Pass Preview Image",

View File

@@ -3,13 +3,13 @@ class CombineTexts:
def INPUT_TYPES(cls):
return {
"required": {
"number_of_inputs": ("INT", {"default": 2, "min": 2, "max": 10, "step": 1}),
"number_of_inputs": ("INT", {"default": 2, "min": 2, "max": 50, "step": 1}),
"delimiter": (["newline", "comma", "space", "slash"], {"default": "newline"}),
"text_1": ("STRING", {"forceInput": True}),
"text_2": ("STRING", {"forceInput": True}),
},
"hidden": {
**{f"text_{i}": ("STRING", {"forceInput": True}) for i in range(3, 11)}
**{f"text_{i}": ("STRING", {"forceInput": True}) for i in range(3, 51)}
}
}

View File

@@ -3,17 +3,17 @@ class CombineTextsByLines:
def INPUT_TYPES(cls):
return {
"required": {
"number_of_inputs": ("INT", {"default": 2, "min": 1, "max": 10, "step": 1}),
"number_of_lines": ("INT", {"default": 3, "min": 1, "max": 20, "step": 1}),
"number_of_inputs": ("INT", {"default": 2, "min": 1, "max": 50, "step": 1}),
"number_of_lines": ("INT", {"default": 3, "min": 1, "max": 50, "step": 1}),
}
}
RETURN_TYPES = tuple(["STRING"] * 20) # Maximum 20 lines
RETURN_NAMES = tuple([f"line_{i+1}" for i in range(20)])
RETURN_TYPES = tuple(["STRING"] * 50) # Maximum 50 lines
RETURN_NAMES = tuple([f"line_{i+1}" for i in range(50)])
FUNCTION = "extract_lines"
OUTPUT_NODE = True
CATEGORY = "text"
OUTPUT_IS_LIST = tuple([True] * 20) # Indicate that all outputs are lists
OUTPUT_IS_LIST = tuple([True] * 50) # Indicate that all outputs are lists
def extract_lines(self, number_of_inputs, number_of_lines, **kwargs):
grouped_lines = [[] for _ in range(number_of_lines)]
@@ -31,8 +31,8 @@ class CombineTextsByLines:
# Instead of joining the lines, keep them as a list
outputs.append(group)
# Pad the output to always return 20 items
outputs.extend([[] for _ in range(20 - len(outputs))])
# Pad the output to always return 50 items
outputs.extend([[] for _ in range(50 - len(outputs))])
return tuple(outputs)
@@ -42,8 +42,8 @@ class CombineTextsByLines:
@classmethod
def VALIDATE_INPUTS(cls, number_of_inputs, number_of_lines, **kwargs):
if number_of_lines < 1 or number_of_lines > 20:
return "Number of lines must be between 1 and 20"
if number_of_inputs < 1 or number_of_inputs > 10:
return "Number of inputs must be between 1 and 10"
if number_of_lines < 1 or number_of_lines > 50:
return "Number of lines must be between 1 and 50"
if number_of_inputs < 1 or number_of_inputs > 50:
return "Number of inputs must be between 1 and 50"
return True

View File

@@ -3,7 +3,7 @@ class LoopImages:
def INPUT_TYPES(cls):
return {
"required": {
"number_of_images": ("INT", {"default": 2, "min": 1, "max": 10, "step": 1}),
"number_of_images": ("INT", {"default": 2, "min": 1, "max": 30, "step": 1}),
},
}

View File

@@ -3,12 +3,12 @@ class LoopTexts:
def INPUT_TYPES(cls):
return {
"required": {
"number_of_inputs": ("INT", {"default": 2, "min": 2, "max": 10, "step": 1}),
"text_1": ("STRING", {"forceInput": "True"}),
"text_2": ("STRING", {"forceInput": "True"}),
"number_of_inputs": ("INT", {"default": 2, "min": 2, "max": 50, "step": 1}),
# "text_1": ("STRING", {"forceInput": "True"}),
# "text_2": ("STRING", {"forceInput": "True"}),
},
"hidden": {
**{f"text_{i}": ("STRING", {"forceInput": "True"}) for i in range(3, 11)}
**{f"text_{i}": ("STRING", {"forceInput": "True"}) for i in range(1, 51)}
}
}

41
loop_write_text.py Normal file
View File

@@ -0,0 +1,41 @@
import re
import itertools
class LoopWriteText:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"text": ("STRING", {"multiline": True}),
},
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("texts",)
FUNCTION = "loop_write_text"
OUTPUT_NODE = True
OUTPUT_IS_LIST = (True,)
CATEGORY = "Bjornulf"
def loop_write_text(self, text):
pattern = r'\{([^}]+)\}'
matches = re.findall(pattern, text)
if not matches:
return ([text],)
options_list = [opt.split('|') for opt in matches]
combinations = list(itertools.product(*options_list))
results = []
for combo in combinations:
result = text
for i, match in enumerate(matches):
result = result.replace(f"{{{match}}}", combo[i], 1)
results.append(result)
return (results,)
@classmethod
def IS_CHANGED(s, text):
return float("nan") # Always re-execute to ensure consistency

View File

@@ -1,7 +1,7 @@
[project]
name = "bjornulf_custom_nodes"
description = "Nodes: Ollama, Text to Speech, Combine Texts, Random Texts, Save image for Bjornulf LobeChat, Text with random Seed, Random line from input, Combine images, Image to grayscale (black & white), Remove image Transparency (alpha), Resize Image, ..."
version = "0.25"
version = "0.26"
license = {file = "LICENSE"}
[project.urls]

View File

@@ -7,12 +7,12 @@ class RandomTexts:
"required": {
"number_of_inputs": ("INT", {"default": 2, "min": 2, "max": 30, "step": 1}),
"number_of_random": ("INT", {"default": 1, "min": 1, "max": 30, "step": 1}),
"text_1": ("STRING", {"forceInput": "True"}),
"text_2": ("STRING", {"forceInput": "True"}),
# "text_1": ("STRING", {"forceInput": "True"}),
# "text_2": ("STRING", {"forceInput": "True"}),
"seed": ("INT", {"default": "1"}), #Used with control_after_generate,
},
"hidden": {
**{f"text_{i}": ("STRING", {"forceInput": "True"}) for i in range(3, 31)}
**{f"text_{i}": ("STRING", {"forceInput": "True"}) for i in range(1, 31)}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB