From 44d69e890757810b224a0178e38da56c192adbe0 Mon Sep 17 00:00:00 2001 From: justumen Date: Fri, 28 Feb 2025 10:07:58 +0100 Subject: [PATCH] ... --- README.md | 19 +++++----- load_images_from_folder.py | 28 ++++++++++++++ web/js/load_images_from_folder.js | 63 +++++++++++++++++++++++++++++++ web/js/video_preview.js | 3 +- 4 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 web/js/load_images_from_folder.js diff --git a/README.md b/README.md index 897a5d3..5b6292c 100644 --- a/README.md +++ b/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 : ❀️❀️❀️ ❀️ `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 diff --git a/load_images_from_folder.py b/load_images_from_folder.py index 8d4f33b..9a6d9b7 100644 --- a/load_images_from_folder.py +++ b/load_images_from_folder.py @@ -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}) \ No newline at end of file diff --git a/web/js/load_images_from_folder.js b/web/js/load_images_from_folder.js new file mode 100644 index 0000000..74e1366 --- /dev/null +++ b/web/js/load_images_from_folder.js @@ -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."); + } + } + ); + }, +}); \ No newline at end of file diff --git a/web/js/video_preview.js b/web/js/video_preview.js index 91b8f26..70d34b7 100644 --- a/web/js/video_preview.js +++ b/web/js/video_preview.js @@ -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 }