This commit is contained in:
justumen
2024-09-14 11:10:31 +02:00
parent 478ad073c4
commit e6bf627150
13 changed files with 186 additions and 66 deletions

View File

@@ -1,4 +1,4 @@
# 🔗 Comfyui : Bjornulf_custom_nodes v0.26 🔗
# 🔗 Comfyui : Bjornulf_custom_nodes v0.28 🔗
# ❤️ Coffee : ☕☕☕☕☕ 5/5
@@ -74,10 +74,12 @@ wget --content-disposition -P /workspace/ComfyUI/models/checkpoints "https://civ
- **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)
- **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.
# 📝 Nodes descriptions
## 1/2 - 👁 + ✒ Show/Write Text
## 1/1b/2 - 👁 + ✒ Show/Write Text
![Show Text](screenshots/write+show_text.png)
![Show Text](screenshots/write_in_console.png)
![Show Text](screenshots/write_text_select.png)
@@ -98,7 +100,8 @@ Combine multiple text inputs into a single output. (can have separation with : c
![Random Text](screenshots/random_text.png)
**Description:**
Generate and display random text from a predefined list. Great for creating random prompts.
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)
## 5 - ♻ Loop
![Loop](screenshots/loop.png)
@@ -112,6 +115,8 @@ General-purpose loop node.
**Description:**
Cycle through a list of text inputs.
Here is an example of usage with combine texts and flux :
![Loop Texts example](screenshots/loop_text_example.png)
## 7 - ♻ Loop Integer
![Loop Integer](screenshots/loop_integer.png)
@@ -120,31 +125,36 @@ Cycle through a list of text inputs.
**Description:**
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 :
![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) :
Here is an example of usage with ksampler (Notice that with "steps" this node isn't optimized, but good enough for quick 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)
**Description:**
Loop through a range of floating-point numbers, good for `cfg`, `denoise`, etc...
Loop through a range of floating-point numbers, good for `cfg`, `denoise`, etc...
Here is an example with controlnet, trying to make a red cat based on a blue rabbit :
![Loop All Samplers](screenshots/loop_float_example.png)
## 10 - ♻ Loop All Samplers
![Loop All Samplers](screenshots/loop_all_samplers.png)
**Description:**
Iterate over all available samplers to apply them sequentially. Ideal for testing.
Iterate over all available samplers to apply them sequentially. Ideal for testing.
Here is an example of looping over all the samplers with the normal scheduler :
![Loop All Samplers](screenshots/example_loop_all_samplers.png)
## 11 - ♻ Loop All Schedulers
![Loop All Schedulers](screenshots/loop_all_schedulers.png)
**Description:**
Iterate over all available schedulers to apply them sequentially. Ideal for testing.
Iterate over all available schedulers to apply them sequentially. Ideal for testing. (same idea as sampler above, but for schedulers)
## 12 - ♻ Loop Combos
![Loop Combos](screenshots/loop_combos.png)
@@ -153,6 +163,9 @@ Iterate over all available schedulers to apply them sequentially. Ideal for test
Generate a loop from a list of my own custom combinations (scheduler+sampler), or select one combo manually.
Good for testing.
Example of usage to see the differences between different combinations :
![example combos](screenshots/example_combos.png)
## 13/14 - 📏 + 🖼 Resize and Save Exact name ⚠️💣
![Resize and Save Exact](screenshots/resize_save_exact.png)
@@ -247,7 +260,8 @@ Need clean greenscreen ofc. (Can adjust threshold but very basic node.)
![Random line from input](screenshots/random_line_from_input.png)
**Description:**
Take a random line from an input text. (When using multiple "Write Text" nodes is annoying for example, you can use that and just copy/paste a list from outside.)
Take a random line from an input text. (When using multiple "Write Text" nodes is annoying for example, you can use that and just copy/paste a list from outside.)
You can change fixed/randomize for `control_after_generate` to have a different text each time you run the workflow. (or not)
## 27 - ♻ Loop (All Lines from input)
![Loop input](screenshots/loop_all_lines.png)
@@ -363,14 +377,14 @@ You can connect this node to anything you want, above is an example with IMAGE.
### 37 - 🎲🖼 Random Image
![pick input](screenshots/random_image.png)
![random image](screenshots/random_image.png)
**Description:**
Just take a random image from a list of images.
### 38 - ♻🖼 Loop (Images)
![pick input](screenshots/loop_images.png)
![loop images](screenshots/loop_images.png)
**Description:**
Loop over a list of images.
@@ -379,8 +393,22 @@ Above is an example of the loop images node sending them to an Ipadapter style t
### 39 - ♻ Loop (✒ Write Text)
![pick input](screenshots/loop_write_text.png)
![loop write text](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.
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.
### 40 - 🎲 Random (Model+Clip+Vae) - aka Checkpoint / Model
![pick input](screenshots/random_checkpoint.png)
**Description:**
Just take a trio at random from a load checkpoint node.
### 41 - ♻ Loop (Model+Clip+Vae) - aka Checkpoint / Model
![pick input](screenshots/loop_checkpoint.png)
**Description:**
Loop over all the trios from several checkpoint node.

View File

@@ -46,6 +46,7 @@ from .loop_images import LoopImages
from .random_image import RandomImage
from .loop_write_text import LoopWriteText
# from .random_checkpoint import RandomCheckpoint
from .loop_model_clip_vae import LoopModelClipVae
# from .pass_preview_image import PassPreviewImage
# from .check_black_image import CheckBlackImage
@@ -56,6 +57,7 @@ from .loop_write_text import LoopWriteText
NODE_CLASS_MAPPINGS = {
# "Bjornulf_CustomStringType": CustomStringType,
"Bjornulf_ollamaLoader": ollamaLoader,
"Bjornulf_LoopModelClipVae": LoopModelClipVae,
# "Bjoenulf_RandomCheckpoint": RandomCheckpoint,
"Bjornulf_LoopWriteText": LoopWriteText,
"Bjornulf_LoopImages": LoopImages,
@@ -110,52 +112,13 @@ 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",
"Bjornulf_PickInput": "⏸️🔍 Paused. Select input, Pick one",
"Bjornulf_PauseResume": "⏸️ Paused. Resume or Stop ?",
"Bjornulf_FreeVRAM": "🧹 Free VRAM hack",
"Bjornulf_CombineTextsByLines": "♻ Loop (All Lines from input 🔗 combine by lines)",
"Bjornulf_TextToSpeech": "🔊 TTS - Text to Speech",
"Bjornulf_CharacterDescriptionGenerator": "🧑📝 Character Description Generator",
"Bjornulf_ImageMaskCutter": "🖼✂ Cut Image with Mask",
"Bjornulf_LoadImageWithTransparency": "🖼 Load Image with Transparency ▢",
"Bjornulf_GreenScreenToTransparency": "🟩➜▢ Green Screen to Transparency",
# "Bjornulf_CheckBlackImage": "🔲 Check Black Image (Empty mask)",
"Bjornulf_SaveBjornulfLobeChat": "🖼💬 Save image for Bjornulf LobeChat",
"Bjornulf_TextToStringAndSeed": "🔢 Text with random Seed",
# "Bjornulf_ClearVRAM": "🧹 Clear VRAM",
"Bjornulf_RandomLineFromInput": "🎲 Random line from input",
"Bjornulf_ShowText": "👁 Show (Text)",
"Bjornulf_ShowInt": "👁 Show (Int)",
"Bjornulf_ShowFloat": "👁 Show (Float)",
"Bjornulf_CombineBackgroundOverlay": "🖼+🖼 Combine images (Background+Overlay alpha)",
"Bjornulf_GrayscaleTransform": "🖼➜🔲 Image to grayscale (black & white)",
"Bjornulf_RemoveTransparency": "▢➜⬛ Remove image Transparency (alpha)",
# "🔲➜⬛ Transparency to color",
"Bjornulf_ResizeImage": "📏 Resize Image",
"Bjornulf_SaveImagePath": "🖼 Save Image (exact path, exact name) ⚠️💣",
"Bjornulf_SaveImageToFolder": "🖼📁 Save Image(s) to a folder",
"Bjornulf_SaveTmpImage": "🖼 Save Image (tmp_api.png) ⚠️💣",
# "Bjornulf_SaveApiImage": "🖼 Save Image (./output/api_00001.png...)",
"Bjornulf_SaveText": "💾 Save Text", #Make SaveCharacter, SaveLocation, SaveCamera, SaveAction, SaveClothes, SaveEmotion...
"Bjornulf_LoadText": "📥 Load Text", #Make LoadCharacter, LoadLocation, LoadCamera, LoadAction, LoadClothes, LoadEmotion...
"Bjornulf_WriteText": "✒ Write Text",
"Bjornulf_WriteTextInConsole": "✒🗔 Write Text (Console too) ",
# "Bjornulf_WriteImageEnvironment": "✒ Write Image Environment",
# "Bjornulf_WriteImageCharacters": "✒ Write Image Characters",
# "Bjornulf_WriteImageCharacter": "✒ Write Image Character",
# "Bjornulf_WriteImageAllInOne": "✒ Write Image All-in-one",
"Bjornulf_CombineTexts": "🔗 Combine (Texts)",
"Bjornulf_LoopWriteText": "♻ Loop (✒ Write Text)",
"Bjornulf_LoopModelClipVae": "♻ Loop (Model+Clip+Vae)",
"Bjornulf_LoopImages": "♻🖼 Loop (Images)",
"Bjornulf_CombineTextsByLines": "♻ Loop (All Lines from input 🔗 combine by lines)",
"Bjornulf_LoopTexts": "♻ Loop (Texts)",
"Bjornulf_RandomTexts": "🎲 Random (Texts)",
"Bjornulf_RandomModelClipVae": "🎲 Random (Model+Clip+Vae)",
"Bjornulf_imagesToVideo": "📹 images to video (FFmpeg)",
"Bjornulf_VideoPingPong": "📹 video PingPong",
"Bjornulf_LoopFloat": "♻ Loop (Float)",
"Bjornulf_LoopInteger": "♻ Loop (Integer)",
"Bjornulf_LoopBasicBatch": "♻ Loop",
@@ -163,6 +126,40 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Bjornulf_LoopSamplers": "♻ Loop (All Samplers)",
"Bjornulf_LoopSchedulers": "♻ Loop (All Schedulers)",
"Bjornulf_LoopCombosSamplersSchedulers": "♻ Loop (My combos Sampler⚔Scheduler)",
"Bjornulf_RandomImage": "🎲🖼 Random Image",
"Bjornulf_RandomLineFromInput": "🎲 Random line from input",
"Bjornulf_RandomTexts": "🎲 Random (Texts)",
"Bjornulf_RandomModelClipVae": "🎲 Random (Model+Clip+Vae)",
# "Bjornulf_PassPreviewImage": "🖼⮕ Pass Preview Image",
"Bjornulf_CharacterDescriptionGenerator": "🧑📝 Character Description Generator",
"Bjornulf_GreenScreenToTransparency": "🟩➜▢ Green Screen to Transparency",
# "Bjornulf_CheckBlackImage": "🔲 Check Black Image (Empty mask)",
"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_ImageMaskCutter": "🖼✂ Cut Image with Mask",
"Bjornulf_LoadImageWithTransparency": "🖼 Load Image with Transparency ▢",
"Bjornulf_CombineBackgroundOverlay": "🖼+🖼 Combine images (Background+Overlay alpha)",
"Bjornulf_GrayscaleTransform": "🖼➜🔲 Image to grayscale (black & white)",
"Bjornulf_RemoveTransparency": "▢➜⬛ Remove image Transparency (alpha)",
"Bjornulf_ResizeImage": "📏 Resize Image",
"Bjornulf_SaveImagePath": "🖼 Save Image (exact path, exact name) ⚠️💣",
"Bjornulf_SaveImageToFolder": "🖼📁 Save Image(s) to a folder",
"Bjornulf_SaveTmpImage": "🖼 Save Image (tmp_api.png) ⚠️💣",
# "Bjornulf_SaveApiImage": "🖼 Save Image (./output/api_00001.png...)",
"Bjornulf_SaveText": "💾 Save Text",
# "Bjornulf_LoadText": "📥 Load Text",
"Bjornulf_CombineTexts": "🔗 Combine (Texts)",
"Bjornulf_imagesToVideo": "📹 images to video (FFmpeg)",
"Bjornulf_VideoPingPong": "📹 video PingPong",
"Bjornulf_ollamaLoader": "🦙 Ollama (Description)",
"Bjornulf_FreeVRAM": "🧹 Free VRAM hack",
"Bjornulf_TextToSpeech": "🔊 TTS - Text to Speech",
"Bjornulf_PickInput": "⏸️🔍 Paused. Select input, Pick one",
"Bjornulf_PauseResume": "⏸️ Paused. Resume or Stop ?",
}
WEB_DIRECTORY = "./web"

View File

@@ -12,7 +12,7 @@ class CombineTextsByLines:
RETURN_NAMES = tuple([f"line_{i+1}" for i in range(50)])
FUNCTION = "extract_lines"
OUTPUT_NODE = True
CATEGORY = "text"
CATEGORY = "Bjornulf"
OUTPUT_IS_LIST = tuple([True] * 50) # Indicate that all outputs are lists
def extract_lines(self, number_of_inputs, number_of_lines, **kwargs):

31
loop_model_clip_vae.py Normal file
View File

@@ -0,0 +1,31 @@
class LoopModelClipVae:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"number_of_inputs": ("INT", {"default": 2, "min": 2, "max": 10, "step": 1}),
"model_1": ("MODEL", {"forceInput": True}),
"clip_1": ("CLIP", {"forceInput": True}),
"vae_1": ("VAE", {"forceInput": True}),
"model_2": ("MODEL", {"forceInput": True}),
"clip_2": ("CLIP", {"forceInput": True}),
"vae_2": ("VAE", {"forceInput": True}),
},
"hidden": {
**{f"model_{i}": ("MODEL", {"forceInput": True}) for i in range(3, 11)},
**{f"clip_{i}": ("CLIP", {"forceInput": True}) for i in range(3, 11)},
**{f"vae_{i}": ("VAE", {"forceInput": True}) for i in range(3, 11)}
}
}
RETURN_TYPES = ("MODEL", "CLIP", "VAE")
FUNCTION = "return_all"
OUTPUT_IS_LIST = (True, True, True)
CATEGORY = "Bjornulf"
def return_all(self, number_of_inputs, **kwargs):
models = [kwargs[f"model_{i}"] for i in range(1, number_of_inputs + 1) if f"model_{i}" in kwargs]
clips = [kwargs[f"clip_{i}"] for i in range(1, number_of_inputs + 1) if f"clip_{i}" in kwargs]
vaes = [kwargs[f"vae_{i}"] for i in range(1, number_of_inputs + 1) if f"vae_{i}" in kwargs]
return (models, clips, vaes)

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

