Files
weekii-comfyui-save-image-pro/core/metadata_handler.py
T
wee 235885adfb v3.0.0: 完全重构为 ComfyUI Save Image Pro
- 重构为模块化架构,11个核心模块
- 采用策略模式处理不同图像格式
- 实现SOLID原则,提升代码质量
- 添加性能优化:缓存、并行处理、内存管理
- 完善的错误处理和日志系统
- 全面的单元测试覆盖
- 完整的文档系统
- 保持完全向后兼容
- 项目重命名为 comfyui-save-image-pro
- 迁移到新仓库:https://github.com/weekii/comfyui-save-image-pro
2025-07-06 19:28:18 +08:00

222 lines
7.0 KiB
Python

"""
元数据处理模块
负责处理图像元数据的生成、格式化和缓存。
"""
import json
from typing import Dict, Any, Optional
from datetime import datetime
class MetadataHandler:
"""元数据处理器"""
def __init__(self):
self.cache = {}
self.cache_enabled = True
def prepare_metadata(self, prompt: Dict, extra_pnginfo: Optional[Dict] = None,
positive_text: Optional[str] = None,
negative_text: Optional[str] = None) -> Dict[str, Any]:
"""
准备元数据
Args:
prompt: ComfyUI提示字典
extra_pnginfo: 额外的PNG信息
positive_text: 正面提示文本
negative_text: 负面提示文本
Returns:
格式化的元数据字典
"""
# 生成缓存键
cache_key = self._generate_cache_key(prompt, extra_pnginfo, positive_text, negative_text)
# 检查缓存
if self.cache_enabled and cache_key in self.cache:
return self.cache[cache_key]
metadata = {}
# 添加基本提示信息
if prompt:
metadata['prompt'] = prompt
# 添加额外信息
if extra_pnginfo:
metadata.update(extra_pnginfo)
# 添加文本提示(如果提供)
if positive_text:
metadata['positive_prompt'] = positive_text
if negative_text:
metadata['negative_prompt'] = negative_text
# 添加生成时间戳
metadata['generated_at'] = datetime.now().isoformat()
# 添加插件信息
metadata['generator'] = {
'name': 'Save Image Extended',
'version': '3.0'
}
# 缓存结果
if self.cache_enabled:
self.cache[cache_key] = metadata
return metadata
def _generate_cache_key(self, prompt: Dict, extra_pnginfo: Optional[Dict],
positive_text: Optional[str], negative_text: Optional[str]) -> str:
"""生成缓存键"""
# 使用JSON序列化生成稳定的键
key_data = {
'prompt_id': id(prompt),
'extra_id': id(extra_pnginfo) if extra_pnginfo else None,
'positive': positive_text,
'negative': negative_text
}
return json.dumps(key_data, sort_keys=True)
def format_for_png(self, metadata: Dict[str, Any]) -> Dict[str, str]:
"""
格式化为PNG元数据格式
Args:
metadata: 原始元数据
Returns:
PNG格式的元数据字典
"""
png_metadata = {}
for key, value in metadata.items():
try:
if isinstance(value, (dict, list)):
png_metadata[key] = json.dumps(value, ensure_ascii=False)
else:
png_metadata[key] = str(value)
except (TypeError, ValueError):
png_metadata[key] = str(value)
return png_metadata
def format_for_exif(self, metadata: Dict[str, Any]) -> bytes:
"""
格式化为EXIF元数据格式
Args:
metadata: 原始元数据
Returns:
EXIF字节数据
"""
try:
from PIL import Image
# 创建临时图像来生成EXIF
temp_img = Image.new('RGB', (1, 1))
exif = temp_img.getexif()
# 将关键信息编码到EXIF字段
if 'prompt' in metadata:
# 0x010f: Make字段存储prompt
exif[0x010f] = "Prompt: " + json.dumps(metadata['prompt'], ensure_ascii=False)
if 'workflow' in metadata:
# 0x010e: ImageDescription字段存储workflow
exif[0x010e] = "Workflow: " + json.dumps(metadata['workflow'], ensure_ascii=False)
# 添加生成器信息
if 'generator' in metadata:
# 0x0131: Software字段
generator = metadata['generator']
exif[0x0131] = f"{generator.get('name', 'Unknown')} v{generator.get('version', '1.0')}"
return exif.tobytes()
except Exception as e:
print(f"EXIF元数据生成错误: {e}")
return b''
def extract_parameters(self, metadata: Dict[str, Any]) -> Dict[str, str]:
"""
从元数据中提取关键参数
Args:
metadata: 元数据字典
Returns:
参数字典
"""
parameters = {}
if 'prompt' in metadata:
prompt = metadata['prompt']
# 递归提取常用参数
extracted = self._extract_common_parameters(prompt)
parameters.update(extracted)
# 添加文本提示
if 'positive_prompt' in metadata:
parameters['positive_prompt'] = metadata['positive_prompt']
if 'negative_prompt' in metadata:
parameters['negative_prompt'] = metadata['negative_prompt']
return parameters
def _extract_common_parameters(self, obj: Any, prefix: str = "") -> Dict[str, str]:
"""递归提取常用参数"""
parameters = {}
# 常用参数名列表
common_params = [
'sampler_name', 'scheduler', 'steps', 'cfg', 'seed',
'width', 'height', 'denoise', 'model', 'ckpt_name',
'vae_name', 'clip_skip', 'lora_name'
]
if isinstance(obj, dict):
for key, value in obj.items():
current_key = f"{prefix}.{key}" if prefix else key
# 检查是否是常用参数
if key.lower() in [p.lower() for p in common_params]:
parameters[key] = str(value)
# 递归搜索
if isinstance(value, (dict, list)):
nested = self._extract_common_parameters(value, current_key)
parameters.update(nested)
elif isinstance(obj, list):
for i, item in enumerate(obj):
current_key = f"{prefix}[{i}]" if prefix else f"[{i}]"
nested = self._extract_common_parameters(item, current_key)
parameters.update(nested)
return parameters
def clear_cache(self):
"""清空缓存"""
self.cache.clear()
def set_cache_enabled(self, enabled: bool):
"""启用或禁用缓存"""
self.cache_enabled = enabled
if not enabled:
self.cache.clear()
def get_cache_stats(self) -> Dict[str, int]:
"""获取缓存统计"""
return {
'cache_size': len(self.cache),
'cache_enabled': self.cache_enabled
}