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:
18
README.md
18
README.md
@@ -1,4 +1,4 @@
|
|||||||
# 🔗 Comfyui : Bjornulf_custom_nodes v0.25 🔗
|
# 🔗 Comfyui : Bjornulf_custom_nodes v0.26 🔗
|
||||||
|
|
||||||
# ❤️ Coffee : ☕☕☕☕☕ 5/5
|
# ❤️ 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.23**: Add a new node: Pause, resume or stop workflow.
|
||||||
- **v0.24**: Add a new node: Pause, select input, pick one.
|
- **v0.24**: Add a new node: Pause, select input, pick one.
|
||||||
- **v0.25**: Two new nodes: Loop Images and Random image.
|
- **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
|
# 📝 Nodes descriptions
|
||||||
|
|
||||||
@@ -111,6 +112,7 @@ General-purpose loop node.
|
|||||||
**Description:**
|
**Description:**
|
||||||
Cycle through a list of text inputs.
|
Cycle through a list of text inputs.
|
||||||
|
|
||||||
|
|
||||||
## 7 - ♻ Loop Integer
|
## 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 :
|
❗ 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
|
## 8 - ♻ Loop Float
|
||||||

|

|
||||||

|

|
||||||
@@ -369,4 +375,12 @@ Just take a random image from a list of images.
|
|||||||
**Description:**
|
**Description:**
|
||||||
Loop over 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.
|
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)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**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 .pick_input import PickInput
|
||||||
from .loop_images import LoopImages
|
from .loop_images import LoopImages
|
||||||
from .random_image import RandomImage
|
from .random_image import RandomImage
|
||||||
|
from .loop_write_text import LoopWriteText
|
||||||
|
# from .random_checkpoint import RandomCheckpoint
|
||||||
|
|
||||||
# from .pass_preview_image import PassPreviewImage
|
# from .pass_preview_image import PassPreviewImage
|
||||||
# from .check_black_image import CheckBlackImage
|
# from .check_black_image import CheckBlackImage
|
||||||
@@ -54,6 +56,8 @@ from .random_image import RandomImage
|
|||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
# "Bjornulf_CustomStringType": CustomStringType,
|
# "Bjornulf_CustomStringType": CustomStringType,
|
||||||
"Bjornulf_ollamaLoader": ollamaLoader,
|
"Bjornulf_ollamaLoader": ollamaLoader,
|
||||||
|
# "Bjoenulf_RandomCheckpoint": RandomCheckpoint,
|
||||||
|
"Bjornulf_LoopWriteText": LoopWriteText,
|
||||||
"Bjornulf_LoopImages": LoopImages,
|
"Bjornulf_LoopImages": LoopImages,
|
||||||
"Bjornulf_RandomImage": RandomImage,
|
"Bjornulf_RandomImage": RandomImage,
|
||||||
# "Bjornulf_PassPreviewImage": PassPreviewImage,
|
# "Bjornulf_PassPreviewImage": PassPreviewImage,
|
||||||
@@ -107,6 +111,8 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
# "Bjornulf_CustomStringType": "!!! CUSTOM STRING TYPE !!!",
|
# "Bjornulf_CustomStringType": "!!! CUSTOM STRING TYPE !!!",
|
||||||
"Bjornulf_ollamaLoader": "🦙 Ollama (Description)",
|
"Bjornulf_ollamaLoader": "🦙 Ollama (Description)",
|
||||||
|
# "Bjoenulf_RandomCheckpoint": "🎲 Random Checkpoint",
|
||||||
|
"Bjornulf_LoopWriteText": "♻ Loop (✒ Write Text)",
|
||||||
"Bjornulf_LoopImages": "♻🖼 Loop (Images)",
|
"Bjornulf_LoopImages": "♻🖼 Loop (Images)",
|
||||||
"Bjornulf_RandomImage": "🎲🖼 Random Image",
|
"Bjornulf_RandomImage": "🎲🖼 Random Image",
|
||||||
# "Bjornulf_PassPreviewImage": "🖼⮕ Pass Preview Image",
|
# "Bjornulf_PassPreviewImage": "🖼⮕ Pass Preview Image",
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ class CombineTexts:
|
|||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
return {
|
return {
|
||||||
"required": {
|
"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"}),
|
"delimiter": (["newline", "comma", "space", "slash"], {"default": "newline"}),
|
||||||
"text_1": ("STRING", {"forceInput": True}),
|
"text_1": ("STRING", {"forceInput": True}),
|
||||||
"text_2": ("STRING", {"forceInput": True}),
|
"text_2": ("STRING", {"forceInput": True}),
|
||||||
},
|
},
|
||||||
"hidden": {
|
"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):
|
def INPUT_TYPES(cls):
|
||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"number_of_inputs": ("INT", {"default": 2, "min": 1, "max": 10, "step": 1}),
|
"number_of_inputs": ("INT", {"default": 2, "min": 1, "max": 50, "step": 1}),
|
||||||
"number_of_lines": ("INT", {"default": 3, "min": 1, "max": 20, "step": 1}),
|
"number_of_lines": ("INT", {"default": 3, "min": 1, "max": 50, "step": 1}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_TYPES = tuple(["STRING"] * 20) # Maximum 20 lines
|
RETURN_TYPES = tuple(["STRING"] * 50) # Maximum 50 lines
|
||||||
RETURN_NAMES = tuple([f"line_{i+1}" for i in range(20)])
|
RETURN_NAMES = tuple([f"line_{i+1}" for i in range(50)])
|
||||||
FUNCTION = "extract_lines"
|
FUNCTION = "extract_lines"
|
||||||
OUTPUT_NODE = True
|
OUTPUT_NODE = True
|
||||||
CATEGORY = "text"
|
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):
|
def extract_lines(self, number_of_inputs, number_of_lines, **kwargs):
|
||||||
grouped_lines = [[] for _ in range(number_of_lines)]
|
grouped_lines = [[] for _ in range(number_of_lines)]
|
||||||
@@ -31,8 +31,8 @@ class CombineTextsByLines:
|
|||||||
# Instead of joining the lines, keep them as a list
|
# Instead of joining the lines, keep them as a list
|
||||||
outputs.append(group)
|
outputs.append(group)
|
||||||
|
|
||||||
# Pad the output to always return 20 items
|
# Pad the output to always return 50 items
|
||||||
outputs.extend([[] for _ in range(20 - len(outputs))])
|
outputs.extend([[] for _ in range(50 - len(outputs))])
|
||||||
|
|
||||||
return tuple(outputs)
|
return tuple(outputs)
|
||||||
|
|
||||||
@@ -42,8 +42,8 @@ class CombineTextsByLines:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def VALIDATE_INPUTS(cls, number_of_inputs, number_of_lines, **kwargs):
|
def VALIDATE_INPUTS(cls, number_of_inputs, number_of_lines, **kwargs):
|
||||||
if number_of_lines < 1 or number_of_lines > 20:
|
if number_of_lines < 1 or number_of_lines > 50:
|
||||||
return "Number of lines must be between 1 and 20"
|
return "Number of lines must be between 1 and 50"
|
||||||
if number_of_inputs < 1 or number_of_inputs > 10:
|
if number_of_inputs < 1 or number_of_inputs > 50:
|
||||||
return "Number of inputs must be between 1 and 10"
|
return "Number of inputs must be between 1 and 50"
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ class LoopImages:
|
|||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
return {
|
return {
|
||||||
"required": {
|
"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):
|
def INPUT_TYPES(cls):
|
||||||
return {
|
return {
|
||||||
"required": {
|
"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}),
|
||||||
"text_1": ("STRING", {"forceInput": "True"}),
|
# "text_1": ("STRING", {"forceInput": "True"}),
|
||||||
"text_2": ("STRING", {"forceInput": "True"}),
|
# "text_2": ("STRING", {"forceInput": "True"}),
|
||||||
},
|
},
|
||||||
"hidden": {
|
"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]
|
[project]
|
||||||
name = "bjornulf_custom_nodes"
|
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, ..."
|
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"}
|
license = {file = "LICENSE"}
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ class RandomTexts:
|
|||||||
"required": {
|
"required": {
|
||||||
"number_of_inputs": ("INT", {"default": 2, "min": 2, "max": 30, "step": 1}),
|
"number_of_inputs": ("INT", {"default": 2, "min": 2, "max": 30, "step": 1}),
|
||||||
"number_of_random": ("INT", {"default": 1, "min": 1, "max": 30, "step": 1}),
|
"number_of_random": ("INT", {"default": 1, "min": 1, "max": 30, "step": 1}),
|
||||||
"text_1": ("STRING", {"forceInput": "True"}),
|
# "text_1": ("STRING", {"forceInput": "True"}),
|
||||||
"text_2": ("STRING", {"forceInput": "True"}),
|
# "text_2": ("STRING", {"forceInput": "True"}),
|
||||||
"seed": ("INT", {"default": "1"}), #Used with control_after_generate,
|
"seed": ("INT", {"default": "1"}), #Used with control_after_generate,
|
||||||
},
|
},
|
||||||
"hidden": {
|
"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