Fix Node Interface Issues - Session 5 Priority 2

- Fix MetaMan Extract Components: Use input connector instead of text field
- Fix MetaMan Embed & Save: Use input connector for metadata_json
- Fix MetaMan Embed & Save: Actually save files to output directory
- Remove image output from save node (OUTPUT_NODE = True)
- Add proper metadata parsing and A1111 format conversion
- Add UI feedback for saved files

Now supports clean workflow chaining:
MetaMan Load Image → MetaMan Extract Components → Individual outputs
MetaMan Load Image → MetaMan Embed & Save (auto-saves with metadata)
This commit is contained in:
PXL8D
2025-08-12 16:44:51 -07:00
parent bc90b37ea2
commit 28d7271ea0
+70 -29
View File
@@ -344,7 +344,7 @@ class MetaManExtractComponents:
def INPUT_TYPES(cls):
return {
"required": {
"metadata_json": ("STRING", {"default": "", "multiline": True})
"metadata_json": ("STRING", {"forceInput": True})
}
}
@@ -412,7 +412,7 @@ class MetaManExtractComponents:
class MetaManEmbedAndSave:
"""
Embed metadata into image and save as PNG with metadata
Embed metadata into image and save as PNG file to output directory
"""
@classmethod
@@ -420,7 +420,7 @@ class MetaManEmbedAndSave:
return {
"required": {
"image": ("IMAGE",),
"metadata_text": ("STRING", {"default": "", "multiline": True}),
"metadata_json": ("STRING", {"forceInput": True}),
},
"optional": {
"filename_prefix": ("STRING", {"default": "MetaMan_converted"})
@@ -428,16 +428,39 @@ class MetaManEmbedAndSave:
}
CATEGORY = "MetaMan"
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("final_image",)
RETURN_TYPES = ()
RETURN_NAMES = ()
FUNCTION = "embed_and_save"
DESCRIPTION = "Embed metadata into image and save as PNG"
OUTPUT_NODE = True
DESCRIPTION = "Embed metadata into image and save as PNG file"
def embed_and_save(self, image, metadata_text, filename_prefix="MetaMan_converted"):
def embed_and_save(self, image, metadata_json, filename_prefix="MetaMan_converted"):
"""
Embed metadata into image and return final image with metadata
Embed metadata into image and save as PNG file to output directory
"""
try:
# Parse metadata JSON to extract target format
if metadata_json and metadata_json.strip():
try:
metadata_data = json.loads(metadata_json)
# Extract target metadata or use universal metadata
if 'target_metadata' in metadata_data:
metadata_text = metadata_data['target_metadata']
print(f"MetaMan Embed & Save: Using target metadata ({len(metadata_text)} chars)")
elif 'metadata' in metadata_data:
# Convert metadata dict to A1111 format
metadata_text = self._format_metadata_for_embedding(metadata_data['metadata'])
print(f"MetaMan Embed & Save: Converted metadata to A1111 format ({len(metadata_text)} chars)")
else:
metadata_text = json.dumps(metadata_data, indent=2)
print(f"MetaMan Embed & Save: Using raw JSON ({len(metadata_text)} chars)")
except Exception as e:
print(f"MetaMan Embed & Save: Error parsing metadata JSON: {e}")
metadata_text = metadata_json # Fallback to raw input
else:
metadata_text = "MetaMan processed image"
print(f"MetaMan Embed & Save: No metadata provided, using default")
# Convert tensor to PIL Image
if isinstance(image, torch.Tensor):
img_tensor = image[0]
@@ -446,43 +469,61 @@ class MetaManEmbedAndSave:
else:
pil_image = image
# Embed metadata in image
result_image = self._embed_metadata_in_image(pil_image, metadata_text)
# Save to ComfyUI output directory
# Save to ComfyUI output directory with metadata
output_dir = folder_paths.get_output_directory()
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{filename_prefix}_{timestamp}.png"
filepath = os.path.join(output_dir, filename)
# Save with metadata
# Create PNG info with metadata
png_info = PngInfo()
png_info.add_text("parameters", metadata_text)
png_info.add_text("metaman_converted", datetime.now().isoformat())
png_info.add_text("metaman_version", "1.0.0")
result_image.save(filepath, "PNG", pnginfo=png_info)
print(f"MetaMan Embed & Save: Saved image with metadata to {filepath}")
# Save image with embedded metadata
pil_image.save(filepath, "PNG", pnginfo=png_info)
# Convert back to tensor
if isinstance(image, torch.Tensor):
import numpy as np
img_array = np.array(result_image).astype(np.float32) / 255.0
output_tensor = torch.from_numpy(img_array).unsqueeze(0)
return (output_tensor,)
print(f"MetaMan Embed & Save: Successfully saved image with metadata to {filepath}")
print(f"MetaMan Embed & Save: File size: {os.path.getsize(filepath)} bytes")
return (result_image,)
return {"ui": {"images": [{"filename": filename, "subfolder": "", "type": "output"}]}}
except Exception as e:
print(f"MetaMan Embed & Save Error: {e}")
return (image,)
return {"ui": {"text": [f"Error: {str(e)}"]}}
def _embed_metadata_in_image(self, image: Image.Image, metadata_text: str) -> Image.Image:
"""Embed metadata into image PNG chunks"""
# Create new image copy
result_image = image.copy() if hasattr(image, 'copy') else image
def _format_metadata_for_embedding(self, metadata: dict) -> str:
"""Convert metadata dict to A1111 format for embedding"""
lines = []
# This is handled in the save process with PngInfo
return result_image
# Positive prompt
if 'prompt' in metadata:
lines.append(metadata['prompt'])
# Negative prompt
if 'negative_prompt' in metadata:
lines.append(f"Negative prompt: {metadata['negative_prompt']}")
# Parameters line
params = []
param_order = ["steps", "sampler", "cfg_scale", "seed", "width", "height", "model_name", "scheduler"]
for param in param_order:
if param in metadata and metadata[param] is not None:
if param == "cfg_scale":
params.append(f"CFG scale: {metadata[param]}")
elif param == "model_name":
params.append(f"Model: {metadata[param]}")
elif param == "width" and "height" in metadata:
params.append(f"Size: {metadata['width']}x{metadata['height']}")
elif param != "height": # Skip height since it's handled with width
params.append(f"{param.title()}: {metadata[param]}")
if params:
lines.append(", ".join(params))
return "\n".join(lines)
class MetaManLoadImage: