This commit is contained in:
justumen
2024-09-15 13:25:03 +02:00
parent 251012ad4a
commit 30b7f71bc3
25 changed files with 194 additions and 361 deletions

View File

@@ -1,4 +1,4 @@
# 🔗 Comfyui : Bjornulf_custom_nodes v0.29 🔗
# 🔗 Comfyui : Bjornulf_custom_nodes v0.31 🔗
# ❤️ Coffee : ☕☕☕☕☕ 5/5
@@ -77,19 +77,39 @@ wget --content-disposition -P /workspace/ComfyUI/models/checkpoints "https://civ
- **v0.27**: Two new nodes : Loop (Model+Clip+Vae) and Random (Model+Clip+Vae) - aka Checkpoint / Model
- **v0.28**: Fix random texts and add a lot of screenshots examples for several nodes.
- **v0.29**: Fix floating points issues with loop float node.
- **v0.30**: Update the basic Loop node with optional input.
- **v0.31**: ❗Sorry, Breaking changes for Write/Show text nodes, cleaner system : 1 simple write text and the other is 1 advanced with console and special syntax. Also Show can now manage INT, FLOAT, TEXT.
# 📝 Nodes descriptions
## 1/2/3 - 👁 + ✒ Show/Write Text
![Show Text](screenshots/write+show_text.png)
![Show Text](screenshots/write_in_console.png)
![Show Text](screenshots/write_text_select.png)
## 1 - 👁 Show (Text, Int, Float)
![Show Text](screenshots/show.png)
**Description:**
Three simple nodes, two to write and one to show text.
Write nodes is a textarea where you can write your text.
The show text node will only display the text. (That's why I made it a different color : green, uneditable, display only.)
Write text nodes now allow for special syntax to accept random variants, like `{hood|helmet}` will randomly choose between hood or helmet.
The show node will only display text, or a list of several texts. (read only node)
3 types are managed : Green is for STRING type, Orange is for FLOAT type and blue is for INT type.
## 2 - ✒ Write Text
![write Text](screenshots/write.png)
**Description:**
Simple node to write text.
## 3 - ✒🗔 Advanced Write Text
![write Text Advanced](screenshots/write_advanced.png)
**Description:**
Advanced Write Text node allows for special syntax to accept random variants, like `{hood|helmet}` will randomly choose between hood or helmet.
You also have `seed` and `control_after_generate` to manage the randomness.
It is also displaying the text in the comfyui console. (Useful for debugging)
Example of console logs :
```
Raw text: photo of a {green|blue|red|orange|yellow} {cat|rat|house}
Picked text: photo of a green house
```
## 4 - 🔗 Combine Texts
![Combine Texts](screenshots/combine_texts.png)
@@ -102,13 +122,19 @@ Combine multiple text inputs into a single output. (can have separation with : c
**Description:**
Generate and display random text from a predefined list. Great for creating random prompts.
You can change fixed/randomize for `control_after_generate` to have a different text each time you run the workflow. (or not)
You also have `control_after_generate` to manage the randomness.
## 6 - ♻ Loop
![Loop](screenshots/loop.png)
**Description:**
General-purpose loop node.
General-purpose loop node, you can connect that in between anything.
It has an optional input, if no input is given, it will loop over the value of the STRING "if_no_input" (take you can edit).
❗ Careful this node accept everything as input and output, so you can use it with texts, integers, images, mask, segs etc... but be consistent with your inputs/outputs.
Do not use this Loop if you can do otherwise.
This is an example together with my node 28, to force a different seed for each iteration :
![Loop](screenshots/loop4.png)
## 7 - ♻ Loop Texts
![Loop Texts](screenshots/loop_texts.png)
@@ -392,7 +418,7 @@ 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)
### 39 - ♻ Loop (✒🗔 Advanced Write Text)
![loop write text](screenshots/loop_write_text.png)

View File

@@ -1,6 +1,5 @@
from .images_to_video import imagesToVideo
from .write_text import WriteText
from .write_text_console import WriteTextInConsole
from .write_image_environment import WriteImageEnvironment
from .write_image_characters import WriteImageCharacters
from .write_image_character import WriteImageCharacter
@@ -17,8 +16,8 @@ from .loop_samplers import LoopSamplers
from .loop_schedulers import LoopSchedulers
from .ollama import ollamaLoader
from .show_text import ShowText
from .show_int import ShowInt
from .show_float import ShowFloat
# from .show_int import ShowInt
# from .show_float import ShowFloat
from .save_text import SaveText
from .save_tmp_image import SaveTmpImage
from .save_image_path import SaveImagePath
@@ -47,6 +46,8 @@ from .random_image import RandomImage
from .loop_write_text import LoopWriteText
# from .random_checkpoint import RandomCheckpoint
from .loop_model_clip_vae import LoopModelClipVae
from .write_text_advanced import WriteTextAdvanced
# from .show import ShowWhatever
# from .pass_preview_image import PassPreviewImage
# from .check_black_image import CheckBlackImage
@@ -57,6 +58,8 @@ from .loop_model_clip_vae import LoopModelClipVae
NODE_CLASS_MAPPINGS = {
# "Bjornulf_CustomStringType": CustomStringType,
"Bjornulf_ollamaLoader": ollamaLoader,
"Bjornulf_WriteText": WriteText,
# "Bjornulf_ShowWhatever": ShowWhatever,
"Bjornulf_LoopModelClipVae": LoopModelClipVae,
# "Bjoenulf_RandomCheckpoint": RandomCheckpoint,
"Bjornulf_LoopWriteText": LoopWriteText,
@@ -78,8 +81,7 @@ NODE_CLASS_MAPPINGS = {
# "Bjornulf_CheckBlackImage": CheckBlackImage,
# "Bjornulf_ClearVRAM": ClearVRAM,
"Bjornulf_SaveBjornulfLobeChat": SaveBjornulfLobeChat,
"Bjornulf_WriteText": WriteText,
"Bjornulf_WriteTextInConsole": WriteTextInConsole,
"Bjornulf_WriteTextAdvanced": WriteTextAdvanced,
"Bjornulf_RemoveTransparency": RemoveTransparency,
"Bjornulf_GrayscaleTransform": GrayscaleTransform,
"Bjornulf_CombineBackgroundOverlay": CombineBackgroundOverlay,
@@ -88,8 +90,8 @@ NODE_CLASS_MAPPINGS = {
# "Bjornulf_WriteImageCharacter": WriteImageCharacter,
# "Bjornulf_WriteImageAllInOne": WriteImageAllInOne,
"Bjornulf_ShowText": ShowText,
"Bjornulf_ShowInt": ShowInt,
"Bjornulf_ShowFloat": ShowFloat,
# "Bjornulf_ShowInt": ShowInt,
# "Bjornulf_ShowFloat": ShowFloat,
"Bjornulf_SaveText": SaveText,
"Bjornulf_ResizeImage": ResizeImage,
"Bjornulf_SaveImageToFolder": SaveImageToFolder,
@@ -111,10 +113,9 @@ NODE_CLASS_MAPPINGS = {
}
NODE_DISPLAY_NAME_MAPPINGS = {
# "Bjornulf_CustomStringType": "!!! CUSTOM STRING TYPE !!!",
"Bjornulf_WriteText": "✒ Write Text",
"Bjornulf_WriteTextInConsole": "✒🗔 Write Text (Console too) ",
"Bjornulf_LoopWriteText": "♻ Loop (✒ Write Text)",
"Bjornulf_WriteTextAdvanced": "✒🗔 Advanced Write Text",
"Bjornulf_LoopWriteTextAdvanced": "♻ Loop (✒🗔 Advanced Write Text)",
"Bjornulf_LoopModelClipVae": "♻ Loop (Model+Clip+Vae)",
"Bjornulf_LoopImages": "♻🖼 Loop (Images)",
"Bjornulf_CombineTextsByLines": "♻ Loop (All Lines from input 🔗 combine by lines)",
@@ -137,9 +138,10 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Bjornulf_SaveBjornulfLobeChat": "🖼💬 Save image for Bjornulf LobeChat",
"Bjornulf_TextToStringAndSeed": "🔢 Text with random Seed",
# "Bjornulf_ClearVRAM": "🧹 Clear VRAM",
"Bjornulf_ShowText": "👁 Show (Text)",
"Bjornulf_ShowInt": "👁 Show (Int)",
"Bjornulf_ShowFloat": "👁 Show (Float)",
# "Bjornulf_ShowWhatever": "👁 Show Anything",
"Bjornulf_ShowText": "👁 Show (Text, Int, Float)",
# "Bjornulf_ShowInt": "👁 Show (Int)",
# "Bjornulf_ShowFloat": "👁 Show (Float)",
"Bjornulf_ImageMaskCutter": "🖼✂ Cut Image with Mask",
"Bjornulf_LoadImageWithTransparency": "🖼 Load Image with Transparency ▢",
"Bjornulf_CombineBackgroundOverlay": "🖼+🖼 Combine images (Background+Overlay alpha)",

View File

@@ -1,21 +1,39 @@
class LoopBasicBatch:
class Everything(str):
def __ne__(self, __value: object) -> bool:
return False
class LoopBasicBatch:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"nb_loops": ("INT", {"default": 1, "min": 0, "max": 1000, "step": 1}),
"default_text": ("STRING", {"default": "Default input"})
},
"optional": {
"input": (Everything("*"),)
},
}
RETURN_TYPES = ("INT",)
OUTPUT_IS_LIST = (True, False)
RETURN_TYPES = (Everything("*"),)
RETURN_NAMES = ("output",)
OUTPUT_IS_LIST = (True,)
FUNCTION = "create_loop_basic_batch"
CATEGORY = "Bjornulf"
def create_loop_basic_batch(self, nb_loops):
range_values = list()
while nb_loops > 0:
range_values.append(1)
nb_loops -= 1
return (range_values,)
def create_loop_basic_batch(self, nb_loops, default_text, input=None):
if input is not None:
return ([input] * nb_loops,)
# Determine the type of the default_text
if default_text.isdigit():
self.RETURN_TYPES = ("INT",)
output = int(default_text)
elif default_text.replace('.', '', 1).isdigit() and default_text.count('.') == 1:
self.RETURN_TYPES = ("FLOAT",)
output = float(default_text)
else:
self.RETURN_TYPES = ("STRING",)
output = default_text
return ([output] * nb_loops,)

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.29"
version = "0.31"
license = {file = "LICENSE"}
[project.urls]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 73 KiB

BIN
screenshots/loop3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 KiB

BIN
screenshots/loop4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 KiB

BIN
screenshots/show.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

BIN
screenshots/write.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -1,18 +0,0 @@
class ShowFloat:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"float_value": ("FLOAT", {"forceInput": True}),
},
}
INPUT_IS_LIST = True
RETURN_TYPES = ()
FUNCTION = "show_float"
OUTPUT_NODE = True
INPUT_IS_LIST = (True,)
CATEGORY = "Bjornulf"
def show_float(self, float_value):
return {"ui": {"text": float_value}}

View File

@@ -1,18 +0,0 @@
class ShowInt:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"int_value": ("INT", {"forceInput": True}),
},
}
INPUT_IS_LIST = True
RETURN_TYPES = ()
FUNCTION = "show_int"
OUTPUT_NODE = True
INPUT_IS_LIST = (True,)
CATEGORY = "Bjornulf"
def show_int(self, int_value):
return {"ui": {"text": int_value}}

