Skip to content

Commit

Permalink
feat(styles): Support multiple JSON files and update README
Browse files Browse the repository at this point in the history
- Enable loading from multiple JSON files in the styles directory.
- Handle duplicates by appending a suffix to style names.
- Update README with migration steps for users with custom styles.
- Add guidance on managing included JSON files.
  • Loading branch information
twri committed Aug 11, 2023
1 parent ff89f24 commit 2ec1d63
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 132 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,25 @@ Custom node for ComfyUI
-----------
![SDXL Prompt Styler Screenshot](examples/sdxl_prompt_styler.png)

SDXL Prompt Styler is a node that enables you to style prompts based on predefined templates stored in a JSON file. The node specifically replaces a {prompt} placeholder in the 'prompt' field of each template with provided positive text.
SDXL Prompt Styler is a node that enables you to style prompts based on predefined templates stored in multiple JSON files. The node specifically replaces a {prompt} placeholder in the 'prompt' field of each template with provided positive text.

The node also effectively manages negative prompts. If negative text is provided, the node combines this with the 'negative_prompt' field from the template. If no negative text is supplied, the system defaults to using the 'negative_prompt' from the JSON template. This flexibility enables the creation of a diverse and specific range of negative prompts.

## Important Update:
With the latest changes, the file structure and naming convention for style JSONs have been modified. If you've added or made changes to the `sdxl_styles.json` file in the past, follow these steps to ensure your styles remain intact:

1. **Backup**: Before pulling the latest changes, back up your `sdxl_styles.json` to a safe location.
2. **Migration**: After updating the repository, create a new JSON file in the styles directory. Move your custom styles from the backup of `sdxl_styles.json` into this new file.
3. **Unique Style Names**: While the system now detects duplicates and appends a suffix to ensure uniqueness, it's a best practice to ensure your style names are originally unique to prevent any potential confusion.
4. **Managing Included JSON Files**: If you prefer not to load specific included JSON files, consider renaming or moving them to a different location outside of the styles directory. The system will load all JSON files present in the specified directory.

## New Features:

1. **Loading from Multiple JSON Files:** The system can now load styles from multiple JSON files present in the specified directory, ensuring the uniqueness of style names by appending a suffix to duplicates.
2. **Enhanced Error Handling:** Improved error handling for file reading, data validity, and template replacement functions.

---

### Usage Example with SDXL Prompt Styler

Template example from a JSON file:
Expand Down
86 changes: 58 additions & 28 deletions sdxl_prompt_styler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,70 @@
import os

def read_json_file(file_path):
"""
Reads the content of a JSON file and returns it as a Python data structure.
"""
if not os.access(file_path, os.R_OK):
print(f"Warning: No read permissions for file {file_path}")
return None

try:
# Open file, load JSON content into python dictionary, and return it.
with open(file_path, 'r') as file:
json_data = json.load(file)
return json_data
with open(file_path, 'r', encoding='utf-8') as file:
content = json.load(file)
# Check if the content matches the expected format.
if not all(['name' in item and 'prompt' in item and 'negative_prompt' in item for item in content]):
print(f"Warning: Invalid content in file {file_path}")
return None
return content
except Exception as e:
print(f"An error occurred: {str(e)}")
print(f"An error occurred while reading {file_path}: {str(e)}")
return None


def read_sdxl_styles(json_data):
# Check that data is a list
"""
Extracts style names from the provided data.
"""
if not isinstance(json_data, list):
print("Error: input data must be a list")
return None

names = []

# Iterate over each item in the data list
for item in json_data:
# Check that the item is a dictionary
if isinstance(item, dict):
# Check that 'name' is a key in the dictionary
if 'name' in item:
# Append the value of 'name' to the names list
names.append(item['name'])
return []

return [item['name'] for item in json_data if isinstance(item, dict) and 'name' in item]

def get_all_json_files(directory):
"""
Retrieves all JSON files present in the specified directory.
"""
return [os.path.join(directory, file) for file in os.listdir(directory) if file.endswith('.json') and os.path.isfile(os.path.join(directory, file))]


def load_styles_from_directory(directory):
"""
Loads style names and combined data from all JSON files in the directory.
Ensures style names are unique by appending a suffix to duplicates.
"""
json_files = get_all_json_files(directory)
combined_data = []
seen = set()

for json_file in json_files:
json_data = read_json_file(json_file)
if json_data:
for item in json_data:
original_style = item['name']
style = original_style
suffix = 1
while style in seen:
style = f"{original_style}_{suffix}"
suffix += 1
item['name'] = style
seen.add(style)
combined_data.append(item)

unique_style_names = [item['name'] for item in combined_data if isinstance(item, dict) and 'name' in item]

return combined_data, unique_style_names

return names

def read_sdxl_templates_replace_and_combine(json_data, template_name, positive_prompt, negative_prompt):
try:
Expand Down Expand Up @@ -67,15 +104,8 @@ def __init__(self):

@classmethod
def INPUT_TYPES(self):
# Get current file's directory
p = os.path.dirname(os.path.realpath(__file__))
# Construct 'sdxl_styles.json' path
file_path = os.path.join(p, 'sdxl_styles.json')

# Read JSON from file
self.json_data = read_json_file(file_path)
# Retrieve styles from JSON data
styles = read_sdxl_styles(self.json_data)
current_directory = os.path.dirname(os.path.realpath(__file__))
self.json_data, styles = load_styles_from_directory(current_directory)

