├── .github └── workflows │ └── publish.yml ├── LICENSE ├── README.md ├── __init__.py ├── build_prompt_from_csv.py ├── examples ├── example_image.png └── example_workflow.json ├── prompt_sets ├── _csv_config.json ├── cat_hat.csv └── example.csv └── pyproject.toml /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to Comfy registry 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - "pyproject.toml" 9 | 10 | jobs: 11 | publish-node: 12 | name: Publish Custom Node to registry 13 | runs-on: ubuntu-latest 14 | # if this is a forked repository. Skipping the workflow. 15 | if: github.event.repository.fork == false 16 | steps: 17 | - name: Check out code 18 | uses: actions/checkout@v4 19 | - name: Publish Custom Node 20 | uses: Comfy-Org/publish-node-action@main 21 | with: 22 | ## Add your own personal access token to your Github Repository secrets and reference it here. 23 | personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }} 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 jroc22 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ComfyUI-CSV-prompt-builder 2 | 3 | This is a simple node for creating prompts using a .csv file. I created this node as an easy way to output different prompts each time a workflow is run. 4 | 5 | Please keep in mind I am not a programmer and this is my first node (and first coding project). There may be some poorly written code or functionality that can be improved. If you have any recommendations for changes or improvements to the code let me know, I'm eager to learn and theres some things I just couldn't figure out or get to work. 6 | 7 | ![Example Image](https://github.com/jroc22/ComfyUI-CSV-prompt-builder/blob/main/examples/example_image.png) 8 | 9 | ## Installation 10 | 11 | Just clone the repo as you would any other node, or download the zip and place the "ComfyUI-CSV-prompt-builder" folder in your custom_nodes directory. 12 | 13 | 14 | To clone the repo: 15 | 16 | - Navigate to the ComfyUI custom_nodes directory and open a command prompt 17 | - Clone the repository: 18 | 19 | ```bash 20 | git clone https://github.com/jroc22/ComfyUI-CSV-prompt-builder.git 21 | 22 | ``` 23 | 24 | ## Usage 25 | 26 | To get started, simply add the BuildPromptFromCSV node to your workflow. It can be found in the "Prompt Nodes" category. The node outputs text, so you will need to encode the text for generation. Also provided is an example workflow with the node added along with pythongosssss' "Show Text" node from https://github.com/pythongosssss/ComfyUI-Custom-Scripts (highly recommend if you don't already have it). 27 | 28 | The BuildPromptFromCSV node reads the contents of the selected .csv file and displays the following fields for each column: 29 | 30 | - **{column A header}_mode**: This is how the value will be selected for output. The options are: 31 | - **Fixed**: Output the value chosen in the following "_val" field. 32 | - **Randomize**: Output a random value from the column. 33 | - **Cycle**: Output values in order from top to bottom. When it reaches the end of the column, it will start from the top again. 34 | 35 | - **{column A header}_val**: The value to output when using Fixed mode. 36 | 37 | - **{column A header}_weight**: How to weight the output value. Will add parenthesis and the weight factor (e.g., (a cat:1.25)) 38 | 39 | - **{column A}_ to _{column B}**: The separator between this column and the next column (default is ", "). This is a single-line string field. It's intended for the separator, but you can turn any of them into an input and connect a multi-line string node to more easily add anything between two columns. Just be sure to begin and end with the desired separator(s) and spaces -- e.g., ", {your text}, " 40 | 41 | ### Changing the Loaded CSV File 42 | 43 | After changing the CSV file in the Node drop-down field, you will need to run the node once and then Restart ComfyUI to see the changes. You should get a notification after changing the CSV file and running it once, telling you to restart ComfyUI. This is the only way I could make it possible to switch CSV files directly on the node. It's not amazing, but it works. Just always double-check that the drop-down is accurate. If the CSV file drop-down does not match what is loaded on the node it will tell you to restart again and it will switch to the CSV file in the dropdown. 44 | 45 | Using the correct method of switching CSV files should give you no trouble -- again, the process is: 46 | 47 | - Select a new CSV File in the drop-down menu. 48 | - Run the node once (and see notification to restart ComfyUI). 49 | - Restart ComfyUI. 50 | 51 | ### Adding CSV Files & Correct Format 52 | 53 | All CSV files should go in the "promt_sets" folder, and ensure there is at least one CSV file in there at all times. There is no hard limit set on the number of CSV files you can have in there, but I imagine after ~100 performance would be impacted. After adding a CSV file, simply refreshing will update the drop-down. 54 | 55 | All CSV files you add to the folder must include column headers in the first row -- the column headers are used to name the fields on the node, so keep these relatively short -- and must also include at least one value beneath each header. It's a basic set up, but you can look at either of the provided example CSV files for a simple outline. 56 | 57 | There is no hard limit set on the number of columns or rows your CSV file can have. I've only really tested up to 30 columns with 50-100 rows each. A significant number of columns and rows may lead to performance issues, so be warned. At 30 columns I had no problems other than the Node was quite tall visually. 58 | 59 | ### Multiple Node Instances 60 | 61 | You can use multiple instances of this node, but due to how the CSV file switching is set up it does not currently support multiple instances with different CSV file sources. If you want to use multiple instances of the Node, I recommend turning the "csv_file" field into an input on each instance, and connecting the same Combo node to each. This way, when you change the CSV file it will correctly update for each instance. If you don't do this, after the restart you will need make sure each CSV file drop-down is set to the correct CSV file on each other instance of the node. 62 | 63 | If anyone knows how to allow multiple instances with difference CSV sources and wants to share, please do! 64 | 65 | ## Additional CSV File Info & Troubleshooting 66 | 67 | ### Updating the Loaded CSV File 68 | 69 | Due to how the CSV files are cached, if you make an update to and replace the CSV file you're currently using in the Node without changing the filename (e.g., just by adding values to a column or changing a header), you will need to Restart ComfyUI to see the changes -- the node will not reflect the changes after Refreshing. 70 | 71 | ### Deleting the Loaded CSV File 72 | 73 | If you delete the CSV file that is currently being used by the Node, Restarting ComfyUI should initialize the Node with the first CSV file (by alphabetical sort) in the "prompt_sets" folder. If no CSV files are in the folder, the Node will fail to initialize. 74 | 75 | Keep in mind that if you delete a CSV file in this way and Restart ComfyUI, the drop-down may still show the previous CSV filename (the deleted one). If that's the case, make sure to switch it to the correct CSV filename or it will fail to run. 76 | 77 | ### CSV Troubleshooting 78 | 79 | If for whatever reason the Node keeps failing to initialize, make sure there is at least one CSV file in the "prompt_sets" folder, and then within that folder double check that the "_csv_config.json" file is referencing that CSV file. If the config file is not there, restart ComfyUI and it should be automatically created and default to the first CSV file (by alphabetical sort) in the "prompt_sets" folder. 80 | 81 | Also check that the CSV file is in the proper format, with headers in the first row and at least one value under each column with a header. 82 | 83 | When the Node is run, it updates the config file with the CSV file selected in the drop-down (or, if it can't locate the CSV file in the folder, it will create the config using the first CSV file (by alphabetical sort) in the folder). When ComfyUI starts up, it reads the config file to determine how to initialize. As I mentioned I am not a programmer and this CSV file switching was a pain to figure out, so if you encounter issues please share! 84 | 85 | ## License 86 | 87 | This project is licensed under the MIT License. 88 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | from .build_prompt_from_csv import BuildPromptFromCSV 2 | 3 | NODE_CLASS_MAPPINGS = { 4 | "BuildPromptFromCSV": BuildPromptFromCSV, 5 | } 6 | 7 | NODE_DISPLAY_NAME_MAPPINGS = { 8 | "BuildPromptFromCSV": "Build Prompt From CSV", 9 | } 10 | 11 | __all__ = ['BuildPromptFromCSV'] 12 | -------------------------------------------------------------------------------- /build_prompt_from_csv.py: -------------------------------------------------------------------------------- 1 | import os 2 | import csv 3 | import json 4 | import random 5 | from collections import defaultdict 6 | 7 | class CSVConfig: 8 | _csv_filename = "cat_hat.csv" 9 | _config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "prompt_sets", "_csv_config.json") 10 | 11 | @classmethod 12 | def load_config(cls): 13 | if os.path.isfile(cls._config_path): 14 | with open(cls._config_path, "r") as file: 15 | config = json.load(file) 16 | cls._csv_filename = config.get("csv_file", cls._csv_filename) 17 | else: 18 | cls._csv_filename = cls.get_first_csv_file() 19 | cls.save_config() 20 | 21 | # Check if the CSV file exists; if not, use the first CSV file in the directory 22 | csv_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), "prompt_sets") 23 | file_path = os.path.join(csv_directory, cls._csv_filename) 24 | if not os.path.isfile(file_path): 25 | cls._csv_filename = cls.get_first_csv_file() 26 | cls.save_config() 27 | 28 | @classmethod 29 | def save_config(cls): 30 | with open(cls._config_path, "w") as file: 31 | json.dump({"csv_file": cls._csv_filename}, file) 32 | 33 | @classmethod 34 | def get_csv_filename(cls): 35 | cls.load_config() 36 | return cls._csv_filename 37 | 38 | @classmethod 39 | def set_csv_filename(cls, filename): 40 | cls._csv_filename = filename 41 | cls.save_config() 42 | 43 | @classmethod 44 | def get_first_csv_file(cls): 45 | csv_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), "prompt_sets") 46 | csv_files = sorted([f for f in os.listdir(csv_directory) if f.endswith('.csv')]) 47 | return csv_files[0] if csv_files else "cat_hat.csv" 48 | 49 | class BuildPromptFromCSV: 50 | cycle_indices = defaultdict(int) 51 | cached_categories = {} 52 | 53 | @classmethod 54 | def get_categories(cls, file_path): 55 | if file_path in cls.cached_categories: 56 | return cls.cached_categories[file_path] 57 | 58 | if not os.path.isfile(file_path): 59 | raise FileNotFoundError(f"File '{file_path}' cannot be found. Please make sure the CSV file exists in the 'prompt_sets' folder and restart ComfyUI.") 60 | 61 | file_extension = os.path.splitext(file_path)[1].lower() 62 | if file_extension != ".csv": 63 | raise ValueError("Unsupported file type. Please provide a .csv file.") 64 | 65 | categories = defaultdict(list) 66 | with open(file_path, "r") as file: 67 | reader = csv.reader(file) 68 | headers = next(reader) # Skip the first row containing category titles 69 | for row in reader: 70 | for i, value in enumerate(row): 71 | if value.strip(): 72 | categories[headers[i]].append(value.strip()) 73 | 74 | if not all(categories.values()): 75 | raise ValueError("One or more categories in the CSV file are empty.") 76 | 77 | cls.cached_categories[file_path] = (categories, headers) 78 | return categories, headers 79 | 80 | @classmethod 81 | def INPUT_TYPES(cls): 82 | script_directory = os.path.dirname(os.path.abspath(__file__)) 83 | csv_directory = os.path.join(script_directory, "prompt_sets") 84 | csv_files = [f for f in os.listdir(csv_directory) if f.endswith('.csv')] 85 | csv_filename = CSVConfig.get_csv_filename() 86 | file_path = os.path.join(csv_directory, csv_filename) 87 | 88 | if not os.path.isfile(file_path): 89 | raise FileNotFoundError(f"File '{file_path}' cannot be found. Please make sure the CSV file exists in the 'prompt_sets' folder and restart ComfyUI.") 90 | 91 | categories, headers = cls.get_categories(file_path) 92 | 93 | inputs = { 94 | "required": { 95 | "csv_file": (csv_files, {"default": csv_filename}), 96 | "seed": ("INT", {"default": 42, "min": 0, "max": 2**32 - 1}), 97 | } 98 | } 99 | 100 | for i, header in enumerate(headers): 101 | category_options = ["None"] + categories[header] 102 | default_mode = "Fixed" 103 | if i == 0: 104 | default_mode = "Cycle" 105 | elif i == 1: 106 | default_mode = "Randomize" 107 | 108 | inputs["required"][f"{header}_mode"] = (["Fixed", "Randomize", "Cycle"], {"default": default_mode}) 109 | inputs["required"][f"{header}_val"] = (category_options, {"label": header}) 110 | inputs["required"][f"{header}_weight"] = ("FLOAT", {"default": 1.0, "min": 0.0, "max": 5.0, "step": 0.01, "precision": 2}) 111 | if i < len(headers) - 1: 112 | next_header = headers[i + 1] 113 | inputs["required"][f"{header}_to_{next_header}"] = ("STRING", {"default": ", "}) 114 | 115 | return inputs 116 | 117 | RETURN_TYPES = ("STRING",) 118 | FUNCTION = "build_prompt" 119 | 120 | CATEGORY = "Prompt Nodes" 121 | 122 | def build_prompt(self, csv_file, seed, **kwargs): 123 | script_directory = os.path.dirname(os.path.abspath(__file__)) 124 | csv_directory = os.path.join(script_directory, "prompt_sets") 125 | current_csv_file = CSVConfig.get_csv_filename() 126 | 127 | if csv_file != current_csv_file: 128 | CSVConfig.set_csv_filename(csv_file) 129 | raise RuntimeError("CSV file has been changed. Please restart ComfyUI to apply the changes.") 130 | 131 | random.seed(seed) # Set the random seed for reproducibility 132 | file_path = os.path.join(csv_directory, csv_file) 133 | categories, headers = self.get_categories(file_path) 134 | 135 | prompt_parts = [] 136 | for i, header in enumerate(headers): 137 | mode = kwargs.get(f"{header}_mode", "Fixed") 138 | weight = kwargs.get(f"{header}_weight", 1.0) 139 | choice = None 140 | 141 | if mode == "Randomize": 142 | choice = random.choice(categories[header]) 143 | elif mode == "Cycle": 144 | if header not in self.cycle_indices: 145 | selected_value = kwargs.get(f"{header}_val", "None") 146 | start_index = categories[header].index(selected_value) if selected_value in categories[header] else 0 147 | self.cycle_indices[header] = start_index 148 | choice = categories[header][self.cycle_indices[header]] 149 | self.cycle_indices[header] = (self.cycle_indices[header] + 1) % len(categories[header]) 150 | else: # Fixed 151 | choice = kwargs.get(f"{header}_val", "None") 152 | if choice == "None": 153 | continue 154 | 155 | if weight == 1.0: 156 | prompt_parts.append(choice) 157 | else: 158 | prompt_parts.append(f"({choice}:{weight:.2f})") 159 | 160 | if i < len(headers) - 1: 161 | next_header = headers[i + 1] 162 | separator = kwargs.get(f"{header}_to_{next_header}", ", ") 163 | prompt_parts.append(separator) 164 | 165 | prompt = "".join(prompt_parts) 166 | return (prompt,) 167 | 168 | NODE_CLASS_MAPPINGS = { 169 | "BuildPromptFromCSV": BuildPromptFromCSV, 170 | } 171 | 172 | NODE_DISPLAY_NAME_MAPPINGS = { 173 | "BuildPromptFromCSV": "Build Prompt From CSV", 174 | } 175 | 176 | __all__ = ['BuildPromptFromCSV'] 177 | -------------------------------------------------------------------------------- /examples/example_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jroc22/ComfyUI-CSV-prompt-builder/e219a40f519413f2961f0b5fd665b17d7d035487/examples/example_image.png -------------------------------------------------------------------------------- /examples/example_workflow.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_node_id": 21, 3 | "last_link_id": 28, 4 | "nodes": [ 5 | { 6 | "id": 6, 7 | "type": "CLIPTextEncode", 8 | "pos": [ 9 | 450, 10 | 180 11 | ], 12 | "size": { 13 | "0": 210, 14 | "1": 54 15 | }, 16 | "flags": {}, 17 | "order": 4, 18 | "mode": 0, 19 | "inputs": [ 20 | { 21 | "name": "clip", 22 | "type": "CLIP", 23 | "link": 3 24 | }, 25 | { 26 | "name": "text", 27 | "type": "STRING", 28 | "link": 27, 29 | "widget": { 30 | "name": "text" 31 | } 32 | } 33 | ], 34 | "outputs": [ 35 | { 36 | "name": "CONDITIONING", 37 | "type": "CONDITIONING", 38 | "links": [ 39 | 4 40 | ], 41 | "slot_index": 0 42 | } 43 | ], 44 | "properties": { 45 | "Node name for S&R": "CLIPTextEncode" 46 | }, 47 | "widgets_values": [ 48 | "beautiful scenery nature glass bottle landscape, , purple galaxy bottle," 49 | ], 50 | "color": "#232", 51 | "bgcolor": "#353" 52 | }, 53 | { 54 | "id": 7, 55 | "type": "CLIPTextEncode", 56 | "pos": [ 57 | 450, 58 | 270 59 | ], 60 | "size": { 61 | "0": 240, 62 | "1": 100 63 | }, 64 | "flags": {}, 65 | "order": 3, 66 | "mode": 0, 67 | "inputs": [ 68 | { 69 | "name": "clip", 70 | "type": "CLIP", 71 | "link": 5 72 | } 73 | ], 74 | "outputs": [ 75 | { 76 | "name": "CONDITIONING", 77 | "type": "CONDITIONING", 78 | "links": [ 79 | 6 80 | ], 81 | "slot_index": 0 82 | } 83 | ], 84 | "properties": { 85 | "Node name for S&R": "CLIPTextEncode" 86 | }, 87 | "widgets_values": [ 88 | "text, watermark" 89 | ], 90 | "color": "#322", 91 | "bgcolor": "#533" 92 | }, 93 | { 94 | "id": 3, 95 | "type": "KSampler", 96 | "pos": [ 97 | 780, 98 | 340 99 | ], 100 | "size": { 101 | "0": 315, 102 | "1": 262 103 | }, 104 | "flags": {}, 105 | "order": 6, 106 | "mode": 0, 107 | "inputs": [ 108 | { 109 | "name": "model", 110 | "type": "MODEL", 111 | "link": 1 112 | }, 113 | { 114 | "name": "positive", 115 | "type": "CONDITIONING", 116 | "link": 4 117 | }, 118 | { 119 | "name": "negative", 120 | "type": "CONDITIONING", 121 | "link": 6 122 | }, 123 | { 124 | "name": "latent_image", 125 | "type": "LATENT", 126 | "link": 2 127 | } 128 | ], 129 | "outputs": [ 130 | { 131 | "name": "LATENT", 132 | "type": "LATENT", 133 | "links": [ 134 | 7 135 | ], 136 | "slot_index": 0 137 | } 138 | ], 139 | "properties": { 140 | "Node name for S&R": "KSampler" 141 | }, 142 | "widgets_values": [ 143 | 595869481542266, 144 | "randomize", 145 | 20, 146 | 8, 147 | "euler", 148 | "normal", 149 | 1 150 | ] 151 | }, 152 | { 153 | "id": 8, 154 | "type": "VAEDecode", 155 | "pos": [ 156 | 1110, 157 | 340 158 | ], 159 | "size": { 160 | "0": 210, 161 | "1": 46 162 | }, 163 | "flags": {}, 164 | "order": 7, 165 | "mode": 0, 166 | "inputs": [ 167 | { 168 | "name": "samples", 169 | "type": "LATENT", 170 | "link": 7 171 | }, 172 | { 173 | "name": "vae", 174 | "type": "VAE", 175 | "link": 8 176 | } 177 | ], 178 | "outputs": [ 179 | { 180 | "name": "IMAGE", 181 | "type": "IMAGE", 182 | "links": [ 183 | 9 184 | ], 185 | "slot_index": 0 186 | } 187 | ], 188 | "properties": { 189 | "Node name for S&R": "VAEDecode" 190 | } 191 | }, 192 | { 193 | "id": 5, 194 | "type": "EmptyLatentImage", 195 | "pos": [ 196 | 80, 197 | 660 198 | ], 199 | "size": { 200 | "0": 315, 201 | "1": 106 202 | }, 203 | "flags": {}, 204 | "order": 0, 205 | "mode": 0, 206 | "outputs": [ 207 | { 208 | "name": "LATENT", 209 | "type": "LATENT", 210 | "links": [ 211 | 2 212 | ], 213 | "slot_index": 0 214 | } 215 | ], 216 | "properties": { 217 | "Node name for S&R": "EmptyLatentImage" 218 | }, 219 | "widgets_values": [ 220 | 512, 221 | 512, 222 | 1 223 | ] 224 | }, 225 | { 226 | "id": 9, 227 | "type": "SaveImage", 228 | "pos": [ 229 | 1340, 230 | 270 231 | ], 232 | "size": { 233 | "0": 526.2899169921875, 234 | "1": 599.5789794921875 235 | }, 236 | "flags": {}, 237 | "order": 8, 238 | "mode": 0, 239 | "inputs": [ 240 | { 241 | "name": "images", 242 | "type": "IMAGE", 243 | "link": 9 244 | } 245 | ], 246 | "properties": { 247 | "Node name for S&R": "SaveImage" 248 | }, 249 | "widgets_values": [ 250 | "ComfyUI" 251 | ] 252 | }, 253 | { 254 | "id": 12, 255 | "type": "ShowText|pysssss", 256 | "pos": [ 257 | 1350, 258 | 120 259 | ], 260 | "size": { 261 | "0": 521.7679443359375, 262 | "1": 102.68501281738281 263 | }, 264 | "flags": {}, 265 | "order": 5, 266 | "mode": 0, 267 | "inputs": [ 268 | { 269 | "name": "text", 270 | "type": "STRING", 271 | "link": 28, 272 | "widget": { 273 | "name": "text" 274 | } 275 | } 276 | ], 277 | "outputs": [ 278 | { 279 | "name": "STRING", 280 | "type": "STRING", 281 | "links": null, 282 | "shape": 6 283 | } 284 | ], 285 | "properties": { 286 | "Node name for S&R": "ShowText|pysssss" 287 | }, 288 | "widgets_values": [ 289 | "", 290 | "a big cat, in a weird hat" 291 | ], 292 | "color": "#232", 293 | "bgcolor": "#353" 294 | }, 295 | { 296 | "id": 4, 297 | "type": "CheckpointLoaderSimple", 298 | "pos": [ 299 | 80, 300 | 510 301 | ], 302 | "size": { 303 | "0": 315, 304 | "1": 98 305 | }, 306 | "flags": {}, 307 | "order": 1, 308 | "mode": 0, 309 | "outputs": [ 310 | { 311 | "name": "MODEL", 312 | "type": "MODEL", 313 | "links": [ 314 | 1 315 | ], 316 | "slot_index": 0 317 | }, 318 | { 319 | "name": "CLIP", 320 | "type": "CLIP", 321 | "links": [ 322 | 3, 323 | 5 324 | ], 325 | "slot_index": 1 326 | }, 327 | { 328 | "name": "VAE", 329 | "type": "VAE", 330 | "links": [ 331 | 8 332 | ], 333 | "slot_index": 2 334 | } 335 | ], 336 | "properties": { 337 | "Node name for S&R": "CheckpointLoaderSimple" 338 | }, 339 | "widgets_values": [ 340 | "v1-5-pruned.safetensors" 341 | ] 342 | }, 343 | { 344 | "id": 21, 345 | "type": "BuildPromptFromCSV", 346 | "pos": [ 347 | 80, 348 | 120 349 | ], 350 | "size": { 351 | "0": 309, 352 | "1": 346 353 | }, 354 | "flags": {}, 355 | "order": 2, 356 | "mode": 0, 357 | "outputs": [ 358 | { 359 | "name": "STRING", 360 | "type": "STRING", 361 | "links": [ 362 | 27, 363 | 28 364 | ], 365 | "shape": 3 366 | } 367 | ], 368 | "properties": { 369 | "Node name for S&R": "BuildPromptFromCSV" 370 | }, 371 | "widgets_values": [ 372 | "cat_hat.csv", 373 | 2071337890, 374 | "randomize", 375 | "Cycle", 376 | "None", 377 | 1, 378 | ", ", 379 | "Randomize", 380 | "None", 381 | 1 382 | ], 383 | "color": "#232", 384 | "bgcolor": "#353" 385 | } 386 | ], 387 | "links": [ 388 | [ 389 | 1, 390 | 4, 391 | 0, 392 | 3, 393 | 0, 394 | "MODEL" 395 | ], 396 | [ 397 | 2, 398 | 5, 399 | 0, 400 | 3, 401 | 3, 402 | "LATENT" 403 | ], 404 | [ 405 | 3, 406 | 4, 407 | 1, 408 | 6, 409 | 0, 410 | "CLIP" 411 | ], 412 | [ 413 | 4, 414 | 6, 415 | 0, 416 | 3, 417 | 1, 418 | "CONDITIONING" 419 | ], 420 | [ 421 | 5, 422 | 4, 423 | 1, 424 | 7, 425 | 0, 426 | "CLIP" 427 | ], 428 | [ 429 | 6, 430 | 7, 431 | 0, 432 | 3, 433 | 2, 434 | "CONDITIONING" 435 | ], 436 | [ 437 | 7, 438 | 3, 439 | 0, 440 | 8, 441 | 0, 442 | "LATENT" 443 | ], 444 | [ 445 | 8, 446 | 4, 447 | 2, 448 | 8, 449 | 1, 450 | "VAE" 451 | ], 452 | [ 453 | 9, 454 | 8, 455 | 0, 456 | 9, 457 | 0, 458 | "IMAGE" 459 | ], 460 | [ 461 | 27, 462 | 21, 463 | 0, 464 | 6, 465 | 1, 466 | "STRING" 467 | ], 468 | [ 469 | 28, 470 | 21, 471 | 0, 472 | 12, 473 | 0, 474 | "STRING" 475 | ] 476 | ], 477 | "groups": [], 478 | "config": {}, 479 | "extra": { 480 | "ds": { 481 | "scale": 0.7513148009015777, 482 | "offset": { 483 | "0": 252.23194885253906, 484 | "1": 226.429443359375 485 | } 486 | } 487 | }, 488 | "version": 0.4 489 | } -------------------------------------------------------------------------------- /prompt_sets/_csv_config.json: -------------------------------------------------------------------------------- 1 | {"csv_file": "cat_hat.csv"} -------------------------------------------------------------------------------- /prompt_sets/cat_hat.csv: -------------------------------------------------------------------------------- 1 | CAT,HAT 2 | a cat,in a hat 3 | a kitty cat,in a big hat 4 | a small cat,in a small hat 5 | a big cat,in a medium hat 6 | a cute cat,in a tall hat 7 | a medium cat,in a short hat 8 | ,in a fancy hat 9 | ,in a classy hat 10 | ,in a cool hat 11 | ,in a weird hat 12 | ,in a spooky hat -------------------------------------------------------------------------------- /prompt_sets/example.csv: -------------------------------------------------------------------------------- 1 | SUBJECT,CLOTHING,BACKGROUND 2 | a man,wearing a track suit,at a sunset beach with golden hues reflecting on the water 3 | a woman,wearing a suit,"on a forest trail surrounded by tall, lush trees" 4 | ,wearing a bathing suit,"in snowy mountains with a crisp, clear blue sky" 5 | ,wearing a bikini,overlooking a city skyline at night with twinkling lights 6 | ,wearing a school uniform,in the middle of desert dunes under a blazing sun 7 | ,dressed like a police officer,by the ocean waves crashing against the shore 8 | ,dressed like a firefighter,on a rainy city street with glistening wet pavements 9 | ,wearing a business casual outfit,in a vibrant flower garden blooming with colorful blossoms 10 | ,wearing a casual dress,under a starry night sky with a bright full moon 11 | ,wearing a party dress,in an autumn park with falling leaves in shades of red and orange 12 | ,wearing athletic wear,in a tropical rainforest with dense foliage and exotic plants 13 | ,wearing yoga pants and a tank top,"on rocky cliffs overlooking a vast, deep blue sea" 14 | ,wearing a hoodie and jeans,"by a serene lake with calm, reflective waters" 15 | ,wearing a tuxedo,among ancient ruins with a sense of historical mystery 16 | ,wearing a cocktail dress,under cherry blossom trees in full pink bloom 17 | ,wearing a ball gown,near a lighthouse by the sea with waves lapping at the rocks 18 | ,wearing pajamas,in a golden wheat field swaying in the breeze 19 | ,wearing a trench coat and scarf,within a coral reef teeming with colorful marine life 20 | ,wearing a leather jacket and boots,in a dramatic volcano landscape with flowing lava 21 | ,wearing a sundress and sandals,at a cozy cabin in the woods with smoke curling from the chimney 22 | ,wearing a cheerleader outfit,in a bustling marketplace filled with vibrant stalls and shoppers 23 | ,wearing a nurse's uniform,at a tranquil zen garden with carefully raked gravel and bonsai trees 24 | ,dressed like a pilot,in a foggy marsh with tall reeds and mysterious waters 25 | ,dressed like a chef,on a rustic farm with a red barn and grazing animals 26 | ,dressed like a construction worker,at a carnival with bright lights and colorful rides 27 | ,wearing a swimsuit and cover-up,in a medieval castle courtyard with cobblestone paths 28 | ,wearing overalls and a t-shirt,by a riverside with a gently flowing stream and lush greenery 29 | ,wearing a kimono,on a sunny hillside with wildflowers and butterflies 30 | ,wearing a lab coat,in a snowy forest with evergreen trees covered in snow 31 | ,wearing a military uniform,in a quaint village with charming cottages and narrow streets 32 | ,wearing a sailor outfit,at a sunflower field with bright yellow blooms stretching to the horizon 33 | ,wearing a magician's outfit,in a lavender field with rows of purple flowers and a distant farmhouse 34 | ,wearing a superhero costume,by a waterfall cascading into a clear pool 35 | ,wearing a fairy costume,at a busy harbor with boats and seagulls 36 | ,wearing a vampire costume,in a misty valley surrounded by towering mountains 37 | ,wearing a wizard's robe,on a tropical beach with palm trees and white sand 38 | ,wearing a medieval knight's armor,in an enchanted forest with glowing mushrooms and magical creatures 39 | ,,at an ancient temple with ornate carvings and statues 40 | ,,on a quiet country road lined with old oak trees 41 | ,,in an open meadow with rolling hills and grazing deer 42 | ,,"in a bustling city park with joggers, cyclists, and people relaxing on benches" 43 | ,,at a serene monastery with peaceful gardens and ancient architecture 44 | ,,in a dense jungle with vines hanging from towering trees 45 | ,,"by a crystal-clear mountain stream with smooth, round pebbles" 46 | ,,on a wind-swept prairie with tall grasses bending in the breeze 47 | ,,at a picturesque vineyard with rows of grapevines and a distant chateau 48 | ,,"in a serene bamboo forest with tall, slender bamboo stalks" 49 | ,,at a colorful street festival with banners and performers 50 | ,,"by a quiet canal with charming, flower-decked houseboats" 51 | ,,in a serene lakeside cabin with a wooden dock extending into the water 52 | ,,at a historical battlefield with rolling hills and monuments 53 | ,,in an icy tundra with snowdrifts and a distant polar bear 54 | ,,by a cozy mountain chalet with snow-capped peaks in the background 55 | ,,in a grand ballroom with sparkling chandeliers and polished floors 56 | ,,at an orchard with apple trees laden with ripe fruit 57 | ,,in a quiet library with towering bookshelves and soft lighting 58 | ,,by an ancient stone bridge over a gently flowing river 59 | ,,in a futuristic cityscape with towering skyscrapers and flying vehicles 60 | ,,at a peaceful riverside gazebo with hanging lanterns 61 | ,,"on a sunny beach boardwalk with shops, restaurants, and a ferris wheel" 62 | ,,in a vibrant aquarium with colorful fish and coral reefs 63 | ,,at an open-air amphitheater with a grand stage and tiered seating 64 | ,,on a snowy village street with festive holiday decorations 65 | ,,by a serene koi pond with lily pads and gentle ripples 66 | ,,at a bustling port with cargo ships and cranes 67 | ,,in a majestic redwood forest with towering trees and dappled sunlight 68 | ,,at a rustic mountain lodge with a roaring fireplace and cozy interiors 69 | ,,on a tranquil riverbank with willow trees and a gentle current 70 | ,,in a mystical cave with glowing crystals and underground streams 71 | ,,at a lively beach party with bonfires and music 72 | ,,in a scenic canyon with layered rock formations and a winding river 73 | ,,at a quaint coastal village with colorful houses and fishing boats 74 | ,,in a bustling train station with travelers and vintage locomotives 75 | ,,by a tranquil monastery garden with stone lanterns and winding paths 76 | ,,in a lush botanical garden with exotic plants and glass conservatories 77 | ,,at a serene hilltop with panoramic views of the countryside 78 | ,,on a peaceful suburban street with neatly kept lawns and family homes 79 | ,,in an eerie swamp with gnarled trees and misty waters 80 | ,,at a grand palace with ornate architecture and lush gardens 81 | ,,"in a bustling souk with spices, textiles, and vibrant market stalls" -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "comfyui-csv-prompt-builder" 3 | description = "This is a simple node for creating prompts using a .csv file. I created this node as an easy way to output different prompts each time a workflow is run." 4 | version = "1.0.0" 5 | license = { file = "LICENSE" } 6 | 7 | [project.urls] 8 | Repository = "https://github.com/jroc22/ComfyUI-CSV-prompt-builder" 9 | # Used by Comfy Registry https://comfyregistry.org 10 | 11 | [tool.comfy] 12 | PublisherId = "jroc22" 13 | DisplayName = "ComfyUI-CSV-prompt-builder" 14 | Icon = "" 15 | --------------------------------------------------------------------------------