Files
TakkunRed-ComfyUI-Metadata-…/Metadata_Inspector.py
T
2026-04-04 23:18:58 +09:00

147 lines
5.1 KiB
Python

import json
import os
import datetime
import re
from PIL import Image, ExifTags
import numpy as np
from nodes import LoadImage
from folder_paths import get_annotated_filepath
class LoadImageWithFileName(LoadImage):
@classmethod
def INPUT_TYPES(s):
return super().INPUT_TYPES()
RETURN_TYPES = ("IMAGE", "MASK", "STRING")
RETURN_NAMES = ("image", "mask", "image_name")
FUNCTION = "load_image_info"
CATEGORY = "image"
def load_image_info(self, image):
img, mask = super().load_image(image)
return (img, mask, image)
class ExtractPngMetadata:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image_name": ("STRING", {"default": "example.png"}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("metadata_json",)
FUNCTION = "extract"
CATEGORY = "image/metadata"
def extract(self, image_name):
if os.path.isabs(image_name):
image_path = image_name
else:
image_path = get_annotated_filepath(image_name)
all_metadata = {}
def json_serializable(obj):
try:
if isinstance(obj, (datetime.date, datetime.datetime)):
return obj.isoformat()
if isinstance(obj, bytes):
try:
s = obj.decode('utf-8', errors="replace").strip('\x00')
except:
s = obj.decode('cp932', errors="replace").strip('\x00')
return re.sub(r'^(ASCII|UNICODE|JIS|UNDEFINED)\x00*', '', s).strip()
return str(obj)
except:
return f"<Unserializable>"
def try_parse_json(value):
val_str = value if isinstance(value, str) else json_serializable(value)
stripped = val_str.strip()
json_match = re.search(r'(\{.*\}|\[.*\])', stripped, re.DOTALL)
if json_match:
try:
parsed = json.loads(json_match.group(0))
if isinstance(parsed, str):
return try_parse_json(parsed)
return parsed
except:
pass
if "Steps:" in stripped and "Seed:" in stripped:
return [line.strip() for line in stripped.split('\n') if line.strip()]
return val_str
if os.path.exists(image_path):
stat = os.stat(image_path)
all_metadata["fileinfo"] = {
"filename": os.path.basename(image_path),
"date": datetime.datetime.fromtimestamp(stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S'),
"size": f"{stat.st_size / 1024:.2f} KB",
}
with Image.open(image_path) as img:
all_metadata["fileinfo"]["resolution"] = f"{img.width}x{img.height}"
for key, value in img.info.items():
if key == "exif": continue
all_metadata[key] = try_parse_json(value)
try:
exif_obj = img.getexif()
if exif_obj:
exif_results = {}
for tag, value in exif_obj.items():
tag_name = ExifTags.TAGS.get(tag, f"Tag_{tag}")
exif_results[f"{tag_name} (ID: {tag})"] = try_parse_json(value)
exif_ifd = exif_obj.get_ifd(0x8769)
if exif_ifd:
for tag, value in exif_ifd.items():
tag_name = ExifTags.TAGS.get(tag, f"ExifIFD_Tag_{tag}")
exif_results[f"{tag_name} (ID: {tag})"] = try_parse_json(value)
all_metadata["EXIF_Full_Data"] = exif_results
except Exception as e:
all_metadata["EXIF_Error"] = f"EXIF Deep Scan failed: {str(e)}"
else:
all_metadata = {"error": "File not found"}
return (json.dumps(all_metadata, default=json_serializable),)
class JsonVisualizer:
@classmethod
def INPUT_TYPES(s):
return {"required": {"json_string": ("STRING", {"forceInput": True})}}
RETURN_TYPES = ("STRING",)
FUNCTION = "visualize"
CATEGORY = "ui/visualizer"
OUTPUT_NODE = True
def visualize(self, json_string):
import json
try:
data = json.loads(json_string)
except:
data = {"error": "Invalid JSON"}
return {"ui": {"json_data": [data]}, "result": (json_string,)}
NODE_CLASS_MAPPINGS = {
"LoadImageWithFileName": LoadImageWithFileName,
"ExtractPngMetadata": ExtractPngMetadata,
"JsonVisualizer": JsonVisualizer
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LoadImageWithFileName": "Load Image w/ Name",
"ExtractPngMetadata": "Extract PNG Metadata",
"JsonVisualizer": "JSON Tree Viewer"
}