Add CSV Prompt Loader node
New node that loads prompts from CSV files with dropdown selection. CSV format: name,prompt,negative_prompt Features dropdown selection of prompt names and outputs selected prompt and negative prompt. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+239
@@ -0,0 +1,239 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be added to the global gitignore or merged into this project gitignore. For a PyCharm
|
||||
# project, it is recommended to add version control directories.
|
||||
.idea/
|
||||
|
||||
# VS Code
|
||||
.vscode/
|
||||
*.code-workspace
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
$RECYCLE.BIN/
|
||||
*.lnk
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
Icon?
|
||||
._*
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# Linux
|
||||
*~
|
||||
.fuse_hidden*
|
||||
.directory
|
||||
.Trash-*
|
||||
.nfs*
|
||||
|
||||
# ComfyUI specific
|
||||
*.safetensors
|
||||
*.ckpt
|
||||
*.pt
|
||||
*.pth
|
||||
*.bin
|
||||
models/
|
||||
checkpoints/
|
||||
temp/
|
||||
output/
|
||||
input/
|
||||
|
||||
# Test images and temporary files
|
||||
test_images/
|
||||
temp_images/
|
||||
*.tmp
|
||||
*.temp
|
||||
|
||||
# Local configuration files
|
||||
config.json
|
||||
settings.json
|
||||
user_config.py
|
||||
|
||||
# Log files
|
||||
*.log
|
||||
logs/
|
||||
|
||||
# Backup files
|
||||
*.bak
|
||||
*.backup
|
||||
*~
|
||||
|
||||
# Cache directories
|
||||
cache/
|
||||
.cache/
|
||||
|
||||
# Node specific temporary files
|
||||
node_temp/
|
||||
processing_temp/
|
||||
+5
-2
@@ -26,6 +26,7 @@ from .gimp_nodes.cie_lch_noise_gegl_like import CIELChNoiseGEGLLike
|
||||
from .gimp_nodes.image_type_detector import ImageTypeDetector
|
||||
from .gimp_nodes.mean_curvature_blur_gegl_like import MeanCurvatureBlurGEGLLike
|
||||
from .gimp_nodes.rgb_noise_gegl_like import RGBNoiseGEGLLike
|
||||
from .csv_prompt_loader import CSVPromptLoader
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"CustomCrop": CustomCropNode,
|
||||
@@ -46,7 +47,8 @@ NODE_CLASS_MAPPINGS = {
|
||||
"CIELChNoiseGEGLLike": CIELChNoiseGEGLLike,
|
||||
"ImageTypeDetector": ImageTypeDetector,
|
||||
"MeanCurvatureBlurGEGLLike": MeanCurvatureBlurGEGLLike,
|
||||
"RGBNoiseGEGLLike": RGBNoiseGEGLLike
|
||||
"RGBNoiseGEGLLike": RGBNoiseGEGLLike,
|
||||
"CSVPromptLoader": CSVPromptLoader
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
@@ -67,7 +69,8 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"CIELChNoiseGEGLLike": "CIE LCH Noise (GEGL-like)",
|
||||
"ImageTypeDetector": "Image Type Detector",
|
||||
"MeanCurvatureBlurGEGLLike": "Mean Curvature Blur (GEGL-like)",
|
||||
"RGBNoiseGEGLLike": "RGB Noise (GEGL-like)"
|
||||
"RGBNoiseGEGLLike": "RGB Noise (GEGL-like)",
|
||||
"CSVPromptLoader": "CSV Prompt Loader"
|
||||
}
|
||||
|
||||
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
||||
@@ -0,0 +1,100 @@
|
||||
import csv
|
||||
import os
|
||||
import torch
|
||||
|
||||
class CSVPromptLoader:
|
||||
"""
|
||||
A custom node for ComfyUI that loads prompts from a CSV file with dropdown selection.
|
||||
The CSV file should have columns: name, prompt, negative_prompt
|
||||
"""
|
||||
|
||||
csv_cache = {} # Class-level cache to share data between instances
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"csv_path": ("STRING", {"default": ""}),
|
||||
"selected_name": (cls._get_names_from_cache, {"default": ""}),
|
||||
},
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _get_names_from_cache(cls):
|
||||
"""Get available names from the cached CSV data"""
|
||||
names = []
|
||||
for path_data in cls.csv_cache.values():
|
||||
names.extend(path_data.keys())
|
||||
if not names:
|
||||
names = ["No CSV loaded"]
|
||||
return names
|
||||
|
||||
@classmethod
|
||||
def _load_csv_data(cls, csv_path):
|
||||
"""Load CSV data and cache it"""
|
||||
if not os.path.exists(csv_path):
|
||||
return {}
|
||||
|
||||
data = {}
|
||||
try:
|
||||
with open(csv_path, 'r', encoding='utf-8') as file:
|
||||
reader = csv.DictReader(file)
|
||||
for row in reader:
|
||||
if 'name' in row and 'prompt' in row and 'negative_prompt' in row:
|
||||
data[row['name']] = {
|
||||
'prompt': row['prompt'],
|
||||
'negative_prompt': row['negative_prompt']
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"Error loading CSV: {e}")
|
||||
return {}
|
||||
|
||||
# Cache the data
|
||||
cls.csv_cache[csv_path] = data
|
||||
return data
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING")
|
||||
RETURN_NAMES = ("prompt", "negative_prompt")
|
||||
FUNCTION = "load_prompt"
|
||||
CATEGORY = "text/prompt"
|
||||
TITLE = "CSV Prompt Loader"
|
||||
|
||||
def load_prompt(self, csv_path, selected_name):
|
||||
# Load CSV data if not cached
|
||||
if csv_path not in self.csv_cache:
|
||||
self._load_csv_data(csv_path)
|
||||
|
||||
# Get cached data
|
||||
csv_data = self.csv_cache.get(csv_path, {})
|
||||
|
||||
# If no data loaded, return empty strings
|
||||
if not csv_data:
|
||||
return ("", "")
|
||||
|
||||
# If selected name exists, return its data
|
||||
if selected_name in csv_data:
|
||||
data = csv_data[selected_name]
|
||||
return (data['prompt'], data['negative_prompt'])
|
||||
|
||||
# Return the first entry as fallback
|
||||
if csv_data:
|
||||
first_name = list(csv_data.keys())[0]
|
||||
first_data = csv_data[first_name]
|
||||
return (first_data['prompt'], first_data['negative_prompt'])
|
||||
|
||||
return ("", "")
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, csv_path, selected_name):
|
||||
return f"{csv_path}_{selected_name}"
|
||||
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(cls, csv_path, selected_name):
|
||||
if not csv_path:
|
||||
return "CSV path cannot be empty"
|
||||
if not os.path.exists(csv_path):
|
||||
return "CSV file does not exist"
|
||||
return True
|
||||
Reference in New Issue
Block a user