Minor tweaks

This commit is contained in:
mang01010
2025-10-03 17:00:54 +02:00
parent cd23aed098
commit 796f79a76a
5 changed files with 218 additions and 149 deletions
+58
View File
@@ -0,0 +1,58 @@
import os
import json
class MangoModelData:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
# Checkpoint name passed from KSamplerMango (may include subfolder)
"ckpt_name": ("STRING", {"tooltip": "Checkpoint name"}),
}
}
RETURN_TYPES = ("STRING",)
FUNCTION = "export_model_data"
CATEGORY = "Mango Node Pack/Metadata"
def export_model_data(self, ckpt_name):
# Determine path to JSON file containing manual model usage tips
data_path = os.path.join(os.path.dirname(__file__), 'model_data.json')
# Check for existence
if not os.path.isfile(data_path):
return (f"[ModelData] ERROR: file not found: {data_path}",)
# Load JSON
try:
with open(data_path, 'r', encoding='utf-8') as f:
db = json.load(f)
except Exception as e:
return (f"[ModelData] ERROR: failed to load JSON ({type(e).__name__}): {e}",)
# Validate JSON structure
if not isinstance(db, dict) or not db:
return ("[ModelData] ERROR: model_data.json is empty or not a JSON object",)
# Extract the filename portion
base_name = os.path.basename(ckpt_name)
# Lookup strategy: full key, basename, then case-insensitive basename
model_entry = db.get(ckpt_name)
if model_entry is None:
model_entry = db.get(base_name)
if model_entry is None:
lower_base = base_name.lower()
for key, val in db.items():
if os.path.basename(key).lower() == lower_base:
model_entry = val
break
# If still not found, list available keys in the error
if model_entry is None:
available = ", ".join(db.keys())
return (f"[ModelData] no entry for '{base_name}'. Available keys: {available}",)
# Format and return the JSON snippet using the base filename
snippet = json.dumps({base_name: model_entry}, indent=2)
return (snippet,)
+85
View File
@@ -0,0 +1,85 @@
import os
import math
import yaml
from PIL import Image
import folder_paths
def collect_dirs(key_name: str):
"""
key_name: "checkpoints" or "loras"
Returns a list of absolute dirs:
1) ComfyUI/models/<key_name>
2) For each section in extra_model_paths.yaml:
section.base_path + '/' + section[key_name]
"""
ROOT = os.getcwd()
dirs = [os.path.join(ROOT, "models", key_name)]
yaml_file = os.path.join(ROOT, "extra_model_paths.yaml")
if os.path.exists(yaml_file):
with open(yaml_file, "r") as f:
cfg = yaml.safe_load(f) or {}
# Each section is a dict with base_path and relative subdirs
for section in cfg.values():
base = section.get("base_path", ROOT)
rel = section.get(key_name)
if rel:
dirs.append(os.path.abspath(os.path.join(base, rel)))
return dirs
class PreviewImageNode:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"ckpt_name": ("STRING",),
"lora_stack": ("LORA_STACK",),
}
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("preview",)
FUNCTION = "preview_images"
CATEGORY = "Mango Node Pack/Loaders"
def preview_images(self, ckpt_name, lora_stack):
# strip off extensions so "foo.ckpt" → "foo"
base_name = os.path.splitext(ckpt_name)[0]
exts = [".png", ".jpg", ".jpeg", ".webp", ".bmp", ".tiff"]
def find_image_in(dirs, name):
for d in dirs:
if not os.path.isdir(d):
continue
for ext in exts:
candidate = os.path.join(d, name + ext)
if os.path.exists(candidate):
print(f"[PreviewImage] ✔ Found {name+ext} in {d}")
return candidate
print(f"[PreviewImage] ✘ No preview for '{name}' in {dirs}")
return None
# build our search lists
model_dirs = collect_dirs("checkpoints")
lora_dirs = collect_dirs("loras")
imgs = []
# 1) model preview
mp = find_image_in(model_dirs, base_name)
imgs.append(Image.open(mp).convert("RGB") if mp else Image.new("RGB",(512,512),(40,40,40)))
# 2) up to 5 LoRAs
for name, *_ in lora_stack[:5]:
if not name:
continue
lp = find_image_in(lora_dirs, name)
imgs.append(Image.open(lp).convert("RGB") if lp else Image.new("RGB",(512,512),(80,80,80)))
# 3) composite 2×3 grid
cols = 3
rows = math.ceil(len(imgs)/cols)
w, h = imgs[0].size
grid = Image.new("RGB", (cols*w, rows*h), (20,20,20))
for i, im in enumerate(imgs):
grid.paste(im, ((i%cols)*w, (i//cols)*h))
return (grid,)
-149
View File
@@ -1,149 +0,0 @@
# ComfyUI MangoNodePack
*A collection of enhanced nodes for ComfyUI with metadata handling for Civitai and streamlined model loading.*
---
## **UPDATE**
Added Image Loader for batch load images for Img2Img creations. To use it:
1. Create a new folder in the output folder
2. Insert a batch of images to the created folder
3. Browse and choose output's subfolders from a dropdown list of the node
4. All images in the folder will be processed in Img2Img creations
---
## 🚀 **Features**
👉 **LoRA Metadata Exporter** – Extracts trigger words from LoRA metadata (local & online).\
👉 **Prompt Formatters** – Organizes quality tags, descriptions, and prompt structure.\
👉 **Smart Model Loader** – Loads models and LoRAs with automatic metadata extraction.\
👉 **Enhanced KSampler** – Improved metadata tracking with LoRA and checkpoint integration.\
👉 **Diffusion Model Loader** – Like SDXL/PONY version, it loads model along with clips and LoRAs, extracting metadata and and passing them as string.\
👉 **FLUX Sampler** – Metadata tracking with LoRA and checkpoint integration.\
👉 **FLUX Guidance** – The very same FLUX Guidance as ComfyUI original, but with guidance extraction to save as metadata.\
👉 **Advanced Image Saver** – Saves images with embedded metadata, filenames, and auto-numbering.\
👉 **Custom Latent Generator** – Predefined resolutions for common aspect ratios.\
👉 **Prompt Saver** – Don't lose your prompts ever again, by saving their copy in the output folder (or specified subfolder).\
👉 **Prompt Loader** – Load saved prompts easily from the dropdown list, and connect to the Positive/Negative nodes.
Known issue: Occasionally, not every LoRA will be visible on Civitai as the hash will not be recognized correctly. It's most likely caused by models' updates on Civitai, and re-downloading the model solves the issue.
---
## 💽 **Installation**
### **Requirements**
- **ComfyUI** installed and running.
- Python dependencies (install via terminal):
```sh
pip install safetensors
```
### **Installation Steps**
Find Mango Node Pack in the ComfyUI Manager!
OR
1. Clone the repository:
```sh
git clone https://github.com/mang01010/MangoNodePack.git
```
2. Move the `MangoNodePack` folder into ComfyUI’s `custom_nodes` directory.
3. Restart ComfyUI to load the new nodes.
---
## 📝 **Node Overview**
### **1️⃣ Trigger Exporter (Mango)**
💡 **Category:** Metadata
- The pack started with this node. It extracts trigger words from LoRAs and concatenates them as comma delimited string that can be pushed further, so you won't have to think about these. Don't fail using downloaded LoRA ever again!
![Overlay Preview](Screenshots/TriggerExporter.png)
### **2️⃣ Prompt (Mango)**
💡 **Category:** Metadata
- This node organizes prompts into three separate text boxes, making them easier to read and edit. It also supports direct integration with the Trigger Exporter.
![Overlay Preview](Screenshots/Prompt.png)
### **3️⃣ Prompt /w Embedding (Mango)**
💡 **Category:** Metadata
- Does the same thing as Prompt (Mango), but divides prompt further to add embeddings in seperate field.
### **4️⃣ Loader (Mango)**
💡 **Category:** Loaders
- Loads Stable Diffusion models and up to 5 LoRAs.
- Computes model hashes and applies LoRAs with weight control.
### **5️⃣ KSampler (Mango)**
💡 **Category:** Sampling
- This version of the KSampler node features eight inputs. The standard inputs—model, latent image, positive, and negative—are used for image generation. Additionally, it includes a LoRA stack, checkpoint name, and separate positive and negative text fields as a metadata workaround for:
### **6️⃣ Image Saver (Mango)**
💡 **Category:** Metadata
- Saves images with embedded metadata (prompt, seed, CFG, steps, sampler, checkpoint and LoRAs info), ensuring compatibility with Civitai for direct metadata reading.
- Supports auto-numbering and custom subdirectories.
![Overlay Preview](Screenshots/Metadata.png)
### **7️⃣ Latent Image (Mango)**
💡 **Category:** Loaders
- Generates latent tensors in predefined aspect ratios (e.g., 1:1, 16:9, etc.).
### **8️⃣ Prompt Saver and Loader**
💡 **Category:** Metadata
- Save a copy of prompts in the default output folder (or specified subfolder).
- Load saved prompts easily from the dropdown list, and connect to the Positive/Negative nodes.
![Overlay Preview](Screenshots/LoadPrompt.png)
---
## 🛠 **Usage Guide**
Once installed, find the **MangoNodePack** nodes inside ComfyUI.
**Example Workflow:**
![Overlay Preview](Screenshots/Workflow.png)
---
## 📝 **License**
This project is licensed under the MIT License – see the [LICENSE](LICENSE) file for details.
---
### **📢 Check out these awesome node packs!**
- [ComfyUI](https://github.com/comfyanonymous/ComfyUI)
- [rgthree-comfy](https://github.com/rgthree/rgthree-comfy)
- [Efficiency-Nodes](https://github.com/jags111/efficiency-nodes-comfyui)
- [ComfyUI Comfyroll Custom Nodes](https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes)
- [ComfyUI Custom Scripts](https://github.com/pythongosssss/ComfyUI-Custom-Scripts)
- [WAS Node Suite](https://github.com/WASasquatch/was-node-suite-comfyui)
- [Easy-Use](https://github.com/yolain/ComfyUI-Easy-Use)
- [PRNodes](https://github.com/pikenrover/ComfyUI_PRNodes)
+3
View File
@@ -13,6 +13,7 @@ from .MangoPromptLoad import MangoPromptLoad
from .LoraStackMango import LoraStackMango
from .MangoImageLoader import MangoImageLoader
from .MangoLoader10Loras import MangoLoader10Loras
from .MangoModelData import MangoModelData
NODE_CLASS_MAPPINGS = {
"MangoTriggerExporter": MangoTriggerExporter,
@@ -30,6 +31,7 @@ NODE_CLASS_MAPPINGS = {
"LoraStackMango": LoraStackMango,
"MangoImageLoader": MangoImageLoader,
"MangoLoader10Loras": MangoLoader10Loras,
"MangoModelData": MangoModelData,
}
NODE_DISPLAY_NAME_MAPPINGS = {
@@ -48,4 +50,5 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"LoraStackMango": "LoRA Stack (Mango)",
"MangoImageLoader": "Image Loader (Mango)",
"MangoLoader10Loras": "Loader (Mango + 10 Loras)",
"MangoModelData": "Model Data (Mango)",
}
+72
View File
@@ -0,0 +1,72 @@
{
"animij_v5.safetensors": {
"clip_skip": "2",
"steps": "30+",
"cfg": "4-7",
"sampler_name": "Euler a",
"scheduler": "N/A",
"positive": "N/A",
"negative": "worst_quality, bad_quality, poorly_detailed",
"resolution": "1024"
},
"realDream_sdxlPony15.safetensors": {
"clip_skip": "2",
"steps": "20-30",
"cfg": "5-9",
"sampler_name": "Euler a / DPM++ SDE",
"scheduler": "Karras",
"positive": "score_9, score_8_up, score_7_up, (Masterpiece, Best Quality, Highres:1.2), 35mm lens, 35mm RAW photo, photorealistic, realistic, extremely detailed, film grain, front light",
"negative": "score_6, score_5, score_4, score_2, score_1",
"resolution": "512 / 768",
"High-Res Fix tip": "Upscale by 1.5 and denoise at 0.6",
"Recommended Lora 1": "amateur_style_v1_pony.safetensors @ 2.5",
"Recommended Lora 2": "Dramatic Lighting Slider.safetensors @ 1.5"
},
"ponyDiffusionV6XL_v6StartWithThisOne.safetensors": {
"clip_skip": "2",
"steps": "25-30",
"cfg": "5-9",
"sampler_name": "Euler a",
"scheduler": "N/A",
"positive": "score_9, score_8_up, score_7_up, score_6_up, score_5_up, score_4_up, just describe what you want, tag1, tag2",
"negative": "N/A",
"resolution": "1024"
},
"ultrarealFineTune_v4.safetensors": {
"steps": "30-50",
"cfg": "0.9-1",
"sampler_name": "DPM++ 2M",
"scheduler": "Beta"
},
"8StepsCreartHyperFlux_creartUltimate.safetensors": {
"steps": "8-10",
"cfg": "3.5",
"sampler_name": "Euler",
"scheduler": "Beta"
},
"c4pacitor_xV1.safetensors": {
"steps": "16-32",
"cfg": "3-6",
"sampler_name": "DEIS",
"scheduler": "Beta/Beta57/Bong Tangent"
},
"77FluxAnime_i.safetensors": {
"steps": "25-30",
"cfg": "3.5-7.5",
"sampler_name": "euler",
"scheduler": "normal"
},
"illustriousNova_V10.safetensors": {
"steps": "30-50",
"cfg": "5-7",
"sampler_name": "DPM++ 2M SDE",
"scheduler": "Karras"
}
}