1.0.0
Initial Version
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
@@ -169,3 +169,5 @@ cython_debug/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
examples/desktop.ini
|
||||
examples/1.mkv
|
||||
|
||||
@@ -1 +1,69 @@
|
||||
# ComfyUI_Simple_String_Repository
|
||||
# ComfyUI_Simple_String_Repository
|
||||
|
||||
A small utility node (in multiple variations) which assists prompt building by allowing you to use "quick and dirty" collections of strings, separated via new-line and indexed inside the string itself, directly in ComfyUI - as opposed to loading from massive collections of external files.
|
||||
|
||||
The nodes also support basic "randomness" syntax, which is explained in the [sd dynamic prompts repo](https://github.com/adieyal/sd-dynamic-prompts).
|
||||
|
||||
However, unlike the dynamic prompts in the above repo, this node aims at helping you build prompts faster using many small collections of strings already relevant to your specific project / workflow, as opposed to exploring different combinations of random elements from massive lists.
|
||||
This is better illustrated below.
|
||||
|
||||
|
||||
Install
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
**Option 1**
|
||||
|
||||
Install via [ComfyUI-Manager](https://github.com/ltdrdata/ComfyUI-Manager).
|
||||
|
||||
**Option 2**
|
||||
|
||||
Clone the repository into your ComfyUI custom_nodes directory.
|
||||
```text
|
||||
git clone https://github.com/IgalOgonov/ComfyUI_Simple_String_Repository
|
||||
```
|
||||
|
||||
The Node(s)
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
The nodes are all called "Simple String Repository".
|
||||
The there are three **variations** based on the number of potentially selected strings (`Small` for 3, no suffix for 5, and `Large` for 10), and each node has a "compact" version which is worse for automated workflow but more comfortable if you intend to set all selection parameters (1 required, 2 optional) manually.
|
||||
|
||||

|
||||
|
||||
Each node has the `string` input, which corresponds to the collection of strings you wish to use.
|
||||
By design, each collection should represent alternative tags belonging to the same "group", e.g. art styles, type of atmosphere, color scheme, artist, etc.
|
||||
|
||||
Below is the `delimiter`, used to separate multiple outputs in case you select more than one strings from the collection.
|
||||
|
||||
Afterwards is where the `Compact` and `Regular` nodes differ.
|
||||
In regular node, each `target` (the index of the sub-string you wish to select) has three relevant widgets:
|
||||
* The index itself (`target`)
|
||||
* Its strength (`strength`)
|
||||
* In case of a collection of randomly interchangeable strings, as explained in the [sd dynamic prompts repo](https://github.com/adieyal/sd-dynamic-prompts), you may explicitly select one of the options via `random` (index starts at 0), or leave it at `-1` for a random output.
|
||||
|
||||
The `Compact` nodes simply shove all three parameters into a single `,`-separated string. All optional parameters can be omitted.
|
||||
|
||||
The **indexes** are highly **recommended**, but **optional**.
|
||||
Any string that does not have an index will get assigned its line number as an index during runtime.
|
||||
When duplicate indexes exist (typically, this would only happen because of a mess caused by mixing indexed and unindexed strings), only the first one can be selected.
|
||||
The **front-end functionality** helps avoid that:
|
||||
|
||||
1. `Add Missing Indexes` - This button will add unique numerical indexes to any strings missing them. Will also fix duplicates, but replacing any duplicate identifier with a new one.
|
||||
The new indexes are always the lowest available unique index.
|
||||
Good for when you already have specific automated workflows reliant on specific numbers, but overall, this is considered a quick and dirty fix.
|
||||
2. `Regenerate All Indexes` - Strips all strings from their existing indexes, then assigns new ones, based on the line number.
|
||||
3. `Remove All Indexes` - Strips all strings from their indexes.
|
||||
4. `Reset Target Parameters` Resets the selection parameters, making the output blank.
|
||||
|
||||
Intended Usage
|
||||
-----------------------------------------------------------------------------
|
||||

|
||||
|
||||
Those nodes don't really have much functionality by themselves - they are meant to supplement existing prompt builder nodes, such as [String Function Tree](https://github.com/wolfden/ComfyUi_String_Function_Tree), that are meant for prompt building (as opposed to prompt generation / random exploration).
|
||||
|
||||
The collections themselves can be created manually (e.g. explore articles about art styles relevant to your project, and copy-paste relevant styles into an "Art Styles" node), or you can take subsets from existing collections [like the ones over here](https://github.com/adieyal/sd-dynamic-prompts/tree/main/collections).
|
||||
As you may have noticed from the earlier preview, the node does support the basic randomness syntax (e.g. `{something|like|this}`), although it's not optimized for it.
|
||||
|
||||

|
||||
|
||||
Note that you don't have to use short strings - you may also use large collections as a single string, as long as you're fine with giving up precision for such strings.
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
from .py.simple_string_repository import SimpleStringRepositorySmall,SimpleStringRepositorySmallCompact, SimpleStringRepository, SimpleStringRepositoryCompact, SimpleStringRepositoryLarge, SimpleStringRepositoryLargeCompact
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"SimpleStringRepositorySmall": SimpleStringRepositorySmall,
|
||||
"SimpleStringRepositorySmallCompact": SimpleStringRepositorySmallCompact,
|
||||
"SimpleStringRepository": SimpleStringRepository,
|
||||
"SimpleStringRepositoryCompact": SimpleStringRepositoryCompact,
|
||||
"SimpleStringRepositoryLarge": SimpleStringRepositoryLarge,
|
||||
"SimpleStringRepositoryLargeCompact": SimpleStringRepositoryLargeCompact,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SimpleStringRepositorySmall": "Simple String Repository Small (IO)",
|
||||
"SimpleStringRepositorySmallCompact": "Simple String Repository Small Compact (IO)",
|
||||
"SimpleStringRepository": "Simple String Repository (IO)",
|
||||
"SimpleStringRepositoryCompact": "Simple String Repository Compact (IO)",
|
||||
"SimpleStringRepositoryLarge": "Simple String Repository Large (IO)",
|
||||
"SimpleStringRepositoryLargeCompact": "Simple String Repository Large Compact (IO)",
|
||||
}
|
||||
|
||||
|
||||
WEB_DIRECTORY = "./web"
|
||||
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.1 MiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 618 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 452 KiB |
@@ -0,0 +1,296 @@
|
||||
from .util import Util
|
||||
|
||||
class SimpleStringRepositorySmall:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"string": ("STRING", {"multiline": True}),
|
||||
"delimiter": ([",", " "], {}),
|
||||
},
|
||||
"optional": {
|
||||
"target1": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength1": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random1": ("INT", {"default":-1,"min": -1,"max": 1000}),
|
||||
"target2": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength2": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random2": ("INT", {"default":-1,"min": -1,"max": 1000}),
|
||||
"target3": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength3": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random3": ("INT", {"default":-1,"min": -1,"max": 1000})
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("parsed_string",)
|
||||
CATEGORY = "utils"
|
||||
FUNCTION = "exec"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def IS_CHANGED(self, string, delimiter, target1=None, strength1=None, random1=None, target2=None, strength2=None, random2=None,
|
||||
target3=None, strength3=None, random3=None):
|
||||
parsedInputs = [
|
||||
{"t":target1,"s":strength1,"r":random1},
|
||||
{"t":target2,"s":strength2,"r":random2},
|
||||
{"t":target3,"s":strength3,"r":random3}
|
||||
]
|
||||
return (True, float("NaN"))[Util.areThereTargetsWithRandomness(parsedInputs)]
|
||||
|
||||
#Main Function
|
||||
def exec(self, string, delimiter, target1=None, strength1=None, random1=None, target2=None, strength2=None, random2=None,
|
||||
target3=None, strength3=None, random3=None):
|
||||
|
||||
#Parse inputs for clarity
|
||||
parsedInputs = [
|
||||
{"t":target1,"s":strength1,"r":random1},
|
||||
{"t":target2,"s":strength2,"r":random2},
|
||||
{"t":target3,"s":strength3,"r":random3}
|
||||
]
|
||||
return Util.common_string_repo_function(string,delimiter,parsedInputs)
|
||||
|
||||
class SimpleStringRepositorySmallCompact:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"string": ("STRING", {"multiline": True}),
|
||||
"delimiter": ([",", " "], {}),
|
||||
},
|
||||
"optional": {
|
||||
"target1": ("STRING", {"default":'0,1,-1'}),
|
||||
"target2": ("STRING", {"default":'0,1,-1'}),
|
||||
"target3": ("STRING", {"default":'0,1,-1'})
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("parsed_string",)
|
||||
CATEGORY = "utils"
|
||||
FUNCTION = "exec"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def IS_CHANGED(self, string, delimiter, target1=None, target2=None, target3=None):
|
||||
targets = [target1,target2,target3]
|
||||
return (True, float("NaN"))[Util.areThereTargetsWithRandomness(Util.parseCompactInputs(targets))]
|
||||
|
||||
#Main Function
|
||||
def exec(self, string, delimiter, target1=None, target2=None, target3=None):
|
||||
targets = [target1,target2,target3]
|
||||
return Util.common_string_repo_function(string,delimiter,Util.parseCompactInputs(targets))
|
||||
|
||||
class SimpleStringRepository:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"string": ("STRING", {"multiline": True}),
|
||||
"delimiter": ([",", " "], {}),
|
||||
},
|
||||
"optional": {
|
||||
"target1": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength1": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random1": ("INT", {"default":-1,"min": -1,"max": 1000}),
|
||||
"target2": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength2": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random2": ("INT", {"default":-1,"min": -1,"max": 1000}),
|
||||
"target3": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength3": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random3": ("INT", {"default":-1,"min": -1,"max": 1000}),
|
||||
"target4": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength4": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random4": ("INT", {"default":-1,"min": -1,"max": 1000}),
|
||||
"target5": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength5": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random5": ("INT", {"default":-1,"min": -1,"max": 1000}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("parsed_string",)
|
||||
CATEGORY = "utils"
|
||||
FUNCTION = "exec"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def IS_CHANGED(self, string, delimiter, target1=None, strength1=None, random1=None, target2=None, strength2=None, random2=None,
|
||||
target3=None, strength3=None, random3=None, target4=None, strength4=None, random4=None, target5=None, strength5=None, random5=None):
|
||||
parsedInputs = [
|
||||
{"t":target1,"s":strength1,"r":random1},
|
||||
{"t":target2,"s":strength2,"r":random2},
|
||||
{"t":target3,"s":strength3,"r":random3},
|
||||
{"t":target4,"s":strength4,"r":random4},
|
||||
{"t":target5,"s":strength5,"r":random5}
|
||||
]
|
||||
return (True, float("NaN"))[Util.areThereTargetsWithRandomness(parsedInputs)]
|
||||
|
||||
#Main Function
|
||||
def exec(self, string, delimiter, target1=None, strength1=None, random1=None, target2=None, strength2=None, random2=None,
|
||||
target3=None, strength3=None, random3=None, target4=None, strength4=None, random4=None, target5=None, strength5=None, random5=None):
|
||||
|
||||
#Parse inputs for clarity
|
||||
parsedInputs = [
|
||||
{"t":target1,"s":strength1,"r":random1},
|
||||
{"t":target2,"s":strength2,"r":random2},
|
||||
{"t":target3,"s":strength3,"r":random3},
|
||||
{"t":target4,"s":strength4,"r":random4},
|
||||
{"t":target5,"s":strength5,"r":random5}
|
||||
]
|
||||
return Util.common_string_repo_function(string,delimiter,parsedInputs)
|
||||
|
||||
class SimpleStringRepositoryCompact:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"string": ("STRING", {"multiline": True}),
|
||||
"delimiter": ([",", " "], {}),
|
||||
},
|
||||
"optional": {
|
||||
"target1": ("STRING", {"default":'0,1,-1'}),
|
||||
"target2": ("STRING", {"default":'0,1,-1'}),
|
||||
"target3": ("STRING", {"default":'0,1,-1'}),
|
||||
"target4": ("STRING", {"default":'0,1,-1'}),
|
||||
"target5": ("STRING", {"default":'0,1,-1'})
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("parsed_string",)
|
||||
CATEGORY = "utils"
|
||||
FUNCTION = "exec"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def IS_CHANGED(self, string, delimiter, target1=None, target2=None, target3=None, target4=None, target5=None):
|
||||
targets = [target1,target2,target3,target4,target5]
|
||||
return (True, float("NaN"))[Util.areThereTargetsWithRandomness(Util.parseCompactInputs(targets))]
|
||||
|
||||
#Main Function
|
||||
def exec(self, string, delimiter, target1=None, target2=None, target3=None, target4=None, target5=None):
|
||||
targets = [target1,target2,target3,target4,target5]
|
||||
return Util.common_string_repo_function(string,delimiter,Util.parseCompactInputs(targets))
|
||||
|
||||
class SimpleStringRepositoryLarge:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"string": ("STRING", {"multiline": True}),
|
||||
"delimiter": ([",", " "], {}),
|
||||
},
|
||||
"optional": {
|
||||
"target1": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength1": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random1": ("INT", {"default":-1,"min": -1,"max": 1000}),
|
||||
"target2": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength2": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random2": ("INT", {"default":-1,"min": -1,"max": 1000}),
|
||||
"target3": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength3": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random3": ("INT", {"default":-1,"min": -1,"max": 1000}),
|
||||
"target4": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength4": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random4": ("INT", {"default":-1,"min": -1,"max": 1000}),
|
||||
"target5": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength5": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random5": ("INT", {"default":-1,"min": -1,"max": 1000}),
|
||||
"target6": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength6": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random6": ("INT", {"default":-1,"min": -1,"max": 1000}),
|
||||
"target7": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength7": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random7": ("INT", {"default":-1,"min": -1,"max": 1000}),
|
||||
"target8": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength8": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random8": ("INT", {"default":-1,"min": -1,"max": 1000}),
|
||||
"target9": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength9": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random9": ("INT", {"default":-1,"min": -1,"max": 1000}),
|
||||
"target10": ("INT", {"default":None,"min": 0,"max": 100000000}),
|
||||
"strength10": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
|
||||
"random10": ("INT", {"default":-1,"min": -1,"max": 1000}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("parsed_string",)
|
||||
CATEGORY = "utils"
|
||||
FUNCTION = "exec"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def IS_CHANGED(self, string, delimiter, target1=None, strength1=None, random1=None, target2=None, strength2=None, random2=None,
|
||||
target3=None, strength3=None, random3=None, target4=None, strength4=None, random4=None, target5=None, strength5=None, random5=None,
|
||||
target6=None, strength6=None, random6=None, target7=None, strength7=None, random7=None,
|
||||
target8=None, strength8=None, random8=None, target9=None, strength9=None, random9=None, target10=None, strength10=None, random10=None):
|
||||
parsedInputs = [
|
||||
{"t":target1,"s":strength1,"r":random1},
|
||||
{"t":target2,"s":strength2,"r":random2},
|
||||
{"t":target3,"s":strength3,"r":random3},
|
||||
{"t":target4,"s":strength4,"r":random4},
|
||||
{"t":target5,"s":strength5,"r":random5},
|
||||
{"t":target6,"s":strength6,"r":random6},
|
||||
{"t":target7,"s":strength7,"r":random7},
|
||||
{"t":target8,"s":strength8,"r":random8},
|
||||
{"t":target9,"s":strength9,"r":random9},
|
||||
{"t":target10,"s":strength10,"r":random10}
|
||||
]
|
||||
return (True, float("NaN"))[Util.areThereTargetsWithRandomness(parsedInputs)]
|
||||
|
||||
|
||||
#Main Function
|
||||
def exec(self, string, delimiter, target1=None, strength1=None, random1=None, target2=None, strength2=None, random2=None,
|
||||
target3=None, strength3=None, random3=None, target4=None, strength4=None, random4=None, target5=None, strength5=None, random5=None,
|
||||
target6=None, strength6=None, random6=None, target7=None, strength7=None, random7=None,
|
||||
target8=None, strength8=None, random8=None, target9=None, strength9=None, random9=None, target10=None, strength10=None, random10=None):
|
||||
|
||||
#Parse inputs for clarity
|
||||
parsedInputs = [
|
||||
{"t":target1,"s":strength1,"r":random1},
|
||||
{"t":target2,"s":strength2,"r":random2},
|
||||
{"t":target3,"s":strength3,"r":random3},
|
||||
{"t":target4,"s":strength4,"r":random4},
|
||||
{"t":target5,"s":strength5,"r":random5},
|
||||
{"t":target6,"s":strength6,"r":random6},
|
||||
{"t":target7,"s":strength7,"r":random7},
|
||||
{"t":target8,"s":strength8,"r":random8},
|
||||
{"t":target9,"s":strength9,"r":random9},
|
||||
{"t":target10,"s":strength10,"r":random10}
|
||||
]
|
||||
return Util.common_string_repo_function(string,delimiter,parsedInputs)
|
||||
|
||||
class SimpleStringRepositoryLargeCompact:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"string": ("STRING", {"multiline": True}),
|
||||
"delimiter": ([",", " "], {}),
|
||||
},
|
||||
"optional": {
|
||||
"target1": ("STRING", {"default":'0,1,-1'}),
|
||||
"target2": ("STRING", {"default":'0,1,-1'}),
|
||||
"target3": ("STRING", {"default":'0,1,-1'}),
|
||||
"target4": ("STRING", {"default":'0,1,-1'}),
|
||||
"target5": ("STRING", {"default":'0,1,-1'}),
|
||||
"target6": ("STRING", {"default":'0,1,-1'}),
|
||||
"target7": ("STRING", {"default":'0,1,-1'}),
|
||||
"target8": ("STRING", {"default":'0,1,-1'}),
|
||||
"target9": ("STRING", {"default":'0,1,-1'}),
|
||||
"target10": ("STRING", {"default":'0,1,-1'})
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("parsed_string",)
|
||||
CATEGORY = "utils"
|
||||
FUNCTION = "exec"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def IS_CHANGED(self, string, delimiter, target1=None, target2=None, target3=None, target4=None, target5=None,
|
||||
target6=None, target7=None, target8=None, target9=None, target10=None):
|
||||
targets = [target1,target2,target3,target4,target5,target6,target7,target8,target9,target10]
|
||||
return (True, float("NaN"))[Util.areThereTargetsWithRandomness(Util.parseCompactInputs(targets))]
|
||||
|
||||
#Main Function
|
||||
def exec(self, string, delimiter, target1=None, target2=None, target3=None, target4=None, target5=None,
|
||||
target6=None, target7=None, target8=None, target9=None, target10=None):
|
||||
targets = [target1,target2,target3,target4,target5,target6,target7,target8,target9,target10]
|
||||
return Util.common_string_repo_function(string,delimiter,Util.parseCompactInputs(targets))
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
import re
|
||||
import random
|
||||
|
||||
class Util:
|
||||
@staticmethod
|
||||
def var_dump(var, prefix=''):
|
||||
"""
|
||||
You know you're a php developer when the first thing you ask for
|
||||
when learning a new language is 'Where's var_dump?????'
|
||||
"""
|
||||
my_type = '[' + var.__class__.__name__ + '(' + str(len(var)) + ')]:'
|
||||
print(prefix, my_type, sep='')
|
||||
prefix += ' '
|
||||
for i in var:
|
||||
if type(i) in (list, tuple, dict, set):
|
||||
Util.var_dump(i, prefix)
|
||||
else:
|
||||
if isinstance(var, dict):
|
||||
print(prefix, i, ': (', var[i].__class__.__name__, ') ', var[i], sep='')
|
||||
else:
|
||||
print(prefix, '(', i.__class__.__name__, ') ', i, sep='')
|
||||
|
||||
@staticmethod
|
||||
def removing_leading_whitespaces(text):
|
||||
"""
|
||||
Remove leading whitespaces
|
||||
"""
|
||||
return re.sub(r"^\s+","",text)
|
||||
|
||||
@staticmethod
|
||||
def areThereTargetsWithRandomness(inputs):
|
||||
for tuple in inputs:
|
||||
if(tuple["r"] == -1 and tuple["t"] != None):
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def parseCompactInputs(inputs):
|
||||
newInputs = []
|
||||
for string in inputs:
|
||||
parsedString = string.split(',')
|
||||
newTuple = {
|
||||
"t":int(parsedString[0])
|
||||
}
|
||||
|
||||
if(len(parsedString) >= 2):
|
||||
newTuple["s"] = float(parsedString[1])
|
||||
else:
|
||||
newTuple["s"] = 1.0
|
||||
|
||||
if(len(parsedString) >= 3):
|
||||
newTuple["r"] = int(parsedString[2])
|
||||
else:
|
||||
newTuple["r"] = -1
|
||||
|
||||
newInputs.append(newTuple)
|
||||
return newInputs
|
||||
|
||||
@staticmethod
|
||||
def common_string_repo_function(string, delimiter, inputs):
|
||||
"""
|
||||
Common function which is actually the string repo main function.
|
||||
The classes are just wrappers around it with a different number of similar inputs.
|
||||
"""
|
||||
orphanStrings = []
|
||||
newDict = {}
|
||||
parsingIndex = 0
|
||||
parsedString = string.splitlines()
|
||||
|
||||
# Parse all strings, separated by newline.
|
||||
# A properly formatted string should either have ALL strings with unique numeric identifiers, or none at all.
|
||||
# Strings without a leading number will be assigned one based on their line number.
|
||||
# In case of a mix of both, unexpected behavior may occur - strings without an identifier will be assigned one, and subsequent duplicate numeric identifiers are ignored.
|
||||
for line in parsedString:
|
||||
|
||||
existingId = None
|
||||
currentString = None
|
||||
parsingIndex += 1
|
||||
|
||||
parsedLine = line.split(".")
|
||||
if(len(parsedLine) != 1):
|
||||
existingId = parsedLine[0]
|
||||
currentString = parsedLine[1]
|
||||
else:
|
||||
currentString = parsedLine[0]
|
||||
|
||||
Util.removing_leading_whitespaces(currentString)
|
||||
|
||||
if(not existingId):
|
||||
existingId = parsingIndex
|
||||
|
||||
existingId = int(existingId)
|
||||
|
||||
if existingId in newDict:
|
||||
orphanStrings.extend((currentString,))
|
||||
else:
|
||||
newDict[existingId] = currentString
|
||||
|
||||
|
||||
# Debug:
|
||||
# print("Dict:")
|
||||
# for k,v in newDict.items():
|
||||
# print(k, "corresponds to", v)
|
||||
# print("Orphans:")
|
||||
# for v in orphanStrings:
|
||||
# print(v)
|
||||
|
||||
|
||||
#Parse all inputs
|
||||
finalString = ''
|
||||
for tuple in inputs:
|
||||
|
||||
#Target does not exist!
|
||||
if(tuple["t"] not in newDict):
|
||||
# Debug:
|
||||
#print("target", tuple["t"], "does not exist!")
|
||||
continue
|
||||
|
||||
#Target exists!
|
||||
baseStr = newDict[tuple["t"]]
|
||||
|
||||
#Handle randomness, if applicable
|
||||
if(re.fullmatch("^{.+}$",baseStr)):
|
||||
baseStr = baseStr[1:][:-1]
|
||||
baseStr = baseStr.split("|")
|
||||
|
||||
if(tuple["r"] == -1):
|
||||
tuple["r"] = random.randint(0, len(baseStr)-1)
|
||||
if(tuple["r"] >= len(baseStr)):
|
||||
# Debug:
|
||||
#print("Random string ", tuple["r"], "in target",tuple["t"],baseStr, "does not exist!")
|
||||
continue
|
||||
baseStr = baseStr[tuple["r"]]
|
||||
|
||||
#Handle Strength
|
||||
if(not tuple["s"]):
|
||||
# Debug:
|
||||
#print("Target",tuple["t"], "Has strength 0")
|
||||
continue
|
||||
elif(tuple["s"] != 1):
|
||||
tuple["s"] = round(tuple["s"],2)
|
||||
baseStr = f'({baseStr}:{tuple["s"]})'
|
||||
|
||||
finalString += baseStr + delimiter
|
||||
|
||||
finalString = finalString[:-1]
|
||||
# Debug:
|
||||
#print(finalString)
|
||||
return {"ui": {"text": (finalString,)}, "result": (finalString,)}
|
||||
@@ -0,0 +1,14 @@
|
||||
[project]
|
||||
name = "comfyui_simple_string_repository"
|
||||
description = "Custom node that allows storing and accessing strings, meant to be parts of a prompt, in a simplified manner. Partially supports dynamic prompt syntax."
|
||||
version = "1.0.0"
|
||||
license = "LICENSE"
|
||||
|
||||
[project.urls]
|
||||
Repository = "https://github.com/IgalOgonov/ComfyUI_Simple_String_Repository"
|
||||
# Used by Comfy Registry https://comfyregistry.org
|
||||
|
||||
[tool.comfy]
|
||||
PublisherId = "io"
|
||||
DisplayName = "Simple String Repository"
|
||||
Icon = ""
|
||||
@@ -0,0 +1,94 @@
|
||||
import { app } from "../../../scripts/app.js";
|
||||
import { ComfyWidgets } from "../../../scripts/widgets.js";
|
||||
|
||||
// Displays input text on a node
|
||||
|
||||
app.registerExtension({
|
||||
name: "io.SimpleStringRepository",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if(nodeType?.comfyClass.startsWith('SimpleStringRepository')){
|
||||
//TODO Perhaps add stuff
|
||||
}
|
||||
},
|
||||
async nodeCreated(node, app) {
|
||||
let nodeClass= node?.comfyClass;
|
||||
if(nodeClass.startsWith('SimpleStringRepository')){
|
||||
let isCompact = node?.comfyClass.endsWith('Compact');
|
||||
|
||||
let extractParsedStrings = ()=>{
|
||||
let str_widget = node.widgets[0];
|
||||
let str = str_widget.value;
|
||||
return str.split('\n');
|
||||
}
|
||||
|
||||
//Adds numerical indexes to strings, does not destroy or change existing ones.
|
||||
node.addWidget("button", "✍ Add Missing Indexes", null, () => {
|
||||
let currentStrings = extractParsedStrings();
|
||||
let map = {}
|
||||
let orphans = {};
|
||||
for(let i in currentStrings){
|
||||
let index = null;
|
||||
let str = currentStrings[i];
|
||||
if(str.includes('.')){
|
||||
index = str.split('.')[0];
|
||||
str = str.split('.')[1];
|
||||
}
|
||||
if(!index || map[index])
|
||||
orphans[i] = str;
|
||||
else
|
||||
map[index] = str;
|
||||
}
|
||||
let newIndex = 1;
|
||||
for(let i in orphans){
|
||||
while(map[newIndex] !== undefined)
|
||||
newIndex++;
|
||||
currentStrings[i] = newIndex + '.' + orphans[i];
|
||||
map[newIndex] = orphans[i];
|
||||
}
|
||||
node.widgets[0].value = currentStrings.join('\n');
|
||||
}, { serialize: false });
|
||||
|
||||
//Regenerates all string indexes, potentially changing existing ones.
|
||||
node.addWidget("button", "⚠ Regenerate All Indexes", null, () => {
|
||||
let currentStrings = extractParsedStrings();
|
||||
for(let i in currentStrings){
|
||||
if(currentStrings[i].includes('.'))
|
||||
currentStrings[i] = currentStrings[i].split('.')[1];
|
||||
currentStrings[i] = (i-0+1) +'.'+ currentStrings[i];
|
||||
}
|
||||
node.widgets[0].value = currentStrings.join('\n');
|
||||
}, { serialize: false });
|
||||
|
||||
//Strips all strings of their indexes
|
||||
node.addWidget("button", "❌ Remove All Indexes", null, () => {
|
||||
let currentStrings = extractParsedStrings();
|
||||
for(let i in currentStrings){
|
||||
if(currentStrings[i].includes('.'))
|
||||
currentStrings[i] = currentStrings[i].split('.')[1];
|
||||
}
|
||||
node.widgets[0].value = currentStrings.join('\n');
|
||||
}, { serialize: false });
|
||||
|
||||
//Resets Parameters (Target/Strength/Randomness)
|
||||
node.addWidget("button", "↺ Reset Target Parameters", null, () => {
|
||||
for(let i in node.widgets){
|
||||
let widget = node.widgets[i];
|
||||
switch(isCompact){
|
||||
case true:
|
||||
if(widget.name.match(/^target\d+$/))
|
||||
widget.value = '0,1,-1';
|
||||
break;
|
||||
case false:
|
||||
if(widget.name.match(/^target\d+$/))
|
||||
widget.value = 0;
|
||||
if(widget.name.match(/^strength\d+$/))
|
||||
widget.value = 1;
|
||||
if(widget.name.match(/^random\d+$/))
|
||||
widget.value = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, { serialize: false });
|
||||
}
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user