View File

@@ -1,18 +1,43 @@
class Everything(str):
def __ne__(self, _):
return False
class ShowText:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"text_value": ("STRING", {"forceInput": True}),
"text_int_float": (Everything("*"), {"forceInput": True}),
},
}
INPUT_IS_LIST = True
RETURN_TYPES = ()
FUNCTION = "show_text"
OUTPUT_NODE = True
INPUT_IS_LIST = (True,)
CATEGORY = "Bjornulf"
def show_text(self, text_value):
return {"ui": {"text": text_value}}
def detect_type(self, value):
if isinstance(value, int):
return 'integer'
elif isinstance(value, float):
# Check if it has a decimal part
if value % 1 == 0:
return 'float' if str(value).endswith('.0') else 'integer'
return 'float'
elif isinstance(value, str):
try:
float_val = float(value)
if '.' in value:
return 'float string'
if float_val.is_integer():
return 'integer string'
return 'float string'
except ValueError:
return 'normal string'
else:
return 'other type'
def show_text(self, text_int_float):
type_info = [f"{value}" for value in text_int_float]
return {"ui": {"text": type_info}}

View File

@@ -1,14 +0,0 @@
import { app } from "../../../scripts/app.js";
app.registerExtension({
name: "Bjornulf.RandomLineFromInput",
async nodeCreated(node) {
if (node.comfyClass === "Bjornulf_RandomLineFromInput") {
// Set seed widget to hidden input
const seedWidget = node.widgets.find(w => w.name === "seed");
if (seedWidget) {
seedWidget.type = "HIDDEN";
}
}
}
});

