diff --git a/.gitignore b/.gitignore index 68bc17f..2dc53ca 100644 --- a/.gitignore +++ b/.gitignore @@ -157,4 +157,4 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +.idea/ diff --git a/README.md b/README.md index 9b6ef48..14656f6 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,8 @@ This repository offers various extension nodes for ComfyUI. Nodes here have diff * `Zip Prompt (Inspire)`: Create ZIPPED_PROMPT from positive, negative, and name_opt. * If name_opt is omitted, it will be considered as an empty name. * `Prompt Extractor (Inspire)`: This node reads prompt information from the image's metadata. Since it retrieves all the text, you need to directly specify the prompts to be used for `positive` and `negative` as indicated in the info. - + * `Global Seed (Inspire)`: This is a node that controls the global seed without a separate connection line. It only controls when the widget's name is 'seed.' Additionally, if 'control_before_generate' is checked, it controls the seed before executing the prompt. + * NOTE: In the case of seed widgets converted into input slots, normal operation is not guaranteed. ## Credits diff --git a/__init__.py b/__init__.py index edc78e6..7100932 100644 --- a/__init__.py +++ b/__init__.py @@ -7,13 +7,14 @@ import importlib -print(f"### Loading: ComfyUI-Inspire-Pack (V0.11.1)") +print(f"### Loading: ComfyUI-Inspire-Pack (V0.12)") node_list = [ "lora_block_weight", "segs_support", "a1111_compat", "prompt_support", + "inspire_server", ] NODE_CLASS_MAPPINGS = {} diff --git a/inspire_server.py b/inspire_server.py new file mode 100644 index 0000000..c3d347a --- /dev/null +++ b/inspire_server.py @@ -0,0 +1,74 @@ +import random + +import server + +def control_seed(node_id, v): + action = v['inputs']['action'] + value = v['inputs']['value'] + + if action == 'increment': + value += 1 + elif action == 'decrement': + value -= 1 + elif action == 'randomize': + value = random.randint(0, 1125899906842624) + + v['inputs']['value'] = value + server.PromptServer.instance.send_sync("inspire-global-seed", {"id": node_id, "value": value}) + + return value + + +def prompt_seed_update(json_data): + value = None + mode = None + node = None + + for k, v in json_data['prompt'].items(): + cls = v['class_type'] + if cls == 'GlobalSeed //Inspire': + mode = v['inputs']['mode'] + node = k, v + + # control before generated + if mode is not None and mode: + value = control_seed(node[0], node[1]) + + if value is not None: + for k, v in json_data['prompt'].items(): + if 'seed' in v['inputs']: + if isinstance(v['inputs']['seed'], int): + v['inputs']['seed'] = value + + # control after generated + if mode is not None and not mode: + control_seed(node[0], node[1]) + + return value + + +def workflow_seed_update(json_data, value): + nodes = json_data['extra_data']['extra_pnginfo']['workflow']['nodes'] + seed_widget_map = json_data['extra_data']['extra_pnginfo']['workflow']['seed_widgets'] + + for node in nodes: + if node['type'] == 'GlobalSeed //Inspire': + node['widgets_values'][0] = value + elif str(node['id']) in seed_widget_map: + node_id = str(node['id']) + widget_idx = seed_widget_map[node_id] + node['widgets_values'][widget_idx] = value + + +def onprompt(json_data): + value = prompt_seed_update(json_data) + workflow_seed_update(json_data, value) + + return json_data + + +server.PromptServer.instance.add_on_prompt_handler(onprompt) + + +NODE_CLASS_MAPPINGS = {} +NODE_DISPLAY_NAME_MAPPINGS = {} diff --git a/js/seed.js b/js/seed.js new file mode 100644 index 0000000..16eadf6 --- /dev/null +++ b/js/seed.js @@ -0,0 +1,46 @@ +import { api } from "../../scripts/api.js"; + +function globalSeedHandler(event) { + let nodes = app.graph._nodes_by_id; + + for(let i in nodes) { + let node = nodes[i]; + + if(node.type == 'GlobalSeed //Inspire') { + if(node.widgets) { + const w = node.widgets.find((w) => w.name == 'value'); + w.value = event.detail.value; + } + } + else + if(node.widgets) { + const w = node.widgets.find((w) => w.name == 'seed' && w.type == 'number'); + if(w) { + w.value = event.detail.value; + } + } + } +} + +api.addEventListener("inspire-global-seed", globalSeedHandler); + + +const original_queuePrompt = api.queuePrompt; +async function queuePrompt_with_seed(number, { output, workflow }) { + workflow.seed_widgets = {}; + + for(let i in workflow.nodes) { + let node_id = workflow.nodes[i].id; + let widgets = app.graph._nodes_by_id[node_id].widgets; + if(widgets) { + for(let j in widgets) { + if(widgets[j].name == 'seed') + workflow.seed_widgets[node_id] = parseInt(j); + } + } + } + + return await original_queuePrompt.call(api, number, { output, workflow }); +} + +api.queuePrompt = queuePrompt_with_seed; \ No newline at end of file diff --git a/prompt_support.py b/prompt_support.py index 350aff1..8472040 100644 --- a/prompt_support.py +++ b/prompt_support.py @@ -174,15 +174,39 @@ class PromptExtractor: return (positive, negative) +class GlobalSeed: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "value": ("INT", {"default": 0, "min": 0, "max": 1125899906842624}), + "mode": ("BOOLEAN", {"default": True, "label_on": "control_before_generate", "label_off": "control_after_generate"}), + "action": (["fixed", "increment", "decrement", "randomize"], ) + } + } + + RETURN_TYPES = () + FUNCTION = "doit" + + CATEGORY = "InspirePack" + + OUTPUT_NODE = True + + def doit(self, **kwargs): + return {} + + NODE_CLASS_MAPPINGS = { "LoadPromptsFromDir //Inspire": LoadPromptsFromDir, "UnzipPrompt //Inspire": UnzipPrompt, "ZipPrompt //Inspire": ZipPrompt, "PromptExtractor //Inspire": PromptExtractor, + "GlobalSeed //Inspire": GlobalSeed, } NODE_DISPLAY_NAME_MAPPINGS = { "LoadPromptsFromDir //Inspire": "Load Prompts From Dir (Inspire)", "UnzipPrompt //Inspire": "Unzip Prompt (Inspire)", "ZipPrompt //Inspire": "Zip Prompt (Inspire)", - "PromptExtractor //Inspire": "Prompt Extractor (Inspire)" + "PromptExtractor //Inspire": "Prompt Extractor (Inspire)", + "GlobalSeed //Inspire": "Global Seed (Inspire)" }