mirror of
https://github.com/justUmen/Bjornulf_custom_nodes.git
synced 2026-03-21 12:42:11 -03:00
0.26
This commit is contained in:
16
README.md
16
README.md
@@ -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
|
||||

|
||||

|
||||
@@ -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 :
|
||||

|
||||
|
||||
Here is an example of usage with ksampler (Notice that it isn't optimized, but good enough for testing) :
|
||||

|
||||
|
||||
|
||||
## 8 - ♻ Loop Float
|
||||

|
||||

|
||||
@@ -370,3 +376,11 @@ Just take a random image from a list of images.
|
||||
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.)
|
||||
|
||||
### 39 - ♻ Loop (✒ Write Text)
|
||||
|
||||

|
||||
|
||||
**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.
|
||||
@@ -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",
|
||||
|
||||
@@ -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)}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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
41
loop_write_text.py
Normal 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
|
||||
@@ -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]
|
||||
|
||||
@@ -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)}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BIN
screenshots/example_loop_integer.png
Normal file
BIN
screenshots/example_loop_integer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 632 KiB |
BIN
screenshots/loop_write_text.png
Normal file
BIN
screenshots/loop_write_text.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
Reference in New Issue
Block a user