cleanup: remove temporary debug files
This commit is contained in:
@@ -1,213 +0,0 @@
|
||||
#!/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()
|
||||
@@ -1,253 +0,0 @@
|
||||
<!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>
|
||||
Reference in New Issue
Block a user