80 lines
5.5 KiB
Python
80 lines
5.5 KiB
Python
"""PromptForge Action node: a pose or act plus place, camera, light and outfit for still-image prompts."""
|
|
import time
|
|
|
|
from . import image_actions, random_picks
|
|
|
|
|
|
class PROMPTFORGE_ImageAction:
|
|
CATEGORY = "🔮 PromptForge/Prompts"
|
|
RETURN_TYPES = ("PROMPTFORGE_ACTION", "STRING")
|
|
RETURN_NAMES = ("ACTION", "BRIEF")
|
|
FUNCTION = "build"
|
|
DESCRIPTION = ("A pose, place and camera as a binding brief for the prompt writer, for any image model. "
|
|
"Works from an input image or from words, for one person or two. Every picker has a random option rolled per run.")
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"category": ([random_picks.RANDOM] + list(image_actions.CATEGORIES), {"default": image_actions.CATEGORIES[0]}),
|
|
"action": ([random_picks.RANDOM] + [f"{group} · {label}" for label, (_, group, _) in image_actions.ACTIONS.items()],
|
|
{"default": f"{next(iter(image_actions.ACTIONS.values()))[1]} · {next(iter(image_actions.ACTIONS))}"}),
|
|
"source": (list(image_actions.SUBJECT_SOURCES), {"default": "input image",
|
|
"tooltip": "input image: keep the person from the connected image. text description: the writer invents the person from the text below."}),
|
|
"place": ([random_picks.RANDOM] + list(image_actions.PLACES), {"default": "auto"}),
|
|
"camera": ([random_picks.RANDOM] + list(image_actions.CAMERAS), {"default": "auto"}),
|
|
"lighting": ([random_picks.RANDOM] + list(image_actions.LIGHTING), {"default": "auto"}),
|
|
"outfit": ([random_picks.RANDOM] + list(image_actions.OUTFITS), {"default": "as in the image"}),
|
|
"style": ([random_picks.RANDOM] + list(image_actions.STYLES), {"default": "auto"}),
|
|
},
|
|
"optional": {
|
|
"subject_1": ("STRING", {"default": "", "multiline": True, "placeholder": "Who the first person is. Empty keeps the person from the input image.", "tooltip": "Identity text for the first person. With an image source this adds to the image; without one it is all the writer knows."}),
|
|
"subject_2": ("STRING", {"default": "", "multiline": True, "placeholder": "Who the second person is. Empty = the other person in the image, or an invented man.", "tooltip": "Identity text for the second person in a couple act."}),
|
|
"notes": ("STRING", {"default": "", "multiline": True, "placeholder": "Anything else: mood, props, weather, exact framing.", "tooltip": "Extra binding detail for this shot."}),
|
|
"overrides": ("STRING", {"default": "", "multiline": True, "placeholder": "Hard overrides, for example: red dress, no glasses.", "tooltip": "Passed last and wins over the rest of the brief."}),
|
|
"instructions": ("STRING", {"default": "", "multiline": True, "placeholder": "Extra instructions for this shot: mood, prop, exact framing. Replaces the enhancer prompt while the action is on.", "tooltip": "Custom instructions appended to the action brief."}),
|
|
},
|
|
"hidden": {"unique_id": "UNIQUE_ID"},
|
|
}
|
|
|
|
@classmethod
|
|
def IS_CHANGED(cls, **values):
|
|
return time.time_ns() if random_picks.RANDOM in values.values() else "static"
|
|
|
|
def build(self, category, action, source, place, camera, lighting, outfit, style,
|
|
subject_1="", subject_2="", notes="", overrides="", instructions="", unique_id=None):
|
|
picks = []
|
|
if action == random_picks.RANDOM:
|
|
pool = [label for label, (_, group, _) in image_actions.ACTIONS.items()
|
|
if category == random_picks.RANDOM or group == category]
|
|
action = random_picks.pick(pool)
|
|
else:
|
|
action = action.split(" · ", 1)[-1]
|
|
if category == random_picks.RANDOM:
|
|
category = image_actions.ACTIONS[action][1]
|
|
picks.append(("Category", category))
|
|
picks.append(("Action", action))
|
|
if place == random_picks.RANDOM:
|
|
place = random_picks.pick(key for key in image_actions.PLACES if key != "auto")
|
|
picks.append(("Place", place))
|
|
if camera == random_picks.RANDOM:
|
|
count = image_actions.ACTIONS[action][0]
|
|
camera = random_picks.pick(key for key in image_actions.CAMERAS
|
|
if key != "auto" and not (count == 1 and key == "side view, full bodies"))
|
|
picks.append(("Camera", camera))
|
|
if lighting == random_picks.RANDOM:
|
|
lighting = random_picks.pick(key for key in image_actions.LIGHTING if key != "auto")
|
|
picks.append(("Lighting", lighting))
|
|
if outfit == random_picks.RANDOM:
|
|
outfit = random_picks.pick(key for key in image_actions.OUTFITS if key != "as in the image")
|
|
picks.append(("Outfit", outfit))
|
|
if style == random_picks.RANDOM:
|
|
style = random_picks.pick(key for key in image_actions.STYLES if key != "auto")
|
|
picks.append(("Style", style))
|
|
random_picks.announce(unique_id, picks)
|
|
data = {"action": action, "source": source, "place": place, "camera": camera, "lighting": lighting,
|
|
"outfit": outfit, "style": style, "subject_1": subject_1,
|
|
"subject_2": subject_2, "notes": notes, "overrides": overrides, "instructions": instructions}
|
|
text = image_actions.brief(action, (subject_1, subject_2), camera, place, lighting, outfit, style, notes, overrides, source, instructions)
|
|
return (data, text)
|