View File

@@ -7,21 +7,20 @@ 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"}),
"seed": ("INT", {"default": "1"}), #Used with control_after_generate,
"seed": ("INT", {"default": 0}),
},
"hidden": {
**{f"text_{i}": ("STRING", {"forceInput": "True"}) for i in range(1, 31)}
**{f"text_{i}": ("STRING", {"forceInput": True}) for i in range(1, 31)}
}
}
RETURN_TYPES = ("STRING",)
FUNCTION = "random_texts"
OUTPUT_IS_LIST = (False,)
OUTPUT_IS_LIST = (True,)
CATEGORY = "Bjornulf"
def random_texts(self, number_of_inputs, number_of_random, **kwargs):
texts = [kwargs[f"text_{i}"] for i in range(1, number_of_inputs + 1) if f"text_{i}" in kwargs]
def random_texts(self, number_of_inputs, number_of_random, seed, **kwargs):
random.seed(seed)
texts = [kwargs[f"text_{i}"] for i in range(1, number_of_inputs + 1) if f"text_{i}" in kwargs and kwargs[f"text_{i}"]]
random_texts = random.sample(texts, min(number_of_random, len(texts)))
return (random_texts,)

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

View File

@@ -0,0 +1,65 @@
import { app } from "../../../scripts/app.js";
app.registerExtension({
name: "Bjornulf.LoopModelClipVae",
async nodeCreated(node) {
if (node.comfyClass === "Bjornulf_LoopModelClipVae") {
const updateInputs = () => {
const numInputsWidget = node.widgets.find(w => w.name === "number_of_inputs");
if (!numInputsWidget) return;
const numInputs = numInputsWidget.value;
// Initialize node.inputs if it doesn't exist
if (!node.inputs) {
node.inputs = [];
}
// Filter existing model, clip, and vae inputs
const existingModelInputs = node.inputs.filter(input => input.name.startsWith('model_'));
const existingClipInputs = node.inputs.filter(input => input.name.startsWith('clip_'));
const existingVaeInputs = node.inputs.filter(input => input.name.startsWith('vae_'));
// Determine if we need to add or remove inputs
if (existingModelInputs.length < numInputs || existingClipInputs.length < numInputs || existingVaeInputs.length < numInputs) {
// Add new model, clip, and vae inputs if not enough existing
for (let i = Math.max(existingModelInputs.length, existingClipInputs.length, existingVaeInputs.length) + 1; i <= numInputs; i++) {
const modelInputName = `model_${i}`;
const clipInputName = `clip_${i}`;
const vaeInputName = `vae_${i}`;
if (!node.inputs.find(input => input.name === modelInputName)) {
node.addInput(modelInputName, "MODEL");
}
if (!node.inputs.find(input => input.name === clipInputName)) {
node.addInput(clipInputName, "CLIP");
}
if (!node.inputs.find(input => input.name === vaeInputName)) {
node.addInput(vaeInputName, "VAE");
}
}
} else {
// Remove excess model, clip, and vae inputs if too many
node.inputs = node.inputs.filter(input =>
(!input.name.startsWith('model_') && !input.name.startsWith('clip_') && !input.name.startsWith('vae_')) ||
(parseInt(input.name.split('_')[1]) <= numInputs)
);
}
node.setSize(node.computeSize());
};
// Move number_of_inputs to the top initially
const numInputsWidget = node.widgets.find(w => w.name === "number_of_inputs");
if (numInputsWidget) {
node.widgets = [numInputsWidget, ...node.widgets.filter(w => w !== numInputsWidget)];
numInputsWidget.callback = () => {
updateInputs();
app.graph.setDirtyCanvas(true);
};
}
// Delay the initial update to ensure node is fully initialized
setTimeout(updateInputs, 0);
}
}
});