Skip to content

Commit

Permalink
feat GlobalSampler
Browse files Browse the repository at this point in the history
  • Loading branch information
ltdrdata committed Dec 26, 2023
1 parent fe57ea6 commit fb7ebc7
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 52 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ This repository offers various extension nodes for ComfyUI. Nodes here have diff
* `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' or 'noise_seed'. Additionally, if 'control_before_generate' is checked, it controls the seed before executing the prompt.
* Seeds that have been converted into inputs are excluded from the target. If you want to control the seed separately, convert it into an input and control it separately.
* `Global Sampler (Inspire)`: This node is similar to GlobalSeed and can simultaneously set the sampler_name and scheduler for all nodes in the workflow.
* It applies only to nodes that have both sampler_name and scheduler, and it won't be effective if `GlobalSampler` is muted.
* If some of the `sampler_name` and `scheduler` have been converted to input and connected to Primitive node, it will not apply only to the converted widget. The widget that has not been converted to input will still be affected.
* `Bind [ImageList, PromptList] (Inspire)`: Bind Image list and zipped prompt list to export `image`, `positive`, `negative`, and `prompt_label` in a list format. If there are more prompts than images, the excess prompts are ignored, and if there are not enough, the remainder is filled with default input based on the images.
* `Wildcard Encode (Inspire)`: The combination node of [ImpactWildcardEncode](https://github.com/ltdrdata/ComfyUI-extension-tutorials/blob/Main/ComfyUI-Impact-Pack/tutorial/ImpactWildcard.md) and BlenderNeko's [CLIP Text Encode (Advanced)](https://github.com/BlenderNeko/ComfyUI_ADV_CLIP_emb).
* To use this node, you need both the [Impact Pack](https://github.com/ltdrdata/ComfyUI-Impact-Pack) and the [Advanced CLIP Text Encode]((https://github.com/BlenderNeko/ComfyUI_ADV_CLIP_emb)) extensions.
Expand Down
2 changes: 1 addition & 1 deletion __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import importlib

print(f"### Loading: ComfyUI-Inspire-Pack (V0.53)")
print(f"### Loading: ComfyUI-Inspire-Pack (V0.54)")

node_list = [
"lora_block_weight",
Expand Down
74 changes: 62 additions & 12 deletions inspire/inspire_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,10 @@ def control_seed(v):

def prompt_seed_update(json_data):
try:
seed_widget_map = json_data['extra_data']['extra_pnginfo']['workflow']['seed_widgets']
except:
widget_idx_map = json_data['extra_data']['extra_pnginfo']['workflow']['widget_idx_map']
except Exception:
return None

seed_widget_map = json_data['extra_data']['extra_pnginfo']['workflow']['seed_widgets']
value = None
mode = None
node = None
Expand Down Expand Up @@ -132,7 +131,7 @@ def prompt_seed_update(json_data):
if isinstance(v2, str) and '$GlobalSeed.value$' in v2:
v['inputs'][k2] = v2.replace('$GlobalSeed.value$', str(value))

if k not in seed_widget_map:
if k not in widget_idx_map or ('seed' not in widget_idx_map[k] and 'noise_seed' not in widget_idx_map[k]):
continue

if 'seed' in v['inputs']:
Expand All @@ -156,7 +155,7 @@ def prompt_seed_update(json_data):

def workflow_seed_update(json_data):
nodes = json_data['extra_data']['extra_pnginfo']['workflow']['nodes']
seed_widget_map = json_data['extra_data']['extra_pnginfo']['workflow']['seed_widgets']
widget_idx_map = json_data['extra_data']['extra_pnginfo']['workflow']['widget_idx_map']
prompt = json_data['prompt']

updated_seed_map = {}
Expand All @@ -167,18 +166,67 @@ def workflow_seed_update(json_data):
if node['type'] == 'GlobalSeed //Inspire':
value = prompt[node_id]['inputs']['value']
node['widgets_values'][0] = value
elif node_id in seed_widget_map:
widget_idx = seed_widget_map[node_id]

elif node_id in widget_idx_map:
widget_idx = None
seed = None
if 'noise_seed' in prompt[node_id]['inputs']:
seed = prompt[node_id]['inputs']['noise_seed']
else:
widget_idx = widget_idx_map[node_id].get('noise_seed')
elif 'seed' in prompt[node_id]['inputs']:
seed = prompt[node_id]['inputs']['seed']
widget_idx = widget_idx_map[node_id].get('seed')

if widget_idx is not None:
node['widgets_values'][widget_idx] = seed
updated_seed_map[node_id] = seed

server.PromptServer.instance.send_sync("inspire-global-seed", {"value": value, "seed_map": updated_seed_map})


def prompt_sampler_update(json_data):
try:
widget_idx_map = json_data['extra_data']['extra_pnginfo']['workflow']['widget_idx_map']
except Exception:
return None

nodes = json_data['extra_data']['extra_pnginfo']['workflow']['nodes']
prompt = json_data['prompt']

sampler_name = None
scheduler = None

node['widgets_values'][widget_idx] = seed
updated_seed_map[node_id] = seed
updated_nodes = set()

server.PromptServer.instance.send_sync("inspire-global-seed", {"id": node_id, "value": value, "seed_map": updated_seed_map})
for v in prompt.values():
cls = v.get('class_type')
if cls == 'GlobalSampler //Inspire':
sampler_name = v['inputs']['sampler_name']
scheduler = v['inputs']['scheduler']

if sampler_name is not None:
for k, v in json_data['prompt'].items():
if v.get('class_type') == 'GlobalSampler //Inspire':
continue

if ('sampler_name' in v['inputs'] and 'scheduler' in v['inputs'] and
isinstance(v['inputs']['sampler_name'], str) and 'scheduler' in v['inputs']):
v['inputs']['sampler_name'] = sampler_name
v['inputs']['scheduler'] = scheduler
server.PromptServer.instance.send_sync("inspire-node-feedback", {"node_id": k, "widget_name": 'sampler_name', "type": "text", "data": sampler_name})
server.PromptServer.instance.send_sync("inspire-node-feedback", {"node_id": k, "widget_name": 'scheduler', "type": "text", "data": scheduler})

updated_nodes.add(k)

for node in nodes:
node_id = str(node['id'])

if node_id in prompt and node_id in widget_idx_map:
sampler_widget_idx = widget_idx_map[node_id].get('sampler_name')
scheduler_widget_idx = widget_idx_map[node_id].get('scheduler')
if sampler_widget_idx is not None:
node['widgets_values'][sampler_widget_idx] = sampler_name
if scheduler_widget_idx is not None:
node['widgets_values'][scheduler_widget_idx] = scheduler


def workflow_loadimage_update(json_data):
Expand Down Expand Up @@ -240,6 +288,8 @@ def onprompt(json_data):
if is_changed:
workflow_seed_update(json_data)

prompt_sampler_update(json_data)

workflow_loadimage_update(json_data)
populate_wildcards(json_data)

Expand Down
23 changes: 23 additions & 0 deletions inspire/prompt_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,27 @@ def doit(self, **kwargs):
return {}


class GlobalSampler:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"sampler_name": (comfy.samplers.KSampler.SAMPLERS, ),
"scheduler": (comfy.samplers.KSampler.SCHEDULERS, ),
}
}

RETURN_TYPES = ()
FUNCTION = "doit"

CATEGORY = "InspirePack/Prompt"

OUTPUT_NODE = True

def doit(self, **kwargs):
return {}


class BindImageListPromptList:
@classmethod
def INPUT_TYPES(s):
Expand Down Expand Up @@ -599,6 +620,7 @@ def encode(self, clip, text, strength, add_weight):
"ZipPrompt //Inspire": ZipPrompt,
"PromptExtractor //Inspire": PromptExtractor,
"GlobalSeed //Inspire": GlobalSeed,
"GlobalSampler //Inspire": GlobalSampler,
"BindImageListPromptList //Inspire": BindImageListPromptList,
"WildcardEncode //Inspire": WildcardEncodeInspire,
"PromptBuilder //Inspire": PromptBuilder,
Expand All @@ -614,6 +636,7 @@ def encode(self, clip, text, strength, add_weight):
"ZipPrompt //Inspire": "Zip Prompt (Inspire)",
"PromptExtractor //Inspire": "Prompt Extractor (Inspire)",
"GlobalSeed //Inspire": "Global Seed (Inspire)",
"GlobalSampler //Inspire": "Global Sampler (Inspire)",
"BindImageListPromptList //Inspire": "Bind [ImageList, PromptList] (Inspire)",
"WildcardEncode //Inspire": "Wildcard Encode (Inspire)",
"PromptBuilder //Inspire": "Prompt Builder (Inspire)",
Expand Down
30 changes: 29 additions & 1 deletion js/prompt.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,32 @@ app.registerExtension({
});
}
}
});
});




const original_queuePrompt = api.queuePrompt;
async function queuePrompt_with_widget_idxs(number, { output, workflow }) {
workflow.widget_idx_map = {};

for(let i in app.graph._nodes_by_id) {
let widgets = app.graph._nodes_by_id[i].widgets;
if(widgets) {
for(let j in widgets) {
if(['seed', 'noise_seed', 'sampler_name', 'scheduler'].includes(widgets[j].name)
&& widgets[j].type != 'converted-widget') {
if(workflow.widget_idx_map[i] == undefined) {
workflow.widget_idx_map[i] = {};
}

workflow.widget_idx_map[i][widgets[j].name] = parseInt(j);
}
}
}
}

return await original_queuePrompt.call(api, number, { output, workflow });
}

api.queuePrompt = queuePrompt_with_widget_idxs;
56 changes: 18 additions & 38 deletions js/seed.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,24 @@ 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');
const last_w = node.widgets.find((w) => w.name == 'last_seed');
last_w.value = w.value;
w.value = event.detail.value;
}
}
else
if(node.widgets) {
const w = node.widgets.find((w) => (w.name == 'seed' || w.name == 'noise_seed') && w.type == 'number');
if(w && event.detail.seed_map[node.id] != undefined) {
w.value = event.detail.seed_map[node.id];
}
}
}
}

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 app.graph._nodes_by_id) {
let widgets = app.graph._nodes_by_id[i].widgets;
if(widgets) {
for(let j in widgets) {
if((widgets[j].name == 'seed' || widgets[j].name == 'noise_seed') && widgets[j].type != 'converted-widget')
workflow.seed_widgets[i] = parseInt(j);
}
}
let node = nodes[i];

if(node.type == 'GlobalSeed //Inspire') {
if(node.widgets) {
const w = node.widgets.find((w) => w.name == 'value');
const last_w = node.widgets.find((w) => w.name == 'last_seed');
last_w.value = w.value;
w.value = event.detail.value;
}
}
else
if(node.widgets) {
const w = node.widgets.find((w) => (w.name == 'seed' || w.name == 'noise_seed') && w.type == 'number');
if(w && event.detail.seed_map[node.id] != undefined) {
w.value = event.detail.seed_map[node.id];
}
}
}

return await original_queuePrompt.call(api, number, { output, workflow });
}

api.queuePrompt = queuePrompt_with_seed;
api.addEventListener("inspire-global-seed", globalSeedHandler);

0 comments on commit fb7ebc7

Please sign in to comment.