commit e59ea47ca9397d93372f6a93ca4f3ef6d45230ad Author: Haoming Date: Wed Dec 20 16:46:29 2023 +0800 init diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..24a8e87 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.png filter=lfs diff=lfs merge=lfs -text diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b0340f4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Haoming + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..5b0b641 --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# ComfyUI Floodgate +This is an Extension for [ComfyUI](https://github.com/comfyanonymous/ComfyUI), which allows you to control the logic flow with just one click! + +

+ +

Motivation

+

While Hires. Fix (or similar workflows) can significantly improve the output, +it also takes a longer time to process. Thus, many will simply run the base resolution until a good seed is found before enabling it. +However, for ComfyUI this means connecting and disconnecting multiple nodes every single time...

+ +

Solution

+

Introducing, Floodgate!
+Simply click one button to toggle between the logic flows. No more connecting and disconnecting multiple nodes!

+ +

How to Use

+

Connect the LATENT output from a Sampler to the input of the Floodgate, +then connect each path to the desired remaining workflow. Under Queue Prompt, you can toggle between the path to take. +The path not chosen will simply not be executed. Also, since ComfyUI caches the intermediate data, +opening the Floodgate will not require the precedent nodes to be processed again!

+ +### Note / ToDo +- Only **one** Floodgate can exist in a Workflow *as of now* +- Only supports `LATENT` connections *as of now* diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..49e5cd6 --- /dev/null +++ b/__init__.py @@ -0,0 +1,96 @@ +from .floodgate import FloodGate +from aiohttp import web +import execution +import server + + +floodgate_open = False + +WEB_DIRECTORY = "js" +NODE_CLASS_MAPPINGS = {"FloodGate": FloodGate} +NODE_DISPLAY_NAME_MAPPINGS = {"FloodGate": "Flood Gate"} + + +def find_gate(prompt:dict) -> str: + '''Find the Unique ID of the Floodgate Node''' + gate_ID = None + + for k, v in prompt.items(): + if v["class_type"] == "FloodGate": + if gate_ID is None: + gate_ID = k + else: + print('[Warning] Multiple Floodgates Detected! This will most likely raise errors!') + + return gate_ID + +def block_gate(prompt:dict, gate_ID:str) -> dict: + '''"Bypass" the Nodes that should be Blocked''' + nodes_affected = [] + + for node, data in prompt.items(): + for k, v in data["inputs"].items(): + if not isinstance(v, list): + continue + + if gate_ID in v: + if (not floodgate_open) and (v[1] == 1): + nodes_affected.append(node) + break + + if (floodgate_open) and (v[1] == 0): + nodes_affected.append(node) + break + + for key in nodes_affected: + del prompt[key] + + if len(nodes_affected) > 0: + return recursive_block_gate(prompt, nodes_affected) + else: + return prompt + +def recursive_block_gate(prompt:dict, node_IDs:list) -> dict: + '''Block the subsequent nodes of which source has been blocked''' + to_delete = [] + + for node, data in prompt.items(): + for k, v in data["inputs"].items(): + # Connection is always a List + if not isinstance(v, list): + continue + + if any(ID in v for ID in node_IDs): + to_delete.append(node) + break + + for key in to_delete: + del prompt[key] + + if len(to_delete) > 0: + return recursive_block_gate(prompt, to_delete) + else: + return prompt + + +@server.PromptServer.instance.routes.get("/floodgate") +async def floodgate_toggle(_): + '''Toggle the Floodgate Status''' + + global floodgate_open + floodgate_open = not floodgate_open + + return web.json_response({"status" : floodgate_open}) + + +original_validate = execution.validate_prompt + +def hijack_validate(prompt): + + gate_ID = find_gate(prompt) + if gate_ID is None: + return original_validate(prompt) + + return original_validate(block_gate(prompt, gate_ID)) + +execution.validate_prompt = hijack_validate diff --git a/floodgate.py b/floodgate.py new file mode 100644 index 0000000..199dc87 --- /dev/null +++ b/floodgate.py @@ -0,0 +1,14 @@ +class FloodGate: + + @classmethod + def INPUT_TYPES(s): + return { "required": { "latent": ("LATENT",) } } + + RETURN_TYPES = ("LATENT", "LATENT") + RETURN_NAMES = ("CLOSE", "OPEN") + + FUNCTION = "gate" + CATEGORY = "latent" + + def gate(self, latent): + return (latent, latent) diff --git a/js/flood_gate.js b/js/flood_gate.js new file mode 100644 index 0000000..57f74f0 --- /dev/null +++ b/js/flood_gate.js @@ -0,0 +1,24 @@ +import { app } from "../../scripts/app.js"; +import { api } from "../../scripts/api.js"; + +app.registerExtension({ + name: "Comfy.FloodGate", + async setup() { + const floodButton = document.createElement("button"); + floodButton.textContent = "Floodgate: Close"; + floodButton.style.margin = '-4px 0px 6px 0px'; + + floodButton.addEventListener("click", async () => { + const resp = await api.fetchApi('/floodgate'); + const stat = await resp.json(); + + if (stat['status']) + floodButton.textContent = "Floodgate: Open"; + else + floodButton.textContent = "Floodgate: Close"; + }); + + const queueButton = document.getElementById("queue-button"); + queueButton.after(floodButton); + } +}); diff --git a/workflow.png b/workflow.png new file mode 100644 index 0000000..55c0c33 --- /dev/null +++ b/workflow.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:adecf8e8dfcf4a2df1671095f1b947e1f1c0dc1b429fddc3e2b249e934e0328a +size 400490