fix(gallery): enhance metadata parsing for modern ComfyUI workflows
- Add support for new ComfyUI node types in gallery metadata extraction: - CFGGuider for CFG scale values - BasicScheduler for steps parameter - KSamplerSelect for sampler selection - RandomNoise/SeedHistory for seed values - Update both main gallery (/gallery) and metadata viewer (/metadata.html) - Fix metadata display issues where technical parameters showed as "Unknown" - Add comprehensive test image and debug tools for validation - Maintain backward compatibility with legacy KSampler nodes This resolves metadata parsing issues with modern ComfyUI workflows that use the newer node architecture for sampling and generation.
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Debug script to extract and analyze PNG metadata from the test image
|
||||
to understand what should be displayed in the gallery views.
|
||||
"""
|
||||
|
||||
import json
|
||||
from PIL import Image
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
|
||||
def extract_png_metadata(image_path):
|
||||
"""Extract PNG metadata chunks from an image file."""
|
||||
try:
|
||||
with Image.open(image_path) as img:
|
||||
metadata = {}
|
||||
|
||||
# Extract text chunks
|
||||
if hasattr(img, 'text'):
|
||||
for key, value in img.text.items():
|
||||
metadata[key] = value
|
||||
print(f"Found text chunk: {key} = {value[:100]}{'...' if len(value) > 100 else ''}")
|
||||
|
||||
# Try to extract PNG info
|
||||
if hasattr(img, 'info'):
|
||||
print(f"\nImage info keys: {list(img.info.keys())}")
|
||||
for key, value in img.info.items():
|
||||
if isinstance(value, str):
|
||||
print(f"Info {key}: {value[:100]}{'...' if len(value) > 100 else ''}")
|
||||
|
||||
return metadata
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error reading PNG metadata: {e}")
|
||||
return {}
|
||||
|
||||
def parse_comfyui_data(metadata):
|
||||
"""Parse ComfyUI workflow and prompt data from PNG metadata."""
|
||||
workflow_data = None
|
||||
prompt_data = None
|
||||
|
||||
# Look for workflow data
|
||||
workflow_fields = ['workflow', 'Workflow', 'comfy', 'ComfyUI']
|
||||
for field in workflow_fields:
|
||||
if field in metadata:
|
||||
try:
|
||||
workflow_data = json.loads(metadata[field])
|
||||
print(f"\nFound workflow data in field '{field}'")
|
||||
if isinstance(workflow_data, dict) and 'nodes' in workflow_data:
|
||||
print(f"Workflow has {len(workflow_data['nodes'])} nodes")
|
||||
break
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"Failed to parse workflow field '{field}': {e}")
|
||||
|
||||
# Look for prompt data
|
||||
prompt_fields = ['prompt', 'Prompt', 'parameters', 'Parameters']
|
||||
for field in prompt_fields:
|
||||
if field in metadata:
|
||||
try:
|
||||
# Clean up NaN values that might break JSON parsing
|
||||
cleaned_json = metadata[field]
|
||||
cleaned_json = cleaned_json.replace(': NaN', ': null')
|
||||
cleaned_json = cleaned_json.replace(':NaN', ':null')
|
||||
cleaned_json = cleaned_json.replace('NaN', 'null')
|
||||
|
||||
prompt_data = json.loads(cleaned_json)
|
||||
print(f"\nFound prompt data in field '{field}'")
|
||||
if isinstance(prompt_data, dict):
|
||||
print(f"Prompt data has {len(prompt_data)} nodes")
|
||||
break
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"Failed to parse prompt field '{field}': {e}")
|
||||
|
||||
return workflow_data, prompt_data
|
||||
|
||||
def extract_generation_params(workflow_data, prompt_data):
|
||||
"""Extract generation parameters from ComfyUI data."""
|
||||
params = {
|
||||
'checkpoint': 'Unknown',
|
||||
'positive_prompt': 'No prompt found',
|
||||
'negative_prompt': 'No negative prompt found',
|
||||
'steps': 'Unknown',
|
||||
'cfg_scale': 'Unknown',
|
||||
'sampler': 'Unknown',
|
||||
'seed': 'Unknown'
|
||||
}
|
||||
|
||||
# Parse prompt data first (more reliable)
|
||||
if prompt_data:
|
||||
print("\nAnalyzing prompt data...")
|
||||
for node_id, node in prompt_data.items():
|
||||
node_type = node.get('class_type', '')
|
||||
inputs = node.get('inputs', {})
|
||||
|
||||
print(f" Node {node_id}: {node_type}")
|
||||
if inputs:
|
||||
print(f" Inputs: {list(inputs.keys())}")
|
||||
|
||||
if node_type == 'CheckpointLoaderSimple' and 'ckpt_name' in inputs:
|
||||
params['checkpoint'] = inputs['ckpt_name']
|
||||
print(f" Found checkpoint: {params['checkpoint']}")
|
||||
|
||||
elif node_type == 'PromptManager' and 'text' in inputs:
|
||||
params['positive_prompt'] = inputs['text']
|
||||
print(f" Found PromptManager text: {params['positive_prompt'][:100]}...")
|
||||
|
||||
elif node_type == 'CLIPTextEncode' and 'text' in inputs:
|
||||
text = inputs['text'].lower()
|
||||
if any(neg_word in text for neg_word in ['bad anatomy', 'unfinished', 'censored', 'negative', 'embedding:']):
|
||||
params['negative_prompt'] = inputs['text']
|
||||
print(f" Found negative prompt: {params['negative_prompt'][:100]}...")
|
||||
elif params['positive_prompt'] == 'No prompt found':
|
||||
params['positive_prompt'] = inputs['text']
|
||||
print(f" Found positive prompt: {params['positive_prompt'][:100]}...")
|
||||
|
||||
elif 'sampler' in node_type.lower() or node_type in ['KSampler', 'SamplerCustomAdvanced', 'DetailDaemonSamplerNode']:
|
||||
if 'seed' in inputs:
|
||||
params['seed'] = inputs['seed']
|
||||
if 'steps' in inputs:
|
||||
params['steps'] = inputs['steps']
|
||||
if 'cfg' in inputs:
|
||||
params['cfg_scale'] = inputs['cfg']
|
||||
if 'sampler_name' in inputs:
|
||||
params['sampler'] = inputs['sampler_name']
|
||||
if 'scheduler' in inputs:
|
||||
params['scheduler'] = inputs['scheduler']
|
||||
print(f" Found {node_type}: seed={inputs.get('seed', 'N/A')}, steps={inputs.get('steps', 'N/A')}, cfg={inputs.get('cfg', 'N/A')}, sampler={inputs.get('sampler_name', 'N/A')}")
|
||||
|
||||
# Also check for BasicScheduler which might have steps
|
||||
elif node_type == 'BasicScheduler' and 'steps' in inputs:
|
||||
if params['steps'] == 'Unknown':
|
||||
params['steps'] = inputs['steps']
|
||||
print(f" Found BasicScheduler with steps: {params['steps']}")
|
||||
|
||||
# Check for CFG values
|
||||
elif node_type == 'CFGGuider' and 'cfg' in inputs:
|
||||
if params['cfg_scale'] == 'Unknown':
|
||||
params['cfg_scale'] = inputs['cfg']
|
||||
print(f" Found CFGGuider with cfg: {params['cfg_scale']}")
|
||||
|
||||
# Check for seed in various nodes
|
||||
elif 'seed' in inputs:
|
||||
if params['seed'] == 'Unknown':
|
||||
params['seed'] = inputs['seed']
|
||||
print(f" Found {node_type} with seed: {params['seed']}")
|
||||
elif 'noise_seed' in inputs:
|
||||
if params['seed'] == 'Unknown':
|
||||
params['seed'] = inputs['noise_seed']
|
||||
print(f" Found {node_type} with noise_seed: {params['seed']}")
|
||||
|
||||
# Check for noise/seed generators
|
||||
elif node_type in ['RandomNoise', 'EmptyLatentImage'] and 'seed' in inputs:
|
||||
if params['seed'] == 'Unknown':
|
||||
params['seed'] = inputs['seed']
|
||||
print(f" Found {node_type} with seed: {params['seed']}")
|
||||
|
||||
# Fallback to workflow data if needed
|
||||
if workflow_data and workflow_data.get('nodes'):
|
||||
print("\nAnalyzing workflow data...")
|
||||
for node in workflow_data['nodes']:
|
||||
node_type = node.get('type', '')
|
||||
widgets = node.get('widgets_values', [])
|
||||
|
||||
if node_type == 'CheckpointLoaderSimple' and widgets:
|
||||
if params['checkpoint'] == 'Unknown':
|
||||
params['checkpoint'] = widgets[0]
|
||||
print(f" Found checkpoint in workflow: {params['checkpoint']}")
|
||||
|
||||
elif node_type == 'PromptManager' and widgets:
|
||||
if params['positive_prompt'] == 'No prompt found':
|
||||
params['positive_prompt'] = widgets[0]
|
||||
print(f" Found PromptManager in workflow: {params['positive_prompt'][:100]}...")
|
||||
|
||||
elif node_type == 'KSampler' and len(widgets) >= 4:
|
||||
if params['seed'] == 'Unknown':
|
||||
params['seed'] = widgets[0]
|
||||
params['steps'] = widgets[1]
|
||||
params['cfg_scale'] = widgets[2]
|
||||
params['sampler'] = widgets[3]
|
||||
print(f" Found KSampler in workflow: seed={params['seed']}, steps={params['steps']}")
|
||||
|
||||
return params
|
||||
|
||||
def main():
|
||||
print("=== PNG Metadata Debug Tool ===\n")
|
||||
|
||||
image_path = "test_metadata_file.png"
|
||||
print(f"Analyzing: {image_path}")
|
||||
|
||||
# Extract raw metadata
|
||||
metadata = extract_png_metadata(image_path)
|
||||
print(f"\nFound {len(metadata)} metadata fields:")
|
||||
for key in metadata.keys():
|
||||
print(f" - {key}")
|
||||
|
||||
# Parse ComfyUI data
|
||||
workflow_data, prompt_data = parse_comfyui_data(metadata)
|
||||
|
||||
# Extract generation parameters
|
||||
params = extract_generation_params(workflow_data, prompt_data)
|
||||
|
||||
print("\n=== EXTRACTED PARAMETERS ===")
|
||||
for key, value in params.items():
|
||||
print(f"{key}: {value}")
|
||||
|
||||
print("\n=== RAW METADATA FIELDS ===")
|
||||
for key, value in metadata.items():
|
||||
if len(value) < 200:
|
||||
print(f"{key}: {value}")
|
||||
else:
|
||||
print(f"{key}: {value[:200]}... (truncated)")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 85 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
@@ -0,0 +1,253 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Metadata Parsing Test</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; background: #1a1a1a; color: #fff; }
|
||||
.result { background: #2a2a2a; padding: 20px; margin: 10px 0; border-radius: 8px; }
|
||||
.error { background: #3a1a1a; border: 1px solid #ff6b6b; }
|
||||
.success { background: #1a3a1a; border: 1px solid #6bcf7f; }
|
||||
input[type="file"] { margin: 20px 0; padding: 10px; background: #3a3a3a; color: #fff; border: none; border-radius: 4px; }
|
||||
pre { white-space: pre-wrap; word-wrap: break-word; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>PNG Metadata Parsing Test</h1>
|
||||
<p>Select the test_metadata_file.png to verify metadata extraction works correctly.</p>
|
||||
|
||||
<input type="file" id="fileInput" accept=".png" />
|
||||
|
||||
<div id="results"></div>
|
||||
|
||||
<script>
|
||||
// Copy the updated parsing functions from metadata.html
|
||||
async function parsePNGMetadata(arrayBuffer) {
|
||||
const dataView = new DataView(arrayBuffer);
|
||||
let offset = 8; // Skip PNG signature
|
||||
const metadata = {};
|
||||
|
||||
while (offset < arrayBuffer.byteLength - 8) {
|
||||
const length = dataView.getUint32(offset);
|
||||
const type = new TextDecoder().decode(arrayBuffer.slice(offset + 4, offset + 8));
|
||||
|
||||
if (type === 'tEXt' || type === 'iTXt' || type === 'zTXt') {
|
||||
const chunkData = arrayBuffer.slice(offset + 8, offset + 8 + length);
|
||||
let text;
|
||||
|
||||
if (type === 'tEXt') {
|
||||
text = new TextDecoder().decode(chunkData);
|
||||
} else if (type === 'iTXt') {
|
||||
const textData = new TextDecoder().decode(chunkData);
|
||||
const parts = textData.split('\0');
|
||||
if (parts.length >= 5) {
|
||||
metadata[parts[0]] = parts[4];
|
||||
}
|
||||
text = textData;
|
||||
} else if (type === 'zTXt') {
|
||||
text = new TextDecoder().decode(chunkData);
|
||||
}
|
||||
|
||||
const nullIndex = text.indexOf('\0');
|
||||
if (nullIndex !== -1) {
|
||||
const key = text.substring(0, nullIndex);
|
||||
const value = text.substring(nullIndex + 1);
|
||||
metadata[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
offset += 8 + length + 4;
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
function extractComfyUIData(metadata) {
|
||||
let workflowData = null;
|
||||
let promptData = null;
|
||||
|
||||
const workflowFields = ['workflow', 'Workflow', 'comfy', 'ComfyUI'];
|
||||
const promptFields = ['prompt', 'Prompt', 'parameters', 'Parameters'];
|
||||
|
||||
for (const field of workflowFields) {
|
||||
if (metadata[field]) {
|
||||
try {
|
||||
let cleanedJson = metadata[field];
|
||||
cleanedJson = cleanedJson.replace(/:\s*NaN\b/g, ': null');
|
||||
cleanedJson = cleanedJson.replace(/\bNaN\b/g, 'null');
|
||||
|
||||
workflowData = JSON.parse(cleanedJson);
|
||||
break;
|
||||
} catch (e) {
|
||||
console.log('Failed to parse workflow field:', field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const field of promptFields) {
|
||||
if (metadata[field]) {
|
||||
try {
|
||||
let cleanedJson = metadata[field];
|
||||
cleanedJson = cleanedJson.replace(/:\s*NaN\b/g, ': null');
|
||||
cleanedJson = cleanedJson.replace(/\bNaN\b/g, 'null');
|
||||
|
||||
promptData = JSON.parse(cleanedJson);
|
||||
break;
|
||||
} catch (e) {
|
||||
console.log('Failed to parse prompt field:', field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { workflow: workflowData, prompt: promptData };
|
||||
}
|
||||
|
||||
function parseWorkflowData(comfyData) {
|
||||
let checkpoint = 'Unknown';
|
||||
let positivePrompt = 'No prompt found';
|
||||
let negativePrompt = 'No negative prompt found';
|
||||
let steps = 'Unknown';
|
||||
let cfgScale = 'Unknown';
|
||||
let sampler = 'Unknown';
|
||||
let seed = 'Unknown';
|
||||
|
||||
// Parse prompt data first (more reliable for actual generation parameters)
|
||||
if (comfyData.prompt) {
|
||||
console.log('Parsing prompt data...');
|
||||
const promptNodes = comfyData.prompt;
|
||||
|
||||
for (const nodeId in promptNodes) {
|
||||
const node = promptNodes[nodeId];
|
||||
console.log(`Node ${nodeId}: ${node.class_type}`);
|
||||
|
||||
// Checkpoint
|
||||
if (node.class_type === 'CheckpointLoaderSimple' && node.inputs) {
|
||||
checkpoint = node.inputs.ckpt_name || checkpoint;
|
||||
}
|
||||
|
||||
// Prompts
|
||||
if (node.class_type === 'PromptManager' && node.inputs && node.inputs.text) {
|
||||
positivePrompt = node.inputs.text;
|
||||
}
|
||||
|
||||
if (node.class_type === 'CLIPTextEncode' && node.inputs && node.inputs.text) {
|
||||
const text = node.inputs.text.toLowerCase();
|
||||
if (text.includes('bad anatomy') || text.includes('unfinished') ||
|
||||
text.includes('censored') || text.includes('weird anatomy') ||
|
||||
text.includes('negative') || text.includes('embedding:')) {
|
||||
negativePrompt = node.inputs.text;
|
||||
} else if (positivePrompt === 'No prompt found') {
|
||||
positivePrompt = node.inputs.text;
|
||||
}
|
||||
}
|
||||
|
||||
// Sampling parameters - check multiple sampler types
|
||||
if ((node.class_type === 'KSampler' ||
|
||||
node.class_type === 'KSamplerAdvanced' ||
|
||||
node.class_type === 'SamplerCustom' ||
|
||||
node.class_type === 'SamplerCustomAdvanced') && node.inputs) {
|
||||
seed = node.inputs.seed || seed;
|
||||
steps = node.inputs.steps || steps;
|
||||
cfgScale = node.inputs.cfg || cfgScale;
|
||||
sampler = node.inputs.sampler_name || sampler;
|
||||
}
|
||||
|
||||
// New ComfyUI node types for modern workflows
|
||||
if (node.class_type === 'CFGGuider' && node.inputs && node.inputs.cfg) {
|
||||
cfgScale = node.inputs.cfg;
|
||||
}
|
||||
|
||||
if (node.class_type === 'BasicScheduler' && node.inputs && node.inputs.steps) {
|
||||
steps = node.inputs.steps;
|
||||
}
|
||||
|
||||
if (node.class_type === 'KSamplerSelect' && node.inputs && node.inputs.sampler_name) {
|
||||
sampler = node.inputs.sampler_name;
|
||||
}
|
||||
|
||||
if ((node.class_type === 'RandomNoise' || node.class_type === 'SeedHistory') && node.inputs) {
|
||||
if (node.inputs.noise_seed) {
|
||||
seed = Array.isArray(node.inputs.noise_seed) ? node.inputs.noise_seed[0] : node.inputs.noise_seed;
|
||||
} else if (node.inputs.seed) {
|
||||
seed = node.inputs.seed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
checkpoint,
|
||||
positivePrompt,
|
||||
negativePrompt,
|
||||
steps,
|
||||
cfgScale,
|
||||
sampler,
|
||||
seed
|
||||
};
|
||||
}
|
||||
|
||||
document.getElementById('fileInput').addEventListener('change', async (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
const resultsDiv = document.getElementById('results');
|
||||
resultsDiv.innerHTML = '<div class="result">Processing...</div>';
|
||||
|
||||
try {
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
const metadata = await parsePNGMetadata(arrayBuffer);
|
||||
const comfyData = extractComfyUIData(metadata);
|
||||
const params = parseWorkflowData(comfyData);
|
||||
|
||||
const expectedParams = {
|
||||
checkpoint: 'illustrious/realism/Realism_Illustrious_BSY_PATREON_FP16_V4.5_C27.safetensors',
|
||||
steps: 35,
|
||||
cfgScale: 4.0,
|
||||
sampler: 'euler_ancestral',
|
||||
positivePrompt: 'An amateur candid photo of kiko9natsumi, looking at the viewer.',
|
||||
negativePrompt: 'embedding:'
|
||||
};
|
||||
|
||||
let html = '<div class="result success"><h2>✅ Metadata Extraction Test Results</h2>';
|
||||
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
const expected = expectedParams[key];
|
||||
let status = '✅';
|
||||
let statusText = 'OK';
|
||||
|
||||
if (expected && (key === 'negativePrompt' ? !String(value).includes(String(expected)) : value !== expected)) {
|
||||
status = '❌';
|
||||
statusText = `Expected: ${expected}`;
|
||||
} else if (value === 'Unknown' || value === 'No prompt found') {
|
||||
status = '⚠️';
|
||||
statusText = 'Not found';
|
||||
}
|
||||
|
||||
html += `
|
||||
<div style="margin: 10px 0; padding: 10px; background: rgba(255,255,255,0.1); border-radius: 4px;">
|
||||
<strong>${key}:</strong> ${status} ${String(value).substring(0, 100)}${String(value).length > 100 ? '...' : ''}
|
||||
<br><small style="color: #aaa;">${statusText}</small>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
html += `
|
||||
<div class="result">
|
||||
<h3>Raw Metadata Fields Found:</h3>
|
||||
<pre>${Object.keys(metadata).join(', ')}</pre>
|
||||
</div>
|
||||
`;
|
||||
|
||||
resultsDiv.innerHTML = html;
|
||||
|
||||
} catch (error) {
|
||||
resultsDiv.innerHTML = `<div class="result error"><h2>❌ Error</h2><p>${error.message}</p></div>`;
|
||||
console.error('Error processing image:', error);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -2014,6 +2014,28 @@ Seed: ${this.currentMetadata.seed || 'Unknown'}`;
|
||||
sampler = node.inputs.sampler_name || sampler;
|
||||
seed = node.inputs.seed || node.inputs.noise_seed || seed;
|
||||
}
|
||||
|
||||
// New ComfyUI node types for modern workflows
|
||||
if (node.class_type === 'CFGGuider' && node.inputs && node.inputs.cfg) {
|
||||
cfgScale = node.inputs.cfg;
|
||||
}
|
||||
|
||||
if (node.class_type === 'BasicScheduler' && node.inputs && node.inputs.steps) {
|
||||
steps = node.inputs.steps;
|
||||
}
|
||||
|
||||
if (node.class_type === 'KSamplerSelect' && node.inputs && node.inputs.sampler_name) {
|
||||
sampler = node.inputs.sampler_name;
|
||||
}
|
||||
|
||||
if ((node.class_type === 'RandomNoise' || node.class_type === 'SeedHistory') && node.inputs) {
|
||||
if (node.inputs.noise_seed) {
|
||||
// RandomNoise nodes might reference other nodes, resolve if needed
|
||||
seed = Array.isArray(node.inputs.noise_seed) ? node.inputs.noise_seed[0] : node.inputs.noise_seed;
|
||||
} else if (node.inputs.seed) {
|
||||
seed = node.inputs.seed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process collected text nodes to identify positive/negative prompts
|
||||
|
||||
+60
-4
@@ -235,13 +235,38 @@ Seed: ${currentWorkflowData.seed || 'Unknown'}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Sampling parameters
|
||||
if (node.class_type === 'KSampler' && node.inputs) {
|
||||
// Sampling parameters - check multiple sampler types
|
||||
if ((node.class_type === 'KSampler' ||
|
||||
node.class_type === 'KSamplerAdvanced' ||
|
||||
node.class_type === 'SamplerCustom' ||
|
||||
node.class_type === 'SamplerCustomAdvanced') && node.inputs) {
|
||||
seed = node.inputs.seed || seed;
|
||||
steps = node.inputs.steps || steps;
|
||||
cfgScale = node.inputs.cfg || cfgScale;
|
||||
sampler = node.inputs.sampler_name || sampler;
|
||||
}
|
||||
|
||||
// New ComfyUI node types for modern workflows
|
||||
if (node.class_type === 'CFGGuider' && node.inputs && node.inputs.cfg) {
|
||||
cfgScale = node.inputs.cfg;
|
||||
}
|
||||
|
||||
if (node.class_type === 'BasicScheduler' && node.inputs && node.inputs.steps) {
|
||||
steps = node.inputs.steps;
|
||||
}
|
||||
|
||||
if (node.class_type === 'KSamplerSelect' && node.inputs && node.inputs.sampler_name) {
|
||||
sampler = node.inputs.sampler_name;
|
||||
}
|
||||
|
||||
if ((node.class_type === 'RandomNoise' || node.class_type === 'SeedHistory') && node.inputs) {
|
||||
if (node.inputs.noise_seed) {
|
||||
// RandomNoise nodes might reference other nodes, resolve if needed
|
||||
seed = Array.isArray(node.inputs.noise_seed) ? node.inputs.noise_seed[0] : node.inputs.noise_seed;
|
||||
} else if (node.inputs.seed) {
|
||||
seed = node.inputs.seed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,10 +305,12 @@ Seed: ${currentWorkflowData.seed || 'Unknown'}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Look for sampler
|
||||
// Look for sampler nodes - check multiple types
|
||||
const samplerNode = nodes.find(node =>
|
||||
node.type === 'KSampler' ||
|
||||
node.type === 'KSamplerAdvanced'
|
||||
node.type === 'KSamplerAdvanced' ||
|
||||
node.type === 'SamplerCustom' ||
|
||||
node.type === 'SamplerCustomAdvanced'
|
||||
);
|
||||
if (samplerNode && samplerNode.widgets_values) {
|
||||
seed = samplerNode.widgets_values[0] || seed;
|
||||
@@ -291,6 +318,35 @@ Seed: ${currentWorkflowData.seed || 'Unknown'}`;
|
||||
cfgScale = samplerNode.widgets_values[2] || cfgScale;
|
||||
sampler = samplerNode.widgets_values[3] || sampler;
|
||||
}
|
||||
|
||||
// Look for newer node types
|
||||
if (steps === 'Unknown') {
|
||||
const schedulerNode = nodes.find(node => node.type === 'BasicScheduler');
|
||||
if (schedulerNode && schedulerNode.widgets_values) {
|
||||
steps = schedulerNode.widgets_values[1]; // steps is usually second parameter
|
||||
}
|
||||
}
|
||||
|
||||
if (cfgScale === 'Unknown') {
|
||||
const cfgNode = nodes.find(node => node.type === 'CFGGuider');
|
||||
if (cfgNode && cfgNode.widgets_values) {
|
||||
cfgScale = cfgNode.widgets_values[0]; // cfg is usually first parameter
|
||||
}
|
||||
}
|
||||
|
||||
if (sampler === 'Unknown') {
|
||||
const samplerSelectNode = nodes.find(node => node.type === 'KSamplerSelect');
|
||||
if (samplerSelectNode && samplerSelectNode.widgets_values) {
|
||||
sampler = samplerSelectNode.widgets_values[0]; // sampler name is usually first parameter
|
||||
}
|
||||
}
|
||||
|
||||
if (seed === 'Unknown') {
|
||||
const noiseNode = nodes.find(node => node.type === 'RandomNoise');
|
||||
if (noiseNode && noiseNode.widgets_values) {
|
||||
seed = noiseNode.widgets_values[0]; // seed is usually first parameter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the HTML
|
||||
|
||||
Reference in New Issue
Block a user