Refactor: Simplify file picker and auto-extract folder path
Major changes: - Changed input parameter from 'path' to 'image' for clarity - Auto-extract folder path from file selection in folder mode - Simplified JavaScript widget implementation - Added OUTPUT_IS_LIST flag for proper output handling - Improved file selection UI with emoji icons - Added helpful info widget explaining the modes Features: - Single mode: Load selected file directly - Folder mode: Extract folder from selected file, then pick random image - Better user feedback with console logging - More robust path handling (file or directory input)
This commit is contained in:
+22
-14
@@ -28,10 +28,9 @@ class RandomImagePicker:
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"path": ("STRING", {
|
||||
"image": ("STRING", {
|
||||
"default": default_path,
|
||||
"multiline": False,
|
||||
"placeholder": "Path to image file or folder"
|
||||
}),
|
||||
"folder_mode": ("BOOLEAN", {
|
||||
"default": False,
|
||||
@@ -56,6 +55,7 @@ class RandomImagePicker:
|
||||
FUNCTION = "load_image"
|
||||
CATEGORY = "image"
|
||||
OUTPUT_NODE = True
|
||||
OUTPUT_IS_LIST = (False, False, False)
|
||||
|
||||
@staticmethod
|
||||
def get_image_files(directory: str, include_subfolders: bool = False) -> List[str]:
|
||||
@@ -114,12 +114,12 @@ class RandomImagePicker:
|
||||
|
||||
return img_tensor, width, height
|
||||
|
||||
def load_image(self, path: str, folder_mode: bool, include_subfolders: bool, seed: int):
|
||||
def load_image(self, image: str, folder_mode: bool, include_subfolders: bool, seed: int):
|
||||
"""
|
||||
Load image based on mode settings.
|
||||
|
||||
Args:
|
||||
path: File or folder path
|
||||
image: File path (single mode) or file path to extract folder from (folder mode)
|
||||
folder_mode: True for folder mode, False for single file mode
|
||||
include_subfolders: Whether to scan subfolders
|
||||
seed: Random seed for folder mode
|
||||
@@ -127,31 +127,39 @@ class RandomImagePicker:
|
||||
Returns:
|
||||
Tuple of (image_tensor, width, height)
|
||||
"""
|
||||
if not path or not os.path.exists(path):
|
||||
raise ValueError(f"Path does not exist: {path}")
|
||||
if not image or not os.path.exists(image):
|
||||
raise ValueError(f"Path does not exist: {image}")
|
||||
|
||||
if folder_mode:
|
||||
# Folder mode: pick random image
|
||||
if not os.path.isdir(path):
|
||||
raise ValueError(f"Folder mode enabled but path is not a directory: {path}")
|
||||
# Folder mode: extract folder from file path and pick random image
|
||||
if os.path.isfile(image):
|
||||
# Extract folder path from file path
|
||||
folder_path = os.path.dirname(image)
|
||||
elif os.path.isdir(image):
|
||||
# Direct folder path
|
||||
folder_path = image
|
||||
else:
|
||||
raise ValueError(f"Invalid path: {image}")
|
||||
|
||||
image_files = self.get_image_files(path, include_subfolders)
|
||||
image_files = self.get_image_files(folder_path, include_subfolders)
|
||||
|
||||
if not image_files:
|
||||
raise ValueError(f"No image files found in directory: {path}")
|
||||
raise ValueError(f"No image files found in directory: {folder_path}")
|
||||
|
||||
# Use seed for reproducible random selection
|
||||
random.seed(seed)
|
||||
selected_image = random.choice(image_files)
|
||||
|
||||
print(f"[Random Image Picker] Folder: {folder_path}")
|
||||
print(f"[Random Image Picker] Selected: {selected_image}")
|
||||
return self.load_image_file(selected_image)
|
||||
else:
|
||||
# Single file mode
|
||||
if not os.path.isfile(path):
|
||||
raise ValueError(f"Single file mode enabled but path is not a file: {path}")
|
||||
if not os.path.isfile(image):
|
||||
raise ValueError(f"Single file mode enabled but path is not a file: {image}")
|
||||
|
||||
return self.load_image_file(path)
|
||||
print(f"[Random Image Picker] Loading: {image}")
|
||||
return self.load_image_file(image)
|
||||
|
||||
|
||||
# Node registration
|
||||
|
||||
+46
-54
@@ -1,4 +1,5 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { ComfyWidgets } from "../../scripts/widgets.js";
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.RandomImagePicker",
|
||||
@@ -8,63 +9,54 @@ app.registerExtension({
|
||||
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
||||
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
const result = onNodeCreated?.apply(this, arguments);
|
||||
const ret = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
|
||||
|
||||
// Add file browser button
|
||||
const pathWidget = this.widgets.find(w => w.name === "path");
|
||||
if (pathWidget) {
|
||||
// Add button to open file dialog
|
||||
const button = this.addWidget("button", "Browse...", null, () => {
|
||||
// Create file input element
|
||||
const input = document.createElement("input");
|
||||
|
||||
// Get folder_mode widget value
|
||||
const folderModeWidget = this.widgets.find(w => w.name === "folder_mode");
|
||||
const isFolderMode = folderModeWidget?.value || false;
|
||||
|
||||
if (isFolderMode) {
|
||||
// Folder selection
|
||||
input.type = "file";
|
||||
input.webkitdirectory = true;
|
||||
input.directory = true;
|
||||
} else {
|
||||
// File selection
|
||||
input.type = "file";
|
||||
input.accept = "image/png, image/jpeg, image/webp, image/bmp, image/gif";
|
||||
}
|
||||
|
||||
input.onchange = (e) => {
|
||||
if (e.target.files && e.target.files[0]) {
|
||||
if (isFolderMode) {
|
||||
// Get folder path from first file
|
||||
const firstFile = e.target.files[0];
|
||||
const fullPath = firstFile.webkitRelativePath || firstFile.name;
|
||||
const folderPath = fullPath.split('/')[0];
|
||||
pathWidget.value = folderPath;
|
||||
} else {
|
||||
// Get file path
|
||||
const file = e.target.files[0];
|
||||
// Note: For security reasons, browsers don't expose full file paths
|
||||
// We can only get the file name, not the full path
|
||||
// Users will need to manually enter the full path or use relative paths
|
||||
pathWidget.value = file.name;
|
||||
|
||||
// Show info message
|
||||
console.log("Note: Due to browser security, full path is not available. Please enter the complete path manually.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
input.click();
|
||||
});
|
||||
// Find the image widget
|
||||
const imageWidget = this.widgets.find(w => w.name === "image");
|
||||
if (!imageWidget) return ret;
|
||||
|
||||
// Create file input button widget
|
||||
const fileWidget = this.addWidget("button", "📁 Choose File", "file", () => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = "image/png,image/jpeg,image/jpg,image/webp,image/bmp,image/gif";
|
||||
|
||||
// Add helper text
|
||||
this.addWidget("text", "helper", "Tip: Enter full path or use ComfyUI input folder", () => {}, {
|
||||
serialize: false
|
||||
});
|
||||
}
|
||||
input.onchange = (e) => {
|
||||
if (e.target.files && e.target.files[0]) {
|
||||
const file = e.target.files[0];
|
||||
|
||||
// Create a URL for the file
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
// Get file path (name only, browser security limitation)
|
||||
const fileName = file.name;
|
||||
|
||||
// Try to use the webkitRelativePath or fall back to name
|
||||
const filePath = file.webkitRelativePath || file.path || fileName;
|
||||
|
||||
// Update the image widget value
|
||||
imageWidget.value = filePath;
|
||||
|
||||
console.log("Selected file:", filePath);
|
||||
|
||||
// Show notification
|
||||
app.ui.dialog.show(`File selected: ${fileName}\n\nNote: You may need to enter the full path manually due to browser security restrictions.`);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
|
||||
input.click();
|
||||
});
|
||||
|
||||
return result;
|
||||
// Add info widget
|
||||
const infoWidget = this.addWidget("text", "ℹ️ Info",
|
||||
"Single mode: Select any image file\nFolder mode: File's folder will be used",
|
||||
() => {},
|
||||
{ serialize: false }
|
||||
);
|
||||
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user