View File

@@ -1,19 +0,0 @@
import { app } from "../../../scripts/app.js";
app.registerExtension({
name: "Bjornulf.TextToStringAndSeed",
async nodeCreated(node) {
if (node.comfyClass === "Bjornulf_TextToStringAndSeed") {
// Set seed widget to hidden input
const seedWidget = node.widgets.find(w => w.name === "seed");
if (seedWidget) {
seedWidget.type = "HIDDEN";
}
// Set seed widget to hidden input
const controlWidget = node.widgets.find(w => w.name === "control_after_generate");
if (controlWidget) {
controlWidget.type = "HIDDEN";
}
}
}
});

View File

@@ -36,10 +36,10 @@ app.registerExtension({
};
// Set seed widget to hidden input
const seedWidget = node.widgets.find(w => w.name === "seed");
if (seedWidget) {
seedWidget.type = "HIDDEN";
}
// const seedWidget = node.widgets.find(w => w.name === "seed");
// if (seedWidget) {
// seedWidget.type = "HIDDEN";
// }
// Move number_of_inputs to the top initially
const numInputsWidget = node.widgets.find(w => w.name === "number_of_inputs");

View File

@@ -1,76 +0,0 @@
import { app } from "../../../scripts/app.js";
import { ComfyWidgets } from "../../../scripts/widgets.js";
// Styles for the text area
const textAreaStyles = {
readOnly: true,
opacity: 1,
padding: '10px',
border: '1px solid #ccc',
borderRadius: '5px',
backgroundColor: '#222',
color: 'Lime',
fontFamily: 'Arial, sans-serif',
fontSize: '14px',
lineHeight: '1.4',
resize: 'vertical',
overflowY: 'auto',
};
// Displays input text on a node
app.registerExtension({
name: "Bjornulf.ShowFloat",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name === "Bjornulf_ShowFloat") {
function createStyledTextArea(text) {
const widget = ComfyWidgets["STRING"](this, "text", ["STRING", { multiline: true }], app).widget;
widget.inputEl.readOnly = true;
const textArea = widget.inputEl;
Object.assign(textArea.style, textAreaStyles);
textArea.classList.add('bjornulf-show-text');
widget.value = text;
return widget;
}
function populate(text) {
if (this.widgets) {
for (let i = 1; i < this.widgets.length; i++) {
this.widgets[i].onRemove?.();
}
this.widgets.length = 1;
}
const v = Array.isArray(text) ? text : [text];
for (const list of v) {
if (list) {
createStyledTextArea.call(this, list);
}
}
requestAnimationFrame(() => {
const sz = this.computeSize();
if (sz[0] < this.size[0]) sz[0] = this.size[0];
if (sz[1] < this.size[1]) sz[1] = this.size[1];
this.onResize?.(sz);
app.graph.setDirtyCanvas(true, false);
});
}
// When the node is executed we will be sent the input text, display this in the widget
const onExecuted = nodeType.prototype.onExecuted;
nodeType.prototype.onExecuted = function (message) {
onExecuted?.apply(this, arguments);
populate.call(this, message.text);
};
const onConfigure = nodeType.prototype.onConfigure;
nodeType.prototype.onConfigure = function () {
onConfigure?.apply(this, arguments);
if (this.widgets_values?.length) {
populate.call(this, this.widgets_values);
}
};
}
},
});

