diff --git a/.others/version.py b/.others/version.py index 50922f6..ac4beff 100644 --- a/.others/version.py +++ b/.others/version.py @@ -38,3 +38,23 @@ print(f"> File2 {file2}") with open(file2, "w") as f2: f2.writelines([f"version = \"{version}\"\n"]) + +# modify pyproject.toml file with @version +file3 = os.path.join(os.path.dirname(__file__), "../pyproject.toml") +print(f"> File3: {file3}") + +file3VersionFound = False +# lines = None +with open(file3, "r") as f: + lines = f.readlines() + for line in lines: + + if "version = " in line: + file3VersionFound = True + lines[lines.index(line)] = f"version = \"{version}\"\n" + +if not file3VersionFound: + raise RuntimeError(f"Version label not found in file: {file3}") + +with open(file3, "w") as f3: + f3.writelines(lines) diff --git a/__init__.py b/__init__.py index 55bde3b..ed1d33f 100644 --- a/__init__.py +++ b/__init__.py @@ -2,7 +2,7 @@ @author: Crystian @title: Crystools @nickname: Crystools -@version: 1.12.0 +@version: 1.13.0 @project: "https://github.com/crystian/ComfyUI-Crystools", @description: Plugins for multiples uses, mainly for debugging, you need them! IG: https://www.instagram.com/crystian.ia """ diff --git a/core/version.py b/core/version.py index 6f73501..e3d9739 100644 --- a/core/version.py +++ b/core/version.py @@ -1 +1 @@ -version = "1.12.0" +version = "1.13.0" diff --git a/docs/screwdriver.png b/docs/screwdriver.png new file mode 100644 index 0000000..61a0574 Binary files /dev/null and b/docs/screwdriver.png differ diff --git a/general/gpu.py b/general/gpu.py index 0298085..54ef2f1 100644 --- a/general/gpu.py +++ b/general/gpu.py @@ -1,162 +1,169 @@ import torch import pynvml import comfy.model_management - from ..core import logger - class CGPUInfo: - """ - This class is responsible for getting information from GPU (ONLY). - """ - cuda = False - pynvmlLoaded = False - cudaAvailable = False - torchDevice = 'cpu' - cudaDevice = 'cpu' - cudaDevicesFound = 0 - switchGPU = True - switchVRAM = True - switchTemperature = True - gpus = [] - gpusUtilization = [] - gpusVRAM = [] - gpusTemperature = [] - - def __init__(self): - try: - pynvml.nvmlInit() - self.pynvmlLoaded = True - except Exception as e: - self.pynvmlLoaded = False - logger.error('Could not init pynvml.' + str(e)) - - if self.pynvmlLoaded and pynvml.nvmlDeviceGetCount() > 0: - self.cudaDevicesFound = pynvml.nvmlDeviceGetCount() - - logger.info(f"GPU/s:") - - # for simulate multiple GPUs (for testing) interchange these comments: - # for deviceIndex in range(3): - # deviceHandle = pynvml.nvmlDeviceGetHandleByIndex(0) - for deviceIndex in range(self.cudaDevicesFound): - deviceHandle = pynvml.nvmlDeviceGetHandleByIndex(deviceIndex) - - gpuName = pynvml.nvmlDeviceGetName(deviceHandle) - - logger.info(f"{deviceIndex}) {gpuName}") - - self.gpus.append({ - 'index': deviceIndex, - 'name': gpuName, - }) - - # same index as gpus, with default values - self.gpusUtilization.append(True) - self.gpusVRAM.append(True) - self.gpusTemperature.append(True) - - self.cuda = True - logger.info(f'NVIDIA Driver: {pynvml.nvmlSystemGetDriverVersion()}') - else: - logger.warn('No GPU with CUDA detected.') - - try: - self.torchDevice = comfy.model_management.get_torch_device_name(comfy.model_management.get_torch_device()) - except Exception as e: - logger.error('Could not pick default device.' + str(e)) - - self.cudaDevice = 'cpu' if self.torchDevice == 'cpu' else 'cuda' - self.cudaAvailable = torch.cuda.is_available() - - if self.cuda and self.cudaAvailable and self.torchDevice == 'cpu': - logger.warn('CUDA is available, but torch is using CPU.') - - def getInfo(self): - logger.debug('Getting GPUs info...') - return self.gpus - - def getStatus(self): - # logger.debug('CGPUInfo getStatus') - gpuUtilization = -1 - gpuTemperature = -1 - vramUsed = -1 - vramTotal = -1 - vramPercent = -1 - - gpuType = '' + """ + This class is responsible for getting information from GPU (ONLY). + """ + cuda = False + pynvmlLoaded = False + cudaAvailable = False + torchDevice = 'cpu' + cudaDevice = 'cpu' + cudaDevicesFound = 0 + switchGPU = True + switchVRAM = True + switchTemperature = True gpus = [] + gpusUtilization = [] + gpusVRAM = [] + gpusTemperature = [] - if self.cudaDevice == 'cpu': - gpuType = 'cpu' - gpus.append({ - 'gpu_utilization': 0, - 'gpu_temperature': 0, - 'vram_total': 0, - 'vram_used': 0, - 'vram_used_percent': 0, - }) - else: - gpuType = self.cudaDevice + def __init__(self): + try: + pynvml.nvmlInit() + self.pynvmlLoaded = True + except Exception as e: + self.pynvmlLoaded = False + logger.error('Could not init pynvml.' + str(e)) - if self.pynvmlLoaded and self.cuda and self.cudaAvailable: + if self.pynvmlLoaded and pynvml.nvmlDeviceGetCount() > 0: + self.cudaDevicesFound = pynvml.nvmlDeviceGetCount() - # for simulate multiple GPUs (for testing) interchange these comments: - # for deviceIndex in range(3): - # deviceHandle = pynvml.nvmlDeviceGetHandleByIndex(0) - for deviceIndex in range(self.cudaDevicesFound): - deviceHandle = pynvml.nvmlDeviceGetHandleByIndex(deviceIndex) + logger.info(f"GPU/s:") - gpuUtilization = 0 - vramPercent = 0 - vramUsed = 0 - vramTotal = 0 - gpuTemperature = 0 + # for simulate multiple GPUs (for testing) interchange these comments: + # for deviceIndex in range(3): + # deviceHandle = pynvml.nvmlDeviceGetHandleByIndex(0) + for deviceIndex in range(self.cudaDevicesFound): + deviceHandle = pynvml.nvmlDeviceGetHandleByIndex(deviceIndex) + gpuName = 'Unknown GPU' - # GPU Utilization - if self.switchGPU and self.gpusUtilization[deviceIndex]: - try: - utilization = pynvml.nvmlDeviceGetUtilizationRates(deviceHandle) - gpuUtilization = utilization.gpu - except Exception as e: - if str(e) == "Unknown Error": - logger.error('For some reason, pynvml is not working in a laptop with only battery, try to connect and turn on the monitor') - else: - logger.error('Could not get GPU utilization.' + str(e)) + try: + gpuName = pynvml.nvmlDeviceGetName(deviceHandle) + try: + gpuName = gpuName.decode('utf-8', errors='ignore') + except AttributeError as e: + pass - logger.error('Monitor of GPU is turning off (not on UI!)') - self.switchGPU = False + except UnicodeDecodeError as e: + gpuName = 'Unknown GPU (decoding error)' + print(f"UnicodeDecodeError: {e}") - # VRAM - if self.switchVRAM and self.gpusVRAM[deviceIndex]: - # Torch or pynvml?, pynvml is more accurate with the system, torch is more accurate with comfyUI - memory = pynvml.nvmlDeviceGetMemoryInfo(deviceHandle) - vramUsed = memory.used - vramTotal = memory.total + logger.info(f"{deviceIndex}) {gpuName}") - # device = torch.device(gpuType) - # vramUsed = torch.cuda.memory_allocated(device) - # vramTotal = torch.cuda.get_device_properties(device).total_memory + self.gpus.append({ + 'index': deviceIndex, + 'name': gpuName, + }) - vramPercent = vramUsed / vramTotal * 100 + # same index as gpus, with default values + self.gpusUtilization.append(True) + self.gpusVRAM.append(True) + self.gpusTemperature.append(True) - # Temperature - if self.switchTemperature and self.gpusTemperature[deviceIndex]: - try: - gpuTemperature = pynvml.nvmlDeviceGetTemperature(deviceHandle, 0) - except Exception as e: - logger.error('Could not get GPU temperature. Turning off this feature. ' + str(e)) - self.switchTemperature = False + self.cuda = True + logger.info(f'NVIDIA Driver: {pynvml.nvmlSystemGetDriverVersion()}') + else: + logger.warn('No GPU with CUDA detected.') - gpus.append({ - 'gpu_utilization': gpuUtilization, - 'gpu_temperature': gpuTemperature, - 'vram_total': vramTotal, - 'vram_used': vramUsed, - 'vram_used_percent': vramPercent, - }) + try: + self.torchDevice = comfy.model_management.get_torch_device_name(comfy.model_management.get_torch_device()) + except Exception as e: + logger.error('Could not pick default device.' + str(e)) - return { - 'device_type': gpuType, - 'gpus': gpus, - } + self.cudaDevice = 'cpu' if self.torchDevice == 'cpu' else 'cuda' + self.cudaAvailable = torch.cuda.is_available() + + if self.cuda and self.cudaAvailable and self.torchDevice == 'cpu': + logger.warn('CUDA is available, but torch is using CPU.') + + def getInfo(self): + logger.debug('Getting GPUs info...') + return self.gpus + + def getStatus(self): + # logger.debug('CGPUInfo getStatus') + gpuUtilization = -1 + gpuTemperature = -1 + vramUsed = -1 + vramTotal = -1 + vramPercent = -1 + + gpuType = '' + gpus = [] + + if self.cudaDevice == 'cpu': + gpuType = 'cpu' + gpus.append({ + 'gpu_utilization': 0, + 'gpu_temperature': 0, + 'vram_total': 0, + 'vram_used': 0, + 'vram_used_percent': 0, + }) + else: + gpuType = self.cudaDevice + + if self.pynvmlLoaded and self.cuda and self.cudaAvailable: + # for simulate multiple GPUs (for testing) interchange these comments: + # for deviceIndex in range(3): + # deviceHandle = pynvml.nvmlDeviceGetHandleByIndex(0) + for deviceIndex in range(self.cudaDevicesFound): + deviceHandle = pynvml.nvmlDeviceGetHandleByIndex(deviceIndex) + + gpuUtilization = 0 + vramPercent = 0 + vramUsed = 0 + vramTotal = 0 + gpuTemperature = 0 + + # GPU Utilization + if self.switchGPU and self.gpusUtilization[deviceIndex]: + try: + utilization = pynvml.nvmlDeviceGetUtilizationRates(deviceHandle) + gpuUtilization = utilization.gpu + except Exception as e: + if str(e) == "Unknown Error": + logger.error('For some reason, pynvml is not working in a laptop with only battery, try to connect and turn on the monitor') + else: + logger.error('Could not get GPU utilization.' + str(e)) + + logger.error('Monitor of GPU is turning off (not on UI!)') + self.switchGPU = False + + # VRAM + if self.switchVRAM and self.gpusVRAM[deviceIndex]: + # Torch or pynvml?, pynvml is more accurate with the system, torch is more accurate with comfyUI + memory = pynvml.nvmlDeviceGetMemoryInfo(deviceHandle) + vramUsed = memory.used + vramTotal = memory.total + + # device = torch.device(gpuType) + # vramUsed = torch.cuda.memory_allocated(device) + # vramTotal = torch.cuda.get_device_properties(device).total_memory + + vramPercent = vramUsed / vramTotal * 100 + + # Temperature + if self.switchTemperature and self.gpusTemperature[deviceIndex]: + try: + gpuTemperature = pynvml.nvmlDeviceGetTemperature(deviceHandle, 0) + except Exception as e: + logger.error('Could not get GPU temperature. Turning off this feature. ' + str(e)) + self.switchTemperature = False + + gpus.append({ + 'gpu_utilization': gpuUtilization, + 'gpu_temperature': gpuTemperature, + 'vram_total': vramTotal, + 'vram_used': vramUsed, + 'vram_used_percent': vramPercent, + }) + + return { + 'device_type': gpuType, + 'gpus': gpus, + } diff --git a/nodes/image.py b/nodes/image.py index 8de2c8a..f61a611 100644 --- a/nodes/image.py +++ b/nodes/image.py @@ -2,6 +2,7 @@ import os import random import sys import json +import piexif import hashlib from datetime import datetime import torch @@ -262,7 +263,15 @@ class CImageLoadWithMetadata: def execute(self, image): image_path = folder_paths.get_annotated_filepath(image) + imgF = Image.open(image_path) img, prompt, metadata = buildMetadata(image_path) + if imgF.format == 'WEBP': + # Use piexif to extract EXIF data from WebP image + try: + exif_data = piexif.load(image_path) + prompt, metadata = self.process_exif_data(exif_data) + except ValueError: + prompt = {} img = ImageOps.exif_transpose(img) image = img.convert("RGB") @@ -276,6 +285,34 @@ class CImageLoadWithMetadata: return image, mask.unsqueeze(0), prompt, metadata + def process_exif_data(self, exif_data): + metadata = {} + # 检查 '0th' 键下的 271 值,提取 Prompt 信息 + if '0th' in exif_data and 271 in exif_data['0th']: + prompt_data = exif_data['0th'][271].decode('utf-8') + # 移除可能的前缀 'Prompt:' + prompt_data = prompt_data.replace('Prompt:', '', 1) + # 假设 prompt_data 是一个字符串,尝试将其转换为 JSON 对象 + try: + metadata['prompt'] = json.loads(prompt_data) + except json.JSONDecodeError: + metadata['prompt'] = prompt_data + + # 检查 '0th' 键下的 270 值,提取 Workflow 信息 + if '0th' in exif_data and 270 in exif_data['0th']: + workflow_data = exif_data['0th'][270].decode('utf-8') + # 移除可能的前缀 'Workflow:' + workflow_data = workflow_data.replace('Workflow:', '', 1) + try: + # 尝试将字节字符串转换为 JSON 对象 + metadata['workflow'] = json.loads(workflow_data) + except json.JSONDecodeError: + # 如果转换失败,则将原始字符串存储在 metadata 中 + metadata['workflow'] = workflow_data + + metadata.update(exif_data) + return metadata + @classmethod def IS_CHANGED(cls, image): image_path = folder_paths.get_annotated_filepath(image) @@ -354,7 +391,8 @@ class CImageSaveWithExtraMetadata(SaveImage): if metadata_extra is not None and metadata_extra != 'undefined': try: - metadata_extra = json.loads(f"{{{metadata_extra}}}") + # metadata_extra = json.loads(f"{{{metadata_extra}}}") // a fix? + metadata_extra = json.loads(metadata_extra) except Exception as e: logger.error(f"Error parsing metadata_extra (it will send as string), error: {e}") metadata_extra = {"extra": str(metadata_extra)} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5e0f1da --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,14 @@ +[project] +name = "comfyui-crystools" +description = "With this suit, you can see the resources monitor, progress bar & time elapsed, metadata and compare between two images, compare between two JSONs, show any value to console/display, pipes, and more!\nThis provides better nodes to load/save images, previews, etc, and see \"hidden\" data without loading a new workflow." +version = "1.13.0" +license = "LICENSE" +dependencies = ["deepdiff", "torch", "numpy", "Pillow>=9.5.0", "pynvml", "py-cpuinfo"] + +[project.urls] +Repository = "https://github.com/crystian/ComfyUI-Crystools" + +[tool.comfy] +PublisherId = "crystian" +DisplayName = "ComfyUI-Crystools" +Icon = "https://raw.githubusercontent.com/crystian/ComfyUI-Crystools/main/docs/screwdriver.png" diff --git a/requirements.txt b/requirements.txt index 21f0496..d61acb5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ numpy Pillow>=9.5.0 pynvml py-cpuinfo +piexif diff --git a/version b/version index 0eed1a2..feaae22 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.12.0 +1.13.0