Masked editor changes.
Add a way to upload to subfolders. Clean up code. Fix some issues.
This commit is contained in:
parent
ae08fdb999
commit
850daf0416
|
@ -57,10 +57,6 @@ def get_input_directory():
|
||||||
global input_directory
|
global input_directory
|
||||||
return input_directory
|
return input_directory
|
||||||
|
|
||||||
def get_clipspace_directory():
|
|
||||||
global input_directory
|
|
||||||
return input_directory+"/clipspace"
|
|
||||||
|
|
||||||
|
|
||||||
#NOTE: used in http server so don't put folders that should not be accessed remotely
|
#NOTE: used in http server so don't put folders that should not be accessed remotely
|
||||||
def get_directory_by_type(type_name):
|
def get_directory_by_type(type_name):
|
||||||
|
@ -70,8 +66,6 @@ def get_directory_by_type(type_name):
|
||||||
return get_temp_directory()
|
return get_temp_directory()
|
||||||
if type_name == "input":
|
if type_name == "input":
|
||||||
return get_input_directory()
|
return get_input_directory()
|
||||||
if type_name == "clipspace":
|
|
||||||
return get_clipspace_directory()
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,9 +81,6 @@ def annotated_filepath(name):
|
||||||
elif name.endswith("[temp]"):
|
elif name.endswith("[temp]"):
|
||||||
base_dir = get_temp_directory()
|
base_dir = get_temp_directory()
|
||||||
name = name[:-7]
|
name = name[:-7]
|
||||||
elif name.endswith("[clipspace]"):
|
|
||||||
base_dir = get_clipspace_directory()
|
|
||||||
name = name[:-12]
|
|
||||||
else:
|
else:
|
||||||
return name, None
|
return name, None
|
||||||
|
|
||||||
|
|
8
nodes.py
8
nodes.py
|
@ -973,9 +973,9 @@ class LoadImage:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
input_dir = folder_paths.get_input_directory()
|
input_dir = folder_paths.get_input_directory()
|
||||||
input_dir = [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": ("FILE_COMBO", {"base_dir": "input", "files": sorted(input_dir)}, )},
|
{"image": (sorted(files), )},
|
||||||
}
|
}
|
||||||
|
|
||||||
CATEGORY = "image"
|
CATEGORY = "image"
|
||||||
|
@ -1015,9 +1015,9 @@ class LoadImageMask:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
input_dir = folder_paths.get_input_directory()
|
input_dir = folder_paths.get_input_directory()
|
||||||
input_dir = [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": ("FILE_COMBO", {"base_dir": "input", "files": sorted(input_dir)}, ),
|
{"image": (sorted(files), ),
|
||||||
"channel": (s._color_channels, ), }
|
"channel": (s._color_channels, ), }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
74
server.py
74
server.py
|
@ -118,8 +118,6 @@ class PromptServer():
|
||||||
type_dir = folder_paths.get_input_directory()
|
type_dir = folder_paths.get_input_directory()
|
||||||
elif dir_type == "input":
|
elif dir_type == "input":
|
||||||
type_dir = folder_paths.get_input_directory()
|
type_dir = folder_paths.get_input_directory()
|
||||||
elif dir_type == "clipspace":
|
|
||||||
type_dir = folder_paths.get_clipspace_directory()
|
|
||||||
elif dir_type == "temp":
|
elif dir_type == "temp":
|
||||||
type_dir = folder_paths.get_temp_directory()
|
type_dir = folder_paths.get_temp_directory()
|
||||||
elif dir_type == "output":
|
elif dir_type == "output":
|
||||||
|
@ -127,73 +125,63 @@ class PromptServer():
|
||||||
|
|
||||||
return type_dir
|
return type_dir
|
||||||
|
|
||||||
@routes.post("/upload/image")
|
def image_upload(post, image_save_function=None):
|
||||||
async def upload_image(request):
|
|
||||||
post = await request.post()
|
|
||||||
image = post.get("image")
|
image = post.get("image")
|
||||||
|
|
||||||
upload_dir = get_dir_by_type(post.get("type"))
|
image_upload_type = post.get("type")
|
||||||
|
upload_dir = get_dir_by_type(image_upload_type)
|
||||||
if not os.path.exists(upload_dir):
|
|
||||||
os.makedirs(upload_dir)
|
|
||||||
|
|
||||||
if image and image.file:
|
if image and image.file:
|
||||||
filename = image.filename
|
filename = image.filename
|
||||||
if not filename:
|
if not filename:
|
||||||
return web.Response(status=400)
|
return web.Response(status=400)
|
||||||
|
|
||||||
|
subfolder = post.get("subfolder", "")
|
||||||
|
full_output_folder = os.path.join(upload_dir, os.path.normpath(subfolder))
|
||||||
|
|
||||||
|
if os.path.commonpath((upload_dir, os.path.abspath(full_output_folder))) != upload_dir:
|
||||||
|
return web.Response(status=400)
|
||||||
|
|
||||||
|
if not os.path.exists(full_output_folder):
|
||||||
|
os.makedirs(full_output_folder)
|
||||||
|
|
||||||
split = os.path.splitext(filename)
|
split = os.path.splitext(filename)
|
||||||
|
filepath = os.path.join(full_output_folder, filename)
|
||||||
|
|
||||||
i = 1
|
i = 1
|
||||||
while os.path.exists(os.path.join(upload_dir, filename)):
|
while os.path.exists(filepath):
|
||||||
filename = f"{split[0]} ({i}){split[1]}"
|
filename = f"{split[0]} ({i}){split[1]}"
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
filepath = os.path.join(upload_dir, filename)
|
if image_save_function is not None:
|
||||||
|
image_save_function(image, post, filepath)
|
||||||
|
else:
|
||||||
|
with open(filepath, "wb") as f:
|
||||||
|
f.write(image.file.read())
|
||||||
|
|
||||||
with open(filepath, "wb") as f:
|
return web.json_response({"name" : filename, "subfolder": subfolder, "type": image_upload_type})
|
||||||
f.write(image.file.read())
|
|
||||||
|
|
||||||
return web.json_response({"name" : filename})
|
|
||||||
else:
|
else:
|
||||||
return web.Response(status=400)
|
return web.Response(status=400)
|
||||||
|
|
||||||
|
@routes.post("/upload/image")
|
||||||
|
async def upload_image(request):
|
||||||
|
post = await request.post()
|
||||||
|
return image_upload(post)
|
||||||
|
|
||||||
@routes.post("/upload/mask")
|
@routes.post("/upload/mask")
|
||||||
async def upload_mask(request):
|
async def upload_mask(request):
|
||||||
post = await request.post()
|
post = await request.post()
|
||||||
image = post.get("image")
|
|
||||||
original_image = post.get("original_image")
|
|
||||||
|
|
||||||
upload_dir = get_dir_by_type(post.get("type"))
|
def image_save_function(image, post, filepath):
|
||||||
|
original_pil = Image.open(post.get("original_image").file).convert('RGBA')
|
||||||
if not os.path.exists(upload_dir):
|
|
||||||
os.makedirs(upload_dir)
|
|
||||||
|
|
||||||
if image and image.file:
|
|
||||||
filename = image.filename
|
|
||||||
if not filename:
|
|
||||||
return web.Response(status=400)
|
|
||||||
|
|
||||||
split = os.path.splitext(filename)
|
|
||||||
i = 1
|
|
||||||
while os.path.exists(os.path.join(upload_dir, filename)):
|
|
||||||
filename = f"{split[0]} ({i}){split[1]}"
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
filepath = os.path.join(upload_dir, filename)
|
|
||||||
|
|
||||||
original_pil = Image.open(original_image.file).convert('RGBA')
|
|
||||||
mask_pil = Image.open(image.file).convert('RGBA')
|
mask_pil = Image.open(image.file).convert('RGBA')
|
||||||
|
|
||||||
# alpha copy
|
# alpha copy
|
||||||
new_alpha = mask_pil.getchannel('A')
|
new_alpha = mask_pil.getchannel('A')
|
||||||
original_pil.putalpha(new_alpha)
|
original_pil.putalpha(new_alpha)
|
||||||
|
|
||||||
original_pil.save(filepath)
|
original_pil.save(filepath)
|
||||||
|
|
||||||
return web.json_response({"name": filename})
|
return image_upload(post, image_save_function)
|
||||||
else:
|
|
||||||
return web.Response(status=400)
|
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/view")
|
@routes.get("/view")
|
||||||
async def view_image(request):
|
async def view_image(request):
|
||||||
|
@ -201,10 +189,6 @@ class PromptServer():
|
||||||
filename = request.rel_url.query["filename"]
|
filename = request.rel_url.query["filename"]
|
||||||
filename,output_dir = folder_paths.annotated_filepath(filename)
|
filename,output_dir = folder_paths.annotated_filepath(filename)
|
||||||
|
|
||||||
if request.rel_url.query.get("type", "input") and filename.startswith("clipspace/"):
|
|
||||||
output_dir = folder_paths.get_clipspace_directory()
|
|
||||||
filename = filename[10:]
|
|
||||||
|
|
||||||
# validation for security: prevent accessing arbitrary path
|
# validation for security: prevent accessing arbitrary path
|
||||||
if filename[0] == '/' or '..' in filename:
|
if filename[0] == '/' or '..' in filename:
|
||||||
return web.Response(status=400)
|
return web.Response(status=400)
|
||||||
|
|
|
@ -41,7 +41,7 @@ async function uploadMask(filepath, formData) {
|
||||||
});
|
});
|
||||||
|
|
||||||
ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']] = new Image();
|
ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']] = new Image();
|
||||||
ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']].src = `view?filename=${filepath.filename}&type=${filepath.type}`;
|
ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']].src = "/view?" + new URLSearchParams(filepath).toString();
|
||||||
|
|
||||||
if(ComfyApp.clipspace.images)
|
if(ComfyApp.clipspace.images)
|
||||||
ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']] = filepath;
|
ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']] = filepath;
|
||||||
|
@ -546,8 +546,8 @@ class MaskEditorDialog extends ComfyDialog {
|
||||||
const item =
|
const item =
|
||||||
{
|
{
|
||||||
"filename": filename,
|
"filename": filename,
|
||||||
"subfolder": "",
|
"subfolder": "clipspace",
|
||||||
"type": "clipspace",
|
"type": "input",
|
||||||
};
|
};
|
||||||
|
|
||||||
if(ComfyApp.clipspace.images)
|
if(ComfyApp.clipspace.images)
|
||||||
|
@ -567,7 +567,8 @@ class MaskEditorDialog extends ComfyDialog {
|
||||||
|
|
||||||
formData.append('image', blob, filename);
|
formData.append('image', blob, filename);
|
||||||
formData.append('original_image', original_blob);
|
formData.append('original_image', original_blob);
|
||||||
formData.append('type', "clipspace");
|
formData.append('type', "input");
|
||||||
|
formData.append('subfolder', "clipspace");
|
||||||
|
|
||||||
uploadMask(item, formData);
|
uploadMask(item, formData);
|
||||||
this.close();
|
this.close();
|
||||||
|
|
|
@ -183,7 +183,6 @@ export class ComfyApp {
|
||||||
if(ComfyApp.clipspace) {
|
if(ComfyApp.clipspace) {
|
||||||
// image paste
|
// image paste
|
||||||
if(ComfyApp.clipspace.imgs && this.imgs) {
|
if(ComfyApp.clipspace.imgs && this.imgs) {
|
||||||
var filename = "";
|
|
||||||
if(this.images && ComfyApp.clipspace.images) {
|
if(this.images && ComfyApp.clipspace.images) {
|
||||||
if(ComfyApp.clipspace['img_paste_mode'] == 'selected') {
|
if(ComfyApp.clipspace['img_paste_mode'] == 'selected') {
|
||||||
app.nodeOutputs[this.id + ""].images = this.images = [ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']]];
|
app.nodeOutputs[this.id + ""].images = this.images = [ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']]];
|
||||||
|
@ -209,49 +208,25 @@ export class ComfyApp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ComfyApp.clipspace.images) {
|
|
||||||
const clip_image = ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']];
|
|
||||||
if(clip_image.subfolder != '')
|
|
||||||
filename = `${clip_image.subfolder}/`;
|
|
||||||
filename += `${clip_image.filename} [${clip_image.type}]`;
|
|
||||||
}
|
|
||||||
else if(ComfyApp.clipspace.widgets) {
|
|
||||||
const index_in_clip = ComfyApp.clipspace.widgets.findIndex(obj => obj.name === 'image');
|
|
||||||
if(index_in_clip >= 0) {
|
|
||||||
const item = ComfyApp.clipspace.widgets[index_in_clip].value;
|
|
||||||
if(item.type)
|
|
||||||
filename = `${item.filename} [${item.type}]`;
|
|
||||||
else
|
|
||||||
filename = item.filename;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// for Load Image node.
|
|
||||||
if(this.widgets) {
|
|
||||||
const index = this.widgets.findIndex(obj => obj.name === 'image');
|
|
||||||
if(index >= 0 && filename != "") {
|
|
||||||
const postfix = ' [clipspace]';
|
|
||||||
if(filename.endsWith(postfix) && this.widgets[index].options.base_dir == 'input') {
|
|
||||||
filename = "clipspace/" + filename.slice(0, filename.indexOf(postfix));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.widgets[index].value = filename;
|
|
||||||
if(this.widgets_values != undefined) {
|
|
||||||
this.widgets_values[index] = filename;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure render after update widget_value
|
if(this.widgets) {
|
||||||
if(ComfyApp.clipspace.widgets && this.widgets) {
|
if(ComfyApp.clipspace.images) {
|
||||||
ComfyApp.clipspace.widgets.forEach(({ type, name, value }) => {
|
const clip_image = ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']];
|
||||||
const prop = Object.values(this.widgets).find(obj => obj.type === type && obj.name === name);
|
const index = this.widgets.findIndex(obj => obj.name === 'image');
|
||||||
if (prop && prop.type != 'button') {
|
if(index >= 0) {
|
||||||
prop.callback(value);
|
this.widgets[index].value = clip_image;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
if(ComfyApp.clipspace.widgets) {
|
||||||
|
ComfyApp.clipspace.widgets.forEach(({ type, name, value }) => {
|
||||||
|
const prop = Object.values(this.widgets).find(obj => obj.type === type && obj.name === name);
|
||||||
|
if (prop && prop.type != 'button') {
|
||||||
|
prop.value = value;
|
||||||
|
prop.callback(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1323,12 +1298,7 @@ export class ComfyApp {
|
||||||
for(const widgetNum in node.widgets) {
|
for(const widgetNum in node.widgets) {
|
||||||
const widget = node.widgets[widgetNum]
|
const widget = node.widgets[widgetNum]
|
||||||
if(widget.type == "combo" && def["input"]["required"][widget.name] !== undefined) {
|
if(widget.type == "combo" && def["input"]["required"][widget.name] !== undefined) {
|
||||||
if(def["input"]["required"][widget.name][0] == "FILE_COMBO") {
|
widget.options.values = def["input"]["required"][widget.name][0];
|
||||||
console.log(widget.options.values = def["input"]["required"][widget.name][1].files);
|
|
||||||
widget.options.values = def["input"]["required"][widget.name][1].files;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
widget.options.values = def["input"]["required"][widget.name][0];
|
|
||||||
|
|
||||||
if(!widget.options.values.includes(widget.value)) {
|
if(!widget.options.values.includes(widget.value)) {
|
||||||
widget.value = widget.options.values[0];
|
widget.value = widget.options.values[0];
|
||||||
|
|
|
@ -256,20 +256,6 @@ export const ComfyWidgets = {
|
||||||
}
|
}
|
||||||
return { widget: node.addWidget("combo", inputName, defaultValue, () => {}, { values: type }) };
|
return { widget: node.addWidget("combo", inputName, defaultValue, () => {}, { values: type }) };
|
||||||
},
|
},
|
||||||
FILE_COMBO(node, inputName, inputData) {
|
|
||||||
const base_dir = inputData[1].base_dir;
|
|
||||||
let defaultValue = inputData[1].files[0];
|
|
||||||
|
|
||||||
const files = []
|
|
||||||
for(let i in inputData[1].files) {
|
|
||||||
files[i] = inputData[1].files[i];
|
|
||||||
const postfix = ' [clipspace]';
|
|
||||||
if(base_dir == 'input' && files[i].endsWith(postfix))
|
|
||||||
files[i] = "clipspace/" + files[i].slice(0, files[i].indexOf(postfix));
|
|
||||||
}
|
|
||||||
|
|
||||||
return { widget: node.addWidget("combo", inputName, defaultValue, () => {}, { base_dir:base_dir, values: files }) };
|
|
||||||
},
|
|
||||||
IMAGEUPLOAD(node, inputName, inputData, app) {
|
IMAGEUPLOAD(node, inputName, inputData, app) {
|
||||||
const imageWidget = node.widgets.find((w) => w.name === "image");
|
const imageWidget = node.widgets.find((w) => w.name === "image");
|
||||||
let uploadWidget;
|
let uploadWidget;
|
||||||
|
@ -280,10 +266,46 @@ export const ComfyWidgets = {
|
||||||
node.imgs = [img];
|
node.imgs = [img];
|
||||||
app.graph.setDirtyCanvas(true);
|
app.graph.setDirtyCanvas(true);
|
||||||
};
|
};
|
||||||
img.src = `/view?filename=${name}&type=input`;
|
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?.();
|
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
|
// Add our own callback to the combo widget to render an image when it changes
|
||||||
const cb = node.callback;
|
const cb = node.callback;
|
||||||
imageWidget.callback = function () {
|
imageWidget.callback = function () {
|
||||||
|
|
Loading…
Reference in New Issue