From 195d7aec9f9c170ea94643ea71333ef8d1a6b84c Mon Sep 17 00:00:00 2001 From: m957ymj75urz Date: Sun, 12 Mar 2023 19:51:39 +0100 Subject: [PATCH 01/16] can specify a subfolder in the SaveImage node --- nodes.py | 16 ++++++++++------ server.py | 9 ++++++--- web/scripts/app.js | 4 +++- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/nodes.py b/nodes.py index 0a0a0a9cd..d7708e34f 100644 --- a/nodes.py +++ b/nodes.py @@ -780,7 +780,8 @@ class SaveImage: def INPUT_TYPES(s): return {"required": {"images": ("IMAGE", ), - "filename_prefix": ("STRING", {"default": "ComfyUI"})}, + "filename_prefix": ("STRING", {"default": "ComfyUI"}), + "subfolder": ("STRING", {})}, "hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"}, } @@ -791,7 +792,7 @@ class SaveImage: CATEGORY = "image" - def save_images(self, images, filename_prefix="ComfyUI", prompt=None, extra_pnginfo=None): + def save_images(self, images, filename_prefix="ComfyUI", subfolder=None, prompt=None, extra_pnginfo=None): def map_filename(filename): prefix_len = len(filename_prefix) prefix = filename[:prefix_len + 1] @@ -800,12 +801,15 @@ class SaveImage: except: digits = 0 return (digits, prefix) + + outputDirectory = os.path.join(os.path.dirname(os.path.realpath(__file__)), "output", subfolder); + try: - counter = max(filter(lambda a: a[1][:-1] == filename_prefix and a[1][-1] == "_", map(map_filename, os.listdir(self.output_dir))))[0] + 1 + counter = max(filter(lambda a: a[1][:-1] == filename_prefix and a[1][-1] == "_", map(map_filename, os.listdir(outputDirectory))))[0] + 1 except ValueError: counter = 1 except FileNotFoundError: - os.mkdir(self.output_dir) + os.mkdir(outputDirectory) counter = 1 paths = list() @@ -819,8 +823,8 @@ class SaveImage: for x in extra_pnginfo: metadata.add_text(x, json.dumps(extra_pnginfo[x])) file = f"{filename_prefix}_{counter:05}_.png" - img.save(os.path.join(self.output_dir, file), pnginfo=metadata, optimize=True) - paths.append(file) + img.save(os.path.join(outputDirectory, file), pnginfo=metadata, optimize=True) + paths.append(os.path.join(subfolder, file)) counter += 1 return { "ui": { "images": paths } } diff --git a/server.py b/server.py index 5aba57619..05d1af04a 100644 --- a/server.py +++ b/server.py @@ -109,15 +109,18 @@ class PromptServer(): return web.Response(status=400) - @routes.get("/view/{file}") + @routes.get("/view") async def view_image(request): - if "file" in request.match_info: + if "file" in request.rel_url.query: type = request.rel_url.query.get("type", "output") if type != "output" and type != "input": return web.Response(status=400) output_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), type) - file = request.match_info["file"] + if "subfolder" in request.rel_url.query: + output_dir = os.path.join(output_dir, request.rel_url.query["subfolder"]) + + file = request.rel_url.query["file"] file = os.path.basename(file) file = os.path.join(output_dir, file) if os.path.isfile(file): diff --git a/web/scripts/app.js b/web/scripts/app.js index e70e1c157..931eb1f7b 100644 --- a/web/scripts/app.js +++ b/web/scripts/app.js @@ -110,7 +110,9 @@ class ComfyApp { const img = new Image(); img.onload = () => r(img); img.onerror = () => r(null); - img.src = "/view/" + src; + var filename = src.replace(/^.*[\\\/]/, ''); + var subfolder = src.replace(filename, ''); + img.src = "/view?file=" + filename + "&subfolder=" + subfolder; }); }) ).then((imgs) => { From 70f9bd1408ef58733f6072e549cbf9a9e97bed75 Mon Sep 17 00:00:00 2001 From: m957ymj75urz Date: Sun, 12 Mar 2023 20:23:46 +0100 Subject: [PATCH 02/16] fix makedirs on save --- nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodes.py b/nodes.py index d7708e34f..7a37342f7 100644 --- a/nodes.py +++ b/nodes.py @@ -809,7 +809,7 @@ class SaveImage: except ValueError: counter = 1 except FileNotFoundError: - os.mkdir(outputDirectory) + os.makedirs(outputDirectory, exist_ok=True) counter = 1 paths = list() From 5b425aaa4012e9dd0a7dddff4f43fecacf2c06f8 Mon Sep 17 00:00:00 2001 From: m957ymj75urz Date: Tue, 14 Mar 2023 09:08:54 +0100 Subject: [PATCH 03/16] remove subfolder widget and use filename_prefix --- nodes.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/nodes.py b/nodes.py index 7a37342f7..dbbe9eba7 100644 --- a/nodes.py +++ b/nodes.py @@ -780,8 +780,7 @@ class SaveImage: def INPUT_TYPES(s): return {"required": {"images": ("IMAGE", ), - "filename_prefix": ("STRING", {"default": "ComfyUI"}), - "subfolder": ("STRING", {})}, + "filename_prefix": ("STRING", {"default": "ComfyUI"})}, "hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"}, } @@ -792,9 +791,9 @@ class SaveImage: CATEGORY = "image" - def save_images(self, images, filename_prefix="ComfyUI", subfolder=None, prompt=None, extra_pnginfo=None): + def save_images(self, images, filename_prefix="ComfyUI", prompt=None, extra_pnginfo=None): def map_filename(filename): - prefix_len = len(filename_prefix) + prefix_len = len(os.path.basename(filename_prefix)) prefix = filename[:prefix_len + 1] try: digits = int(filename[prefix_len + 1:].split('_')[0]) @@ -802,14 +801,21 @@ class SaveImage: digits = 0 return (digits, prefix) - outputDirectory = os.path.join(os.path.dirname(os.path.realpath(__file__)), "output", subfolder); + subfolder = os.path.dirname(filename_prefix) + filename = os.path.basename(filename_prefix) + comfy_output_folder = os.path.join(os.path.dirname(os.path.realpath(__file__)), "output") + full_output_folder = os.path.join(comfy_output_folder, subfolder) + + if os.path.commonpath((comfy_output_folder, os.path.abspath(full_output_folder))) != comfy_output_folder: + print("Saving image outside the output folder is not allowed.") + return try: - counter = max(filter(lambda a: a[1][:-1] == filename_prefix and a[1][-1] == "_", map(map_filename, os.listdir(outputDirectory))))[0] + 1 + counter = max(filter(lambda a: a[1][:-1] == filename and a[1][-1] == "_", map(map_filename, os.listdir(full_output_folder))))[0] + 1 except ValueError: counter = 1 except FileNotFoundError: - os.makedirs(outputDirectory, exist_ok=True) + os.makedirs(full_output_folder, exist_ok=True) counter = 1 paths = list() @@ -822,8 +828,8 @@ class SaveImage: if extra_pnginfo is not None: for x in extra_pnginfo: metadata.add_text(x, json.dumps(extra_pnginfo[x])) - file = f"{filename_prefix}_{counter:05}_.png" - img.save(os.path.join(outputDirectory, file), pnginfo=metadata, optimize=True) + file = f"{filename}_{counter:05}_.png" + img.save(os.path.join(full_output_folder, file), pnginfo=metadata, optimize=True) paths.append(os.path.join(subfolder, file)) counter += 1 return { "ui": { "images": paths } } From b1294fa49f3a53981e8a194d3d2797be7a7714a0 Mon Sep 17 00:00:00 2001 From: m957ymj75urz Date: Tue, 14 Mar 2023 09:27:17 +0100 Subject: [PATCH 04/16] fix path traversal for /view --- server.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server.py b/server.py index 05d1af04a..be2fc0560 100644 --- a/server.py +++ b/server.py @@ -118,11 +118,15 @@ class PromptServer(): output_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), type) if "subfolder" in request.rel_url.query: - output_dir = os.path.join(output_dir, request.rel_url.query["subfolder"]) + full_output_dir = os.path.join(output_dir, request.rel_url.query["subfolder"]) + if os.path.commonpath((os.path.realpath(full_output_dir), output_dir)) != output_dir: + return web.Response(status=403) + output_dir = full_output_dir file = request.rel_url.query["file"] file = os.path.basename(file) file = os.path.join(output_dir, file) + if os.path.isfile(file): return web.FileResponse(file) From 6daf9bb22c6b4cc268bf818a11358ee6629098c6 Mon Sep 17 00:00:00 2001 From: m957ymj75urz Date: Tue, 14 Mar 2023 09:27:53 +0100 Subject: [PATCH 05/16] switch to realpath to check path traversal --- nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodes.py b/nodes.py index dbbe9eba7..e7c212c8d 100644 --- a/nodes.py +++ b/nodes.py @@ -806,7 +806,7 @@ class SaveImage: comfy_output_folder = os.path.join(os.path.dirname(os.path.realpath(__file__)), "output") full_output_folder = os.path.join(comfy_output_folder, subfolder) - if os.path.commonpath((comfy_output_folder, os.path.abspath(full_output_folder))) != comfy_output_folder: + if os.path.commonpath((comfy_output_folder, os.path.realpath(full_output_folder))) != comfy_output_folder: print("Saving image outside the output folder is not allowed.") return From 629272c0cae419e7610411f2a162fa111e6eb040 Mon Sep 17 00:00:00 2001 From: m957ymj75urz Date: Wed, 15 Mar 2023 12:10:45 +0100 Subject: [PATCH 06/16] resolve conflict with the new PreviewImage node --- nodes.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/nodes.py b/nodes.py index 8c360311e..7b27ef546 100644 --- a/nodes.py +++ b/nodes.py @@ -804,10 +804,9 @@ class SaveImage: subfolder = os.path.dirname(filename_prefix) filename = os.path.basename(filename_prefix) - comfy_output_folder = os.path.join(os.path.dirname(os.path.realpath(__file__)), "output") - full_output_folder = os.path.join(comfy_output_folder, subfolder) + full_output_folder = os.path.join(self.output_dir, subfolder) - if os.path.commonpath((comfy_output_folder, os.path.realpath(full_output_folder))) != comfy_output_folder: + if os.path.commonpath((self.output_dir, os.path.realpath(full_output_folder))) != self.output_dir: print("Saving image outside the output folder is not allowed.") return @@ -842,7 +841,7 @@ class SaveImage: class PreviewImage(SaveImage): def __init__(self): self.output_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "temp") - self.url_suffix = "?type=temp" + self.url_suffix = "&type=temp" @classmethod def INPUT_TYPES(s): From 16001e94298d02756e2029781927694097ec4284 Mon Sep 17 00:00:00 2001 From: m957ymj75urz Date: Thu, 16 Mar 2023 19:48:59 +0100 Subject: [PATCH 07/16] fixes for specific paths --- nodes.py | 5 +++-- web/scripts/app.js | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/nodes.py b/nodes.py index 7b27ef546..b4901a855 100644 --- a/nodes.py +++ b/nodes.py @@ -802,8 +802,9 @@ class SaveImage: digits = 0 return (digits, prefix) - subfolder = os.path.dirname(filename_prefix) - filename = os.path.basename(filename_prefix) + subfolder = os.path.dirname(os.path.normpath(filename_prefix)) + filename = os.path.basename(os.path.normpath(filename_prefix)) + full_output_folder = os.path.join(self.output_dir, subfolder) if os.path.commonpath((self.output_dir, os.path.realpath(full_output_folder))) != self.output_dir: diff --git a/web/scripts/app.js b/web/scripts/app.js index 31c575d72..1d777130a 100644 --- a/web/scripts/app.js +++ b/web/scripts/app.js @@ -110,9 +110,11 @@ class ComfyApp { const img = new Image(); img.onload = () => r(img); img.onerror = () => r(null); + var filename = src.replace(/^.*[\\\/]/, ''); var subfolder = src.replace(filename, ''); - img.src = "/view?file=" + filename + "&subfolder=" + subfolder; + var params = new URLSearchParams({file: filename, subfolder}); + img.src = "/view?" + params.toString(); }); }) ).then((imgs) => { From 5fa514fa512f321574e7385097345c05198972e9 Mon Sep 17 00:00:00 2001 From: m957ymj75urz Date: Sat, 18 Mar 2023 20:13:25 +0100 Subject: [PATCH 08/16] rollback urlparams --- web/scripts/app.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/scripts/app.js b/web/scripts/app.js index 1d777130a..51e67b927 100644 --- a/web/scripts/app.js +++ b/web/scripts/app.js @@ -113,8 +113,7 @@ class ComfyApp { var filename = src.replace(/^.*[\\\/]/, ''); var subfolder = src.replace(filename, ''); - var params = new URLSearchParams({file: filename, subfolder}); - img.src = "/view?" + params.toString(); + img.src = "/view?file=" + filename + "&subfolder=" + subfolder; }); }) ).then((imgs) => { From d1138e8ba0a8fd007c4277b2a4bb89e1c6d82349 Mon Sep 17 00:00:00 2001 From: m957ymj75urz Date: Sun, 19 Mar 2023 12:54:29 +0100 Subject: [PATCH 09/16] rework payload from server --- nodes.py | 15 ++++++++++----- server.py | 4 ++-- web/scripts/app.js | 7 ++----- web/scripts/widgets.js | 2 +- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/nodes.py b/nodes.py index b4901a855..f758c8d02 100644 --- a/nodes.py +++ b/nodes.py @@ -775,7 +775,7 @@ class KSamplerAdvanced: class SaveImage: def __init__(self): self.output_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "output") - self.url_suffix = "" + self.type = "output" @classmethod def INPUT_TYPES(s): @@ -822,7 +822,7 @@ class SaveImage: if not os.path.exists(self.output_dir): os.makedirs(self.output_dir) - paths = list() + results = list() for image in images: i = 255. * image.cpu().numpy() img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) @@ -835,14 +835,19 @@ class SaveImage: file = f"{filename}_{counter:05}_.png" img.save(os.path.join(full_output_folder, file), pnginfo=metadata, optimize=True) - paths.append(os.path.join(subfolder, file + self.url_suffix)) + results.append({ + "filename": file, + "subfolder": subfolder, + "type": self.type + }); counter += 1 - return { "ui": { "images": paths } } + + return { "ui": { "images": results } } class PreviewImage(SaveImage): def __init__(self): self.output_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "temp") - self.url_suffix = "&type=temp" + self.type = "temp" @classmethod def INPUT_TYPES(s): diff --git a/server.py b/server.py index a58aeec71..94928ae2e 100644 --- a/server.py +++ b/server.py @@ -111,7 +111,7 @@ class PromptServer(): @routes.get("/view") async def view_image(request): - if "file" in request.rel_url.query: + if "filename" in request.rel_url.query: type = request.rel_url.query.get("type", "output") if type not in ["output", "input", "temp"]: return web.Response(status=400) @@ -123,7 +123,7 @@ class PromptServer(): return web.Response(status=403) output_dir = full_output_dir - file = request.rel_url.query["file"] + file = request.rel_url.query["filename"] file = os.path.basename(file) file = os.path.join(output_dir, file) diff --git a/web/scripts/app.js b/web/scripts/app.js index 51e67b927..8d773b93b 100644 --- a/web/scripts/app.js +++ b/web/scripts/app.js @@ -109,11 +109,8 @@ class ComfyApp { return new Promise((r) => { const img = new Image(); img.onload = () => r(img); - img.onerror = () => r(null); - - var filename = src.replace(/^.*[\\\/]/, ''); - var subfolder = src.replace(filename, ''); - img.src = "/view?file=" + filename + "&subfolder=" + subfolder; + img.onerror = () => r(null); + img.src = "/view?" + new URLSearchParams(src).toString(); }); }) ).then((imgs) => { diff --git a/web/scripts/widgets.js b/web/scripts/widgets.js index 55bdd8f18..866195529 100644 --- a/web/scripts/widgets.js +++ b/web/scripts/widgets.js @@ -141,7 +141,7 @@ export const ComfyWidgets = { node.imgs = [img]; app.graph.setDirtyCanvas(true); }; - img.src = `/view/${name}?type=input`; + img.src = `/view?filename=${name}&type=input`; } // Add our own callback to the combo widget to render an image when it changes From bf9c17a975234f03b3c6d23afa17037cba438942 Mon Sep 17 00:00:00 2001 From: comfyanonymous Date: Sun, 19 Mar 2023 17:41:27 -0400 Subject: [PATCH 10/16] No longer needed. --- .github/workflows/windows_release.yml | 64 --------- .github/workflows/windows_release_cu118.yml | 142 -------------------- 2 files changed, 206 deletions(-) delete mode 100644 .github/workflows/windows_release.yml delete mode 100644 .github/workflows/windows_release_cu118.yml diff --git a/.github/workflows/windows_release.yml b/.github/workflows/windows_release.yml deleted file mode 100644 index 3f7d4d739..000000000 --- a/.github/workflows/windows_release.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: "Windows Release" - -on: - workflow_dispatch: -# push: -# branches: -# - master - -jobs: - build: - permissions: - contents: "write" - packages: "write" - pull-requests: "read" - runs-on: windows-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - shell: bash - run: | - cd .. - cp -r ComfyUI ComfyUI_copy - curl https://www.python.org/ftp/python/3.10.9/python-3.10.9-embed-amd64.zip -o python_embeded.zip - unzip python_embeded.zip -d python_embeded - cd python_embeded - echo 'import site' >> ./python310._pth - curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py - ./python.exe get-pip.py - ./python.exe -s -m pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu117 xformers -r ../ComfyUI/requirements.txt pygit2 - sed -i '1i../ComfyUI' ./python310._pth - cd .. - - - mkdir ComfyUI_windows_portable - mv python_embeded ComfyUI_windows_portable - mv ComfyUI_copy ComfyUI_windows_portable/ComfyUI - - cd ComfyUI_windows_portable - - mkdir update - cp -r ComfyUI/.ci/update_windows/* ./update/ - cp -r ComfyUI/.ci/windows_base_files/* ./ - - cd .. - - "C:\Program Files\7-Zip\7z.exe" a -t7z -m0=lzma -mx=8 -mfb=64 -md=32m -ms=on ComfyUI_windows_portable.7z ComfyUI_windows_portable - mv ComfyUI_windows_portable.7z ComfyUI/ComfyUI_windows_portable_nvidia_or_cpu.7z - - cd ComfyUI_windows_portable - python_embeded/python.exe -s ComfyUI/main.py --quick-test-for-ci --cpu - - ls - - - - name: Upload binaries to release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ComfyUI_windows_portable_nvidia_or_cpu.7z - tag: "latest" - overwrite: true - diff --git a/.github/workflows/windows_release_cu118.yml b/.github/workflows/windows_release_cu118.yml deleted file mode 100644 index 49a39e29b..000000000 --- a/.github/workflows/windows_release_cu118.yml +++ /dev/null @@ -1,142 +0,0 @@ -name: "Windows Release cu118" - -on: - workflow_dispatch: -# push: -# branches: -# - master - -jobs: - build_dependencies: - env: - # you need at least cuda 5.0 for some of the stuff compiled here. - TORCH_CUDA_ARCH_LIST: "5.0+PTX 6.0 6.1 7.0 7.5 8.0 8.6 8.9" - FORCE_CUDA: 1 - MAX_JOBS: 1 # will crash otherwise - DISTUTILS_USE_SDK: 1 # otherwise distutils will complain on windows about multiple versions of msvc - XFORMERS_BUILD_TYPE: "Release" - runs-on: windows-latest - steps: - - name: Cache Built Dependencies - uses: actions/cache@v3 - id: cache-cu118_python_stuff - with: - path: cu118_python_deps.tar - key: ${{ runner.os }}-build-cu118 - - - if: steps.cache-cu118_python_stuff.outputs.cache-hit != 'true' - uses: actions/checkout@v3 - - - if: steps.cache-cu118_python_stuff.outputs.cache-hit != 'true' - uses: actions/setup-python@v4 - with: - python-version: '3.10.9' - - - if: steps.cache-cu118_python_stuff.outputs.cache-hit != 'true' - uses: comfyanonymous/cuda-toolkit@test - id: cuda-toolkit - with: - cuda: '11.8.0' - # copied from xformers github - - name: Setup MSVC - uses: ilammy/msvc-dev-cmd@v1 - - name: Configure Pagefile - # windows runners will OOM with many CUDA architectures - # we cheat here with a page file - uses: al-cheb/configure-pagefile-action@v1.3 - with: - minimum-size: 2GB - # really unfortunate: https://github.com/ilammy/msvc-dev-cmd#name-conflicts-with-shell-bash - - name: Remove link.exe - shell: bash - run: rm /usr/bin/link - - - if: steps.cache-cu118_python_stuff.outputs.cache-hit != 'true' - shell: bash - run: | - python -m pip wheel --no-cache-dir torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu118 -r requirements.txt pygit2 -w ./temp_wheel_dir - python -m pip install --no-cache-dir ./temp_wheel_dir/* - echo installed basic - git clone --recurse-submodules https://github.com/facebookresearch/xformers.git - cd xformers - python -m pip install --no-cache-dir wheel setuptools twine - echo building xformers - python setup.py bdist_wheel -d ../temp_wheel_dir/ - cd .. - rm -rf xformers - ls -lah temp_wheel_dir - mv temp_wheel_dir cu118_python_deps - tar cf cu118_python_deps.tar cu118_python_deps - - uses: actions/upload-artifact@v3 - with: - name: cu118_python_deps - path: cu118_python_deps.tar - retention-days: 1 - - - package_comfyui: - needs: build_dependencies - permissions: - contents: "write" - packages: "write" - pull-requests: "read" - runs-on: windows-latest - steps: - - uses: actions/download-artifact@v3 - with: - name: cu118_python_deps - - shell: bash - run: | - mv cu118_python_deps.tar ../ - cd .. - tar xf cu118_python_deps.tar - pwd - ls - - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - shell: bash - run: | - cd .. - cp -r ComfyUI ComfyUI_copy - curl https://www.python.org/ftp/python/3.10.9/python-3.10.9-embed-amd64.zip -o python_embeded.zip - unzip python_embeded.zip -d python_embeded - cd python_embeded - echo 'import site' >> ./python310._pth - curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py - ./python.exe get-pip.py - ./python.exe -s -m pip install ../cu118_python_deps/* - sed -i '1i../ComfyUI' ./python310._pth - cd .. - - - mkdir ComfyUI_windows_portable - mv python_embeded ComfyUI_windows_portable - mv ComfyUI_copy ComfyUI_windows_portable/ComfyUI - - cd ComfyUI_windows_portable - - mkdir update - cp -r ComfyUI/.ci/update_windows/* ./update/ - cp -r ComfyUI/.ci/update_windows_cu118/* ./update/ - cp -r ComfyUI/.ci/windows_base_files/* ./ - - cd .. - - "C:\Program Files\7-Zip\7z.exe" a -t7z -m0=lzma -mx=8 -mfb=64 -md=32m -ms=on ComfyUI_windows_portable.7z ComfyUI_windows_portable - mv ComfyUI_windows_portable.7z ComfyUI/new_ComfyUI_windows_portable_nvidia_cu118_or_cpu.7z - - cd ComfyUI_windows_portable - python_embeded/python.exe -s ComfyUI/main.py --quick-test-for-ci --cpu - - ls - - - name: Upload binaries to release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: new_ComfyUI_windows_portable_nvidia_cu118_or_cpu.7z - tag: "latest" - overwrite: true - From 1261a70034506a8220c584fcbbb833ab6ff87531 Mon Sep 17 00:00:00 2001 From: Joe Colburn Date: Mon, 20 Mar 2023 00:04:20 -0400 Subject: [PATCH 11/16] add extra_model_paths.yaml to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6da32900f..41d517048 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ models/checkpoints models/vae models/embeddings models/loras +extra_model_paths.yaml From db34645f53d57bd19b9e2edffa498fb1c03b1f39 Mon Sep 17 00:00:00 2001 From: comfyanonymous Date: Mon, 20 Mar 2023 00:31:17 -0400 Subject: [PATCH 12/16] .gitignore some folders. --- .gitignore | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 41d517048..71c3b45ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ __pycache__/ *.py[cod] output/ -models/checkpoints -models/vae -models/embeddings -models/loras +input/ +models/ +temp/ +custom_nodes/ extra_model_paths.yaml From 840464af93bae2ad2a2deba697a71e58c19fa8e9 Mon Sep 17 00:00:00 2001 From: comfyanonymous Date: Mon, 20 Mar 2023 00:37:32 -0400 Subject: [PATCH 13/16] Don't .gitignore the examples. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 71c3b45ae..d311a2a09 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,9 @@ __pycache__/ *.py[cod] output/ input/ +!input/example.png models/ temp/ custom_nodes/ +!custom_nodes/example_node.py.example extra_model_paths.yaml From 46b674c9c29e2844887d340fdf505d18ce59be15 Mon Sep 17 00:00:00 2001 From: comfyanonymous Date: Mon, 20 Mar 2023 02:23:47 -0400 Subject: [PATCH 14/16] Make saved json more pretty. --- web/scripts/ui.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/scripts/ui.js b/web/scripts/ui.js index 51f9b52c7..58012fe6c 100644 --- a/web/scripts/ui.js +++ b/web/scripts/ui.js @@ -310,7 +310,7 @@ export class ComfyUI { $el("button", { textContent: "Save", onclick: () => { - const json = JSON.stringify(app.graph.serialize()); // convert the data to a JSON string + const json = JSON.stringify(app.graph.serialize(), null, 2); // convert the data to a JSON string const blob = new Blob([json], { type: "application/json" }); const url = URL.createObjectURL(blob); const a = $el("a", { From e85fcb822b342a9743b5c5c8e67511ea8dccda3f Mon Sep 17 00:00:00 2001 From: comfyanonymous Date: Mon, 20 Mar 2023 14:55:28 -0400 Subject: [PATCH 15/16] Small fix. --- nodes.py | 10 +++++----- web/scripts/app.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nodes.py b/nodes.py index 6adf7b4ec..1658a4c88 100644 --- a/nodes.py +++ b/nodes.py @@ -744,16 +744,16 @@ class SaveImage: except: digits = 0 return (digits, prefix) - + subfolder = os.path.dirname(os.path.normpath(filename_prefix)) filename = os.path.basename(os.path.normpath(filename_prefix)) - full_output_folder = os.path.join(self.output_dir, subfolder) + full_output_folder = os.path.join(self.output_dir, subfolder) if os.path.commonpath((self.output_dir, os.path.realpath(full_output_folder))) != self.output_dir: print("Saving image outside the output folder is not allowed.") - return - + return {} + try: counter = max(filter(lambda a: a[1][:-1] == filename and a[1][-1] == "_", map(map_filename, os.listdir(full_output_folder))))[0] + 1 except ValueError: @@ -784,7 +784,7 @@ class SaveImage: "type": self.type }); counter += 1 - + return { "ui": { "images": results } } class PreviewImage(SaveImage): diff --git a/web/scripts/app.js b/web/scripts/app.js index 5f853ca03..3f06629ee 100644 --- a/web/scripts/app.js +++ b/web/scripts/app.js @@ -109,7 +109,7 @@ class ComfyApp { return new Promise((r) => { const img = new Image(); img.onload = () => r(img); - img.onerror = () => r(null); + img.onerror = () => r(null); img.src = "/view?" + new URLSearchParams(src).toString(); }); }) From fc935f8e127bb8eab53ffd1048c0b53ca7d2b2e4 Mon Sep 17 00:00:00 2001 From: comfyanonymous Date: Mon, 20 Mar 2023 22:25:03 -0400 Subject: [PATCH 16/16] Readme update. --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aab892531..d83174e3c 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ This ui will let you design and execute advanced stable diffusion pipelines usin - [Upscale Models (ESRGAN, ESRGAN variants, SwinIR, Swin2SR, etc...)](https://comfyanonymous.github.io/ComfyUI_examples/upscale_models/) - Starts up very fast. - Works fully offline: will never download anything. +- [Config file](extra_model_paths.yaml.example) to set the search paths for models. Workflow examples can be found on the [Examples page](https://comfyanonymous.github.io/ComfyUI_examples/) @@ -136,9 +137,9 @@ This will let you use: pip3.10 to install all the dependencies. ## How to increase generation speed? -The fp16 model configs in the CheckpointLoader can be used to load them in fp16 mode, depending on your GPU this will increase your gen speed by a significant amount. +Make sure you use the CheckpointLoaderSimple node to load checkpoints. It will auto pick the right settings depending on your GPU. -You can also set this command line setting to disable the upcasting to fp32 in some cross attention operations which will increase your speed. Note that this will very likely give you black images on SD2.x models. +You can set this command line setting to disable the upcasting to fp32 in some cross attention operations which will increase your speed. Note that this doesn't do anything when xformers is enabled and will very likely give you black images on SD2.x models. ```--dont-upcast-attention```