Features: - Subfolder navigation and dynamic image filtering - Support for all common image formats - Transparency mask extraction - Security validation and error handling - JavaScript frontend for real-time UI updates
150 lines
6.9 KiB
JavaScript
150 lines
6.9 KiB
JavaScript
import { app } from "../../../scripts/app.js";
|
|
import { api } from "../../../scripts/api.js";
|
|
|
|
// Extension for SubfolderImageLoader dynamic input filtering
|
|
app.registerExtension({
|
|
name: "comfyui.SubfolderImageLoader",
|
|
|
|
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
|
if (nodeData.name === "SubfolderImageLoader") {
|
|
const originalNodeCreated = nodeType.prototype.onNodeCreated;
|
|
|
|
nodeType.prototype.onNodeCreated = function() {
|
|
const result = originalNodeCreated?.apply(this, arguments);
|
|
|
|
// Setup widget callbacks after node is fully created
|
|
setTimeout(() => {
|
|
const subfolderWidget = this.widgets?.find(w => w.name === "subfolder");
|
|
|
|
if (subfolderWidget) {
|
|
const originalCallback = subfolderWidget.callback;
|
|
|
|
subfolderWidget.callback = (value, ...args) => {
|
|
if (originalCallback) {
|
|
originalCallback.call(subfolderWidget, value, ...args);
|
|
}
|
|
|
|
this.updateImageList(value);
|
|
};
|
|
|
|
// Also run initial filtering based on current subfolder value
|
|
const currentSubfolder = subfolderWidget.value || "";
|
|
this.updateImageList(currentSubfolder);
|
|
|
|
} else {
|
|
console.error("SubfolderImageLoader: Could not find subfolder widget!");
|
|
}
|
|
}, 100);
|
|
|
|
return result;
|
|
};
|
|
|
|
// Method to update image list based on selected subfolder
|
|
nodeType.prototype.updateImageList = async function(selectedSubfolder) {
|
|
try {
|
|
// Get the image widget
|
|
const imageWidget = this.widgets?.find(w => w.name === "image");
|
|
if (!imageWidget) {
|
|
return;
|
|
}
|
|
|
|
// Fetch updated image list from the backend
|
|
const response = await fetch("/subfolder_loader/refresh", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
node_id: this.id,
|
|
subfolder: selectedSubfolder || ""
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
console.error("SubfolderImageLoader: Failed to refresh image list:", response.statusText);
|
|
return;
|
|
}
|
|
|
|
const data = await response.json();
|
|
if (!data.success) {
|
|
console.error("SubfolderImageLoader: Server error:", data.error);
|
|
return;
|
|
}
|
|
|
|
// Use the pre-filtered images from the server
|
|
const filteredImages = data.filtered_images || [];
|
|
|
|
// Remove and recreate the image widget
|
|
const imageIndex = this.widgets.findIndex(w => w.name === "image");
|
|
if (imageIndex >= 0) {
|
|
// Store the old widget's callback for reference
|
|
const oldWidget = this.widgets[imageIndex];
|
|
const oldCallback = oldWidget.callback;
|
|
|
|
// Remove the old widget
|
|
this.widgets.splice(imageIndex, 1);
|
|
|
|
// Determine the new value - always use first image from filtered list
|
|
const newValue = filteredImages.length > 0 ? filteredImages[0] : "";
|
|
|
|
// Create new widget with updated options
|
|
const newWidget = this.addWidget(
|
|
"combo",
|
|
"image",
|
|
newValue,
|
|
(value) => {
|
|
// Call original callback if it existed
|
|
if (oldCallback) {
|
|
oldCallback.call(newWidget, value);
|
|
}
|
|
},
|
|
{
|
|
values: filteredImages.length > 0 ? filteredImages : [""]
|
|
}
|
|
);
|
|
|
|
// Move the new widget to the correct position
|
|
if (imageIndex < this.widgets.length - 1) {
|
|
const widget = this.widgets.pop();
|
|
this.widgets.splice(imageIndex, 0, widget);
|
|
}
|
|
|
|
// Force update the node's properties
|
|
this.setProperty("image", newValue);
|
|
|
|
// Trigger the widget callback to ensure the change is registered
|
|
if (newWidget.callback) {
|
|
newWidget.callback(newValue);
|
|
}
|
|
}
|
|
|
|
// Force UI update
|
|
// Note: ComfyUI has a quirk where combo widget visual updates sometimes require
|
|
// mouse movement to refresh the display. The functionality works correctly,
|
|
// but the visual update may be delayed until the next mouse interaction.
|
|
this.setDirtyCanvas(true, true);
|
|
|
|
} catch (error) {
|
|
console.error("SubfolderImageLoader: Error updating image list:", error);
|
|
}
|
|
};
|
|
|
|
// Override the original getExtraMenuOptions to add refresh option
|
|
const originalGetExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
|
|
nodeType.prototype.getExtraMenuOptions = function(_, options) {
|
|
const result = originalGetExtraMenuOptions?.apply(this, arguments) || [];
|
|
|
|
options.push({
|
|
content: "🔄 Refresh Files",
|
|
callback: () => {
|
|
const subfolderWidget = this.widgets?.find(w => w.name === "subfolder");
|
|
const currentSubfolder = subfolderWidget?.value || "";
|
|
this.updateImageList(currentSubfolder);
|
|
}
|
|
});
|
|
|
|
return result;
|
|
};
|
|
}
|
|
}
|
|
}); |