diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..3875bac --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Antonio Cordero Balcazar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 0ad8105..9db80a2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,29 @@ -# Send To Negative +# Send To Negative for Stable Diffusion WebUI ## Purpose -This extension allows the use of negative weights in the prompt and moves them to the negative prompt. This allows useful tricks when using a wildcard extension since you can add negative parts from the positive prompt. It will process recursive weights (one inside of another), but won't calculate the multiplied weights. +This extension allows the marking of parts of the prompt and moves them to the negative prompt. This allows useful tricks when using a wildcard extension since you can add negative parts from choices made in the positive prompt. -The extension must be loaded after the wildcard extension. With the "Dynamic Prompts" extension this happens by default due to both extensions default folder names. +The extension must be loaded after the wildcard extension. With the "Dynamic Prompts" extension this happens by default due to default folder names for both extensions. + +## Format + +The format is like this: + +```text + +``` + +And an optional position in the negative prompt can be specified like this: + +```text + +``` + +Where position can be: + +* s: at the start +* e: at the end +* pN: at the position of the insertion point "" with N being 0-9 + +The default position is the start, and also if the insertion point is not found. diff --git a/scripts/sendtonegative.py b/scripts/sendtonegative.py index c05c89c..40acdc8 100644 --- a/scripts/sendtonegative.py +++ b/scripts/sendtonegative.py @@ -3,11 +3,126 @@ from modules.processing import StableDiffusionProcessing import modules.scripts as scripts import re -VERSION = '0.1' +VERSION = "0.2" + +# TODO : support marking in the negative prompt to move to the positive +# TODO : add separator and ignoreRepeats to ui +# TODO : add tests +# TODO : replace regex with proper parsing to detect recursion logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) +strStart = "" +strParamStart = "!" +strParamEnd = "!" +""" +Default format: or + with x = s (start position) e (end position) p (specified position) i (insertion point) + both p and i have to be followed by a number 0-9 + the insertion point does not accept content +""" +escapeSequence = r"(? 0: + if content not in alreadyProcessed: + if ignoreRepeats: + alreadyProcessed.append(content) + logger.debug("Processing content: %s", content) + if position is "e": + addAtEnd.append(content) + elif position.startswith("p"): + n = int(position[1]) + addAtInsertionPoint[n].append(content) + else: # position is "s" or invalid + addAtStart.append(content) + else: + logger.warn("Ignoring repeated content: %s", content) + prompt = prompt.replace(match[0], "") + + # Add content to insertion points + for n in range(10): + if insertionPointPositions[n] >= 0: + ipp = insertionPointPositions[n] + ipl = len(insertionPointTags[n]) + if negative_prompt[ipp - len(separator) : ipp] == separator: + ipp -= len(separator) # adjust for existing start separator + ipl += len(separator) + addAtInsertionPoint[n].insert(0, negative_prompt[:ipp]) + if negative_prompt[ipp + ipl : ipp + ipl + len(separator)] == separator: + ipl += len(separator) # adjust for existing end separator + endPart = negative_prompt[ipp + ipl :] + if len(endPart) > 0: + addAtInsertionPoint[n].append(endPart) + negative_prompt = separator.join(addAtInsertionPoint[n]) + else: + ipp = 0 + if negative_prompt.startswith(separator): + ipp = len(separator) + addAtInsertionPoint[n].append(negative_prompt[ipp:]) + negative_prompt = separator.join(addAtInsertionPoint[n]) + + # Add content to start + if len(addAtStart) > 0: + if len(negative_prompt) > 0: + ipp = 0 + if negative_prompt.startswith(separator): + ipp = len(separator) # adjust for existing end separator + addAtStart.append(negative_prompt[ipp:]) + negative_prompt = separator.join(addAtStart) + + # Add content to end + if len(addAtEnd) > 0: + if len(negative_prompt) > 0: + ipl = len(negative_prompt) + if negative_prompt.endswith(separator): + ipl -= len(separator) # adjust for existing start separator + addAtEnd.insert(0, negative_prompt[:ipl]) + negative_prompt = separator.join(addAtEnd) + + return prompt, negative_prompt + except Exception as e: + logger.exception(e) + return original_prompt, original_negative_prompt + + class Script(scripts.Script): def __init__(self): pass @@ -19,27 +134,7 @@ class Script(scripts.Script): return scripts.AlwaysVisible def process(self, p: StableDiffusionProcessing, *args, **kwargs): - try: - find = r"(((?