mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-02-10 13:32:36 +08:00
Image multi upload widget
This commit is contained in:
parent
29c50954ea
commit
d934119333
69
nodes.py
69
nodes.py
@ -1085,7 +1085,7 @@ class LoadImage:
|
|||||||
input_dir = folder_paths.get_input_directory()
|
input_dir = folder_paths.get_input_directory()
|
||||||
files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))]
|
files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))]
|
||||||
return {"required":
|
return {"required":
|
||||||
{"image": (sorted(files), )},
|
{"image": (sorted(files), { "forceInput": True })},
|
||||||
}
|
}
|
||||||
|
|
||||||
CATEGORY = "image"
|
CATEGORY = "image"
|
||||||
@ -1121,6 +1121,72 @@ class LoadImage:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
class LoadImageBatch:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
input_dir = folder_paths.get_input_directory()
|
||||||
|
files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))]
|
||||||
|
return {"required":
|
||||||
|
{"images": (sorted(files), )},
|
||||||
|
}
|
||||||
|
|
||||||
|
CATEGORY = "image"
|
||||||
|
|
||||||
|
RETURN_TYPES = ("IMAGE", "MASK")
|
||||||
|
FUNCTION = "load_images"
|
||||||
|
|
||||||
|
INPUT_IS_LIST = True
|
||||||
|
OUTPUT_IS_LIST = (True, True, )
|
||||||
|
|
||||||
|
def load_images(self, images):
|
||||||
|
output_images = []
|
||||||
|
output_masks = []
|
||||||
|
|
||||||
|
for i in range(len(images)):
|
||||||
|
image_path = folder_paths.get_annotated_filepath(images[i])
|
||||||
|
|
||||||
|
i = Image.open(image_path)
|
||||||
|
i = ImageOps.exif_transpose(i)
|
||||||
|
image = i.convert("RGB")
|
||||||
|
image = np.array(image).astype(np.float32) / 255.0
|
||||||
|
image = torch.from_numpy(image)[None,]
|
||||||
|
if 'A' in i.getbands():
|
||||||
|
mask = np.array(i.getchannel('A')).astype(np.float32) / 255.0
|
||||||
|
mask = 1. - torch.from_numpy(mask)
|
||||||
|
else:
|
||||||
|
mask = torch.zeros((64,64), dtype=torch.float32, device="cpu")
|
||||||
|
|
||||||
|
output_images.append(image)
|
||||||
|
output_masks.append(mask)
|
||||||
|
|
||||||
|
return (output_images, output_masks, )
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def IS_CHANGED(s, images):
|
||||||
|
hashes = []
|
||||||
|
|
||||||
|
for image in images:
|
||||||
|
image_path = folder_paths.get_annotated_filepath(image)
|
||||||
|
m = hashlib.sha256()
|
||||||
|
with open(image_path, 'rb') as f:
|
||||||
|
m.update(f.read())
|
||||||
|
hashes.append(m.digest().hex())
|
||||||
|
|
||||||
|
return hashes
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def VALIDATE_INPUTS(s, images):
|
||||||
|
invalid = []
|
||||||
|
|
||||||
|
for image in images:
|
||||||
|
if not folder_paths.exists_annotated_filepath(image):
|
||||||
|
invalid.append(image)
|
||||||
|
|
||||||
|
if len(invalid) > 0:
|
||||||
|
return "Invalid image file(s): {}".format(", ".join(invalid))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
class LoadImageMask:
|
class LoadImageMask:
|
||||||
_color_channels = ["alpha", "red", "green", "blue"]
|
_color_channels = ["alpha", "red", "green", "blue"]
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -1289,6 +1355,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"PreviewImage": PreviewImage,
|
"PreviewImage": PreviewImage,
|
||||||
"LoadImage": LoadImage,
|
"LoadImage": LoadImage,
|
||||||
"LoadImageMask": LoadImageMask,
|
"LoadImageMask": LoadImageMask,
|
||||||
|
"LoadImageBatch": LoadImageBatch,
|
||||||
"ImageScale": ImageScale,
|
"ImageScale": ImageScale,
|
||||||
"ImageInvert": ImageInvert,
|
"ImageInvert": ImageInvert,
|
||||||
"ImagePadForOutpaint": ImagePadForOutpaint,
|
"ImagePadForOutpaint": ImagePadForOutpaint,
|
||||||
|
|||||||
@ -5,8 +5,14 @@ import { app } from "/scripts/app.js";
|
|||||||
app.registerExtension({
|
app.registerExtension({
|
||||||
name: "Comfy.UploadImage",
|
name: "Comfy.UploadImage",
|
||||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||||
if (nodeData.name === "LoadImage" || nodeData.name === "LoadImageMask") {
|
switch (nodeData.name) {
|
||||||
|
case "LoadImage":
|
||||||
|
case "LoadImageMask":
|
||||||
nodeData.input.required.upload = ["IMAGEUPLOAD"];
|
nodeData.input.required.upload = ["IMAGEUPLOAD"];
|
||||||
|
break;
|
||||||
|
case "LoadImageBatch":
|
||||||
|
nodeData.input.required.upload = ["MULTIIMAGEUPLOAD"];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1110,22 +1110,23 @@ export class ComfyApp {
|
|||||||
for (const inputName in inputs) {
|
for (const inputName in inputs) {
|
||||||
const inputData = inputs[inputName];
|
const inputData = inputs[inputName];
|
||||||
const type = inputData[0];
|
const type = inputData[0];
|
||||||
|
const inputShape = nodeData["input_is_list"] ? LiteGraph.GRID_SHAPE : LiteGraph.CIRCLE_SHAPE;
|
||||||
|
|
||||||
if(inputData[1]?.forceInput) {
|
if(inputData[1]?.forceInput) {
|
||||||
this.addInput(inputName, type);
|
this.addInput(inputName, type, { shape: inputShape });
|
||||||
} else {
|
} else {
|
||||||
if (Array.isArray(type)) {
|
if (Array.isArray(type)) {
|
||||||
// Enums
|
// Enums
|
||||||
Object.assign(config, widgets.COMBO(this, inputName, inputData, app) || {});
|
Object.assign(config, widgets.COMBO(this, inputName, inputData, nodeData, app) || {});
|
||||||
} else if (`${type}:${inputName}` in widgets) {
|
} else if (`${type}:${inputName}` in widgets) {
|
||||||
// Support custom widgets by Type:Name
|
// Support custom widgets by Type:Name
|
||||||
Object.assign(config, widgets[`${type}:${inputName}`](this, inputName, inputData, app) || {});
|
Object.assign(config, widgets[`${type}:${inputName}`](this, inputName, inputData, nodeData, app) || {});
|
||||||
} else if (type in widgets) {
|
} else if (type in widgets) {
|
||||||
// Standard type widgets
|
// Standard type widgets
|
||||||
Object.assign(config, widgets[type](this, inputName, inputData, app) || {});
|
Object.assign(config, widgets[type](this, inputName, inputData, nodeData, app) || {});
|
||||||
} else {
|
} else {
|
||||||
// Node connection inputs
|
// Node connection inputs
|
||||||
this.addInput(inputName, type);
|
this.addInput(inputName, type, { shape: inputShape });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1133,7 +1134,7 @@ export class ComfyApp {
|
|||||||
for (const o in nodeData["output"]) {
|
for (const o in nodeData["output"]) {
|
||||||
const output = nodeData["output"][o];
|
const output = nodeData["output"][o];
|
||||||
const outputName = nodeData["output_name"][o] || output;
|
const outputName = nodeData["output_name"][o] || output;
|
||||||
const outputShape = nodeData["output_is_list"][o] ? LiteGraph.GRID_SHAPE : LiteGraph.CIRCLE_SHAPE ;
|
const outputShape = nodeData["output_is_list"][o] ? LiteGraph.GRID_SHAPE : LiteGraph.CIRCLE_SHAPE;
|
||||||
this.addOutput(outputName, output, { shape: outputShape });
|
this.addOutput(outputName, output, { shape: outputShape });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -246,55 +246,217 @@ function addMultilineWidget(node, name, opts, app) {
|
|||||||
return { minWidth: 400, minHeight: 200, widget };
|
return { minWidth: 400, minHeight: 200, widget };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ComfyWidgets = {
|
const FLOAT = (node, inputName, inputData) => {
|
||||||
"INT:seed": seedWidget,
|
const { val, config } = getNumberDefaults(inputData, 0.5);
|
||||||
"INT:noise_seed": seedWidget,
|
return { widget: node.addWidget("number", inputName, val, () => {}, config) };
|
||||||
FLOAT(node, inputName, inputData) {
|
}
|
||||||
const { val, config } = getNumberDefaults(inputData, 0.5);
|
|
||||||
return { widget: node.addWidget("number", inputName, val, () => {}, config) };
|
|
||||||
},
|
|
||||||
INT(node, inputName, inputData) {
|
|
||||||
const { val, config } = getNumberDefaults(inputData, 1);
|
|
||||||
Object.assign(config, { precision: 0 });
|
|
||||||
return {
|
|
||||||
widget: node.addWidget(
|
|
||||||
"number",
|
|
||||||
inputName,
|
|
||||||
val,
|
|
||||||
function (v) {
|
|
||||||
const s = this.options.step / 10;
|
|
||||||
this.value = Math.round(v / s) * s;
|
|
||||||
},
|
|
||||||
config
|
|
||||||
),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
STRING(node, inputName, inputData, app) {
|
|
||||||
const defaultVal = inputData[1].default || "";
|
|
||||||
const multiline = !!inputData[1].multiline;
|
|
||||||
|
|
||||||
if (multiline) {
|
const INT = (node, inputName, inputData) => {
|
||||||
return addMultilineWidget(node, inputName, { defaultVal, ...inputData[1] }, app);
|
const { val, config } = getNumberDefaults(inputData, 1);
|
||||||
} else {
|
Object.assign(config, { precision: 0 });
|
||||||
return { widget: node.addWidget("text", inputName, defaultVal, () => {}, {}) };
|
return {
|
||||||
}
|
widget: node.addWidget(
|
||||||
},
|
"number",
|
||||||
COMBO(node, inputName, inputData) {
|
inputName,
|
||||||
const type = inputData[0];
|
val,
|
||||||
let defaultValue = type[0];
|
function (v) {
|
||||||
if (inputData[1] && inputData[1].default) {
|
const s = this.options.step / 10;
|
||||||
defaultValue = inputData[1].default;
|
this.value = Math.round(v / s) * s;
|
||||||
}
|
},
|
||||||
|
config
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const STRING = (node, inputName, inputData, nodeData, app) => {
|
||||||
|
const defaultVal = inputData[1].default || "";
|
||||||
|
const multiline = !!inputData[1].multiline;
|
||||||
|
|
||||||
|
if (multiline) {
|
||||||
|
return addMultilineWidget(node, inputName, { defaultVal, ...inputData[1] }, app);
|
||||||
|
} else {
|
||||||
|
return { widget: node.addWidget("text", inputName, defaultVal, () => {}, {}) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const COMBO = (node, inputName, inputData, nodeData) => {
|
||||||
|
const type = inputData[0];
|
||||||
|
let defaultValue = type[0];
|
||||||
|
if (inputData[1] && inputData[1].default) {
|
||||||
|
defaultValue = inputData[1].default;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeData["input_is_list"]) {
|
||||||
|
defaultValue = [defaultValue]
|
||||||
|
const widget = node.addWidget("text", inputName, defaultValue, () => {}, { values: type })
|
||||||
|
widget.disabled = true;
|
||||||
|
return { widget };
|
||||||
|
}
|
||||||
|
else {
|
||||||
return { widget: node.addWidget("combo", inputName, defaultValue, () => {}, { values: type }) };
|
return { widget: node.addWidget("combo", inputName, defaultValue, () => {}, { values: type }) };
|
||||||
},
|
}
|
||||||
IMAGEUPLOAD(node, inputName, inputData, app) {
|
}
|
||||||
const imageWidget = node.widgets.find((w) => w.name === "image");
|
|
||||||
let uploadWidget;
|
|
||||||
|
|
||||||
function showImage(name) {
|
const IMAGEUPLOAD = (node, inputName, inputData, nodeData, app) => {
|
||||||
|
const imageWidget = node.widgets.find((w) => w.name === "image");
|
||||||
|
let uploadWidget;
|
||||||
|
|
||||||
|
function showImage(name) {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => {
|
||||||
|
node.imgs = [img];
|
||||||
|
app.graph.setDirtyCanvas(true);
|
||||||
|
};
|
||||||
|
let folder_separator = name.lastIndexOf("/");
|
||||||
|
let subfolder = "";
|
||||||
|
if (folder_separator > -1) {
|
||||||
|
subfolder = name.substring(0, folder_separator);
|
||||||
|
name = name.substring(folder_separator + 1);
|
||||||
|
}
|
||||||
|
img.src = `/view?filename=${name}&type=input&subfolder=${subfolder}`;
|
||||||
|
node.setSizeForImage?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
var default_value = imageWidget.value;
|
||||||
|
Object.defineProperty(imageWidget, "value", {
|
||||||
|
set : function(value) {
|
||||||
|
this._real_value = value;
|
||||||
|
},
|
||||||
|
|
||||||
|
get : function() {
|
||||||
|
let value = "";
|
||||||
|
if (this._real_value) {
|
||||||
|
value = this._real_value;
|
||||||
|
} else {
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.filename) {
|
||||||
|
let real_value = value;
|
||||||
|
value = "";
|
||||||
|
if (real_value.subfolder) {
|
||||||
|
value = real_value.subfolder + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
value += real_value.filename;
|
||||||
|
|
||||||
|
if(real_value.type && real_value.type !== "input")
|
||||||
|
value += ` [${real_value.type}]`;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add our own callback to the combo widget to render an image when it changes
|
||||||
|
const cb = node.callback;
|
||||||
|
imageWidget.callback = function () {
|
||||||
|
showImage(imageWidget.value);
|
||||||
|
if (cb) {
|
||||||
|
return cb.apply(this, arguments);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// On load if we have a value then render the image
|
||||||
|
// The value isnt set immediately so we need to wait a moment
|
||||||
|
// No change callbacks seem to be fired on initial setting of the value
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (imageWidget.value) {
|
||||||
|
showImage(imageWidget.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function uploadFile(file, updateNode) {
|
||||||
|
try {
|
||||||
|
// Wrap file in formdata so it includes filename
|
||||||
|
const body = new FormData();
|
||||||
|
body.append("image", file);
|
||||||
|
const resp = await fetch("/upload/image", {
|
||||||
|
method: "POST",
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (resp.status === 200) {
|
||||||
|
const data = await resp.json();
|
||||||
|
// Add the file as an option and update the widget value
|
||||||
|
if (!imageWidget.options.values.includes(data.name)) {
|
||||||
|
imageWidget.options.values.push(data.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateNode) {
|
||||||
|
showImage(data.name);
|
||||||
|
|
||||||
|
imageWidget.value = data.name;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert(resp.status + " - " + resp.statusText);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
alert(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileInput = document.createElement("input");
|
||||||
|
Object.assign(fileInput, {
|
||||||
|
type: "file",
|
||||||
|
accept: "image/jpeg,image/png,image/webp",
|
||||||
|
style: "display: none",
|
||||||
|
onchange: async () => {
|
||||||
|
if (fileInput.files.length) {
|
||||||
|
await uploadFile(fileInput.files[0], true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
document.body.append(fileInput);
|
||||||
|
|
||||||
|
// Create the button widget for selecting the files
|
||||||
|
uploadWidget = node.addWidget("button", "choose file to upload", "image", () => {
|
||||||
|
fileInput.value = null;
|
||||||
|
fileInput.click();
|
||||||
|
});
|
||||||
|
uploadWidget.serialize = false;
|
||||||
|
|
||||||
|
// Add handler to check if an image is being dragged over our node
|
||||||
|
node.onDragOver = function (e) {
|
||||||
|
if (e.dataTransfer && e.dataTransfer.items) {
|
||||||
|
const image = [...e.dataTransfer.items].find((f) => f.kind === "file" && f.type.startsWith("image/"));
|
||||||
|
return !!image;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// On drop upload files
|
||||||
|
node.onDragDrop = function (e) {
|
||||||
|
console.log("onDragDrop called");
|
||||||
|
let handled = false;
|
||||||
|
for (const file of e.dataTransfer.files) {
|
||||||
|
if (file.type.startsWith("image/")) {
|
||||||
|
uploadFile(file, !handled); // Dont await these, any order is fine, only update on first one
|
||||||
|
handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return handled;
|
||||||
|
};
|
||||||
|
|
||||||
|
return { widget: uploadWidget };
|
||||||
|
}
|
||||||
|
|
||||||
|
const MULTIIMAGEUPLOAD = (node, inputName, inputData, nodeData, app) => {
|
||||||
|
const imagesWidget = node.widgets.find((w) => w.name === "images");
|
||||||
|
let uploadWidget;
|
||||||
|
let clearWidget;
|
||||||
|
|
||||||
|
function showImages(names) {
|
||||||
|
node.imgs = []
|
||||||
|
|
||||||
|
for (const name of names) {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
node.imgs = [img];
|
// TODO await this?
|
||||||
|
node.imgs.push(img)
|
||||||
|
node.imageIndex = null;
|
||||||
|
node.setSizeForImage?.();
|
||||||
app.graph.setDirtyCanvas(true);
|
app.graph.setDirtyCanvas(true);
|
||||||
};
|
};
|
||||||
let folder_separator = name.lastIndexOf("/");
|
let folder_separator = name.lastIndexOf("/");
|
||||||
@ -306,21 +468,20 @@ export const ComfyWidgets = {
|
|||||||
img.src = `/view?filename=${name}&type=input&subfolder=${subfolder}${app.getPreviewFormatParam()}`;
|
img.src = `/view?filename=${name}&type=input&subfolder=${subfolder}${app.getPreviewFormatParam()}`;
|
||||||
node.setSizeForImage?.();
|
node.setSizeForImage?.();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var default_value = imageWidget.value;
|
var default_value = imagesWidget.value;
|
||||||
Object.defineProperty(imageWidget, "value", {
|
Object.defineProperty(imagesWidget, "value", {
|
||||||
set : function(value) {
|
set : function(value) {
|
||||||
this._real_value = value;
|
this._real_value = value;
|
||||||
},
|
},
|
||||||
|
|
||||||
get : function() {
|
get : function() {
|
||||||
let value = "";
|
this._real_value ||= []
|
||||||
if (this._real_value) {
|
|
||||||
value = this._real_value;
|
|
||||||
} else {
|
|
||||||
return default_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const result = []
|
||||||
|
|
||||||
|
for (const value of this._real_value) {
|
||||||
if (value.filename) {
|
if (value.filename) {
|
||||||
let real_value = value;
|
let real_value = value;
|
||||||
value = "";
|
value = "";
|
||||||
@ -333,29 +494,35 @@ export const ComfyWidgets = {
|
|||||||
if(real_value.type && real_value.type !== "input")
|
if(real_value.type && real_value.type !== "input")
|
||||||
value += ` [${real_value.type}]`;
|
value += ` [${real_value.type}]`;
|
||||||
}
|
}
|
||||||
return value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add our own callback to the combo widget to render an image when it changes
|
result.push(value)
|
||||||
const cb = node.callback;
|
|
||||||
imageWidget.callback = function () {
|
|
||||||
showImage(imageWidget.value);
|
|
||||||
if (cb) {
|
|
||||||
return cb.apply(this, arguments);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// On load if we have a value then render the image
|
this._real_value = result
|
||||||
// The value isnt set immediately so we need to wait a moment
|
return this._real_value;
|
||||||
// No change callbacks seem to be fired on initial setting of the value
|
}
|
||||||
requestAnimationFrame(() => {
|
});
|
||||||
if (imageWidget.value) {
|
|
||||||
showImage(imageWidget.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
async function uploadFile(file, updateNode) {
|
// Add our own callback to the combo widget to render an image when it changes
|
||||||
|
const cb = node.callback;
|
||||||
|
imagesWidget.callback = function () {
|
||||||
|
showImages(imagesWidget.value);
|
||||||
|
if (cb) {
|
||||||
|
return cb.apply(this, arguments);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// On load if we have a value then render the image
|
||||||
|
// The value isnt set immediately so we need to wait a moment
|
||||||
|
// No change callbacks seem to be fired on initial setting of the value
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (Array.isArray(imagesWidget.value) && imagesWidget.value.length > 0) {
|
||||||
|
showImages(imagesWidget.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function uploadFiles(files, updateNode) {
|
||||||
|
for (const file of files) {
|
||||||
try {
|
try {
|
||||||
// Wrap file in formdata so it includes filename
|
// Wrap file in formdata so it includes filename
|
||||||
const body = new FormData();
|
const body = new FormData();
|
||||||
@ -368,14 +535,12 @@ export const ComfyWidgets = {
|
|||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
// Add the file as an option and update the widget value
|
// Add the file as an option and update the widget value
|
||||||
if (!imageWidget.options.values.includes(data.name)) {
|
if (!imagesWidget.options.values.includes(data.name)) {
|
||||||
imageWidget.options.values.push(data.name);
|
imagesWidget.options.values.push(data.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateNode) {
|
if (updateNode) {
|
||||||
showImage(data.name);
|
imagesWidget.value.push(data.name)
|
||||||
|
|
||||||
imageWidget.value = data.name;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert(resp.status + " - " + resp.statusText);
|
alert(resp.status + " - " + resp.statusText);
|
||||||
@ -385,49 +550,72 @@ export const ComfyWidgets = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileInput = document.createElement("input");
|
if (updateNode) {
|
||||||
Object.assign(fileInput, {
|
showImages(imagesWidget.value);
|
||||||
type: "file",
|
}
|
||||||
accept: "image/jpeg,image/png,image/webp",
|
}
|
||||||
style: "display: none",
|
|
||||||
onchange: async () => {
|
|
||||||
if (fileInput.files.length) {
|
|
||||||
await uploadFile(fileInput.files[0], true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
document.body.append(fileInput);
|
|
||||||
|
|
||||||
// Create the button widget for selecting the files
|
const fileInput = document.createElement("input");
|
||||||
uploadWidget = node.addWidget("button", "choose file to upload", "image", () => {
|
Object.assign(fileInput, {
|
||||||
fileInput.click();
|
type: "file",
|
||||||
});
|
multiple: "multiple",
|
||||||
uploadWidget.serialize = false;
|
accept: "image/jpeg,image/png,image/webp",
|
||||||
|
style: "display: none",
|
||||||
// Add handler to check if an image is being dragged over our node
|
onchange: async () => {
|
||||||
node.onDragOver = function (e) {
|
if (fileInput.files.length) {
|
||||||
if (e.dataTransfer && e.dataTransfer.items) {
|
await uploadFiles(fileInput.files, true);
|
||||||
const image = [...e.dataTransfer.items].find((f) => f.kind === "file" && f.type.startsWith("image/"));
|
|
||||||
return !!image;
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
document.body.append(fileInput);
|
||||||
|
|
||||||
return false;
|
// Create the button widget for selecting the files
|
||||||
};
|
uploadWidget = node.addWidget("button", "choose files to upload", "images", () => {
|
||||||
|
fileInput.value = null;
|
||||||
|
fileInput.click();
|
||||||
|
});
|
||||||
|
uploadWidget.serialize = false;
|
||||||
|
|
||||||
// On drop upload files
|
clearWidget = node.addWidget("button", "clear all uploads", "images", () => {
|
||||||
node.onDragDrop = function (e) {
|
imagesWidget.value = []
|
||||||
console.log("onDragDrop called");
|
showImages(imagesWidget.value);
|
||||||
let handled = false;
|
});
|
||||||
for (const file of e.dataTransfer.files) {
|
clearWidget.serialize = false;
|
||||||
if (file.type.startsWith("image/")) {
|
|
||||||
uploadFile(file, !handled); // Dont await these, any order is fine, only update on first one
|
// Add handler to check if an image is being dragged over our node
|
||||||
handled = true;
|
node.onDragOver = function (e) {
|
||||||
}
|
if (e.dataTransfer && e.dataTransfer.items) {
|
||||||
|
const image = [...e.dataTransfer.items].find((f) => f.kind === "file" && f.type.startsWith("image/"));
|
||||||
|
return !!image;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// On drop upload files
|
||||||
|
node.onDragDrop = function (e) {
|
||||||
|
console.log("onDragDrop called");
|
||||||
|
let handled = false;
|
||||||
|
for (const file of e.dataTransfer.files) {
|
||||||
|
if (file.type.startsWith("image/")) {
|
||||||
|
uploadFile(file, !handled); // Dont await these, any order is fine, only update on first one
|
||||||
|
handled = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return handled;
|
return handled;
|
||||||
};
|
};
|
||||||
|
|
||||||
return { widget: uploadWidget };
|
return { widget: uploadWidget };
|
||||||
},
|
}
|
||||||
|
|
||||||
|
export const ComfyWidgets = {
|
||||||
|
"INT:seed": seedWidget,
|
||||||
|
"INT:noise_seed": seedWidget,
|
||||||
|
FLOAT,
|
||||||
|
INT,
|
||||||
|
STRING,
|
||||||
|
COMBO,
|
||||||
|
IMAGEUPLOAD,
|
||||||
|
MULTIIMAGEUPLOAD,
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user