This commit is contained in:
Chris
2023-10-10 09:43:17 +11:00
parent 57664dcec9
commit 9e2b8243f8
10 changed files with 103 additions and 57 deletions
+59 -46
View File
@@ -4,80 +4,93 @@ Shameless plug for my other nodes -> Check out [Image Picker](https://github.com
---
## v2.5 update
Main addition is visualising the virtual nodes - right click on the background (like when you add a node) and `Toggle UE Link Visibility`.
Also added `Anything Everywhere3` which is just 3 `Anything Everywhere` nodes in one - designed for when you load a checkpoint.
Under the hood a big refactor to make the visualisation work cleanly, and lots of logging (optional) added.
## v2 Update
The whole zoo of UE nodes has been replaced by `Anything Everywhere` nodes which take any input.
For backward compatibility, all the old nodes are still supported for the time being - you can find them in everywhere/deprecated. The only one still in the main folder is Seed Everywhere, which plays a slightly special role. Below are the instructions for the new nodes - then further down you can find the old documentation.
|Separate your workflow logically without spaghetti|This image has a simple example workflow you can drop onto Comfy|
|-|-|
|![separate](docs/separate.png)|![simple](docs/girl.png)|
## Anything Everywhere - send data to every unconnected input of the same type
<details><summary>Details</summary>
The Anything Everywhere node has a single input, initially labelled 'anything'. Connect anything to it (you probably can't connect via a reroute, but you shouldn't need to anyway), and the input name changes to match the input type. Disconnect and it goes back to 'anything'.
# v3 - 10th October 2023
When you run the prompt, any unconnected input, anywhere in the workflow, which matches that type, will act as if it were connected to the same input. The node can also gain a text box showing exactly what passed through the node (you need to turn this on if you want it - it's in the main settings, 'Anything Everywhere node details')
## Anything Everywhere (start here!)
|Unconnected|Connected|Run|
|-|-|-|
|![Unconnected](docs/unconnected.png)|![Connected](docs/connected.png)|![Run](docs/run.png)|
</details>
The `Anything Everywhere` node has a single input, initially labelled 'anything'. Connect anything to it (directly - not via a reroute), and the input name changes to match the input type. Disconnect and it goes back to 'anything'.
When you run the prompt, any unconnected input, anywhere in the workflow, which matches that type, will act as if it were connected to the same input.
To visualise what it's being connected to, right-click on the background canvas and select `Toggle UE Link Visibility`.
## Anything Everywhere? - control matching with regex rules
<details><summary>Details</summary>
This node adds two widgets - title_regex and input_regex. It will only send to input which match. So in the example, title_regex is 'Preview' so the image is sent to the Preview Image node but not the Save Image node.
![regex](docs/regex.png)
## Help! How do I connect to prompt but not negative_prompt?
*The matches are regular expressions, not string matches.* Most simple strings will work (matching any part of the title or input name), but some characters have special meanings (including various sorts of brackets, ^, $, /, and . in particular) so just avoid them if you aren't regex-inclined.
The matches are regular expressions, not straight string matches. So `^prompt` will match `prompt` at the beginning of the title only.
Using regex means you can use `^prompt` to match `prompt` at the beginning of the title only, to avoid matching `negative_prompt`.
Regex 101 - ^ means 'the start', $ means 'the end', '.' matches anything, '.*' matches any number of anything. For more than that, visit [regex101](https://regex101.com/) (the flavour you want is ECMAScript, though that probably won't matter).
</details>
Regex 101 - `^` means 'the start', `$` means 'the end', `.` matches any single character, `.*` matches anything of any length (including zero). For more than that, visit [regex101](https://regex101.com/) (the flavour you want is ECMAScript, though that probably won't matter).
## Seed Everywhere
Seed Everywhere connects to any unconnected INT input with `seed` in the input name (seed, noise_seed, etc), and it has the control_after_generate feature. So if you convert the seed widgets to inputs you can use the same seed everywhere.
## Anything Everywhere3 - One node, three inputs.
Really just three `Anything Everywhere` nodes packaged together. Designed for the outputs of Checkpoint Loader.
## Seed Everywhere - Send data to unconnected inputs with 'seed' in the input name
Seed Everywhere connects to any unconnected INT input with 'seed' in the input name. So you can use the same seed everywhere.
Really just three `Anything Everywhere` nodes packaged together. Designed for the outputs of Checkpoint Loader.
## What if there is more than one match?
<details><summary>Details</summary>
What if there is more than one possible Everywhere node that an input could connect to? The nodes have priorities:
![UE3](docs/UE3.png)
|Node|Priority|
## Prompts Everywhere - two strings or conditionings
Prompt Everywhere has two inputs. They will be sent with regex matching rules of `(^prompt|^positive)` and `neg` respectively. These should match the various versions of names that get used for prompts and negative prompts or conditionings.
|strings|conditionings|
|-|-|
|`Anything Everywhere?`|10|
|`Seed Everywhere`|5|
|`Anything Everywhere`|0|
|![pe](docs/PE.png)|![pe](docs/conditioning.png)
So a matching regex is highest, and a Seed is above a general INT. If this rule isn't enough to decide, *no connection is made* and there's a message in the Javascript log.
</details>
# Other features
## Visualising the links - menu option to show all the virtual links
<details><summary>Details</summary>
Right-click on the canvas (like when you add a new node) and there is an option to visualise the links. The links thus shown will update dynamically as you change the graph, bypass nodes, edit regex's etc..
## Priorities
If there is more than one sending node that matches an input, the basic rules is that the more specific node wins. The order of priorities is:
- `Anything Everywhere?`
- `Seed Everywhere` and `Prompts Everywhere`
- `Anything Everywhere`
- `Anything Everywhere3`
If two nodes with the same priority both match *neither will connect*
## Visualise
If something isn't working right, right click on the background canvas and `Toggle UE Link Visibility` to see all the links being made by the UE nodes.
|Visualise off|Visualise on|
|-|-|
|![off](docs/off.png)|![on](docs/on.png)|
</details>
## See what is sent
The nodes which only have one output can also gain a text box showing exactly what passed through the node. You need to turn this on if you want it - it's in the main settings, 'Anything Everywhere node details'.
## Logging/Debugging
The JavaScript console (press f12 in some browsers) has logging information about what is being connected. You can change the level of detail by finding the file `[comfy_install]/custom_nodes/cg-use-everywhere/js/use_everywhre_utilities.js` and near the top finding this bit:
```javascript
static ERROR = 0; // actual errors
static PROBLEM = 1; // things that stop the workflow working
static INFORMATION = 2; // record of good things
static DETAIL = 3; // details
static LEVEL = Logger.PROBLEM;
static TRACE = false; // most of the method calls
```
Change the `LEVEL` to `Logger.INFORMATION` for more, or `Logger.DETAIL` for even more; set `TRACE` to `true` for some other debugging information.
## Caution
It's possible to create a loop with UE, and that currently isn't detected ([issue](https://github.com/chrisgoringe/cg-use-everywhere/issues/6)). If you get a RecursionError that's probably what you've done. Remember, *every* unconnected input gets connected to the UE output, even optional ones... you might want to use Anything Everywhere? nodes if this is a problem.
It's possible to create a loop with UE, and that currently isn't detected ([issue](https://github.com/chrisgoringe/cg-use-everywhere/issues/6), [PR for ComfyUI](https://github.com/comfyanonymous/ComfyUI/pull/1652)). If you get a RecursionError that's probably what you've done. Remember, *every* unconnected input gets connected to the UE output, even optional ones... you might want to use `Anything Everywhere?` nodes if this is a problem.
## Deprecated Nodes
[Old documentation](docs/deprecated.md)
# Deprecated nodes
For backward compatibility, all the old nodes are still supported for the time being - you can find them in everywhere/deprecated. [Old documentation](docs/deprecated.md)
+5 -4
View File
@@ -10,9 +10,9 @@ except:
application_root_directory = os.path.dirname(folder_paths.__file__)
installer(os.path.join(application_root_directory,"custom_nodes"))
from .use_everywhere import UseEverywhere, UseSomewhere, SeedEverywhere
from .use_everywhere import UseEverywhere, UseSomewhere, SeedEverywhere, AnythingEverywherePrompts
UE_VERSION = 2.6
UE_VERSION = 3.0
# Types and the UE {t} and UE? {t} nodes are deprecated in v2, will be hidden in v3 and removed in v4
types = {
@@ -37,6 +37,7 @@ from .use_everywhere import AnythingEverywhere, AnythingSomewhere, AnythingEvery
NODE_CLASS_MAPPINGS["Anything Everywhere"] = AnythingEverywhere
NODE_CLASS_MAPPINGS["Anything Everywhere3"] = AnythingEverywhereTriplet
NODE_CLASS_MAPPINGS["Anything Everywhere?"] = AnythingSomewhere
NODE_CLASS_MAPPINGS["Prompts Everywhere"] = AnythingEverywherePrompts
__all__ = ['NODE_CLASS_MAPPINGS']
@@ -44,6 +45,6 @@ import os, shutil
import folder_paths
module_js_directory = os.path.join(os.path.dirname(os.path.realpath(__file__)), "js")
application_root_directory = os.path.dirname(folder_paths.__file__)
application_web_extensions_directory = os.path.join(application_root_directory, "web", "extensions", "use_everywhere")
extension_web_extensions_directory = os.path.join(application_root_directory, "web", "extensions", "use_everywhere")
shutil.copytree(module_js_directory, application_web_extensions_directory, dirs_exist_ok=True)
shutil.copytree(module_js_directory, extension_web_extensions_directory, dirs_exist_ok=True)
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 983 KiB

After

Width:  |  Height:  |  Size: 978 KiB

+12 -2
View File
@@ -1,4 +1,4 @@
import os, git, sys
import os, git, sys, shutil
sys.path.insert(0,os.path.join(os.path.dirname(os.path.realpath(__file__)),"..",".."))
def installer(custom_node_path):
@@ -18,5 +18,15 @@ def installer(custom_node_path):
repo.git.submodule('update','--init','--recursive')
repo.close()
print("Removing deployed web extensions - this may mean you need to restart ComfyUI")
application_web_extensions_directory = os.path.join(custom_node_path, "..", "web", "extensions")
for thing in os.listdir(application_web_extensions_directory):
path = os.path.join(application_web_extensions_directory,thing)
if os.path.isdir(path):
if thing != 'core':
shutil.rmtree(path)
else:
os.remove(path)
if 'custom_nodes' in os.getcwd():
installer(os.path.join(os.getcwd(),".."))
installer(os.path.join(os.getcwd(),".."))
+13 -1
View File
@@ -24,6 +24,8 @@ function add_ue_from_node(ues, node) {
}
}
/* end of deprecated section */
if (node.type === "Seed Everywhere") ues.add_ue(node, -1, "INT", [node.id.toString(),0],
undefined, new RegExp("seed"), 5);
@@ -39,12 +41,22 @@ function add_ue_from_node(ues, node) {
}
}
}
if (node.type === "Prompts Everywhere") {
for (var i=0; i<2; i++) {
const in_link = node?.inputs[i].link;
if (in_link) {
const link = app.graph.links[in_link];
const type = app.graph._nodes_by_id[node.id.toString()].input_type[i];
if (type) ues.add_ue(node, i, type, [link.origin_id.toString(), link.origin_slot], undefined, new RegExp(["(^prompt|^positive)","neg"][i]), 5);
}
}
}
if (node.type === "Anything Everywhere") {
const in_link = node?.inputs[0].link;
if (in_link) {
const link = app.graph.links[in_link];
const type = app.graph._nodes_by_id[node.id.toString()].input_type[0];
if (type) ues.add_ue(node, 0, type, [link.origin_id.toString(), link.origin_slot]);
if (type) ues.add_ue(node, 0, type, [link.origin_id.toString(), link.origin_slot], undefined, undefined, 2);
}
}
if (node.type === "Anything Everywhere3") {
+3 -3
View File
@@ -4,8 +4,8 @@ class Logger {
static INFORMATION = 2; // record of good things
static DETAIL = 3; // details
static LEVEL = Logger.INFORMATION;
static TRACE = true;
static LEVEL = Logger.PROBLEM;
static TRACE = false; // most of the method calls
static log(level, message, array) {
if (level <= Logger.LEVEL) {
@@ -69,7 +69,7 @@ function is_UEnode(node_or_nodeType) {
if (title===undefined) title = node_or_nodeType.type;
if (title===undefined) title = node_or_nodeType.comfyClass;
if (title===undefined) return false;
return (title.startsWith("Anything Everywhere") || title==="Seed Everywhere")
return (title.startsWith("Anything Everywhere") || title==="Seed Everywhere" || title==="Prompts Everywhere")
}
/*
+11 -1
View File
@@ -7,7 +7,6 @@ class UseEverywhere():
"optional": { x.lower() : (x, {}) for x in s.RETURN_TYPES }
}
FUNCTION = "func"
CATEGORY = "everywhere/deprecated"
OUTPUT_NODE = True
RETURN_TYPES = ()
@@ -51,7 +50,18 @@ class AnythingEverywhere(UseEverywhere):
for key in kwargs:
return (f"{key} : {kwargs[key]}",)
return ("unconnected",)
class AnythingEverywherePrompts(UseEverywhere):
@classmethod
def INPUT_TYPES(s):
return {"required":{},
"optional": { "(^prompt|^positive)" : ("*", {}), "neg" : ("*", {}), } }
CATEGORY = "everywhere"
def func(self, **kwargs):
return ()
class AnythingEverywhereTriplet(UseEverywhere):
@classmethod
def INPUT_TYPES(s):