mirror of
https://github.com/justUmen/Bjornulf_custom_nodes.git
synced 2026-03-21 12:42:11 -03:00
0.37
This commit is contained in:
35
README.md
35
README.md
@@ -1,4 +1,4 @@
|
|||||||
# 🔗 Comfyui : Bjornulf_custom_nodes v0.36 🔗
|
# 🔗 Comfyui : Bjornulf_custom_nodes v0.37 🔗
|
||||||
|
|
||||||
# ❤️ Coffee : ☕☕☕☕☕ 5/5
|
# ❤️ Coffee : ☕☕☕☕☕ 5/5
|
||||||
|
|
||||||
@@ -84,6 +84,7 @@ wget --content-disposition -P /workspace/ComfyUI/models/checkpoints "https://civ
|
|||||||
- **v0.34**: Two new nodes : Load Images from output folder and Select an Image, Pick.
|
- **v0.34**: Two new nodes : Load Images from output folder and Select an Image, Pick.
|
||||||
- **v0.35**: Great improvements of the TTS node 31. It will also save the audio file in the "ComfyUI/Bjornulf_TTS/" folder. - Not tested on windows yet -
|
- **v0.35**: Great improvements of the TTS node 31. It will also save the audio file in the "ComfyUI/Bjornulf_TTS/" folder. - Not tested on windows yet -
|
||||||
- **v0.36**: Fix random model.
|
- **v0.36**: Fix random model.
|
||||||
|
- **v0.37**: New node : Random Load checkpoint (Model Selector). Alternative to the random checkpoint node. (Not preloading all checkpoints in memory, slower to switch between checkpoints, but more outputs to decide where to store your results.)
|
||||||
|
|
||||||
# 📝 Nodes descriptions
|
# 📝 Nodes descriptions
|
||||||
|
|
||||||
@@ -460,19 +461,41 @@ It will take the same special syntax as the Advanced write text node `{blue|red}
|
|||||||
|
|
||||||
### 40 - 🎲 Random (Model+Clip+Vae) - aka Checkpoint / Model
|
### 40 - 🎲 Random (Model+Clip+Vae) - aka Checkpoint / Model
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Description:**
|
**Description:**
|
||||||
Just take a trio at random from a load checkpoint node.
|
Just simply take a trio at random from a load checkpoint node.
|
||||||
|
Notice that it is using the core Load checkpoint node. It means that all checkpoint will be preloaded in memory.
|
||||||
|
|
||||||
### 41 - ♻ Loop (Model+Clip+Vae) - aka Checkpoint / Model
|
Details :
|
||||||
|
- It will take more VRAM, but it will be faster to switch between checkpoints.
|
||||||
|
- It can't give you the currently loaded checkpoint name's.
|
||||||
|
|
||||||
|
Check node number 41 before deciding which one to use.
|
||||||
|
|
||||||
|
### 41 - 🎲 Random Load checkpoint (Model Selector)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**Description:**
|
||||||
|
This is another way to select a load checkpoint node randomly.
|
||||||
|
It will not preload all the checkpoints in memory, so it will be slower to switch between checkpoints.
|
||||||
|
But you can use more outputs to decide where to store your results. (`model_folder` is returning the last folder name of the checkpoint.)
|
||||||
|
I always store my checkpoints in a folder with the type of the model like `SD1.5`, `SDXL`, etc... So it's a good way for me to recover that information quickly.
|
||||||
|
|
||||||
|
Details :
|
||||||
|
- Note that compared to node 40, you can't have separate configuration depending of the selected checkpoint. (For example `CLIP Set Last Layer` node set at -2 for a specific model, or a separate vae or clip.) Aka : All models are going to share the exact same workflow.
|
||||||
|
|
||||||
|
Check node number 40 before deciding which one to use.
|
||||||
|
|
||||||
|
### 42 - ♻ Loop (Model+Clip+Vae) - aka Checkpoint / Model
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Description:**
|
**Description:**
|
||||||
Loop over all the trios from several checkpoint node.
|
Loop over all the trios from several checkpoint node.
|
||||||
|
|
||||||
### 42 - 📂🖼 Load Images from output folder
|
### 43 - 📂🖼 Load Images from output folder
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -494,7 +517,7 @@ If you are satisfied with this logic, you can then select all these nodes, right
|
|||||||
Here is another example of the same thing but excluding the save folder node :
|
Here is another example of the same thing but excluding the save folder node :
|
||||||

|

|
||||||
|
|
||||||
### 43 - 🖼🔍 Select an Image, Pick
|
### 44 - 🖼🔍 Select an Image, Pick
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -43,29 +43,27 @@ 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 .random_checkpoint import RandomCheckpoint
|
|
||||||
from .loop_model_clip_vae import LoopModelClipVae
|
from .loop_model_clip_vae import LoopModelClipVae
|
||||||
from .write_text_advanced import WriteTextAdvanced
|
from .write_text_advanced import WriteTextAdvanced
|
||||||
from .loop_write_text import LoopWriteText
|
from .loop_write_text import LoopWriteText
|
||||||
from .load_images_from_folder import LoadImagesFromSelectedFolder
|
from .load_images_from_folder import LoadImagesFromSelectedFolder
|
||||||
# from .show import ShowWhatever
|
|
||||||
from .select_image_from_list import SelectImageFromList
|
from .select_image_from_list import SelectImageFromList
|
||||||
|
from .random_model_selector import RandomModelSelector
|
||||||
|
|
||||||
# from .pass_preview_image import PassPreviewImage
|
# from .pass_preview_image import PassPreviewImage
|
||||||
# from .check_black_image import CheckBlackImage
|
# from .check_black_image import CheckBlackImage
|
||||||
# from .clear_vram import ClearVRAM
|
|
||||||
|
|
||||||
# from .CUSTOM_STRING import CustomStringType
|
# from .CUSTOM_STRING import CustomStringType
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
# "Bjornulf_CustomStringType": CustomStringType,
|
# "Bjornulf_CustomStringType": CustomStringType,
|
||||||
"Bjornulf_ollamaLoader": ollamaLoader,
|
"Bjornulf_ollamaLoader": ollamaLoader,
|
||||||
|
"Bjornulf_RandomModelSelector": RandomModelSelector,
|
||||||
"Bjornulf_SelectImageFromList": SelectImageFromList,
|
"Bjornulf_SelectImageFromList": SelectImageFromList,
|
||||||
"Bjornulf_WriteText": WriteText,
|
"Bjornulf_WriteText": WriteText,
|
||||||
"Bjornulf_LoadImagesFromSelectedFolder": LoadImagesFromSelectedFolder,
|
"Bjornulf_LoadImagesFromSelectedFolder": LoadImagesFromSelectedFolder,
|
||||||
# "Bjornulf_ShowWhatever": ShowWhatever,
|
# "Bjornulf_ShowWhatever": ShowWhatever,
|
||||||
"Bjornulf_LoopModelClipVae": LoopModelClipVae,
|
"Bjornulf_LoopModelClipVae": LoopModelClipVae,
|
||||||
# "Bjoenulf_RandomCheckpoint": RandomCheckpoint,
|
|
||||||
"Bjornulf_LoopWriteText": LoopWriteText,
|
"Bjornulf_LoopWriteText": LoopWriteText,
|
||||||
"Bjornulf_LoopImages": LoopImages,
|
"Bjornulf_LoopImages": LoopImages,
|
||||||
"Bjornulf_RandomImage": RandomImage,
|
"Bjornulf_RandomImage": RandomImage,
|
||||||
@@ -135,6 +133,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"Bjornulf_RandomLineFromInput": "🎲 Random line from input",
|
"Bjornulf_RandomLineFromInput": "🎲 Random line from input",
|
||||||
"Bjornulf_RandomTexts": "🎲 Random (Texts)",
|
"Bjornulf_RandomTexts": "🎲 Random (Texts)",
|
||||||
"Bjornulf_RandomModelClipVae": "🎲 Random (Model+Clip+Vae)",
|
"Bjornulf_RandomModelClipVae": "🎲 Random (Model+Clip+Vae)",
|
||||||
|
"Bjornulf_RandomModelSelector": "🎲 Random Load checkpoint (Model Selector)",
|
||||||
# "Bjornulf_PassPreviewImage": "🖼⮕ Pass Preview Image",
|
# "Bjornulf_PassPreviewImage": "🖼⮕ Pass Preview Image",
|
||||||
"Bjornulf_CharacterDescriptionGenerator": "🧑📝 Character Description Generator",
|
"Bjornulf_CharacterDescriptionGenerator": "🧑📝 Character Description Generator",
|
||||||
"Bjornulf_GreenScreenToTransparency": "🟩➜▢ Green Screen to Transparency",
|
"Bjornulf_GreenScreenToTransparency": "🟩➜▢ Green Screen to Transparency",
|
||||||
|
|||||||
@@ -15,11 +15,10 @@ class LoadImageWithTransparency:
|
|||||||
{"image": (sorted(files), {"image_upload": True})},
|
{"image": (sorted(files), {"image_upload": True})},
|
||||||
}
|
}
|
||||||
|
|
||||||
CATEGORY = "image"
|
|
||||||
|
|
||||||
RETURN_TYPES = ("IMAGE", "MASK", "STRING") # Added "STRING" for the image path
|
RETURN_TYPES = ("IMAGE", "MASK", "STRING") # Added "STRING" for the image path
|
||||||
RETURN_NAMES = ("image", "mask", "image_path")
|
RETURN_NAMES = ("image", "mask", "image_path")
|
||||||
FUNCTION = "load_image_alpha"
|
FUNCTION = "load_image_alpha"
|
||||||
|
CATEGORY = "Bjornulf"
|
||||||
|
|
||||||
def load_image_alpha(self, image):
|
def load_image_alpha(self, image):
|
||||||
image_path = folder_paths.get_annotated_filepath(image)
|
image_path = folder_paths.get_annotated_filepath(image)
|
||||||
|
|||||||
@@ -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.36"
|
version = "0.37"
|
||||||
license = {file = "LICENSE"}
|
license = {file = "LICENSE"}
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
import random
|
|
||||||
|
|
||||||
class RandomCheckpoint:
|
|
||||||
@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}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = ("MODEL", "CLIP", "VAE")
|
|
||||||
FUNCTION = "random_select"
|
|
||||||
|
|
||||||
def random_select(self, number_of_inputs, **kwargs):
|
|
||||||
selected_index = random.randint(1, number_of_inputs)
|
|
||||||
|
|
||||||
selected_model = kwargs[f"model_{selected_index}"]
|
|
||||||
selected_clip = kwargs[f"clip_{selected_index}"]
|
|
||||||
selected_vae = kwargs[f"vae_{selected_index}"]
|
|
||||||
|
|
||||||
return (selected_model, selected_clip, selected_vae)
|
|
||||||
@@ -24,6 +24,7 @@ class RandomModelClipVae:
|
|||||||
|
|
||||||
RETURN_TYPES = ("MODEL", "CLIP", "VAE")
|
RETURN_TYPES = ("MODEL", "CLIP", "VAE")
|
||||||
FUNCTION = "random_select"
|
FUNCTION = "random_select"
|
||||||
|
CATEGORY = "Bjornulf"
|
||||||
|
|
||||||
def random_select(self, number_of_inputs, **kwargs):
|
def random_select(self, number_of_inputs, **kwargs):
|
||||||
random.seed(kwargs.get('seed', 0))
|
random.seed(kwargs.get('seed', 0))
|
||||||
|
|||||||
56
random_model_selector.py
Normal file
56
random_model_selector.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import os
|
||||||
|
import random
|
||||||
|
from folder_paths import get_filename_list, get_full_path
|
||||||
|
import comfy.sd
|
||||||
|
|
||||||
|
class RandomModelSelector:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
model_list = get_filename_list("checkpoints")
|
||||||
|
optional_inputs = {}
|
||||||
|
|
||||||
|
for i in range(1, 11):
|
||||||
|
optional_inputs[f"model_{i}"] = (model_list, {"default": model_list[min(i-1, len(model_list)-1)]})
|
||||||
|
|
||||||
|
optional_inputs["seed"] = ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff})
|
||||||
|
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"number_of_models": ("INT", {"default": 3, "min": 1, "max": 20, "step": 1}),
|
||||||
|
},
|
||||||
|
"optional": optional_inputs
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("MODEL", "CLIP", "VAE", "STRING", "STRING", "STRING")
|
||||||
|
RETURN_NAMES = ("model", "clip", "vae", "model_path", "model_name", "model_folder")
|
||||||
|
FUNCTION = "random_select_model"
|
||||||
|
CATEGORY = "Bjornulf"
|
||||||
|
|
||||||
|
def random_select_model(self, number_of_models, seed, **kwargs):
|
||||||
|
random.seed(seed)
|
||||||
|
|
||||||
|
available_models = [kwargs[f"model_{i}"] for i in range(1, number_of_models + 1) if f"model_{i}" in kwargs]
|
||||||
|
|
||||||
|
if not available_models:
|
||||||
|
raise ValueError("No models selected")
|
||||||
|
|
||||||
|
selected_model = random.choice(available_models)
|
||||||
|
|
||||||
|
# Extract just the name of the model (no folders and no extensions)
|
||||||
|
model_name = os.path.splitext(os.path.basename(selected_model))[0]
|
||||||
|
|
||||||
|
# Get the full path of the selected model
|
||||||
|
model_path = get_full_path("checkpoints", selected_model)
|
||||||
|
|
||||||
|
# Get the folder of the selected model (Hopefully people use that to organize their models...)
|
||||||
|
model_folder = os.path.basename(os.path.dirname(model_path))
|
||||||
|
|
||||||
|
# Load the model
|
||||||
|
loaded_objects = comfy.sd.load_checkpoint_guess_config(model_path)
|
||||||
|
|
||||||
|
# Unpack only the values we need
|
||||||
|
model = loaded_objects[0]
|
||||||
|
clip = loaded_objects[1]
|
||||||
|
vae = loaded_objects[2]
|
||||||
|
|
||||||
|
return (model, clip, vae, model_path, model_name, model_folder)
|
||||||
BIN
screenshots/random_load_checkpoint.png
Normal file
BIN
screenshots/random_load_checkpoint.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 182 KiB |
62
web/js/random_model_selector.js
Normal file
62
web/js/random_model_selector.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { app } from "../../../scripts/app.js";
|
||||||
|
|
||||||
|
app.registerExtension({
|
||||||
|
name: "Bjornulf.RandomModelSelector",
|
||||||
|
async nodeCreated(node) {
|
||||||
|
if (node.comfyClass === "Bjornulf_RandomModelSelector") {
|
||||||
|
const updateModelInputs = () => {
|
||||||
|
const numModelsWidget = node.widgets.find(w => w.name === "number_of_models");
|
||||||
|
if (!numModelsWidget) return;
|
||||||
|
|
||||||
|
const numModels = numModelsWidget.value;
|
||||||
|
const checkpointsList = node.widgets.find(w => w.name === "model_1").options.values;
|
||||||
|
|
||||||
|
// Remove excess model widgets
|
||||||
|
node.widgets = node.widgets.filter(w => !w.name.startsWith("model_") || parseInt(w.name.split("_")[1]) <= numModels);
|
||||||
|
|
||||||
|
// Add new model widgets if needed
|
||||||
|
for (let i = 1; i <= numModels; i++) {
|
||||||
|
const widgetName = `model_${i}`;
|
||||||
|
if (!node.widgets.find(w => w.name === widgetName)) {
|
||||||
|
const defaultIndex = Math.min(i - 1, checkpointsList.length - 1);
|
||||||
|
node.addWidget("combo", widgetName, checkpointsList[defaultIndex], () => {}, {
|
||||||
|
values: checkpointsList
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reorder widgets
|
||||||
|
node.widgets.sort((a, b) => {
|
||||||
|
if (a.name === "number_of_models") return -1;
|
||||||
|
if (b.name === "number_of_models") return 1;
|
||||||
|
if (a.name === "seed") return 1;
|
||||||
|
if (b.name === "seed") return -1;
|
||||||
|
if (a.name.startsWith("model_") && b.name.startsWith("model_")) {
|
||||||
|
return parseInt(a.name.split("_")[1]) - parseInt(b.name.split("_")[1]);
|
||||||
|
}
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
node.setSize(node.computeSize());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set up number_of_models widget
|
||||||
|
const numModelsWidget = node.widgets.find(w => w.name === "number_of_models");
|
||||||
|
if (numModelsWidget) {
|
||||||
|
numModelsWidget.callback = () => {
|
||||||
|
updateModelInputs();
|
||||||
|
app.graph.setDirtyCanvas(true);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set seed widget to hidden input
|
||||||
|
const seedWidget = node.widgets.find((w) => w.name === "seed");
|
||||||
|
if (seedWidget) {
|
||||||
|
seedWidget.type = "HIDDEN";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial update
|
||||||
|
updateModelInputs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user