return {
"required": {
Expand Down
7 changes: 7 additions & 0 deletions sdxl_styles_base.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{
"name": "base",
"prompt": "{prompt}",
"negative_prompt": ""
}
]
87 changes: 87 additions & 0 deletions sdxl_styles_sai.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
[
{
"name": "sai-3d-model",
"prompt": "professional 3d model {prompt} . octane render, highly detailed, volumetric, dramatic lighting",
"negative_prompt": "ugly, deformed, noisy, low poly, blurry, painting"
},
{
"name": "sai-analog film",
"prompt": "analog film photo {prompt} . faded film, desaturated, 35mm photo, grainy, vignette, vintage, Kodachrome, Lomography, stained, highly detailed, found footage",
"negative_prompt": "painting, drawing, illustration, glitch, deformed, mutated, cross-eyed, ugly, disfigured"
},
{
"name": "sai-anime",
"prompt": "anime artwork {prompt} . anime style, key visual, vibrant, studio anime, highly detailed",
"negative_prompt": "photo, deformed, black and white, realism, disfigured, low contrast"
},
{
"name": "sai-cinematic",
"prompt": "cinematic film still {prompt} . shallow depth of field, vignette, highly detailed, high budget, bokeh, cinemascope, moody, epic, gorgeous, film grain, grainy",
"negative_prompt": "anime, cartoon, graphic, text, painting, crayon, graphite, abstract, glitch, deformed, mutated, ugly, disfigured"
},
{
"name": "sai-comic book",
"prompt": "comic {prompt} . graphic illustration, comic art, graphic novel art, vibrant, highly detailed",
"negative_prompt": "photograph, deformed, glitch, noisy, realistic, stock photo"
},
{
"name": "sai-craft clay",
"prompt": "play-doh style {prompt} . sculpture, clay art, centered composition, Claymation",
"negative_prompt": "sloppy, messy, grainy, highly detailed, ultra textured, photo"
},
{
"name": "sai-digital art",
"prompt": "concept art {prompt} . digital artwork, illustrative, painterly, matte painting, highly detailed",
"negative_prompt": "photo, photorealistic, realism, ugly"
},
{
"name": "sai-enhance",
"prompt": "breathtaking {prompt} . award-winning, professional, highly detailed",
"negative_prompt": "ugly, deformed, noisy, blurry, distorted, grainy"
},
{
"name": "sai-fantasy art",
"prompt": "ethereal fantasy concept art of {prompt} . magnificent, celestial, ethereal, painterly, epic, majestic, magical, fantasy art, cover art, dreamy",
"negative_prompt": "photographic, realistic, realism, 35mm film, dslr, cropped, frame, text, deformed, glitch, noise, noisy, off-center, deformed, cross-eyed, closed eyes, bad anatomy, ugly, disfigured, sloppy, duplicate, mutated, black and white"
},
{
"name": "sai-isometric",
"prompt": "isometric style {prompt} . vibrant, beautiful, crisp, detailed, ultra detailed, intricate",
"negative_prompt": "deformed, mutated, ugly, disfigured, blur, blurry, noise, noisy, realistic, photographic"
},
{
"name": "sai-line art",
"prompt": "line art drawing {prompt} . professional, sleek, modern, minimalist, graphic, line art, vector graphics",
"negative_prompt": "anime, photorealistic, 35mm film, deformed, glitch, blurry, noisy, off-center, deformed, cross-eyed, closed eyes, bad anatomy, ugly, disfigured, mutated, realism, realistic, impressionism, expressionism, oil, acrylic"
},
{
"name": "sai-lowpoly",
"prompt": "low-poly style {prompt} . low-poly game art, polygon mesh, jagged, blocky, wireframe edges, centered composition",
"negative_prompt": "noisy, sloppy, messy, grainy, highly detailed, ultra textured, photo"
},
{
"name": "sai-neonpunk",
"prompt": "neonpunk style {prompt} . cyberpunk, vaporwave, neon, vibes, vibrant, stunningly beautiful, crisp, detailed, sleek, ultramodern, magenta highlights, dark purple shadows, high contrast, cinematic, ultra detailed, intricate, professional",
"negative_prompt": "painting, drawing, illustration, glitch, deformed, mutated, cross-eyed, ugly, disfigured"
},
{
"name": "sai-origami",
"prompt": "origami style {prompt} . paper art, pleated paper, folded, origami art, pleats, cut and fold, centered composition",
"negative_prompt": "noisy, sloppy, messy, grainy, highly detailed, ultra textured, photo"
},
{
"name": "sai-photographic",
"prompt": "cinematic photo {prompt} . 35mm photograph, film, bokeh, professional, 4k, highly detailed",
"negative_prompt": "drawing, painting, crayon, sketch, graphite, impressionist, noisy, blurry, soft, deformed, ugly"
},
{
"name": "sai-pixel art",
"prompt": "pixel-art {prompt} . low-res, blocky, pixel art style, 8-bit graphics",
"negative_prompt": "sloppy, messy, blurry, noisy, highly detailed, ultra textured, photo, realistic"
},
{
"name": "sai-texture",
"prompt": "texture {prompt} top down close-up",
"negative_prompt": "ugly, deformed, noisy, blurry"
}
]
Loading

0 comments on commit 2ec1d63

Please sign in to comment.