Skip to content

Commit d720b8a

Browse files
committed
Merge branch 'main' into dev/uv-tools
2 parents 7f46e98 + 21acc87 commit d720b8a

22 files changed

+2106
-248
lines changed

.github/ISSUE_TEMPLATE/bug_report.yml

+26-10
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ name: 🐞 Bug Report
22
title: "[bug] "
33
description: Report a bug
44
labels: ["type: 🐛 bug", "status: 🧹 needs triage"]
5-
5+
assignees:
6+
- melMass
7+
68
body:
79
- type: markdown
810
attributes:
@@ -40,16 +42,30 @@ body:
4042
label: Expected behavior
4143
description: A clear description of what you expected to happen.
4244

43-
- type: textarea
44-
id: info
45+
- type: dropdown
46+
id: os
4547
attributes:
46-
label: Platform and versions
47-
description: "informations about the environment you run Comfy in"
48-
render: sh
49-
placeholder: |
50-
- OS: [e.g. Linux]
51-
- Comfy Mode [e.g. custom env, standalone, google colab]
52-
48+
label: Operating System
49+
description: What OS are you using?
50+
options:
51+
- Windows (Default)
52+
- Linux
53+
- Mac
54+
default: 0
55+
validations:
56+
required: true
57+
58+
- type: dropdown
59+
id: comfy_mode
60+
attributes:
61+
label: Comfy Mode
62+
description: What flavor of Comfy do you use?
63+
options:
64+
- Comfy Portable (embed) (Default)
65+
- In a custom virtual env (venv, virtualenv, conda...)
66+
- Google Colab
67+
- Other (online services, containers etc..)
68+
default: 0
5369
validations:
5470
required: true
5571

README.md

+15
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ Before proceeding, please be aware of the licenses associated with certain libra
4646
<img src="https://github.com/melMass/comfy_mtb/assets/7041726/7c20ac83-31ff-40ea-a1a0-06c2acefb2ef" width=345/>
4747

