Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
chenpangpang
ComfyUI
Commits
850daf04
Commit
850daf04
authored
May 08, 2023
by
comfyanonymous
Browse files
Masked editor changes.
Add a way to upload to subfolders. Clean up code. Fix some issues.
parent
ae08fdb9
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
89 additions
and
121 deletions
+89
-121
folder_paths.py
folder_paths.py
+0
-9
nodes.py
nodes.py
+4
-4
server.py
server.py
+29
-45
web/extensions/core/maskeditor.js
web/extensions/core/maskeditor.js
+5
-4
web/scripts/app.js
web/scripts/app.js
+14
-44
web/scripts/widgets.js
web/scripts/widgets.js
+37
-15
No files found.
folder_paths.py
View file @
850daf04
...
...
@@ -57,10 +57,6 @@ def get_input_directory():
global
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
def
get_directory_by_type
(
type_name
):
...
...
@@ -70,8 +66,6 @@ def get_directory_by_type(type_name):
return
get_temp_directory
()
if
type_name
==
"input"
:
return
get_input_directory
()
if
type_name
==
"clipspace"
:
return
get_clipspace_directory
()
return
None
...
...
@@ -87,9 +81,6 @@ def annotated_filepath(name):
elif
name
.
endswith
(
"[temp]"
):
base_dir
=
get_temp_directory
()
name
=
name
[:
-
7
]
elif
name
.
endswith
(
"[clipspace]"
):
base_dir
=
get_clipspace_directory
()
name
=
name
[:
-
12
]
else
:
return
name
,
None
...
...
nodes.py
View file @
850daf04
...
...
@@ -973,9 +973,9 @@ class LoadImage:
@
classmethod
def
INPUT_TYPES
(
s
):
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"
:
{
"image"
:
(
"FILE_COMBO"
,
{
"base_dir"
:
"input"
,
"files"
:
sorted
(
input_dir
)}
,
)},
{
"image"
:
(
sorted
(
files
)
,
)},
}
CATEGORY
=
"image"
...
...
@@ -1015,9 +1015,9 @@ class LoadImageMask:
@
classmethod
def
INPUT_TYPES
(
s
):
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"
:
{
"image"
:
(
"FILE_COMBO"
,
{
"base_dir"
:
"input"
,
"files"
:
sorted
(
input_dir
)}
,
),
{
"image"
:
(
sorted
(
files
)
,
),
"channel"
:
(
s
.
_color_channels
,
),
}
}
...
...
server.py
View file @
850daf04
...
...
@@ -118,8 +118,6 @@ class PromptServer():
type_dir
=
folder_paths
.
get_input_directory
()
elif
dir_type
==
"input"
:
type_dir
=
folder_paths
.
get_input_directory
()
elif
dir_type
==
"clipspace"
:
type_dir
=
folder_paths
.
get_clipspace_directory
()
elif
dir_type
==
"temp"
:
type_dir
=
folder_paths
.
get_temp_directory
()
elif
dir_type
==
"output"
:
...
...
@@ -127,73 +125,63 @@ class PromptServer():
return
type_dir
@
routes
.
post
(
"/upload/image"
)
async
def
upload_image
(
request
):
post
=
await
request
.
post
()
def
image_upload
(
post
,
image_save_function
=
None
):
image
=
post
.
get
(
"image"
)
upload_dir
=
get_dir_by_type
(
post
.
get
(
"type"
))
if
not
os
.
path
.
exists
(
upload_dir
):
os
.
makedirs
(
upload_dir
)
image_upload_type
=
post
.
get
(
"type"
)
upload_dir
=
get_dir_by_type
(
image_upload_type
)
if
image
and
image
.
file
:
filename
=
image
.
filename
if
not
filename
:
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
)
filepath
=
os
.
path
.
join
(
full_output_folder
,
filename
)
i
=
1
while
os
.
path
.
exists
(
os
.
path
.
join
(
upload_dir
,
filename
)
):
while
os
.
path
.
exists
(
filepath
):
filename
=
f
"
{
split
[
0
]
}
(
{
i
}
)
{
split
[
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
:
f
.
write
(
image
.
file
.
read
())
return
web
.
json_response
({
"name"
:
filename
})
return
web
.
json_response
({
"name"
:
filename
,
"subfolder"
:
subfolder
,
"type"
:
image_upload_type
})
else
:
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"
)
async
def
upload_mask
(
request
):
post
=
await
request
.
post
()
image
=
post
.
get
(
"image"
)
original_image
=
post
.
get
(
"original_image"
)
upload_dir
=
get_dir_by_type
(
post
.
get
(
"type"
))
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'
)
def
image_save_function
(
image
,
post
,
filepath
):
original_pil
=
Image
.
open
(
post
.
get
(
"original_image"
).
file
).
convert
(
'RGBA'
)
mask_pil
=
Image
.
open
(
image
.
file
).
convert
(
'RGBA'
)
# alpha copy
new_alpha
=
mask_pil
.
getchannel
(
'A'
)
original_pil
.
putalpha
(
new_alpha
)
original_pil
.
save
(
filepath
)
return
web
.
json_response
({
"name"
:
filename
})
else
:
return
web
.
Response
(
status
=
400
)
return
image_upload
(
post
,
image_save_function
)
@
routes
.
get
(
"/view"
)
async
def
view_image
(
request
):
...
...
@@ -201,10 +189,6 @@ class PromptServer():
filename
=
request
.
rel_url
.
query
[
"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
if
filename
[
0
]
==
'/'
or
'..'
in
filename
:
return
web
.
Response
(
status
=
400
)
...
...
web/extensions/core/maskeditor.js
View file @
850daf04
...
...
@@ -41,7 +41,7 @@ async function uploadMask(filepath, formData) {
});
ComfyApp
.
clipspace
.
imgs
[
ComfyApp
.
clipspace
[
'
selectedIndex
'
]]
=
new
Image
();
ComfyApp
.
clipspace
.
imgs
[
ComfyApp
.
clipspace
[
'
selectedIndex
'
]].
src
=
`
view?
filename=
${
filepath
.
filename
}
&type=
${
filepath
.
t
ype
}
`
;
ComfyApp
.
clipspace
.
imgs
[
ComfyApp
.
clipspace
[
'
selectedIndex
'
]].
src
=
"
/
view?
"
+
new
URLSearchParams
(
filepath
)
.
t
oString
()
;
if
(
ComfyApp
.
clipspace
.
images
)
ComfyApp
.
clipspace
.
images
[
ComfyApp
.
clipspace
[
'
selectedIndex
'
]]
=
filepath
;
...
...
@@ -546,8 +546,8 @@ class MaskEditorDialog extends ComfyDialog {
const
item
=
{
"
filename
"
:
filename
,
"
subfolder
"
:
""
,
"
type
"
:
"
clipspace
"
,
"
subfolder
"
:
"
clipspace
"
,
"
type
"
:
"
input
"
,
};
if
(
ComfyApp
.
clipspace
.
images
)
...
...
@@ -567,7 +567,8 @@ class MaskEditorDialog extends ComfyDialog {
formData
.
append
(
'
image
'
,
blob
,
filename
);
formData
.
append
(
'
original_image
'
,
original_blob
);
formData
.
append
(
'
type
'
,
"
clipspace
"
);
formData
.
append
(
'
type
'
,
"
input
"
);
formData
.
append
(
'
subfolder
'
,
"
clipspace
"
);
uploadMask
(
item
,
formData
);
this
.
close
();
...
...
web/scripts/app.js
View file @
850daf04
...
...
@@ -183,7 +183,6 @@ export class ComfyApp {
if
(
ComfyApp
.
clipspace
)
{
// image paste
if
(
ComfyApp
.
clipspace
.
imgs
&&
this
.
imgs
)
{
var
filename
=
""
;
if
(
this
.
images
&&
ComfyApp
.
clipspace
.
images
)
{
if
(
ComfyApp
.
clipspace
[
'
img_paste_mode
'
]
==
'
selected
'
)
{
app
.
nodeOutputs
[
this
.
id
+
""
].
images
=
this
.
images
=
[
ComfyApp
.
clipspace
.
images
[
ComfyApp
.
clipspace
[
'
selectedIndex
'
]]];
...
...
@@ -209,49 +208,25 @@ export class ComfyApp {
}
}
}
}
if
(
this
.
widgets
)
{
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
;
}
if
(
index
>=
0
)
{
this
.
widgets
[
index
].
value
=
clip_image
;
}
}
}
// ensure render after update widget_value
if
(
ComfyApp
.
clipspace
.
widgets
&&
this
.
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
.
callback
(
value
);
}
});
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
)
{
const
widget
=
node
.
widgets
[
widgetNum
]
if
(
widget
.
type
==
"
combo
"
&&
def
[
"
input
"
][
"
required
"
][
widget
.
name
]
!==
undefined
)
{
if
(
def
[
"
input
"
][
"
required
"
][
widget
.
name
][
0
]
==
"
FILE_COMBO
"
)
{
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
];
widget
.
options
.
values
=
def
[
"
input
"
][
"
required
"
][
widget
.
name
][
0
];
if
(
!
widget
.
options
.
values
.
includes
(
widget
.
value
))
{
widget
.
value
=
widget
.
options
.
values
[
0
];
...
...
web/scripts/widgets.js
View file @
850daf04
...
...
@@ -256,20 +256,6 @@ export const ComfyWidgets = {
}
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
)
{
const
imageWidget
=
node
.
widgets
.
find
((
w
)
=>
w
.
name
===
"
image
"
);
let
uploadWidget
;
...
...
@@ -280,10 +266,46 @@ export const ComfyWidgets = {
node
.
imgs
=
[
img
];
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
?.();
}
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
()
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment