forked from andreszs/comfyui-styles-all
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sdxl_prompt_styler.py
198 lines (155 loc) · 8.64 KB
/
sdxl_prompt_styler.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
import json
import pathlib
from collections import defaultdict
class Template:
def __init__(self, prompt, negative_prompt, **kwargs):
self.prompt = prompt
self.negative_prompt = negative_prompt
def split_template_advanced(self):
if "{prompt} ." in self.prompt:
template_prompt_g, template_prompt_l = self.prompt.split("{prompt} .", 1)
template_prompt_g = template_prompt_g.strip() + " {prompt}"
template_prompt_l = template_prompt_l.strip()
else:
template_prompt_g = self.prompt
template_prompt_l = ""
return template_prompt_g, template_prompt_l
def replace_prompts(self, positive_prompt, negative_prompt):
positive_result = self.prompt.replace('{prompt}', positive_prompt)
negative_result = ', '.join(x for x in (self.negative_prompt, negative_prompt) if x)
return positive_result, negative_result
def replace_prompts_advanced(self, positive_prompt_g, positive_prompt_l, negative_prompt, negative_prompt_to):
template_prompt_g, template_prompt_l_template = self.split_template_advanced()
text_g_positive = template_prompt_g.replace("{prompt}", positive_prompt_g)
text_l_positive = ', '.join(x for x in (template_prompt_l_template, positive_prompt_l) if x)
text_positive = f"{text_g_positive} . {text_l_positive}" if text_l_positive else text_g_positive
text_negative = ', '.join(x for x in (self.negative_prompt, negative_prompt) if x)
text_g_negative = ""
if negative_prompt_to in ("Both", "G only"):
text_g_negative = text_negative
text_l_negative = ""
if negative_prompt_to in ("Both", "L only"):
text_l_negative = text_negative
return text_g_positive, text_l_positive, text_positive, text_g_negative, text_l_negative, text_negative
class StylerData:
def __init__(self, datadir=None):
self._data = defaultdict(dict)
if datadir is None:
datadir = pathlib.Path(__file__).parent / 'data'
for j in datadir.glob('*/*.json'):
try:
with j.open('r') as f:
content = json.load(f)
group = j.parent.name
for template in content:
self._data[group][template['name']] = Template(**template)
except PermissionError:
print(f"Warning: No read permissions for file {j}")
except KeyError:
print(f"Warning: Malformed data in {j}")
def __getitem__(self, item):
return self._data[item]
def keys(self):
return self._data.keys()
styler_data = StylerData()
class SDXLPromptStyler:
menus = ()
@classmethod
def INPUT_TYPES(cls):
menus = {menu: (list(styler_data[menu].keys()), ) for menu in cls.menus}
inputs = {
"required": {
"text_positive": ("STRING", {"default": "", "multiline": True}),
"text_negative": ("STRING", {"default": "", "multiline": True}),
**menus,
"log_prompt": ("BOOLEAN", {"default": True, "label_on": "Yes", "label_off": "No"}),
},
}
return inputs
RETURN_TYPES = ('STRING','STRING',)
RETURN_NAMES = ('text_positive','text_negative',)
FUNCTION = 'prompt_styler'
CATEGORY = 'AegisFlow/stylers'
def prompt_styler(self, text_positive, text_negative, log_prompt, **kwargs):
text_positive_styled, text_negative_styled = text_positive, text_negative
for menu, selection in kwargs.items():
text_positive_styled, text_negative_styled = styler_data[menu][selection].replace_prompts(text_positive_styled, text_negative_styled)
if log_prompt:
for menu, selection in kwargs.items():
print(f"{menu}: {selection}")
print(f"text_positive: {text_positive}")
print(f"text_negative: {text_negative}")
print(f"text_positive_styled: {text_positive_styled}")
print(f"text_negative_styled: {text_negative_styled}")
return text_positive_styled, text_negative_styled
class SDXLPromptStylerAdvanced:
menus = () # when we subclass, we will put the list of menus (directories) here
@classmethod
def INPUT_TYPES(cls):
menus = {menu: (list(styler_data[menu].keys()), ) for menu in cls.menus}
return {
"required": {
"text_positive_g": ("STRING", {"default": "", "multiline": True}),
"text_positive_l": ("STRING", {"default": "", "multiline": True}),
"text_negative": ("STRING", {"default": "", "multiline": True}),
**menus,
"negative_prompt_to": (["Both", "G only", "L only"], {"default":"Both"}),
"log_prompt": ("BOOLEAN", {"default": True, "label_on": "Yes", "label_off": "No"}),
},
}
RETURN_TYPES = ('STRING','STRING','STRING','STRING','STRING','STRING',)
RETURN_NAMES = ('text_positive_g','text_positive_l','text_positive','text_negative_g','text_negative_l','text_negative',)
FUNCTION = 'prompt_styler_advanced'
CATEGORY = 'AegisFlow/stylers'
def prompt_styler_advanced(self, text_positive_g, text_positive_l, text_negative, negative_prompt_to, log_prompt, **kwargs):
text_positive_g_styled, text_positive_l_styled, text_negative_styled = text_positive_g, text_positive_l, text_negative
text_positive_styled, text_negative_g_styled, text_negative_l_styled = "", "", ""
for menu, selection in kwargs.items():
text_positive_g_styled, text_positive_l_styled, text_positive_styled, text_negative_g_styled, text_negative_l_styled, text_negative_styled = styler_data[menu][selection].replace_prompts_advanced(text_positive_l_styled, text_positive_g_styled, text_negative_styled, negative_prompt_to)
if log_prompt:
for menu, selection in kwargs.items():
print(f"{menu}: {selection}")
print(f"text_positive_g: {text_positive_g}")
print(f"text_positive_l: {text_positive_l}")
print(f"text_negative: {text_negative}")
print(f"text_positive_g_styled: {text_positive_g_styled}")
print(f"text_positive_l_styled: {text_positive_l_styled}")
print(f"text_positive_styled: {text_positive_styled}")
print(f"text_negative_g_styled: {text_negative_g_styled}")
print(f"text_negative_l_styled: {text_negative_l_styled}")
print(f"text_negative_styled: {text_negative_styled}")
return text_positive_g_styled, text_positive_l_styled, text_positive_styled, text_negative_g_styled, text_negative_l_styled, text_negative_styled
NODES = {
# define your nodes here
# first the friendly name, then a sequence of strings
# corresponding to subdirectories of the data dir.
# individual stylers have one menu item
# build them automatically from the scanned directories
**{f'{name.title()} Styler': (name, ) for name in styler_data.keys()},
# alternatively you can list every single-menu node like this:
# 'Camera Styler': ('camera', ),
# etc...
# perfection styler has everything except artist and milehigh
# we can define it by excluding those
# downside of this method is you have no control over the order
#'ComfyUI Styler': [x for x in styler_data.keys() if x not in ('artist', 'milehigh')],
# alternatively define it manually, and you can reorder the items
'ComfyUI Styler': (
'adjective', 'Aesthetic', 'Anime', 'artist', 'breast_state', 'camera', 'camera_angles',
'clothing_state', 'clothing_style', 'composition', 'depth', 'environment', 'face', 'Fantasy',
'filter', 'focal point', 'focus', 'Gothic', 'Halloween', 'lighting', 'Line_Art',
'LUTs', 'materials', 'milehigh', 'mood', 'Movie_Poster', 'nouns', 'preposition',
'Punk', 'subject', 'theme', 'timeofday', 'Travel_Poster', 'verbing'
),
}
for k, v in NODES.items():
assert not isinstance(v, str), f"Error: {k} has a string instead of a sequence for its menu list. Did you forget a comma?"
# now we subclass the base node classes dynamically using 3-arg type
NODE_CLASS_MAPPINGS = {
**{name.replace(' ', ''): type(name.replace(' ', ''), (SDXLPromptStyler, ), {'menus': menus}) for name, menus in NODES.items()},
**{name.replace(' ', '') + 'Advanced': type(name.replace(' ', '')+'Advanced', (SDXLPromptStylerAdvanced, ), {'menus': menus}) for name, menus in NODES.items()},
}
NODE_DISPLAY_NAME_MAPPINGS = {
**{name.replace(' ', ''): name for name in NODES.keys()},
**{name.replace(' ', '') + 'Advanced': name + ' (Advanced)' for name in NODES.keys()},
}