4848
## face detection / swapping
49+
> **Warning**
50+
> Those nodes were among the first to be implemented they do work, but on windows the installation is still not properly handled for everyone
51+
> As alternatives you can use [reactor](https://github.com/Gourieff/comfyui-reactor-node) for face swap and [facerestore](https://github.com/Haidra-Org/hordelib/tree/main/hordelib/nodes/facerestore) for restoration
52+
> You can check [this video](https://www.youtube.com/watch?v=FShlpMxbU0E) for a tutorial by Ferniclestix using these alternatives
53+
4954
- `Face Swap`: Face swap using deepinsight/insightface models (this node used to be called `Roop` in early versions, it does the same, roop is *just* an app that uses those model)
5055
> **Note**
5156
> The face index allow you to choose which face to replace as you can see here:
@@ -54,6 +59,12 @@ Before proceeding, please be aware of the licenses associated with certain libra
5459
- `Restore Face`: Using [GFPGan](https://github.com/TencentARC/GFPGAN) to restore faces, works great in conjunction with `Face Swap` and supports Comfy native upscalers for the `bg_upscaler`
5560

5661
## image interpolation (animation)
62+
> **Warning**
63+
> **Windows only issue**: This requires tensorflow-gpu that is unfortunately not a thing anymore on Windows since 2.10.1 (unless you use a complex WSL passthrough setup but it's still not "Windows")
64+
> Using this old version is quite clunky and require some patching that install.py does automatically, but the main issue is that no wheels are available for python > 3.10
65+
> Comfy-nightly is already using Python 11 so installing this old tf version won't work there.
66+
> You can in any case install the normal up to date tensorflow but that will run on CPU and is much MUCH slower for FILM inference.
67+
5768
- `Load Film Model`: Loads a [FILM](https://github.com/google-research/frame-interpolation) model
5869
- `Film Interpolation`: Process input frames using [FILM](https://github.com/google-research/frame-interpolation)
5970
<img src="https://github.com/melMass/comfy_mtb/assets/7041726/3afd1647-6634-4b92-a34b-51432e6a9834" width=400/>
@@ -93,6 +104,10 @@ Before proceeding, please be aware of the licenses associated with certain libra
93104

94105
# Comfy Resources
95106

107+
**Misc**
108+
109+
- [Slick ComfyUI by NoCrypt](https://colab.research.google.com/drive/1ZMvLWEiYITmBJngtqeIQToeNuiydwI0z#scrollTo=1fWMaexXS188): A colab notebook with batteries included!
110+
96111
**Guides**:
97112
- [Official Examples (eng)](https://comfyanonymous.github.io/ComfyUI_examples/)
98113
- [ComfyUI Community Manual (eng)](https://blenderneko.github.io/ComfyUI-docs/) by @BlenderNeko

__init__.py

+27
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,32 @@ def load_nodes():
192192
"LoadFaceAnalysisModel": restore_deps,
193193
}
194194

195+
PromptServer.instance.app.router.add_static(
196+
"/mtb-assets/", path=(here / "html").as_posix()
197+
)
198+
199+
@PromptServer.instance.routes.get("/mtb/manage")
200+
async def manage(request):
201+
from . import endpoint
202+
203+
reload(endpoint)
204+
205+
endlog.debug("Initializing Manager")
206+
if "text/html" in request.headers.get("Accept", ""):
207+
csv_editor = endpoint.csv_editor()
208+
209+
tabview = endpoint.render_tab_view(Styles=csv_editor)
210+
return web.Response(
211+
text=endpoint.render_base_template("MTB", tabview),
212+
content_type="text/html",
213+
)
214+
215+
return web.json_response(
216+
{
217+
"message": "manage only has a POST api for now",
218+
}
219+
)
220+
195221
@PromptServer.instance.routes.get("/mtb/status")
196222
async def get_full_library(request):
197223
from . import endpoint
@@ -255,6 +281,7 @@ async def get_home(request):
255281
# # Return an HTML page
256282
html_response = """
257283
<div class="flex-container menu">
284+
<a href="/mtb/manage">manage</a>
258285
<a href="/mtb/debug">debug</a>
259286
<a href="/mtb/status">status</a>
260287
</div>

endpoint.py

+162-14
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
1-
from .utils import here, run_command, comfy_mode
1+
from .utils import (
2+
here,
3+
import_install,
4+
styles_dir,
5+
backup_file,
6+
)
27
from aiohttp import web
38
from .log import mklog
4-
import sys
9+
import csv
10+
511

612
endlog = mklog("mtb endpoint")
713

814
# - ACTIONS
9-
import requirements
15+
import platform
1016

17+
import_install("requirements")
1118

1219

1320
def ACTIONS_installDependency(dependency_names=None):
1421
if dependency_names is None:
1522
return {"error": "No dependency name provided"}
1623
endlog.debug(f"Received Install Dependency request for {dependency_names}")
1724
reqs = []
18-
if comfy_mode == "embeded":
19-
reqs = list(requirements.parse((here / "reqs_portable.txt").read_text()))
25+
if platform.system() == "Windows":
26+
reqs = list(requirements.parse((here / "reqs_windows.txt").read_text()))
2027
else:
2128
reqs = list(requirements.parse((here / "reqs.txt").read_text()))
2229
print([x.specs for x in reqs])
@@ -48,6 +55,32 @@ def ACTIONS_getStyles(style_name=None):
4855
return {"error": "No styles found"}
4956

5057

58+
def ACTIONS_saveStyle(data):
59+
# endlog.debug(f"Received Save Styles for {data.keys()}")
60+
# endlog.debug(data)
61+
62+
styles = [f.name for f in styles_dir.iterdir() if f.suffix == ".csv"]
63+
target = None
64+
rows = []
65+
for fp, content in data.items():
66+
if fp in styles:
67+
endlog.debug(f"Overwriting {fp}")
68+
target = styles_dir / fp
69+
rows = content
70+
break
71+
72+
if not target:
73+
endlog.warning(f"Could not determine the target file for {data.keys()}")
74+
return {"error": "Could not determine the target file for the style"}
75+
76+
backup_file(target)
77+
78+
with target.open("w", newline="", encoding="utf-8") as file:
79+
csv_writer = csv.writer(file, quoting=csv.QUOTE_ALL)
80+
for row in rows:
81+
csv_writer.writerow(row)
82+
83+
5184
async def do_action(request) -> web.Response:
5285
endlog.debug("Init action request")
5386
request_data = await request.json()
@@ -83,6 +116,129 @@ def dependencies_button(name, dependencies):
83116
"""
84117

85118

119+
def csv_editor():
120+
inputs = [f for f in styles_dir.iterdir() if f.suffix == ".csv"]
121+
# rows = {f.stem: list(csv.reader(f.read_text("utf8"))) for f in styles}
122+
123+
style_files = {}
124+
for file in inputs:
125+
with open(file, "r", encoding="utf8") as f:
126+
parsed = csv.reader(f)
127+
style_files[file.name] = []
128+
for row in parsed:
129+
endlog.debug(f"Adding style {row[0]}")
130+
style_files[file.name].append((row[0], row[1], row[2]))
131+
132+
html_out = """
133+
<div id="style-editor">
134+
<h1>Style Editor</h1>
135+
136+
"""
137+
for current, styles in style_files.items():
138+
current_out = f"<h3>{current}</h3>"
139+
table_rows = []
140+
for index, style in enumerate(styles):
141+
table_rows += (
142+
(["<tr>"] + [f"<th>{cell}</th>" for cell in style] + ["</tr>"])
143+
if index == 0
144+
else (
145+
["<tr>"]
146+
+ [
147+
f"<td><input type='text' value='{cell}'></td>"
148+
if i == 0
149+
else f"<td><textarea name='Text1' cols='40' rows='5'>{cell}</textarea></td>"
150+
for i, cell in enumerate(style)
151+
]
152+
+ ["</tr>"]
153+
)
154+
)
155+
current_out += (
156+
f"<table data-id='{current}' data-filename='{current}'>"
157+
+ "".join(table_rows)
158+
+ "</table>"
159+
)
160+
current_out += f"<button data-id='{current}' onclick='saveTableData(this.getAttribute(\"data-id\"))'>Save {current}</button>"
161+
162+
html_out += add_foldable_region(current, current_out)
163+
164+
html_out += "</div>"
165+
html_out += """<script src='/mtb-assets/js/saveTableData.js'></script>"""
166+
167+
return html_out
168+
169+
170+
def render_tab_view(**kwargs):
171+
tab_headers = []
172+
tab_contents = []
173+
174+
for idx, (tab_name, content) in enumerate(kwargs.items()):
175+
active_class = "active" if idx == 0 else ""
176+
tab_headers.append(
177+
f"<button class='tablinks {active_class}' onclick=\"openTab(event, '{tab_name}')\">{tab_name}</button>"
178+
)
179+
tab_contents.append(
180+
f"<div id='{tab_name}' class='tabcontent {active_class}'>{content}</div>"
181+
)
182+
183+
headers_str = "\n".join(tab_headers)
184+
contents_str = "\n".join(tab_contents)
185+
186+
return f"""
187+
<div class='tab-container'>
188+
<div class='tab'>
189+
{headers_str}
190+
</div>
191+
{contents_str}
192+
</div>
193+
<script src='/mtb-assets/js/tabSwitch.js'></script>
194+
"""
195+
196+
197+
def add_foldable_region(title, content):
198+
symbol_id = f"{title}-symbol"
199+
return f"""
200+
<div class='foldable'>
201+
<div class='foldable-title' onclick="toggleFoldable('{title}', '{symbol_id}')">
202+
<span id='{symbol_id}' class='foldable-symbol'>&#9655;</span>
203+
{title}
204+
</div>
205+
<div id='{title}' class='foldable-content'>
206+
{content}
207+
</div>
208+
</div>
209+
<script src='/mtb-assets/js/foldable.js'></script>
210+
"""
211+
212+
213+
def add_split_pane(left_content, right_content, vertical=True):
214+
orientation = "vertical" if vertical else "horizontal"
215+
return f"""
216+
<div class="split-pane {orientation}">
217+
<div id="leftPane">
218+
{left_content}
219+
</div>
220+
<div id="resizer"></div>
221+
<div id="rightPane">
222+
{right_content}
223+
</div>
224+
</div>
225+
<script>
226+
initSplitPane({str(vertical).lower()});
227+
</script>
228+
<script src='/mtb-assets/js/splitPane.js'></script>
229+
"""
230+
231+
232+
def add_dropdown(title, options):
233+
option_str = "\n".join([f"<option value='{opt}'>{opt}</option>" for opt in options])
234+
return f"""
235+
<select>
236+
<option disabled selected>{title}</option>
237+
{option_str}
238+
</select>
239+
"""
240+
241+
86242
def render_table(table_dict, sort=True, title=None):
87243
table_dict = sorted(
88244
table_dict.items(), key=lambda item: item[0]
@@ -122,21 +278,13 @@ def render_table(table_dict, sort=True, title=None):
122278

123279

124280
def render_base_template(title, content):
125-
css_content = ""
126-
css_path = here / "html" / "style.css"
127-
if css_path:
128-
with open(css_path, "r") as css_file:
129-
css_content = css_file.read()
130-
131281
github_icon_svg = """<svg xmlns="http://www.w3.org/2000/svg" fill="whitesmoke" height="3em" viewBox="0 0 496 512"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg>"""
132282
return f"""
133283
<!DOCTYPE html>
134284
<html>
135285
<head>
136286
<title>{title}</title>
137-
<style>
138-
{css_content}
139-
</style>
287+
<link rel="stylesheet" href="/mtb-assets/style.css"/>
140288
</head>
141289
<script type="module">
142290
import {{ api }} from '/scripts/api.js'

0 commit comments

Comments
 (0)