This makes the node pack work on the latest ComfyUI. Swap the JobIterator node for a JobToList node. Nothing else needs to change. This does mean that the workflow will be evaluated breadth-first instead of depth-first, which means intermediate results will be cached in memory and you won't get any output until the whole job is finished.
181 lines
4.3 KiB
Python
181 lines
4.3 KiB
Python
import collections
|
|
import itertools
|
|
|
|
from execution import PromptExecutor
|
|
|
|
from . import register_node
|
|
|
|
|
|
@register_node
|
|
class MakeJob:
|
|
"""Turns a sequence into a job with one attribute."""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"sequence": ("SEQUENCE", ),
|
|
"name": ("STRING", {"default": ''}),
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = ("JOB", "INT")
|
|
RETURN_NAMES = ("job", "count")
|
|
FUNCTION = "go"
|
|
CATEGORY = "ali1234/job"
|
|
|
|
def merge_dicts(self, *dicts):
|
|
#return collections.ChainMap(*reversed(dicts))
|
|
return dict(itertools.chain.from_iterable(d.items() for d in dicts))
|
|
|
|
def go(self, sequence, name):
|
|
result = [{name: value} for value in sequence]
|
|
return (result, len(result))
|
|
|
|
|
|
@register_node
|
|
class CombineJobs(MakeJob):
|
|
"""Combines multiple jobs."""
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"a": ("JOB", ),
|
|
"method": (("zip", "product"), {"default": "zip"}),
|
|
},
|
|
"optional": {
|
|
x: ("JOB", ) for x in ('b', 'c', 'd', 'e')
|
|
}
|
|
}
|
|
|
|
def go(self, method, **kwargs):
|
|
method = {'product': itertools.product, 'zip': zip}[method]
|
|
result = [self.merge_dicts(*steps) for steps in method(*kwargs.values())]
|
|
return (result, len(result))
|
|
|
|
|
|
@register_node
|
|
class EnumerateJob(MakeJob):
|
|
"""Combines multiple jobs."""
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"job": ("JOB", ),
|
|
"name": ("STRING", {"default": ''}),
|
|
},
|
|
}
|
|
|
|
def go(self, job, name):
|
|
result = [self.merge_dicts(step, {name: n}) for n, step in enumerate(job)]
|
|
return (result, len(result))
|
|
|
|
|
|
@register_node
|
|
class GetJobStep:
|
|
"""Gets the job step by number."""
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"job": ("JOB", ),
|
|
"step": ("INT", {"default": 0}),
|
|
"wrap": (("repeat", "clamp"), {"default": "repeat"}),
|
|
},
|
|
}
|
|
RETURN_TYPES = ("ATTRIBUTES", )
|
|
RETURN_NAMES = ("attributes", )
|
|
FUNCTION = "go"
|
|
CATEGORY = "ali1234/job"
|
|
|
|
def go(self, job, step, wrap):
|
|
if wrap == 'repeat':
|
|
while step < 0:
|
|
step += len(job)
|
|
step = step % len(job)
|
|
elif wrap == 'clamp':
|
|
step = max(min(step, len(job)), 0)
|
|
return (job[step], )
|
|
|
|
|
|
@register_node
|
|
class FormatAttributes:
|
|
"""Applies attributes to a format string."""
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"attributes": ("ATTRIBUTES",),
|
|
"format": ("STRING", {'default': '', 'multiline': True, "dynamicPrompts": False})
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING", )
|
|
RETURN_NAMES = ("string", )
|
|
FUNCTION = "go"
|
|
CATEGORY = "ali1234/job"
|
|
|
|
def go(self, attributes, format):
|
|
return (format.format(**attributes), )
|
|
|
|
|
|
class AnyType(str):
|
|
def __ne__(self, __value: object) -> bool:
|
|
return False
|
|
|
|
|
|
@register_node
|
|
class GetAttribute:
|
|
"""Gets a named attribute from a step."""
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"attributes": ("ATTRIBUTES", ),
|
|
"name": ("STRING", {"default": ''}),
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = (AnyType("*"), )
|
|
RETURN_NAMES = ("value", )
|
|
FUNCTION = "go"
|
|
CATEGORY = "ali1234/job"
|
|
|
|
def go(self, attributes, name):
|
|
return (attributes[name], )
|
|
|
|
|
|
# Dynamically register typed attribute getters to avoid wildcard bug
|
|
# https://github.com/comfyanonymous/ComfyUI/pull/770
|
|
for t in ('INT', 'FLOAT', 'STRING'):
|
|
register_node(type(
|
|
'GetAttribute'+t.title(),
|
|
(GetAttribute, ),
|
|
{
|
|
'RETURN_TYPES': (t, ),
|
|
}
|
|
))
|
|
|
|
|
|
|
|
|
|
@register_node
|
|
class JobToList:
|
|
"""Converts a job into a list."""
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"job": ("JOB",),
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = ("ATTRIBUTES",)
|
|
RETURN_NAMES = ("attributes",)
|
|
OUTPUT_IS_LIST = (True,)
|
|
FUNCTION = "go"
|
|
CATEGORY = "ali1234/job"
|
|
|
|
def go(self, job):
|
|
return (job,)
|