View File

@@ -1,76 +0,0 @@
import { app } from "../../../scripts/app.js";
import { ComfyWidgets } from "../../../scripts/widgets.js";
// Styles for the text area
const textAreaStyles = {
readOnly: true,
opacity: 1,
padding: '10px',
border: '1px solid #ccc',
borderRadius: '5px',
backgroundColor: '#222',
color: 'Lime',
fontFamily: 'Arial, sans-serif',
fontSize: '14px',
lineHeight: '1.4',
resize: 'vertical',
overflowY: 'auto',
};
// Displays input text on a node
app.registerExtension({
name: "Bjornulf.ShowInt",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name === "Bjornulf_ShowInt") {
function createStyledTextArea(text) {
const widget = ComfyWidgets["STRING"](this, "text", ["STRING", { multiline: true }], app).widget;
widget.inputEl.readOnly = true;
const textArea = widget.inputEl;
Object.assign(textArea.style, textAreaStyles);
textArea.classList.add('bjornulf-show-text');
widget.value = text;
return widget;
}
function populate(text) {
if (this.widgets) {
for (let i = 1; i < this.widgets.length; i++) {
this.widgets[i].onRemove?.();
}
this.widgets.length = 1;
}
const v = Array.isArray(text) ? text : [text];
for (const list of v) {
if (list) {
createStyledTextArea.call(this, list);
}
}
requestAnimationFrame(() => {
const sz = this.computeSize();
if (sz[0] < this.size[0]) sz[0] = this.size[0];
if (sz[1] < this.size[1]) sz[1] = this.size[1];
this.onResize?.(sz);
app.graph.setDirtyCanvas(true, false);
});
}
// When the node is executed we will be sent the input text, display this in the widget
const onExecuted = nodeType.prototype.onExecuted;
nodeType.prototype.onExecuted = function (message) {
onExecuted?.apply(this, arguments);
populate.call(this, message.text);
};
const onConfigure = nodeType.prototype.onConfigure;
nodeType.prototype.onConfigure = function () {
onConfigure?.apply(this, arguments);
if (this.widgets_values?.length) {
populate.call(this, this.widgets_values);
}
};
}
},
});

