mirror of
https://github.com/justUmen/Bjornulf_custom_nodes.git
synced 2026-03-21 12:42:11 -03:00
...
This commit is contained in:
19
README.md
19
README.md
@@ -1,4 +1,4 @@
|
|||||||
# 🔗 Comfyui : Bjornulf_custom_nodes v0.76 🔗
|
# 🔗 Comfyui : Bjornulf_custom_nodes v0.77 🔗
|
||||||
|
|
||||||
A list of 142 custom nodes for Comfyui : Display, manipulate, create and edit text, images, videos, loras, generate characters and more.
|
A list of 142 custom nodes for Comfyui : Display, manipulate, create and edit text, images, videos, loras, generate characters and more.
|
||||||
You can manage looping operations, generate randomized content, trigger logical conditions, pause and manually control your workflows and even work with external AI tools, like Ollama or Text To Speech.
|
You can manage looping operations, generate randomized content, trigger logical conditions, pause and manually control your workflows and even work with external AI tools, like Ollama or Text To Speech.
|
||||||
@@ -232,6 +232,7 @@ Support me and my work : ❤️❤️❤️ <https://ko-fi.com/bjornulf> ❤️
|
|||||||
`36.` [⏸️ Paused. Select input, Pick 👇](#36---%EF%B8%8F-paused-select-input-pick-one)
|
`36.` [⏸️ Paused. Select input, Pick 👇](#36---%EF%B8%8F-paused-select-input-pick-one)
|
||||||
`117.` [📝👈🅰️ Line selector (🎲 or ♻ or ♻📑)](#117---🅰%EF%B8%8F-line-selector--or--or-)
|
`117.` [📝👈🅰️ Line selector (🎲 or ♻ or ♻📑)](#117---🅰%EF%B8%8F-line-selector--or--or-)
|
||||||
`135.` [🔛✨ Anything Switch On/Off](#135----anything-switch-onoff)
|
`135.` [🔛✨ Anything Switch On/Off](#135----anything-switch-onoff)
|
||||||
|
`136.` [🔛📝 Text Switch On/Off](#136----text-switch-onoff)
|
||||||
|
|
||||||
## 🧠 Logic / Conditional Operations 🧠
|
## 🧠 Logic / Conditional Operations 🧠
|
||||||
`45.` [🔀 If-Else (input / compare_with)](#45----if-else-input--compare_with)
|
`45.` [🔀 If-Else (input / compare_with)](#45----if-else-input--compare_with)
|
||||||
@@ -411,14 +412,14 @@ Fix a lot of code everywhere, a little better logging system, etc...
|
|||||||
WIP : Rewrite of all my ffmpeg nodes. (Still need improvements and fixes, will do that in 0.71?) Maybe don't use them yet...
|
WIP : Rewrite of all my ffmpeg nodes. (Still need improvements and fixes, will do that in 0.71?) Maybe don't use them yet...
|
||||||
- **0.71**: ❗Breaking changes for Global variable nodes. (add to global variable system a "filename", which is a a separate global variable file.) bug fix speech to text node, 5 new nodes 129-133. combine text limit raised to 100. improve Save image in folder node.
|
- **0.71**: ❗Breaking changes for Global variable nodes. (add to global variable system a "filename", which is a a separate global variable file.) bug fix speech to text node, 5 new nodes 129-133. combine text limit raised to 100. improve Save image in folder node.
|
||||||
- **0.71-0.75**: Many bug fixing. Civitai nodes are working on windows. (encoding, links problem are solved ? - at least on my machines...)
|
- **0.71-0.75**: Many bug fixing. Civitai nodes are working on windows. (encoding, links problem are solved ? - at least on my machines...)
|
||||||
- **0.76**: Removed kokoro_onnx from requirements.txt due to sonflict with other nodes (need to be installed manually if you want to use this node.)
|
- **0.76**: Removed kokoro_onnx from requirements.txt due to some conflict with other nodes (need to be installed manually if you want to use this node.)
|
||||||
New syntaxes for advanced text/line selector, ex: {left|right|middle|group=LMR}+{left|right|middle|group=LMR}+{left|right|middle|group=LMR} and {A(80%)|B(15%)|C(5%)}
|
New syntaxes for advanced text/line selector, ex: {left|right|middle|group=LMR}+{left|right|middle|group=LMR}+{left|right|middle|group=LMR} and {A(80%)|B(15%)|C(5%)}
|
||||||
2 new nodes switch : 🔛✨ Anything Switch On/Off (compatible with combine images) AND 🔛📝 Text Switch On/Off (Compatible with combine texts)
|
2 new nodes switch : 🔛✨ Anything Switch On/Off (compatible with combine images) AND 🔛📝 Text Switch On/Off (Compatible with combine texts)
|
||||||
2 new pick Me global nodes, using an identifier instead of chain : 🌎✒👉 Global Write Pick Me AND 🌎📥 Load Global Pick Me
|
2 new pick Me global nodes, using an identifier instead of chain : 🌎✒👉 Global Write Pick Me AND 🌎📥 Load Global Pick Me
|
||||||
3 random nodes : 🌎🎲 Global Random Seed, 🎲 Random Integer, 🎲 Random Float (Each return their value but also TEXT version of it.) "Seed node" more advanced.
|
3 random nodes : 🌎🎲 Global Random Seed, 🎲 Random Integer, 🎲 Random Float (Each return their value but also TEXT version of it.) "Seed node" more advanced.
|
||||||
1 new node to quickly select element from list : 📑👈 Select from List
|
1 new node to quickly select element from list : 📑👈 Select from List
|
||||||
1 new audio node : 🔊▶ Play Audio (Just play an audio file, will default to bell.m4a if none provided.) Can take AUDIO format or audio_path.
|
1 new audio node : 🔊▶ Play Audio (Just play an audio file, will default to bell.m4a if none provided.) Can take AUDIO format or audio_path.
|
||||||
❗Breaking changes. Large rewrite for all FFMPEG related nodes. With options for video preview. (Still have few changes to make, next version.)
|
❗Breaking changes. Large rewrite for all FFMPEG related nodes. With options for video preview. (Still have few changes to make, next version.)
|
||||||
|
|
||||||
# 📝 Nodes descriptions
|
# 📝 Nodes descriptions
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import numpy as np
|
|||||||
from PIL import Image, ImageSequence, ImageOps
|
from PIL import Image, ImageSequence, ImageOps
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
|
from aiohttp import web
|
||||||
|
from server import PromptServer
|
||||||
class LoadImagesFromSelectedFolder:
|
class LoadImagesFromSelectedFolder:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
@@ -94,3 +96,29 @@ class LoadImagesFromSelectedFolder:
|
|||||||
outputs.append(placeholder_image)
|
outputs.append(placeholder_image)
|
||||||
|
|
||||||
return tuple(outputs)
|
return tuple(outputs)
|
||||||
|
|
||||||
|
# Define the API endpoint to get the list of image folders
|
||||||
|
@PromptServer.instance.routes.get("/get_image_folders")
|
||||||
|
async def get_image_folders(request):
|
||||||
|
# Get the ComfyUI output directory
|
||||||
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
comfyui_root = os.path.abspath(os.path.join(script_dir, '..', '..'))
|
||||||
|
output_dir = os.path.join(comfyui_root, 'output')
|
||||||
|
|
||||||
|
# Collect folders with images
|
||||||
|
folders = []
|
||||||
|
for root, dirs, files in os.walk(output_dir):
|
||||||
|
rel_path = os.path.relpath(root, output_dir)
|
||||||
|
if rel_path == '.':
|
||||||
|
continue # Skip the root output directory itself
|
||||||
|
# Count image files in the folder
|
||||||
|
image_count = len([f for f in os.listdir(root) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp'))])
|
||||||
|
if image_count > 0:
|
||||||
|
folder_name = f"{rel_path} ({image_count} images)"
|
||||||
|
folders.append(folder_name)
|
||||||
|
|
||||||
|
# Sort the list alphabetically (case-insensitive)
|
||||||
|
folders.sort(key=lambda x: x.lower())
|
||||||
|
|
||||||
|
# Return the folder list as JSON
|
||||||
|
return web.json_response({"success": True, "folders": folders})
|
||||||
63
web/js/load_images_from_folder.js
Normal file
63
web/js/load_images_from_folder.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { app } from "../../../scripts/app.js";
|
||||||
|
|
||||||
|
app.registerExtension({
|
||||||
|
name: "Bjornulf.LoadImagesFromSelectedFolder",
|
||||||
|
async nodeCreated(node) {
|
||||||
|
if (node.comfyClass !== "Bjornulf_LoadImagesFromSelectedFolder") return;
|
||||||
|
|
||||||
|
// Add a "Refresh Folders" button
|
||||||
|
node.addWidget(
|
||||||
|
"button",
|
||||||
|
"Refresh Folders",
|
||||||
|
null,
|
||||||
|
async () => {
|
||||||
|
if (!node.graph) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Fetch the folder list from the server
|
||||||
|
const response = await fetch("/get_image_folders");
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.success && data.folders) {
|
||||||
|
const dropdown = node.widgets.find(w => w.name === "selected_folder");
|
||||||
|
if (dropdown) {
|
||||||
|
// Remember the current selection
|
||||||
|
const currentValue = dropdown.value;
|
||||||
|
|
||||||
|
// Update the dropdown with the new folder list
|
||||||
|
dropdown.options.values = data.folders;
|
||||||
|
|
||||||
|
// Try to keep the same folder selected (accounting for image count changes)
|
||||||
|
const folderNameRegex = /^(.+?)\s*\(\d+\s*images\)$/;
|
||||||
|
const match = currentValue.match(folderNameRegex);
|
||||||
|
let updatedValue = null;
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const folderName = match[1]; // e.g., "folder1"
|
||||||
|
updatedValue = data.folders.find(f => f.startsWith(folderName + " ("));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the new value: keep the folder if it exists, otherwise pick the first one
|
||||||
|
if (updatedValue) {
|
||||||
|
dropdown.value = updatedValue;
|
||||||
|
} else if (data.folders.length > 0) {
|
||||||
|
dropdown.value = data.folders[0];
|
||||||
|
} else {
|
||||||
|
dropdown.value = null; // No folders available
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let you know it worked
|
||||||
|
app.ui.dialog.show("Folder list refreshed successfully.");
|
||||||
|
} else {
|
||||||
|
app.ui.dialog.show("Dropdown widget not found.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
app.ui.dialog.show("Failed to fetch folder list.");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
app.ui.dialog.show("An error occurred while fetching the folder list.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -65,7 +65,8 @@ function displayVideoPreview(component, filename, category, autoplay, mute) {
|
|||||||
else videoWidget.videoElement.muted = false;
|
else videoWidget.videoElement.muted = false;
|
||||||
if(autoplay) videoWidget.videoElement.autoplay = !videoWidget.value.paused && !videoWidget.value.hidden;
|
if(autoplay) videoWidget.videoElement.autoplay = !videoWidget.value.paused && !videoWidget.value.hidden;
|
||||||
else videoWidget.videoElement.autoplay = false;
|
else videoWidget.videoElement.autoplay = false;
|
||||||
videoWidget.videoElement.src = `http://localhost:8188/api/view?${urlParams.toString()}`;
|
// videoWidget.videoElement.src = `http://localhost:8188/api/view?${urlParams.toString()}`;
|
||||||
|
videoWidget.videoElement.src = `api/view?${urlParams.toString()}`;
|
||||||
|
|
||||||
adjustSize(component); // Adjust the component size
|
adjustSize(component); // Adjust the component size
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user