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.
|
||||
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)
|
||||
`117.` [📝👈🅰️ Line selector (🎲 or ♻ or ♻📑)](#117---🅰%EF%B8%8F-line-selector--or--or-)
|
||||
`135.` [🔛✨ Anything Switch On/Off](#135----anything-switch-onoff)
|
||||
`136.` [🔛📝 Text Switch On/Off](#136----text-switch-onoff)
|
||||
|
||||
## 🧠 Logic / Conditional Operations 🧠
|
||||
`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...
|
||||
- **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.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.)
|
||||
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 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.
|
||||
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.
|
||||
❗Breaking changes. Large rewrite for all FFMPEG related nodes. With options for video preview. (Still have few changes to make, next version.)
|
||||
- **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%)}
|
||||
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
|
||||
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 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.)
|
||||
|
||||
# 📝 Nodes descriptions
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ import numpy as np
|
||||
from PIL import Image, ImageSequence, ImageOps
|
||||
import torch
|
||||
|
||||
from aiohttp import web
|
||||
from server import PromptServer
|
||||
class LoadImagesFromSelectedFolder:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
@@ -94,3 +96,29 @@ class LoadImagesFromSelectedFolder:
|
||||
outputs.append(placeholder_image)
|
||||
|
||||
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;
|
||||
if(autoplay) videoWidget.videoElement.autoplay = !videoWidget.value.paused && !videoWidget.value.hidden;
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user