View File

@@ -5,7 +5,8 @@ import { ComfyWidgets } from "../../../scripts/widgets.js";
const textAreaStyles = {
readOnly: true,
opacity: 1,
padding: '10px',
padding: '4px',
paddingLeft: '7px',
border: '1px solid #ccc',
borderRadius: '5px',
backgroundColor: '#222',
@@ -13,7 +14,7 @@ const textAreaStyles = {
fontFamily: 'Arial, sans-serif',
fontSize: '14px',
lineHeight: '1.4',
resize: 'vertical',
resize: 'none',
overflowY: 'auto',
};
@@ -22,30 +23,34 @@ app.registerExtension({
name: "Bjornulf.ShowText",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name === "Bjornulf_ShowText") {
function createStyledTextArea(text) {
const widget = ComfyWidgets["STRING"](this, "text", ["STRING", { multiline: true }], app).widget;
widget.inputEl.readOnly = true;
const textArea = widget.inputEl;
Object.assign(textArea.style, textAreaStyles);
textArea.classList.add('bjornulf-show-text');
widget.value = text;
return widget;
}
function populate(text) {
if (this.widgets) {
for (let i = 1; i < this.widgets.length; i++) {
const pos = this.widgets.findIndex((w) => w.name === "text");
if (pos !== -1) {
for (let i = pos; i < this.widgets.length; i++) {
this.widgets[i].onRemove?.();
}
this.widgets.length = 1;
this.widgets.length = pos;
}
}
const v = Array.isArray(text) ? text : [text];
for (const list of v) {
if (list) {
createStyledTextArea.call(this, list);
for (const list of text) {
const w = ComfyWidgets["STRING"](this, "text", ["STRING", { multiline: true }], app).widget;
w.inputEl.readOnly = true;
Object.assign(w.inputEl.style, textAreaStyles);
// Improved type detection
let color = 'Lime'; // Default color for strings
const value = list.toString().trim();
if (/^-?\d+$/.test(value)) {
color = '#0096FF'; // Integer
} else if (/^-?\d*\.?\d+$/.test(value)) {
color = 'orange'; // Float
}
w.inputEl.style.color = color;
w.value = list;
}
requestAnimationFrame(() => {

View File

@@ -1,17 +1,9 @@
import re
import random
import time
class WriteText:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"text": ("STRING", {"multiline": True}),
},
"hidden": {
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"control_after_update": ("INT", {"default": 0})
"text": ("STRING", {"multiline": True, "lines": 10}),
},
}
@@ -21,24 +13,5 @@ class WriteText:
OUTPUT_NODE = True
CATEGORY = "Bjornulf"
def write_text(self, text, seed=None, control_after_update=None):
# If seed is not provided, generate a new one
if seed is None:
seed = int(time.time() * 1000)
# Use the seed to initialize the random number generator
random.seed(seed)
def replace_random(match):
options = match.group(1).split('|')
return random.choice(options)
pattern = r'\{([^}]+)\}'
result = re.sub(pattern, replace_random, text)
return (result,)
@classmethod
def IS_CHANGED(s, text, seed=None, control_after_update=None):
# This method is called to determine if the node needs to be re-executed
return float("nan") # Always re-execute to ensure consistency
def write_text(self, text):
return (text,)

48
write_text_advanced.py Normal file
View File

@@ -0,0 +1,48 @@
import re
import random
import time
import logging
class WriteTextAdvanced:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"text": ("STRING", {"multiline": True, "lines": 10}),
},
"optional": {
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
},
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("text",)
FUNCTION = "write_text_special"
OUTPUT_NODE = True
CATEGORY = "Bjornulf"
def write_text_special(self, text, seed=None):
logging.info(f"Raw text: {text}")
# If seed is not provided, generate a new one
if len(text) > 10000:
return ("Text too large to process at once. Please split into smaller parts.",)
if seed is None or seed == 0:
seed = int(time.time() * 1000)
random.seed(seed)
pattern = r'\{([^}]+)\}'
def replace_random(match):
return random.choice(match.group(1).split('|'))
result = re.sub(pattern, replace_random, text)
logging.info(f"Picked text: {result}")
return (result,)
@classmethod
def IS_CHANGED(s, text, seed=None):
return (text, seed)

View File

@@ -1,43 +0,0 @@
import re
import random
import time
import logging
class WriteTextInConsole:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"text": ("STRING", {"multiline": True}),
}
}
# INPUT_IS_LIST = True
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("text",)
FUNCTION = "write_text_in_console"
OUTPUT_NODE = True
OUTPUT_IS_LIST = (False,)
CATEGORY = "Bjornulf"
def write_text_in_console(self, text, seed=None, control_after_update=None):
logging.info(f"Text: {text}")
# If seed is not provided, generate a new one
if seed is None:
seed = int(time.time() * 1000)
# Use the seed to initialize the random number generator
random.seed(seed)
def replace_random(match):
options = match.group(1).split('|')
return random.choice(options)
pattern = r'\{([^}]+)\}'
result = re.sub(pattern, replace_random, text)
return (result,)
@classmethod
def IS_CHANGED(s, text, seed=None, control_after_update=None):
# This method is called to determine if the node needs to be re-executed
return float("nan") # Always re-execute to ensure consistency