Add Krea2 formatting and scalable scene pools
This commit is contained in:
@@ -9,6 +9,7 @@ The node is registered as:
|
|||||||
- `prompt_builder / SxCP Seed Control`
|
- `prompt_builder / SxCP Seed Control`
|
||||||
- `prompt_builder / SxCP Camera Control`
|
- `prompt_builder / SxCP Camera Control`
|
||||||
- `prompt_builder / SxCP Caption Naturalizer`
|
- `prompt_builder / SxCP Caption Naturalizer`
|
||||||
|
- `prompt_builder / SxCP Krea2 Formatter`
|
||||||
- `prompt_builder / SxCP Insta/OF Options`
|
- `prompt_builder / SxCP Insta/OF Options`
|
||||||
- `prompt_builder / SxCP Insta/OF Prompt Pair`
|
- `prompt_builder / SxCP Insta/OF Prompt Pair`
|
||||||
|
|
||||||
@@ -65,6 +66,35 @@ It outputs:
|
|||||||
- `natural_caption`
|
- `natural_caption`
|
||||||
- `method`
|
- `method`
|
||||||
|
|
||||||
|
`SxCP Krea2 Formatter` rewrites an existing prompt or `metadata_json` into a
|
||||||
|
Krea2-oriented natural-language paragraph. It is a formatter, not a safety or
|
||||||
|
content downgrade pass: hardcore items, role graphs, sexual pose wording, and
|
||||||
|
camera controls are preserved. Negative prompts stay separate.
|
||||||
|
|
||||||
|
Important behavior:
|
||||||
|
|
||||||
|
- Age wording is preserved deliberately. Phrases like `21-year-old adult`,
|
||||||
|
`late 20s adult woman`, and `all participants 21+ and visibly adult` are kept
|
||||||
|
because they help avoid unwanted young-looking outputs.
|
||||||
|
- Trigger words are removed by default because Krea2 prompting generally reads
|
||||||
|
better as natural language. Enable `preserve_trigger` if you still need a LoRA
|
||||||
|
trigger in the positive prompt.
|
||||||
|
- `style_mode`: `preserve` keeps the current generated style text,
|
||||||
|
`photographic` converts the style tail toward creator-photo language, and
|
||||||
|
`minimal` omits most style text.
|
||||||
|
- For Insta/OF paired metadata, the node returns both `krea_softcore_prompt` and
|
||||||
|
`krea_hardcore_prompt`, with separate softcore and hardcore negatives.
|
||||||
|
|
||||||
|
It outputs:
|
||||||
|
|
||||||
|
- `krea_prompt`
|
||||||
|
- `negative_prompt`
|
||||||
|
- `krea_softcore_prompt`
|
||||||
|
- `krea_hardcore_prompt`
|
||||||
|
- `softcore_negative_prompt`
|
||||||
|
- `hardcore_negative_prompt`
|
||||||
|
- `method`
|
||||||
|
|
||||||
`SxCP Insta/OF Prompt Pair` is a special paired-output mode. It creates one
|
`SxCP Insta/OF Prompt Pair` is a special paired-output mode. It creates one
|
||||||
shared primary creator descriptor, then returns both a softcore prompt and a
|
shared primary creator descriptor, then returns both a softcore prompt and a
|
||||||
hardcore prompt from that same descriptor. This is useful when you want the same
|
hardcore prompt from that same descriptor. This is useful when you want the same
|
||||||
@@ -180,6 +210,46 @@ provided, the node uses a generic composer that selects subject appearance,
|
|||||||
scene, pose, expression, composition, and a random item from the selected
|
scene, pose, expression, composition, and a random item from the selected
|
||||||
subcategory.
|
subcategory.
|
||||||
|
|
||||||
|
Reusable location banks can be defined with top-level `scene_pools` in any
|
||||||
|
`categories/*.json` file. Categories, subcategories, and items can reference
|
||||||
|
them with `scene_pools`; referenced pools are merged with any local `scenes`.
|
||||||
|
This keeps location expansion scalable without duplicating the same bedroom,
|
||||||
|
selfie, mirror, creator, or group-sex locations across every subcategory.
|
||||||
|
|
||||||
|
Set `"inherit_scenes": false` on a subcategory or item when it should use only
|
||||||
|
its own `scenes` and `scene_pools` instead of also inheriting parent category
|
||||||
|
locations. This is useful for narrow subcategories such as group scenes, vehicle
|
||||||
|
sets, outdoor-only sets, or any category where a generic parent room would be a
|
||||||
|
bad match.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scene_pools": {
|
||||||
|
"creator_selfie_rooms": [
|
||||||
|
{
|
||||||
|
"slug": "bedroom_phone_tripod",
|
||||||
|
"prompt": "private creator bedroom with a phone tripod, rumpled bedding, and warm lamps"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"categories": [
|
||||||
|
{
|
||||||
|
"name": "Example",
|
||||||
|
"subcategories": [
|
||||||
|
{
|
||||||
|
"name": "Selfie set",
|
||||||
|
"inherit_scenes": false,
|
||||||
|
"scene_pools": ["creator_selfie_rooms"],
|
||||||
|
"items": ["simple outfit prompt"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
For large categories, prefer `item_templates` plus `item_axes` instead of writing
|
For large categories, prefer `item_templates` plus `item_axes` instead of writing
|
||||||
every final item by hand:
|
every final item by hand:
|
||||||
|
|
||||||
|
|||||||
+73
@@ -21,6 +21,7 @@ try:
|
|||||||
subcategory_choices,
|
subcategory_choices,
|
||||||
)
|
)
|
||||||
from .caption_naturalizer import naturalize_caption
|
from .caption_naturalizer import naturalize_caption
|
||||||
|
from .krea_formatter import format_krea2_prompt
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from prompt_builder import (
|
from prompt_builder import (
|
||||||
build_camera_config_json,
|
build_camera_config_json,
|
||||||
@@ -40,6 +41,7 @@ except ImportError:
|
|||||||
subcategory_choices,
|
subcategory_choices,
|
||||||
)
|
)
|
||||||
from caption_naturalizer import naturalize_caption
|
from caption_naturalizer import naturalize_caption
|
||||||
|
from krea_formatter import format_krea2_prompt
|
||||||
|
|
||||||
|
|
||||||
class SxCPPromptBuilder:
|
class SxCPPromptBuilder:
|
||||||
@@ -277,6 +279,75 @@ class SxCPCaptionNaturalizer:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SxCPKrea2Formatter:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"source_text": ("STRING", {"default": "", "multiline": True}),
|
||||||
|
"input_hint": (["auto", "metadata_json", "prompt"], {"default": "auto"}),
|
||||||
|
"target": (["auto", "single", "softcore", "hardcore"], {"default": "auto"}),
|
||||||
|
"detail_level": (["balanced", "concise", "dense"], {"default": "balanced"}),
|
||||||
|
"style_mode": (["preserve", "photographic", "minimal"], {"default": "preserve"}),
|
||||||
|
"preserve_trigger": ("BOOLEAN", {"default": False}),
|
||||||
|
},
|
||||||
|
"optional": {
|
||||||
|
"metadata_json": ("STRING", {"default": "", "multiline": True}),
|
||||||
|
"negative_prompt": ("STRING", {"default": "", "multiline": True}),
|
||||||
|
"extra_positive": ("STRING", {"default": "", "multiline": True}),
|
||||||
|
"extra_negative": ("STRING", {"default": "", "multiline": True}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING")
|
||||||
|
RETURN_NAMES = (
|
||||||
|
"krea_prompt",
|
||||||
|
"negative_prompt",
|
||||||
|
"krea_softcore_prompt",
|
||||||
|
"krea_hardcore_prompt",
|
||||||
|
"softcore_negative_prompt",
|
||||||
|
"hardcore_negative_prompt",
|
||||||
|
"method",
|
||||||
|
)
|
||||||
|
FUNCTION = "build"
|
||||||
|
CATEGORY = "prompt_builder"
|
||||||
|
|
||||||
|
def build(
|
||||||
|
self,
|
||||||
|
source_text,
|
||||||
|
input_hint,
|
||||||
|
target,
|
||||||
|
detail_level,
|
||||||
|
style_mode,
|
||||||
|
preserve_trigger,
|
||||||
|
metadata_json="",
|
||||||
|
negative_prompt="",
|
||||||
|
extra_positive="",
|
||||||
|
extra_negative="",
|
||||||
|
):
|
||||||
|
row = format_krea2_prompt(
|
||||||
|
source_text=source_text or "",
|
||||||
|
metadata_json=metadata_json or "",
|
||||||
|
negative_prompt=negative_prompt or "",
|
||||||
|
input_hint=input_hint,
|
||||||
|
target=target,
|
||||||
|
detail_level=detail_level,
|
||||||
|
style_mode=style_mode,
|
||||||
|
preserve_trigger=preserve_trigger,
|
||||||
|
extra_positive=extra_positive or "",
|
||||||
|
extra_negative=extra_negative or "",
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
row["krea_prompt"],
|
||||||
|
row["negative_prompt"],
|
||||||
|
row["krea_softcore_prompt"],
|
||||||
|
row["krea_hardcore_prompt"],
|
||||||
|
row["softcore_negative_prompt"],
|
||||||
|
row["hardcore_negative_prompt"],
|
||||||
|
row["method"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SxCPInstaOFOptions:
|
class SxCPInstaOFOptions:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
@@ -417,6 +488,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"SxCPSeedControl": SxCPSeedControl,
|
"SxCPSeedControl": SxCPSeedControl,
|
||||||
"SxCPCameraControl": SxCPCameraControl,
|
"SxCPCameraControl": SxCPCameraControl,
|
||||||
"SxCPCaptionNaturalizer": SxCPCaptionNaturalizer,
|
"SxCPCaptionNaturalizer": SxCPCaptionNaturalizer,
|
||||||
|
"SxCPKrea2Formatter": SxCPKrea2Formatter,
|
||||||
"SxCPInstaOFOptions": SxCPInstaOFOptions,
|
"SxCPInstaOFOptions": SxCPInstaOFOptions,
|
||||||
"SxCPInstaOFPromptPair": SxCPInstaOFPromptPair,
|
"SxCPInstaOFPromptPair": SxCPInstaOFPromptPair,
|
||||||
}
|
}
|
||||||
@@ -426,6 +498,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"SxCPSeedControl": "SxCP Seed Control",
|
"SxCPSeedControl": "SxCP Seed Control",
|
||||||
"SxCPCameraControl": "SxCP Camera Control",
|
"SxCPCameraControl": "SxCP Camera Control",
|
||||||
"SxCPCaptionNaturalizer": "SxCP Caption Naturalizer",
|
"SxCPCaptionNaturalizer": "SxCP Caption Naturalizer",
|
||||||
|
"SxCPKrea2Formatter": "SxCP Krea2 Formatter",
|
||||||
"SxCPInstaOFOptions": "SxCP Insta/OF Options",
|
"SxCPInstaOFOptions": "SxCP Insta/OF Options",
|
||||||
"SxCPInstaOFPromptPair": "SxCP Insta/OF Prompt Pair",
|
"SxCPInstaOFPromptPair": "SxCP Insta/OF Prompt Pair",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,136 @@
|
|||||||
"name": "Streetwear",
|
"name": "Streetwear",
|
||||||
"slug": "streetwear",
|
"slug": "streetwear",
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
"items": [
|
"scene_pools": ["casual_urban_scenes"],
|
||||||
"oversized hoodie with slim jeans and clean sneakers",
|
"items": ["streetwear outfit generator"],
|
||||||
"cropped bomber jacket with cargo pants and chunky trainers",
|
"item_templates": [
|
||||||
"graphic tee under an open flannel with fitted denim",
|
"{outerwear} layered over {top}, with {bottom}, {footwear}, and {accessory}",
|
||||||
"sleek track jacket with joggers and minimal sneakers",
|
"{top} with {bottom}, {outerwear}, {footwear}, and {detail}",
|
||||||
"denim-on-denim outfit with a fitted white tee"
|
"{set_piece} styled with {footwear}, {accessory}, and {color_mood}",
|
||||||
|
"{outerwear} with {bottom}, {bag}, {footwear}, and {texture_detail}",
|
||||||
|
"{top} tucked into {bottom}, paired with {footwear}, {jewelry}, and {detail}",
|
||||||
|
"{outerwear} half-zipped over {top}, with {bottom}, {bag}, and {color_mood}",
|
||||||
|
"{set_piece} with {outerwear}, {footwear}, {jewelry}, and {texture_detail}",
|
||||||
|
"{top} under {outerwear}, with {bottom}, {accessory}, {bag}, and {footwear}"
|
||||||
],
|
],
|
||||||
|
"item_axes": {
|
||||||
|
"accessory": [
|
||||||
|
"a ribbed beanie",
|
||||||
|
"thin sunglasses",
|
||||||
|
"a baseball cap",
|
||||||
|
"wire-frame glasses",
|
||||||
|
"a silk neck scarf",
|
||||||
|
"fingerless gloves",
|
||||||
|
"a chunky belt",
|
||||||
|
"a low-profile headphones detail"
|
||||||
|
],
|
||||||
|
"bag": [
|
||||||
|
"a small crossbody bag",
|
||||||
|
"a canvas tote",
|
||||||
|
"a compact shoulder bag",
|
||||||
|
"a mini backpack",
|
||||||
|
"a waist pack worn crossbody",
|
||||||
|
"a structured leather satchel",
|
||||||
|
"a nylon sling bag",
|
||||||
|
"a glossy phone pouch"
|
||||||
|
],
|
||||||
|
"bottom": [
|
||||||
|
"relaxed cargo pants",
|
||||||
|
"straight-leg jeans",
|
||||||
|
"wide-leg denim",
|
||||||
|
"pleated utility trousers",
|
||||||
|
"fitted black jeans",
|
||||||
|
"soft joggers",
|
||||||
|
"a denim mini skirt with tights",
|
||||||
|
"tailored track pants",
|
||||||
|
"high-waist cropped trousers",
|
||||||
|
"washed grey jeans"
|
||||||
|
],
|
||||||
|
"color_mood": [
|
||||||
|
"muted city neutrals",
|
||||||
|
"black and cream contrast",
|
||||||
|
"soft grey-blue tones",
|
||||||
|
"washed denim colors",
|
||||||
|
"olive and charcoal accents",
|
||||||
|
"clean monochrome styling",
|
||||||
|
"warm brick-red accents",
|
||||||
|
"navy, white, and graphite layers"
|
||||||
|
],
|
||||||
|
"detail": [
|
||||||
|
"rolled cuffs",
|
||||||
|
"visible layered hems",
|
||||||
|
"a sharp cropped silhouette",
|
||||||
|
"clean oversized proportions",
|
||||||
|
"a relaxed off-duty fit",
|
||||||
|
"asymmetric tucked fabric",
|
||||||
|
"subtle graphic print details",
|
||||||
|
"a neat high-waist line"
|
||||||
|
],
|
||||||
|
"footwear": [
|
||||||
|
"clean white sneakers",
|
||||||
|
"chunky trainers",
|
||||||
|
"platform sneakers",
|
||||||
|
"minimal leather sneakers",
|
||||||
|
"black ankle boots",
|
||||||
|
"retro running shoes",
|
||||||
|
"canvas high-tops",
|
||||||
|
"sleek street boots"
|
||||||
|
],
|
||||||
|
"jewelry": [
|
||||||
|
"small hoop earrings",
|
||||||
|
"layered chain necklaces",
|
||||||
|
"a simple watch",
|
||||||
|
"stacked rings",
|
||||||
|
"a thin choker",
|
||||||
|
"minimal silver earrings",
|
||||||
|
"a pendant necklace",
|
||||||
|
"a narrow bracelet"
|
||||||
|
],
|
||||||
|
"outerwear": [
|
||||||
|
"oversized hoodie",
|
||||||
|
"cropped bomber jacket",
|
||||||
|
"open flannel shirt",
|
||||||
|
"sleek track jacket",
|
||||||
|
"denim jacket",
|
||||||
|
"boxy leather jacket",
|
||||||
|
"lightweight varsity jacket",
|
||||||
|
"oversized zip hoodie",
|
||||||
|
"short trench jacket",
|
||||||
|
"quilted street jacket"
|
||||||
|
],
|
||||||
|
"set_piece": [
|
||||||
|
"denim-on-denim outfit",
|
||||||
|
"matching track set",
|
||||||
|
"monochrome hoodie-and-jogger set",
|
||||||
|
"oversized shirt-and-cargo outfit",
|
||||||
|
"cropped jacket and wide denim outfit",
|
||||||
|
"clean athleisure set",
|
||||||
|
"utility vest and trouser outfit",
|
||||||
|
"layered tee and open-shirt outfit"
|
||||||
|
],
|
||||||
|
"texture_detail": [
|
||||||
|
"washed cotton texture",
|
||||||
|
"soft fleece texture",
|
||||||
|
"matte nylon fabric",
|
||||||
|
"structured denim seams",
|
||||||
|
"subtle ribbed knit trim",
|
||||||
|
"smooth leather accents",
|
||||||
|
"canvas strap details",
|
||||||
|
"creased everyday fabric"
|
||||||
|
],
|
||||||
|
"top": [
|
||||||
|
"fitted white tee",
|
||||||
|
"cropped tank top",
|
||||||
|
"graphic tee",
|
||||||
|
"ribbed long-sleeve top",
|
||||||
|
"simple black tee",
|
||||||
|
"slim turtleneck",
|
||||||
|
"soft henley shirt",
|
||||||
|
"cropped sweatshirt",
|
||||||
|
"mesh-layered tee",
|
||||||
|
"clean sleeveless top"
|
||||||
|
]
|
||||||
|
},
|
||||||
"scenes": [
|
"scenes": [
|
||||||
{
|
{
|
||||||
"slug": "city_crosswalk",
|
"slug": "city_crosswalk",
|
||||||
@@ -34,20 +157,166 @@
|
|||||||
"poses": [
|
"poses": [
|
||||||
"standing with one hand in a pocket",
|
"standing with one hand in a pocket",
|
||||||
"leaning against a wall with relaxed confidence",
|
"leaning against a wall with relaxed confidence",
|
||||||
"walking forward with a casual runway stride"
|
"walking forward with a casual runway stride",
|
||||||
|
"looking over one shoulder while stepping off a curb",
|
||||||
|
"sitting on a low concrete wall with one knee raised",
|
||||||
|
"adjusting sunglasses with a relaxed half-smile",
|
||||||
|
"hands in jacket pockets with a slight hip shift",
|
||||||
|
"turning mid-step as fabric layers move naturally"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Summer casual",
|
"name": "Summer casual",
|
||||||
"slug": "summer_casual",
|
"slug": "summer_casual",
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
"items": [
|
"scene_pools": ["casual_summer_scenes"],
|
||||||
"light cotton sundress with simple sandals",
|
"items": ["summer casual outfit generator"],
|
||||||
"linen shorts with a tucked-in sleeveless blouse",
|
"item_templates": [
|
||||||
"soft camp-collar shirt with relaxed trousers",
|
"{dress} with {footwear}, {accessory}, and {fabric_detail}",
|
||||||
"ribbed tank top with a flowing midi skirt",
|
"{top} with {bottom}, {footwear}, {bag}, and {color_mood}",
|
||||||
"breathable linen two-piece in pale summer colors"
|
"{layer} over {top}, with {bottom}, {accessory}, and {texture_detail}",
|
||||||
|
"{two_piece} with {footwear}, {jewelry}, and {fabric_detail}",
|
||||||
|
"{top} tucked into {bottom}, paired with {bag}, {footwear}, and {detail}",
|
||||||
|
"{dress} under {layer}, with {accessory}, {jewelry}, and {color_mood}",
|
||||||
|
"{two_piece} styled with {bag}, {footwear}, {texture_detail}, and {detail}",
|
||||||
|
"{top} with {bottom}, {layer}, {accessory}, and {fabric_detail}"
|
||||||
],
|
],
|
||||||
|
"item_axes": {
|
||||||
|
"accessory": [
|
||||||
|
"wide sunglasses",
|
||||||
|
"a straw sunhat",
|
||||||
|
"a silk hair scarf",
|
||||||
|
"a woven belt",
|
||||||
|
"thin oval sunglasses",
|
||||||
|
"a cotton bandana",
|
||||||
|
"a simple sun visor",
|
||||||
|
"a lightweight shawl"
|
||||||
|
],
|
||||||
|
"bag": [
|
||||||
|
"a woven tote",
|
||||||
|
"a small raffia bag",
|
||||||
|
"a canvas beach bag",
|
||||||
|
"a soft shoulder bag",
|
||||||
|
"a compact basket purse",
|
||||||
|
"a linen drawstring bag",
|
||||||
|
"a minimal crossbody pouch",
|
||||||
|
"a straw clutch"
|
||||||
|
],
|
||||||
|
"bottom": [
|
||||||
|
"linen shorts",
|
||||||
|
"relaxed cropped trousers",
|
||||||
|
"a flowing midi skirt",
|
||||||
|
"high-waist cotton shorts",
|
||||||
|
"a breezy wrap skirt",
|
||||||
|
"wide-leg linen pants",
|
||||||
|
"a soft pleated skirt",
|
||||||
|
"light denim shorts",
|
||||||
|
"paperbag-waist trousers",
|
||||||
|
"a calf-length summer skirt"
|
||||||
|
],
|
||||||
|
"color_mood": [
|
||||||
|
"pale summer colors",
|
||||||
|
"sun-washed white and blue",
|
||||||
|
"coral and cream accents",
|
||||||
|
"sage green and ivory tones",
|
||||||
|
"soft yellow and denim blue",
|
||||||
|
"warm terracotta details",
|
||||||
|
"fresh white and citrus accents",
|
||||||
|
"muted sea-glass colors"
|
||||||
|
],
|
||||||
|
"detail": [
|
||||||
|
"rolled hems",
|
||||||
|
"a loose tucked waist",
|
||||||
|
"wind-lifted fabric edges",
|
||||||
|
"open collar styling",
|
||||||
|
"a relaxed vacation fit",
|
||||||
|
"thin shoulder straps",
|
||||||
|
"a clean high-waist line",
|
||||||
|
"softly draped fabric"
|
||||||
|
],
|
||||||
|
"dress": [
|
||||||
|
"light cotton sundress",
|
||||||
|
"button-front linen dress",
|
||||||
|
"soft wrap dress",
|
||||||
|
"ribbed tank dress",
|
||||||
|
"flowing midi sundress",
|
||||||
|
"simple slip dress",
|
||||||
|
"tiered cotton dress",
|
||||||
|
"halter summer dress"
|
||||||
|
],
|
||||||
|
"fabric_detail": [
|
||||||
|
"breathable linen texture",
|
||||||
|
"soft cotton folds",
|
||||||
|
"light gauze fabric",
|
||||||
|
"fine ribbed cotton",
|
||||||
|
"sunlit woven texture",
|
||||||
|
"thin summer knit texture",
|
||||||
|
"crisp poplin folds",
|
||||||
|
"airy voile fabric"
|
||||||
|
],
|
||||||
|
"footwear": [
|
||||||
|
"simple sandals",
|
||||||
|
"flat leather sandals",
|
||||||
|
"white canvas sneakers",
|
||||||
|
"espadrilles",
|
||||||
|
"strappy sandals",
|
||||||
|
"woven slides",
|
||||||
|
"minimal mule sandals",
|
||||||
|
"low platform sandals"
|
||||||
|
],
|
||||||
|
"jewelry": [
|
||||||
|
"small gold hoops",
|
||||||
|
"a shell necklace",
|
||||||
|
"a delicate anklet",
|
||||||
|
"thin stacked bracelets",
|
||||||
|
"a small pendant necklace",
|
||||||
|
"pearl stud earrings",
|
||||||
|
"a beaded bracelet",
|
||||||
|
"a fine waist chain over fabric"
|
||||||
|
],
|
||||||
|
"layer": [
|
||||||
|
"soft camp-collar shirt",
|
||||||
|
"open linen shirt",
|
||||||
|
"light cotton overshirt",
|
||||||
|
"cropped denim jacket",
|
||||||
|
"sheer beach shirt",
|
||||||
|
"loose short-sleeve cardigan",
|
||||||
|
"thin knit wrap",
|
||||||
|
"unbuttoned chambray shirt"
|
||||||
|
],
|
||||||
|
"texture_detail": [
|
||||||
|
"natural linen slubs",
|
||||||
|
"matte cotton finish",
|
||||||
|
"subtle woven stripes",
|
||||||
|
"soft wrinkled fabric",
|
||||||
|
"delicate eyelet texture",
|
||||||
|
"smooth summer poplin",
|
||||||
|
"sunlit thread texture",
|
||||||
|
"lightweight ribbing"
|
||||||
|
],
|
||||||
|
"top": [
|
||||||
|
"tucked-in sleeveless blouse",
|
||||||
|
"ribbed tank top",
|
||||||
|
"cropped cotton tee",
|
||||||
|
"linen camisole",
|
||||||
|
"halter knit top",
|
||||||
|
"soft bandeau under a shirt",
|
||||||
|
"square-neck tank",
|
||||||
|
"thin-strapped summer top",
|
||||||
|
"buttoned linen vest",
|
||||||
|
"loose cotton blouse"
|
||||||
|
],
|
||||||
|
"two_piece": [
|
||||||
|
"breathable linen two-piece",
|
||||||
|
"matching crop top and skirt set",
|
||||||
|
"soft resort shirt-and-short set",
|
||||||
|
"ribbed tank and midi skirt set",
|
||||||
|
"cotton co-ord set",
|
||||||
|
"linen vest and trouser set",
|
||||||
|
"open shirt and wrap-skirt set",
|
||||||
|
"minimal summer knit set"
|
||||||
|
]
|
||||||
|
},
|
||||||
"scenes": [
|
"scenes": [
|
||||||
{
|
{
|
||||||
"slug": "sunny_market",
|
"slug": "sunny_market",
|
||||||
@@ -61,20 +330,148 @@
|
|||||||
"poses": [
|
"poses": [
|
||||||
"turning slightly with fabric moving in the breeze",
|
"turning slightly with fabric moving in the breeze",
|
||||||
"standing in warm sunlight with relaxed shoulders",
|
"standing in warm sunlight with relaxed shoulders",
|
||||||
"sitting on a low wall with one knee bent"
|
"sitting on a low wall with one knee bent",
|
||||||
|
"walking with one hand holding a sunhat",
|
||||||
|
"leaning on a promenade railing with a soft smile",
|
||||||
|
"lifting sunglasses while looking toward the light",
|
||||||
|
"standing barefoot near warm paving stones",
|
||||||
|
"mid-step with loose fabric trailing behind"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Cozy lounge",
|
"name": "Cozy lounge",
|
||||||
"slug": "cozy_lounge",
|
"slug": "cozy_lounge",
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
"items": [
|
"scene_pools": ["casual_lounge_scenes"],
|
||||||
"soft knit cardigan with a fitted tee and lounge trousers",
|
"items": ["cozy lounge outfit generator"],
|
||||||
"oversized sweater with leggings and wool socks",
|
"item_templates": [
|
||||||
"relaxed sweatshirt with drawstring joggers",
|
"{layer} over {top}, with {bottom}, {footwear}, and {texture_detail}",
|
||||||
"ribbed lounge set with a long open cardigan",
|
"{set_piece} with {layer}, {accessory}, and {color_mood}",
|
||||||
"simple cotton tee with loose pajama-style trousers"
|
"{top} with {bottom}, {footwear}, {prop}, and {detail}",
|
||||||
|
"{layer} wrapped around {top}, with {bottom}, {jewelry}, and {texture_detail}",
|
||||||
|
"{set_piece} styled with {footwear}, {prop}, and {detail}",
|
||||||
|
"{top} tucked loosely into {bottom}, with {layer}, {accessory}, and {color_mood}",
|
||||||
|
"{layer} slipping off one shoulder over {top}, with {bottom}, {footwear}, and {texture_detail}",
|
||||||
|
"{set_piece} with {accessory}, {jewelry}, {prop}, and {detail}"
|
||||||
],
|
],
|
||||||
|
"item_axes": {
|
||||||
|
"accessory": [
|
||||||
|
"a soft hair clip",
|
||||||
|
"thin reading glasses",
|
||||||
|
"a scrunchie at the wrist",
|
||||||
|
"a loose blanket scarf",
|
||||||
|
"a simple headband",
|
||||||
|
"a warm knit beanie",
|
||||||
|
"a delicate sleep mask pushed up",
|
||||||
|
"a cotton bandana"
|
||||||
|
],
|
||||||
|
"bottom": [
|
||||||
|
"lounge trousers",
|
||||||
|
"soft leggings",
|
||||||
|
"drawstring joggers",
|
||||||
|
"loose pajama-style trousers",
|
||||||
|
"ribbed lounge pants",
|
||||||
|
"wide knit pants",
|
||||||
|
"cotton sleep shorts",
|
||||||
|
"thermal leggings",
|
||||||
|
"slouchy sweatpants",
|
||||||
|
"soft waffle-knit pants"
|
||||||
|
],
|
||||||
|
"color_mood": [
|
||||||
|
"warm oatmeal and grey tones",
|
||||||
|
"soft rose and cream colors",
|
||||||
|
"muted sage and ivory",
|
||||||
|
"heather grey and white",
|
||||||
|
"warm mocha neutrals",
|
||||||
|
"powder blue and cream",
|
||||||
|
"charcoal and blush accents",
|
||||||
|
"soft lavender and warm beige"
|
||||||
|
],
|
||||||
|
"detail": [
|
||||||
|
"relaxed draped proportions",
|
||||||
|
"rolled sleeves",
|
||||||
|
"softly rumpled fabric",
|
||||||
|
"a loose off-duty silhouette",
|
||||||
|
"visible cozy layering",
|
||||||
|
"a slightly oversized fit",
|
||||||
|
"bare ankle styling",
|
||||||
|
"a gentle high-waist line"
|
||||||
|
],
|
||||||
|
"footwear": [
|
||||||
|
"wool socks",
|
||||||
|
"soft house slippers",
|
||||||
|
"bare feet",
|
||||||
|
"fuzzy socks",
|
||||||
|
"knit ankle socks",
|
||||||
|
"shearling slippers",
|
||||||
|
"simple cotton socks",
|
||||||
|
"ribbed knee socks"
|
||||||
|
],
|
||||||
|
"jewelry": [
|
||||||
|
"tiny hoop earrings",
|
||||||
|
"a simple pendant necklace",
|
||||||
|
"a thin bracelet",
|
||||||
|
"small stud earrings",
|
||||||
|
"a delicate ring",
|
||||||
|
"a minimal chain necklace",
|
||||||
|
"a soft fabric wristband",
|
||||||
|
"a subtle ankle chain"
|
||||||
|
],
|
||||||
|
"layer": [
|
||||||
|
"soft knit cardigan",
|
||||||
|
"long open cardigan",
|
||||||
|
"oversized sweater",
|
||||||
|
"relaxed sweatshirt",
|
||||||
|
"chunky cable-knit cardigan",
|
||||||
|
"brushed fleece hoodie",
|
||||||
|
"thin robe cardigan",
|
||||||
|
"cropped lounge hoodie",
|
||||||
|
"slouchy wrap sweater",
|
||||||
|
"soft waffle-knit cardigan"
|
||||||
|
],
|
||||||
|
"prop": [
|
||||||
|
"a casual magazine nearby",
|
||||||
|
"a warm mug in one hand",
|
||||||
|
"a paperback book nearby",
|
||||||
|
"a folded blanket at the hip",
|
||||||
|
"a phone resting on the sofa",
|
||||||
|
"a small tray on a side table",
|
||||||
|
"a pillow tucked against the waist",
|
||||||
|
"a half-open notebook nearby"
|
||||||
|
],
|
||||||
|
"set_piece": [
|
||||||
|
"ribbed lounge set",
|
||||||
|
"matching sweatshirt and jogger set",
|
||||||
|
"soft knit co-ord set",
|
||||||
|
"cotton tee and pajama trouser set",
|
||||||
|
"waffle-knit lounge set",
|
||||||
|
"brushed fleece lounge set",
|
||||||
|
"cardigan and knit pant set",
|
||||||
|
"simple sleepwear-inspired set"
|
||||||
|
],
|
||||||
|
"texture_detail": [
|
||||||
|
"soft knit texture",
|
||||||
|
"brushed fleece texture",
|
||||||
|
"ribbed cotton texture",
|
||||||
|
"waffle-knit fabric",
|
||||||
|
"warm wool fibers",
|
||||||
|
"smooth jersey folds",
|
||||||
|
"plush cardigan texture",
|
||||||
|
"softly worn cotton"
|
||||||
|
],
|
||||||
|
"top": [
|
||||||
|
"fitted tee",
|
||||||
|
"simple cotton tee",
|
||||||
|
"ribbed tank top",
|
||||||
|
"soft long-sleeve top",
|
||||||
|
"thin camisole",
|
||||||
|
"slouchy henley shirt",
|
||||||
|
"cropped lounge tee",
|
||||||
|
"thermal knit top",
|
||||||
|
"light sleep tank",
|
||||||
|
"soft V-neck tee"
|
||||||
|
]
|
||||||
|
},
|
||||||
"scenes": [
|
"scenes": [
|
||||||
{
|
{
|
||||||
"slug": "sunny_apartment",
|
"slug": "sunny_apartment",
|
||||||
@@ -88,7 +485,12 @@
|
|||||||
"poses": [
|
"poses": [
|
||||||
"curled comfortably on a sofa",
|
"curled comfortably on a sofa",
|
||||||
"standing barefoot near a window with a relaxed smile",
|
"standing barefoot near a window with a relaxed smile",
|
||||||
"sitting cross-legged with a casual magazine nearby"
|
"sitting cross-legged with a casual magazine nearby",
|
||||||
|
"leaning into a couch corner with one knee raised",
|
||||||
|
"stretching lazily with sleeves falling over the hands",
|
||||||
|
"kneeling on a rug while reaching for a warm mug",
|
||||||
|
"standing in profile near soft curtains",
|
||||||
|
"reclining with one arm draped over a pillow"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
"style": "explicit adult erotic fashion illustration, sensual pin-up coloured-pencil comic style, adults only",
|
"style": "explicit adult erotic fashion illustration, sensual pin-up coloured-pencil comic style, adults only",
|
||||||
"positive_suffix": "Use crisp clean comic linework, detailed hatching, soft skin shading, tactile fabric texture, warm intimate lighting, and textured paper.",
|
"positive_suffix": "Use crisp clean comic linework, detailed hatching, soft skin shading, tactile fabric texture, warm intimate lighting, and textured paper.",
|
||||||
"negative_prompt": "minors, childlike appearance, schoolgirl, childlike costume, non-consensual, coercion, violence, injury, watermark",
|
"negative_prompt": "minors, childlike appearance, schoolgirl, childlike costume, non-consensual, coercion, violence, injury, watermark",
|
||||||
|
"scene_pools": ["softcore_creator_scenes", "mirror_scenes"],
|
||||||
"expressions": [
|
"expressions": [
|
||||||
"heavy-lidded seductive gaze",
|
"heavy-lidded seductive gaze",
|
||||||
"direct erotic stare",
|
"direct erotic stare",
|
||||||
@@ -47,6 +48,7 @@
|
|||||||
"name": "Provocative lingerie",
|
"name": "Provocative lingerie",
|
||||||
"slug": "provocative_lingerie",
|
"slug": "provocative_lingerie",
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"scene_pools": ["boudoir_bedroom_scenes"],
|
||||||
"item_templates": [
|
"item_templates": [
|
||||||
"{color} {fabric} {bra_style} with {bottom_style}, {garter_detail}, {stocking_style}, and {shoe_style}",
|
"{color} {fabric} {bra_style} with {bottom_style}, {garter_detail}, {stocking_style}, and {shoe_style}",
|
||||||
"{color} {bodywear} with {neckline}, {hip_cut}, {back_detail}, and {trim_detail}",
|
"{color} {bodywear} with {neckline}, {hip_cut}, {back_detail}, and {trim_detail}",
|
||||||
@@ -306,6 +308,7 @@
|
|||||||
"name": "Sheer exposed",
|
"name": "Sheer exposed",
|
||||||
"slug": "sheer_exposed",
|
"slug": "sheer_exposed",
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"scene_pools": ["softcore_creator_scenes", "mirror_scenes"],
|
||||||
"item_templates": [
|
"item_templates": [
|
||||||
"{color} {sheer_fabric} {sheer_garment} with {exposed_breast_detail}, {hip_detail}, and {shoe_style}",
|
"{color} {sheer_fabric} {sheer_garment} with {exposed_breast_detail}, {hip_detail}, and {shoe_style}",
|
||||||
"{color} open sheer robe exposing {exposed_breast_detail}, {lower_exposure}, and {jewelry}",
|
"{color} open sheer robe exposing {exposed_breast_detail}, {lower_exposure}, and {jewelry}",
|
||||||
@@ -549,6 +552,7 @@
|
|||||||
"name": "Fetish inspired",
|
"name": "Fetish inspired",
|
||||||
"slug": "fetish_inspired",
|
"slug": "fetish_inspired",
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"scene_pools": ["fetish_studio_scenes"],
|
||||||
"item_templates": [
|
"item_templates": [
|
||||||
"{color} {material} {latex_piece} with {zipper_detail}, {boot_style}, and {glove_style}",
|
"{color} {material} {latex_piece} with {zipper_detail}, {boot_style}, and {glove_style}",
|
||||||
"{color} {harness_style} over {breast_exposure} with {bottom_detail}, {hardware}, and {shoe_style}",
|
"{color} {harness_style} over {breast_exposure} with {bottom_detail}, {hardware}, and {shoe_style}",
|
||||||
@@ -808,6 +812,7 @@
|
|||||||
"name": "Nude accessories",
|
"name": "Nude accessories",
|
||||||
"slug": "nude_accessories",
|
"slug": "nude_accessories",
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"scene_pools": ["boudoir_bedroom_scenes", "mirror_scenes"],
|
||||||
"item_templates": [
|
"item_templates": [
|
||||||
"fully nude body styled only with {stocking_style}, {shoe_style}, and {jewelry}",
|
"fully nude body styled only with {stocking_style}, {shoe_style}, and {jewelry}",
|
||||||
"bare breasts and bare hips framed by {accessory_set}, {stocking_style}, and {shoe_style}",
|
"bare breasts and bare hips framed by {accessory_set}, {stocking_style}, and {shoe_style}",
|
||||||
@@ -971,6 +976,7 @@
|
|||||||
"name": "Microwear and body tape",
|
"name": "Microwear and body tape",
|
||||||
"slug": "microwear_body_tape",
|
"slug": "microwear_body_tape",
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"scene_pools": ["softcore_creator_scenes", "fetish_studio_scenes"],
|
||||||
"item_templates": [
|
"item_templates": [
|
||||||
"{color} micro bikini with {top_detail}, {bottom_detail}, {body_tape}, and {shoe_style}",
|
"{color} micro bikini with {top_detail}, {bottom_detail}, {body_tape}, and {shoe_style}",
|
||||||
"{color} body tape design covering only {tape_coverage}, with {bottom_detail}, {jewelry}, and {shoe_style}",
|
"{color} body tape design covering only {tape_coverage}, with {bottom_detail}, {jewelry}, and {shoe_style}",
|
||||||
@@ -1160,6 +1166,7 @@
|
|||||||
"name": "Erotic costumes",
|
"name": "Erotic costumes",
|
||||||
"slug": "erotic_costumes",
|
"slug": "erotic_costumes",
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"scene_pools": ["costume_backstage_scenes", "softcore_creator_scenes"],
|
||||||
"item_templates": [
|
"item_templates": [
|
||||||
"{color} adult {costume_role} look with {top_detail}, {bottom_detail}, {stocking_style}, and {shoe_style}",
|
"{color} adult {costume_role} look with {top_detail}, {bottom_detail}, {stocking_style}, and {shoe_style}",
|
||||||
"{color} fantasy {costume_role} costume with {corset_detail}, {exposure_detail}, {accessory}, and {shoe_style}",
|
"{color} fantasy {costume_role} costume with {corset_detail}, {exposure_detail}, {accessory}, and {shoe_style}",
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"scene_pools": {
|
||||||
|
"casual_urban_scenes": [
|
||||||
|
{"slug": "city_crosswalk_phone_snap", "prompt": "sunlit city crosswalk with storefront reflections and a casual phone-snapshot angle"},
|
||||||
|
{"slug": "subway_tile_selfie_corner", "prompt": "clean subway platform with tiled walls, overhead lights, and a quiet selfie corner"},
|
||||||
|
{"slug": "parking_rooftop_streetwear", "prompt": "parking garage rooftop with painted lines, city haze, and late-afternoon light"},
|
||||||
|
{"slug": "record_shop_mirror_wall", "prompt": "record shop aisle with warm neon, poster walls, and a small mirror behind the counter"},
|
||||||
|
{"slug": "coffee_window_counter", "prompt": "coffee shop window counter with street reflections and soft morning light"},
|
||||||
|
{"slug": "brick_alley_fire_escape", "prompt": "brick alley beside a fire escape with clean pavement and reflected city light"},
|
||||||
|
{"slug": "boutique_changing_mirror", "prompt": "boutique changing-room mirror with clothing hooks, soft bulbs, and a candid outfit-check angle"},
|
||||||
|
{"slug": "streetwear_elevator_lobby", "prompt": "modern apartment elevator lobby with brushed metal doors and a full-length mirror"}
|
||||||
|
],
|
||||||
|
"casual_summer_scenes": [
|
||||||
|
{"slug": "seaside_prom_phone_photo", "prompt": "seaside promenade with bright sky, warm paving stones, and casual phone-photo framing"},
|
||||||
|
{"slug": "weekend_market_canvas_awning", "prompt": "open-air weekend market with fruit stands, canvas awnings, and sunlit walkway space"},
|
||||||
|
{"slug": "rooftop_day_terrace", "prompt": "daytime rooftop terrace with potted plants, white chairs, and clear summer light"},
|
||||||
|
{"slug": "beach_cafe_table", "prompt": "beach cafe table with woven chairs, linen shade, and ocean light in the background"},
|
||||||
|
{"slug": "poolside_daybed_casual", "prompt": "poolside daybed with white towels, glass railings, and soft resort sunlight"},
|
||||||
|
{"slug": "garden_wall_summer", "prompt": "garden wall with climbing flowers, pale stone, and warm afternoon shadows"},
|
||||||
|
{"slug": "lake_boardwalk_summer", "prompt": "wooden lakeside boardwalk with reeds, clean sky, and relaxed vacation light"},
|
||||||
|
{"slug": "sunny_balcony_outfit_check", "prompt": "sunny apartment balcony with plants, city rooftops, and a casual outfit-check composition"}
|
||||||
|
],
|
||||||
|
"casual_lounge_scenes": [
|
||||||
|
{"slug": "sunny_apartment_phone_tripod", "prompt": "sunny apartment corner with bookshelves, a warm rug, and a phone on a small tripod"},
|
||||||
|
{"slug": "window_seat_soft_curtains", "prompt": "comfortable window seat with soft curtains, pillows, and afternoon light"},
|
||||||
|
{"slug": "bedroom_floor_lounge", "prompt": "cozy bedroom floor with folded blankets, low shelves, and soft window light"},
|
||||||
|
{"slug": "sofa_ring_light_corner", "prompt": "living-room sofa corner with a ring light stand, plants, and warm lamps"},
|
||||||
|
{"slug": "loft_kitchen_lounge", "prompt": "open loft kitchen with a breakfast counter, wood floor, and relaxed home light"},
|
||||||
|
{"slug": "reading_nook_phone_snap", "prompt": "reading nook with stacked books, a small mirror, and candid phone-snapshot framing"},
|
||||||
|
{"slug": "hotel_morning_lounge", "prompt": "hotel room lounge chair with rumpled travel bags, curtains, and soft morning sun"},
|
||||||
|
{"slug": "studio_apartment_mirror", "prompt": "small studio apartment with a full-length mirror, neutral bedding, and warm lamp light"}
|
||||||
|
],
|
||||||
|
"softcore_creator_scenes": [
|
||||||
|
{"slug": "creator_bedroom_ring_light", "prompt": "private creator bedroom with a ring light, phone tripod, rumpled bedding, and warm lamps"},
|
||||||
|
{"slug": "onlyfans_mirror_bedroom", "prompt": "bedroom mirror selfie setup with a visible phone, messy sheets, and soft amber light"},
|
||||||
|
{"slug": "walk_in_closet_tryon", "prompt": "mirrored walk-in closet with open drawers, hanging outfits, and warm boutique bulbs"},
|
||||||
|
{"slug": "hotel_bed_phone_tripod", "prompt": "hotel bed content setup with a phone on a mini tripod, city lights, and satin bedding"},
|
||||||
|
{"slug": "bathroom_counter_selfie", "prompt": "private bathroom counter with mirror haze, phone reflection, towels, and warm vanity lights"},
|
||||||
|
{"slug": "vanity_ring_light_close", "prompt": "vanity corner with makeup lights, perfume bottles, silk fabric, and close creator-shot framing"},
|
||||||
|
{"slug": "apartment_floor_content", "prompt": "apartment floor content setup with a low mirror, pillows, and soft practical lamps"},
|
||||||
|
{"slug": "balcony_phone_selfie", "prompt": "private balcony with city lights, glass railing, and handheld phone-selfie perspective"},
|
||||||
|
{"slug": "car_interior_creator_selfie", "prompt": "parked car interior with seat reflections, dashboard glow, and close phone-selfie framing"},
|
||||||
|
{"slug": "shower_steam_phone_reflection", "prompt": "steamy private shower room with wet tile, glass reflections, and implied phone-shot framing"},
|
||||||
|
{"slug": "studio_bedroom_backdrop", "prompt": "small creator studio with a bed, seamless backdrop, ring light, and visible phone stand"},
|
||||||
|
{"slug": "couch_lamp_creator_clip", "prompt": "private couch corner with scattered clothes, warm lamp glow, and vertical creator-video framing"}
|
||||||
|
],
|
||||||
|
"mirror_scenes": [
|
||||||
|
{"slug": "large_bedroom_mirror_selfie", "prompt": "large bedroom mirror with the phone visible, bed behind the subject, and warm side lamps"},
|
||||||
|
{"slug": "antique_mirror_boudoir", "prompt": "antique mirror corner with dark wood, perfume bottles, and golden lamplight"},
|
||||||
|
{"slug": "bathroom_mirror_haze", "prompt": "bathroom mirror selfie setup with steam haze, marble tile, and warm vanity bulbs"},
|
||||||
|
{"slug": "closet_full_length_mirror", "prompt": "full-length closet mirror with outfit racks, shoe shelves, and soft boutique lighting"},
|
||||||
|
{"slug": "hotel_mirror_city_view", "prompt": "hotel suite mirror reflecting city lights, satin bedding, and a phone in hand"},
|
||||||
|
{"slug": "neon_mirror_wall", "prompt": "neon mirror wall with glossy floor reflections and saturated magenta-blue edge light"},
|
||||||
|
{"slug": "gold_vanity_mirror", "prompt": "gold-framed vanity mirror with makeup lights, silk fabric, and close reflected framing"},
|
||||||
|
{"slug": "black_lacquer_mirror_room", "prompt": "black lacquer mirror room with glossy furniture, velvet curtains, and sharp rim light"}
|
||||||
|
],
|
||||||
|
"hardcore_mirror_scenes": [
|
||||||
|
{"slug": "hardcore_bedroom_mirror_pair", "prompt": "large bedroom mirror placed beside a rumpled bed, reflecting explicit adult body contact and the phone-camera angle", "min_people": 2, "max_people": 3},
|
||||||
|
{"slug": "hardcore_hotel_mirror_pair", "prompt": "hotel suite mirror facing a messy bed, city lights behind it, and enough space for explicit adult positioning", "min_people": 2, "max_people": 3},
|
||||||
|
{"slug": "hardcore_floor_mirror_set", "prompt": "floor mattress beside a full-length mirror with reflected bodies, warm lamps, and close creator-shot framing", "min_people": 2, "max_people": 3},
|
||||||
|
{"slug": "hardcore_shower_mirror_pair", "prompt": "private shower room with a fogged mirror wall, wet tile, and reflected explicit adult contact", "min_people": 2, "max_people": 3},
|
||||||
|
{"slug": "hardcore_vanity_mirror_floor", "prompt": "vanity mirror floor setup with bulbs, scattered clothing, low phone-camera angle, and reflected adult bodies", "min_people": 2, "max_people": 3},
|
||||||
|
{"slug": "hardcore_lacquer_mirror_bed", "prompt": "black lacquer mirror bedroom with glossy reflections, a low bed, and warm rim light around explicit adult contact", "min_people": 2, "max_people": 3},
|
||||||
|
{"slug": "hardcore_tripod_mirror_bed", "prompt": "creator bedroom mirror setup with a phone on tripod, bed sheets, reflected bodies, and clear adult-only framing", "min_people": 2, "max_people": 3},
|
||||||
|
{"slug": "hardcore_threesome_mirror_suite", "prompt": "wide mirror-wall suite arranged for three adults, with a bed, floor cushions, and all bodies visible in direct and reflected views", "min_people": 3, "max_people": 3}
|
||||||
|
],
|
||||||
|
"boudoir_bedroom_scenes": [
|
||||||
|
{"slug": "warm_boudoir_canopy_bed", "prompt": "warm boudoir bedroom with satin sheets, canopy curtains, low lamplight, and bedside phone framing"},
|
||||||
|
{"slug": "silk_bed_close_creator", "prompt": "silk-sheet bed with pillows, a phone on the nightstand, and intimate creator-shot light"},
|
||||||
|
{"slug": "velvet_headboard_bedroom", "prompt": "velvet headboard bedroom with gold lamps, rumpled bedding, and close sensual framing"},
|
||||||
|
{"slug": "four_poster_lingerie_room", "prompt": "four-poster bed with sheer drapes, warm lamps, and soft floor shadows"},
|
||||||
|
{"slug": "hotel_satin_bedroom", "prompt": "luxury hotel bedroom with satin bedding, city glow, and a visible mirror near the bed"},
|
||||||
|
{"slug": "rose_lamp_bedroom", "prompt": "intimate bedroom with rose-colored lamps, silk sheets, and soft shadows"}
|
||||||
|
],
|
||||||
|
"fetish_studio_scenes": [
|
||||||
|
{"slug": "black_latex_studio_floor", "prompt": "dark private studio with glossy black floor reflections, rim light, and a phone tripod"},
|
||||||
|
{"slug": "red_velvet_lacquer_room", "prompt": "red velvet room with black lacquer furniture, low spotlights, and reflective surfaces"},
|
||||||
|
{"slug": "industrial_loft_private_set", "prompt": "private industrial loft with brick walls, metal beams, chains, and dramatic light"},
|
||||||
|
{"slug": "neon_lacquer_private_room", "prompt": "private neon-lit lacquer room with magenta highlights, mirrors, and glossy floor"},
|
||||||
|
{"slug": "harness_wall_studio", "prompt": "private harness-wall studio with leather props, matte black backdrop, and controlled rim light"},
|
||||||
|
{"slug": "chrome_fetish_set", "prompt": "chrome studio set with reflective panels, black curtains, and hard-edged erotic lighting"}
|
||||||
|
],
|
||||||
|
"costume_backstage_scenes": [
|
||||||
|
{"slug": "costume_dressing_room_phone", "prompt": "private costume dressing room with racks, warm mirror bulbs, velvet curtains, and a phone selfie angle"},
|
||||||
|
{"slug": "burlesque_stage_close", "prompt": "small private burlesque stage with red curtains, warm spotlights, and close audience-level framing"},
|
||||||
|
{"slug": "cabaret_backstage_vanity", "prompt": "cabaret backstage corner with feather props, vanity mirrors, and dark wood"},
|
||||||
|
{"slug": "after_dark_private_office", "prompt": "stylized private office after dark with blinds, desk lamp, and city glow"},
|
||||||
|
{"slug": "fantasy_parlor_content_set", "prompt": "dark fantasy parlor with velvet chair, candlelight, antique wallpaper, and creator-shot framing"},
|
||||||
|
{"slug": "cosplay_hotel_mirror", "prompt": "hotel mirror cosplay try-on setup with costume pieces on chairs and warm vanity bulbs"}
|
||||||
|
],
|
||||||
|
"hardcore_private_scenes": [
|
||||||
|
{"slug": "hardcore_bedroom_phone_tripod", "prompt": "private bedroom sex-content setup with a phone tripod, rumpled sheets, pillows, and warm lamps"},
|
||||||
|
{"slug": "hardcore_hotel_bed_city", "prompt": "luxury hotel suite with city lights, messy satin bedding, floor clothes, and low amber light"},
|
||||||
|
{"slug": "hardcore_mirror_bedroom", "prompt": "bedroom with a large mirror reflecting the explicit adult scene and a visible phone angle"},
|
||||||
|
{"slug": "hardcore_low_mattress_studio", "prompt": "private photo studio with a low mattress, fabric drapes, phone tripod, and controlled warm spotlighting"},
|
||||||
|
{"slug": "hardcore_velvet_room", "prompt": "private velvet room with dark curtains, soft cushions, scattered clothes, and warm red light"},
|
||||||
|
{"slug": "hardcore_shower_room", "prompt": "large private shower room with steam, wet tile, glass reflections, and warm reflected light"},
|
||||||
|
{"slug": "hardcore_lounge_couch", "prompt": "dim private lounge with a wide couch, scattered clothing, side lamps, and soft golden shadows"},
|
||||||
|
{"slug": "hardcore_floor_cushion_room", "prompt": "intimate room with floor cushions, silk sheets, low candles, and close vertical camera framing"},
|
||||||
|
{"slug": "hardcore_ring_light_bed", "prompt": "creator bedroom with ring light reflection, phone on tripod, bed sheets, and visible content setup"},
|
||||||
|
{"slug": "hardcore_bathroom_counter", "prompt": "private bathroom with counter mirror, wet towels, steam, and explicit creator-shot framing"},
|
||||||
|
{"slug": "hardcore_walk_in_closet_floor", "prompt": "mirrored walk-in closet floor with clothes scattered, warm bulbs, and a phone reflection"},
|
||||||
|
{"slug": "hardcore_car_backseat", "prompt": "parked private car backseat with tinted windows, dashboard glow, and tight phone-camera framing"}
|
||||||
|
],
|
||||||
|
"hardcore_bed_scenes": [
|
||||||
|
{"slug": "bed_edge_close_contact", "prompt": "edge of a rumpled bed with pillows pushed aside, sheets pulled tight, and close phone-camera framing"},
|
||||||
|
{"slug": "low_bed_mirror_angle", "prompt": "low bed beside a full-length mirror with visible reflected bodies and warm lamps"},
|
||||||
|
{"slug": "hotel_bed_overhead", "prompt": "hotel bed arranged for an overhead phone shot with satin sheets and city light"},
|
||||||
|
{"slug": "floor_mattress_creator_set", "prompt": "floor mattress content setup with fabric drapes, ring light shadow, and scattered clothes"},
|
||||||
|
{"slug": "canopy_bed_explicit_set", "prompt": "canopy bed with sheer curtains, warm bedside light, and intimate full-body framing"},
|
||||||
|
{"slug": "velvet_bedroom_wide", "prompt": "velvet bedroom with wide bed, dark curtains, soft cushions, and warm red side light"}
|
||||||
|
],
|
||||||
|
"hardcore_penetrative_scenes": [
|
||||||
|
{"slug": "penetration_mirror_bedroom", "prompt": "mirror-facing bedroom setup designed to show explicit body contact and the phone angle"},
|
||||||
|
{"slug": "penetration_edge_of_bed", "prompt": "edge-of-bed setup with hips near the camera, rumpled sheets, and warm lamp light"},
|
||||||
|
{"slug": "penetration_low_mattress", "prompt": "low mattress studio with side lighting, fabric drapes, and all bodies visible"},
|
||||||
|
{"slug": "penetration_couch_lounge", "prompt": "wide private couch with pillows pushed aside, scattered clothes, and golden shadows"},
|
||||||
|
{"slug": "penetration_shower_bench", "prompt": "wet shower bench with steam, tile reflections, and close explicit framing"},
|
||||||
|
{"slug": "penetration_floor_cushions", "prompt": "floor cushions and silk sheets arranged for full-body explicit contact"}
|
||||||
|
],
|
||||||
|
"hardcore_oral_scenes": [
|
||||||
|
{"slug": "oral_bed_kneeling_close", "prompt": "bedside kneeling setup with pillows, warm lamps, and close mouth-level camera framing"},
|
||||||
|
{"slug": "oral_mirror_floor", "prompt": "mirror-facing floor setup with bodies reflected and phone held low"},
|
||||||
|
{"slug": "oral_couch_front_view", "prompt": "private couch arranged for front-view oral contact with soft side lamps"},
|
||||||
|
{"slug": "oral_shower_steam", "prompt": "steamy shower room with wet tile, glass reflections, and close explicit framing"},
|
||||||
|
{"slug": "oral_vanity_floor", "prompt": "vanity-room floor with mirror bulbs, scattered lingerie, and low phone-camera angle"},
|
||||||
|
{"slug": "oral_hotel_bed_close", "prompt": "hotel bed close-up setup with satin sheets, city light, and tight vertical framing"}
|
||||||
|
],
|
||||||
|
"hardcore_anal_scenes": [
|
||||||
|
{"slug": "anal_rear_mirror_bed", "prompt": "bedroom mirror setup emphasizing rear-view body alignment and explicit contact"},
|
||||||
|
{"slug": "anal_bent_over_couch", "prompt": "wide private couch arranged for bent-over explicit contact and low camera angle"},
|
||||||
|
{"slug": "anal_edge_bed_low_angle", "prompt": "edge-of-bed low-angle setup with sheets pulled aside and hips near the lens"},
|
||||||
|
{"slug": "anal_shower_wall", "prompt": "wet shower wall with steam, tile reflections, and hard side lighting"},
|
||||||
|
{"slug": "anal_velvet_bench", "prompt": "private velvet bench with dark curtains, red light, and clear full-body framing"},
|
||||||
|
{"slug": "anal_floor_mattress_mirror", "prompt": "floor mattress beside a full-length mirror with both direct and reflected views"}
|
||||||
|
],
|
||||||
|
"hardcore_threesome_scenes": [
|
||||||
|
{"slug": "threesome_wide_bedroom", "prompt": "wide bedroom setup with a large bed, mirror wall, phone tripod, and all three bodies visible"},
|
||||||
|
{"slug": "threesome_hotel_suite", "prompt": "hotel suite bed and couch area arranged for three adults and city-window light"},
|
||||||
|
{"slug": "threesome_floor_cushions", "prompt": "floor cushion room with silk sheets, low lamps, and enough space for three bodies"},
|
||||||
|
{"slug": "threesome_studio_mattress", "prompt": "private studio mattress with fabric drapes, ring light shadow, and three-person framing"},
|
||||||
|
{"slug": "threesome_shower_room", "prompt": "large shower room with wet tile, steam, glass reflections, and three-person spacing"},
|
||||||
|
{"slug": "threesome_velvet_lounge", "prompt": "private velvet lounge with wide couch, cushions, and warm red-gold light"}
|
||||||
|
],
|
||||||
|
"hardcore_group_scenes": [
|
||||||
|
{"slug": "group_suite_wide_bed", "prompt": "large private suite with a wide bed, couch area, scattered clothes, and all participants visible"},
|
||||||
|
{"slug": "group_studio_mattress_room", "prompt": "private studio with multiple low mattresses, fabric drapes, ring lights, and full group visibility"},
|
||||||
|
{"slug": "group_velvet_orgy_room", "prompt": "private velvet orgy room with dark curtains, floor cushions, soft lamps, and open floor space"},
|
||||||
|
{"slug": "group_lounge_couches", "prompt": "dim private lounge with multiple couches, pillows, warm side lamps, and wide group framing"},
|
||||||
|
{"slug": "group_floor_pillow_room", "prompt": "large floor-pillow room with silk sheets, low candles, and overhead camera possibility"},
|
||||||
|
{"slug": "group_shower_spa_room", "prompt": "large private shower spa with wet tile, steam, benches, and space for multiple adults"},
|
||||||
|
{"slug": "group_rooftop_private_party", "prompt": "private rooftop after-party suite with glass railings, couches, and city lights"},
|
||||||
|
{"slug": "group_hotel_party_bedroom", "prompt": "messy hotel party bedroom with a king bed, side couch, bottles, and warm practical lights"},
|
||||||
|
{"slug": "group_backstage_private_room", "prompt": "private backstage green room with costume racks, couches, mirror bulbs, and scattered clothes"},
|
||||||
|
{"slug": "group_neon_loft_room", "prompt": "private neon loft with mattresses, glossy floor reflections, and wide explicit group composition"},
|
||||||
|
{"slug": "group_mirror_wall_suite", "prompt": "large mirror-wall suite with a wide bed, floor cushions, phone tripod, and enough space to reflect a full adult group"},
|
||||||
|
{"slug": "group_lacquer_mirror_lounge", "prompt": "wide black-lacquer mirror lounge with multiple couches, glossy reflections, warm rim light, and all participants visible"}
|
||||||
|
],
|
||||||
|
"hardcore_climax_scenes": [
|
||||||
|
{"slug": "climax_bed_close_flash", "prompt": "rumpled bed close-up setup with direct phone flash, sheets, and visible fluid detail"},
|
||||||
|
{"slug": "climax_mirror_counter", "prompt": "mirror-facing vanity counter setup with phone reflection and explicit aftermath framing"},
|
||||||
|
{"slug": "climax_floor_sheets", "prompt": "floor sheets with pillows pushed aside, low lamp light, and close body-detail framing"},
|
||||||
|
{"slug": "climax_hotel_bed_flash", "prompt": "hotel bed with phone-flash lighting, messy satin sheets, and city light behind"},
|
||||||
|
{"slug": "climax_shower_tile", "prompt": "wet shower tile scene with steam, reflected light, and visible fluid detail"},
|
||||||
|
{"slug": "climax_velvet_couch", "prompt": "velvet couch with dark curtains, warm red light, and close explicit aftermath framing"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pool_extensions": {
|
||||||
|
"group_scenes": [
|
||||||
|
{"slug": "private_suite_group_party", "prompt": "large private suite with a wide bed, couch area, scattered clothes, and warm party lighting"},
|
||||||
|
{"slug": "private_studio_group_mattresses", "prompt": "private studio with multiple low mattresses, fabric drapes, and wide group visibility"},
|
||||||
|
{"slug": "velvet_lounge_group_couches", "prompt": "private velvet lounge with multiple couches, pillows, and low red-gold light"},
|
||||||
|
{"slug": "rooftop_afterparty_suite", "prompt": "private rooftop after-party suite with glass railings, couches, and city lights"},
|
||||||
|
{"slug": "hotel_party_bedroom_group", "prompt": "messy hotel party bedroom with a king bed, side couch, bottles, and warm practical lights"},
|
||||||
|
{"slug": "neon_loft_group_room", "prompt": "private neon loft with glossy floor reflections, mattresses, and wide group composition"},
|
||||||
|
{"slug": "backstage_green_room_group", "prompt": "private backstage green room with costume racks, mirror bulbs, couches, and scattered clothes"},
|
||||||
|
{"slug": "shower_spa_group_room", "prompt": "large private shower spa room with benches, steam, wet tile, and multiple adults"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
"style": "explicit consensual adult hardcore sex illustration, anatomically clear erotic comic pin-up style, adults only",
|
"style": "explicit consensual adult hardcore sex illustration, anatomically clear erotic comic pin-up style, adults only",
|
||||||
"positive_suffix": "Use clear adult anatomy, visible sexual contact, intense body language, crisp comic linework, detailed hatching, warm erotic lighting, and tactile textured paper.",
|
"positive_suffix": "Use clear adult anatomy, visible sexual contact, intense body language, crisp comic linework, detailed hatching, warm erotic lighting, and tactile textured paper.",
|
||||||
"negative_prompt": "minors, childlike appearance, teen, schoolgirl, incest, bestiality, non-consensual, coercion, rape, violence, injury, blood, gore, watermark",
|
"negative_prompt": "minors, childlike appearance, teen, schoolgirl, incest, bestiality, non-consensual, coercion, rape, violence, injury, blood, gore, watermark",
|
||||||
|
"scene_pools": ["hardcore_private_scenes"],
|
||||||
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Sexual pose: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit, hardcore, and anatomically clear, with visible genital contact and adult bodies only. {positive_suffix} Avoid: {negative_prompt}.",
|
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Sexual pose: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit, hardcore, and anatomically clear, with visible genital contact and adult bodies only. {positive_suffix} Avoid: {negative_prompt}.",
|
||||||
"caption_template": "{trigger}, {scene_kind}, {cast_summary}, {role_graph}, {item}, {scene}, {composition}, explicit consensual adult hardcore sex illustration",
|
"caption_template": "{trigger}, {scene_kind}, {cast_summary}, {role_graph}, {item}, {scene}, {composition}, explicit consensual adult hardcore sex illustration",
|
||||||
"expressions": [
|
"expressions": [
|
||||||
@@ -84,6 +85,7 @@
|
|||||||
"slug": "penetrative_sex",
|
"slug": "penetrative_sex",
|
||||||
"min_people": 2,
|
"min_people": 2,
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"scene_pools": ["hardcore_penetrative_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
|
||||||
"item_templates": [
|
"item_templates": [
|
||||||
"{penetration_act} in {position}, with {body_contact}, {intensity}, and {visibility}",
|
"{penetration_act} in {position}, with {body_contact}, {intensity}, and {visibility}",
|
||||||
"{position} while {penetration_act}, {hand_detail}, {mouth_detail}, and {visibility}",
|
"{position} while {penetration_act}, {hand_detail}, {mouth_detail}, and {visibility}",
|
||||||
@@ -248,6 +250,7 @@
|
|||||||
"slug": "oral_sex",
|
"slug": "oral_sex",
|
||||||
"min_people": 2,
|
"min_people": 2,
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"scene_pools": ["hardcore_oral_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
|
||||||
"item_templates": [
|
"item_templates": [
|
||||||
"{oral_act} in {position}, with {hand_detail}, {expression_detail}, and {visibility}",
|
"{oral_act} in {position}, with {hand_detail}, {expression_detail}, and {visibility}",
|
||||||
"{position} featuring {oral_act}, {body_contact}, {saliva_detail}, and {climax_hint}",
|
"{position} featuring {oral_act}, {body_contact}, {saliva_detail}, and {climax_hint}",
|
||||||
@@ -380,6 +383,7 @@
|
|||||||
"slug": "anal_double_penetration",
|
"slug": "anal_double_penetration",
|
||||||
"min_people": 2,
|
"min_people": 2,
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"scene_pools": ["hardcore_anal_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
|
||||||
"item_templates": [
|
"item_templates": [
|
||||||
"{anal_act} in {position}, with {leg_detail}, {hand_detail}, and {visibility}",
|
"{anal_act} in {position}, with {leg_detail}, {hand_detail}, and {visibility}",
|
||||||
"{double_act} with {body_arrangement}, {intensity}, {mouth_detail}, and {visibility}",
|
"{double_act} with {body_arrangement}, {intensity}, {mouth_detail}, and {visibility}",
|
||||||
@@ -584,6 +588,7 @@
|
|||||||
"slug": "threesomes",
|
"slug": "threesomes",
|
||||||
"min_people": 3,
|
"min_people": 3,
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"scene_pools": ["hardcore_threesome_scenes", "hardcore_group_scenes", "hardcore_mirror_scenes"],
|
||||||
"item_templates": [
|
"item_templates": [
|
||||||
"{threesome_act} with {body_arrangement}, {oral_detail}, {penetration_detail}, and {visibility}",
|
"{threesome_act} with {body_arrangement}, {oral_detail}, {penetration_detail}, and {visibility}",
|
||||||
"{body_arrangement} while {threesome_act}, with {hand_detail}, {mouth_detail}, and {climax_hint}",
|
"{body_arrangement} while {threesome_act}, with {hand_detail}, {mouth_detail}, and {climax_hint}",
|
||||||
@@ -760,7 +765,9 @@
|
|||||||
"name": "Group sex and orgy",
|
"name": "Group sex and orgy",
|
||||||
"slug": "group_sex_orgy",
|
"slug": "group_sex_orgy",
|
||||||
"min_people": 4,
|
"min_people": 4,
|
||||||
|
"inherit_scenes": false,
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"scene_pools": ["hardcore_group_scenes"],
|
||||||
"item_templates": [
|
"item_templates": [
|
||||||
"{group_act} with {arrangement}, {contact_detail}, {fluid_detail}, and {visibility}",
|
"{group_act} with {arrangement}, {contact_detail}, {fluid_detail}, and {visibility}",
|
||||||
"{arrangement} featuring {group_act}, {oral_detail}, {penetration_detail}, and {intensity}",
|
"{arrangement} featuring {group_act}, {oral_detail}, {penetration_detail}, and {intensity}",
|
||||||
@@ -928,6 +935,7 @@
|
|||||||
"slug": "cumshot_climax",
|
"slug": "cumshot_climax",
|
||||||
"min_people": 1,
|
"min_people": 1,
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"scene_pools": ["hardcore_climax_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
|
||||||
"item_templates": [
|
"item_templates": [
|
||||||
"{climax_act} with {fluid_location}, {body_position}, {expression_detail}, and {visibility}",
|
"{climax_act} with {fluid_location}, {body_position}, {expression_detail}, and {visibility}",
|
||||||
"{body_position} during {climax_act}, with {hand_detail}, {fluid_location}, and {fluid_detail}",
|
"{body_position} during {climax_act}, with {hand_detail}, {fluid_location}, and {fluid_detail}",
|
||||||
|
|||||||
@@ -1331,9 +1331,10 @@ def _expand_scenes() -> list[tuple[str, str]]:
|
|||||||
additions: list[tuple[str, str]] = []
|
additions: list[tuple[str, str]] = []
|
||||||
for base_slug, base_description in bases:
|
for base_slug, base_description in bases:
|
||||||
for mood_slug, mood_description in moods:
|
for mood_slug, mood_description in moods:
|
||||||
|
separator = ", " if mood_description.startswith("with ") else " "
|
||||||
additions.append((
|
additions.append((
|
||||||
_scene_slug(f"{base_slug}_{mood_slug}"),
|
_scene_slug(f"{base_slug}_{mood_slug}"),
|
||||||
f"{base_description} {mood_description}",
|
f"{base_description}{separator}{mood_description}",
|
||||||
))
|
))
|
||||||
additions.extend(
|
additions.extend(
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -0,0 +1,380 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
TRIGGER_CANDIDATES = (
|
||||||
|
"sxcpinup_coloredpencil",
|
||||||
|
"sxcppnl7",
|
||||||
|
)
|
||||||
|
|
||||||
|
PROMPT_FIELD_LABELS = (
|
||||||
|
"Ages",
|
||||||
|
"Body types",
|
||||||
|
"Cast",
|
||||||
|
"Scene",
|
||||||
|
"Setting",
|
||||||
|
"Pose",
|
||||||
|
"Sexual pose",
|
||||||
|
"Facial expression",
|
||||||
|
"Facial expressions",
|
||||||
|
"Clothing",
|
||||||
|
"Erotic outfit",
|
||||||
|
"Prop/detail",
|
||||||
|
"Composition",
|
||||||
|
"Role graph",
|
||||||
|
"Use",
|
||||||
|
"Avoid",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _clean(value: Any) -> str:
|
||||||
|
text = "" if value is None else str(value)
|
||||||
|
text = text.replace("\n", " ")
|
||||||
|
text = re.sub(r"\s+", " ", text).strip()
|
||||||
|
text = re.sub(r"\s+([,.;:])", r"\1", text)
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def _sentence(text: str) -> str:
|
||||||
|
text = _clean(text).strip(" ,;")
|
||||||
|
if not text:
|
||||||
|
return ""
|
||||||
|
text = text[:1].upper() + text[1:]
|
||||||
|
if text[-1] not in ".!?":
|
||||||
|
text += "."
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def _paragraph(parts: list[str]) -> str:
|
||||||
|
return " ".join(part for part in (_sentence(part) for part in parts) if part)
|
||||||
|
|
||||||
|
|
||||||
|
def _maybe_json(text: str) -> dict[str, Any] | None:
|
||||||
|
text = _clean(text)
|
||||||
|
if not text.startswith("{"):
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
value = json.loads(text)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return None
|
||||||
|
return value if isinstance(value, dict) else None
|
||||||
|
|
||||||
|
|
||||||
|
def _row_from_inputs(source_text: str, metadata_json: str, input_hint: str) -> tuple[dict[str, Any] | None, str]:
|
||||||
|
candidates: list[tuple[str, str]] = []
|
||||||
|
if input_hint in ("auto", "metadata_json"):
|
||||||
|
candidates.append((metadata_json, "metadata_json"))
|
||||||
|
candidates.append((source_text, "source_json"))
|
||||||
|
for text, method in candidates:
|
||||||
|
row = _maybe_json(text)
|
||||||
|
if row is not None:
|
||||||
|
return row, method
|
||||||
|
return None, "text"
|
||||||
|
|
||||||
|
|
||||||
|
def _strip_trigger(text: str, preserve_trigger: bool) -> str:
|
||||||
|
text = _clean(text)
|
||||||
|
if preserve_trigger:
|
||||||
|
return text
|
||||||
|
for trigger in TRIGGER_CANDIDATES:
|
||||||
|
if text.lower().startswith(trigger.lower() + ","):
|
||||||
|
return text[len(trigger) + 1 :].strip(" ,")
|
||||||
|
if text.lower().startswith(trigger.lower() + "."):
|
||||||
|
return text[len(trigger) + 1 :].strip(" ,")
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def _split_avoid(text: str) -> tuple[str, str]:
|
||||||
|
match = re.search(r"\bAvoid:\s*(.*)$", text)
|
||||||
|
if not match:
|
||||||
|
return text, ""
|
||||||
|
return text[: match.start()].strip(" ."), match.group(1).strip(" .")
|
||||||
|
|
||||||
|
|
||||||
|
def _prompt_field(text: str, label: str) -> str:
|
||||||
|
text = _clean(text)
|
||||||
|
if not text:
|
||||||
|
return ""
|
||||||
|
labels = "|".join(re.escape(name) for name in PROMPT_FIELD_LABELS)
|
||||||
|
pattern = rf"{re.escape(label)}:\s*(.*?)(?=\. (?:{labels}):|\. Use\b|\. Avoid\b|$)"
|
||||||
|
match = re.search(pattern, text)
|
||||||
|
if not match:
|
||||||
|
return ""
|
||||||
|
return _clean(match.group(1)).rstrip(".")
|
||||||
|
|
||||||
|
|
||||||
|
def _row_value(row: dict[str, Any], key: str, labels: tuple[str, ...] = ()) -> str:
|
||||||
|
value = _clean(row.get(key, ""))
|
||||||
|
if value:
|
||||||
|
return value
|
||||||
|
prompt = _clean(row.get("prompt", ""))
|
||||||
|
for label in labels:
|
||||||
|
value = _prompt_field(prompt, label)
|
||||||
|
if value:
|
||||||
|
return value
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def _single_caption_front(row: dict[str, Any]) -> dict[str, str]:
|
||||||
|
caption = _strip_trigger(_clean(row.get("caption")), False)
|
||||||
|
if not caption:
|
||||||
|
return {}
|
||||||
|
subject = _clean(row.get("primary_subject"))
|
||||||
|
age = _clean(row.get("age_band") or row.get("age"))
|
||||||
|
body = _clean(row.get("body_phrase"))
|
||||||
|
if not body:
|
||||||
|
body_type = _clean(row.get("body_type") or row.get("body"))
|
||||||
|
figure = _clean(row.get("figure"))
|
||||||
|
body = f"{body_type} figure with {figure}" if body_type and figure else f"{body_type} figure".strip()
|
||||||
|
front = f"{subject}, {age}, {body}, "
|
||||||
|
if subject in ("woman", "man") and age and body and caption.startswith(front):
|
||||||
|
try:
|
||||||
|
skin, hair, eyes, _rest = caption[len(front) :].split(", ", 3)
|
||||||
|
except ValueError:
|
||||||
|
return {}
|
||||||
|
return {"body_phrase": body, "skin": skin, "hair": hair, "eyes": eyes}
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def _combine_negative(*parts: str) -> str:
|
||||||
|
cleaned = [_clean(part).strip(" ,.") for part in parts if _clean(part).strip(" ,.")]
|
||||||
|
return ", ".join(cleaned)
|
||||||
|
|
||||||
|
|
||||||
|
def _clean_age(age: Any) -> str:
|
||||||
|
return _clean(age)
|
||||||
|
|
||||||
|
|
||||||
|
def _age_subject(row: dict[str, Any], fallback_subject: str = "adult person") -> str:
|
||||||
|
subject = _clean(row.get("subject_phrase") or row.get("primary_subject") or row.get("subject") or fallback_subject)
|
||||||
|
age = _clean_age(row.get("age_band") or row.get("age"))
|
||||||
|
if row.get("subject_type") == "configured_cast":
|
||||||
|
return _clean(row.get("subject_phrase") or subject)
|
||||||
|
if subject in ("woman", "man"):
|
||||||
|
if age:
|
||||||
|
return f"{age} {subject}" if "adult" in age.lower() else f"{age} adult {subject}"
|
||||||
|
return f"adult {subject}"
|
||||||
|
if age and "adult" not in subject.lower():
|
||||||
|
return f"{age} {subject}"
|
||||||
|
return subject or fallback_subject
|
||||||
|
|
||||||
|
|
||||||
|
def _appearance_phrase(row: dict[str, Any]) -> str:
|
||||||
|
front = _single_caption_front(row)
|
||||||
|
parts = [
|
||||||
|
_row_value(row, "body_phrase") or front.get("body_phrase"),
|
||||||
|
_row_value(row, "skin") or front.get("skin"),
|
||||||
|
_row_value(row, "hair") or front.get("hair"),
|
||||||
|
_row_value(row, "eyes") or front.get("eyes"),
|
||||||
|
]
|
||||||
|
return ", ".join(_clean(part) for part in parts if _clean(part))
|
||||||
|
|
||||||
|
|
||||||
|
def _camera_phrase(row: dict[str, Any]) -> str:
|
||||||
|
directive = _clean(row.get("camera_directive"))
|
||||||
|
if directive:
|
||||||
|
return directive
|
||||||
|
config = row.get("camera_config")
|
||||||
|
if isinstance(config, dict):
|
||||||
|
mode = _clean(config.get("camera_mode")).replace("_", " ")
|
||||||
|
shot = _clean(config.get("shot_size")).replace("_", " ")
|
||||||
|
angle = _clean(config.get("angle")).replace("_", " ")
|
||||||
|
pieces = [piece for piece in (mode, shot, angle) if piece and piece != "auto" and piece != "standard"]
|
||||||
|
if pieces:
|
||||||
|
return "Camera framing uses " + ", ".join(pieces)
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def _style_phrase(row: dict[str, Any], style_mode: str) -> str:
|
||||||
|
if style_mode == "minimal":
|
||||||
|
return ""
|
||||||
|
if style_mode == "photographic":
|
||||||
|
return "realistic creator-shot photography with natural lighting, tactile skin and fabric detail, and clean social-media composition"
|
||||||
|
style = _clean(row.get("style"))
|
||||||
|
suffix = _clean(row.get("positive_suffix")) or _prompt_field(_clean(row.get("prompt")), "Use")
|
||||||
|
if style and suffix:
|
||||||
|
return f"{style}; {suffix}"
|
||||||
|
return style or suffix
|
||||||
|
|
||||||
|
|
||||||
|
def _normal_row_to_krea(row: dict[str, Any], detail_level: str, style_mode: str) -> tuple[str, str]:
|
||||||
|
subject_type = _clean(row.get("subject_type"))
|
||||||
|
primary = _clean(row.get("primary_subject"))
|
||||||
|
item = _row_value(row, "item", ("Sexual pose", "Erotic outfit", "Clothing")) or _clean(row.get("custom_item"))
|
||||||
|
item = re.sub(r",?\s*(fashion editorial|resort) styling$", "", item, flags=re.IGNORECASE)
|
||||||
|
scene = _row_value(row, "scene_text", ("Setting", "Scene")) or _clean(row.get("scene"))
|
||||||
|
pose = _row_value(row, "pose", ("Sexual pose", "Pose"))
|
||||||
|
expression = _row_value(row, "expression", ("Facial expressions", "Facial expression"))
|
||||||
|
composition = re.sub(r"^vertical\s+", "", _row_value(row, "composition", ("Composition",)), flags=re.IGNORECASE)
|
||||||
|
camera = _camera_phrase(row)
|
||||||
|
style = _style_phrase(row, style_mode)
|
||||||
|
|
||||||
|
if subject_type == "configured_cast" or _clean(row.get("cast_summary")):
|
||||||
|
subject = _clean(row.get("subject_phrase") or primary or "adult sexual scene")
|
||||||
|
cast = _clean(row.get("cast_summary"))
|
||||||
|
role_graph = _clean(row.get("role_graph"))
|
||||||
|
parts = [
|
||||||
|
f"A consensual explicit adult scene with {subject}, all participants 21+ and visibly adult",
|
||||||
|
f"The cast includes {cast}" if cast else "",
|
||||||
|
role_graph,
|
||||||
|
f"The sexual action is {item}" if item else "",
|
||||||
|
f"The setting is {scene}" if scene else "",
|
||||||
|
f"Facial expressions are {expression}" if expression else "",
|
||||||
|
f"The image is framed as {composition}" if composition else "",
|
||||||
|
camera,
|
||||||
|
style if detail_level != "concise" else "",
|
||||||
|
]
|
||||||
|
return _paragraph(parts), "metadata(configured_cast)"
|
||||||
|
|
||||||
|
if primary in ("woman", "man") or subject_type in ("woman", "man", "single_any"):
|
||||||
|
subject = _age_subject(row, "adult woman")
|
||||||
|
appearance = _appearance_phrase(row)
|
||||||
|
parts = [
|
||||||
|
f"A {subject}" if not subject.lower().startswith(("a ", "an ")) else subject,
|
||||||
|
f"with {appearance}" if appearance else "",
|
||||||
|
f"wearing {item}" if item else "",
|
||||||
|
f"{pose}" if pose else "",
|
||||||
|
f"with {expression}" if expression else "",
|
||||||
|
f"in {scene}" if scene else "",
|
||||||
|
f"framed as {composition}" if composition else "",
|
||||||
|
camera,
|
||||||
|
style if detail_level != "concise" else "",
|
||||||
|
]
|
||||||
|
return _paragraph([", ".join(part for part in parts[:6] if part), *parts[6:]]), "metadata(single)"
|
||||||
|
|
||||||
|
subject = _age_subject(row, primary or "adult scene")
|
||||||
|
parts = [
|
||||||
|
f"{subject}",
|
||||||
|
f"featuring {item}" if item else "",
|
||||||
|
f"in {scene}" if scene else "",
|
||||||
|
f"with {expression}" if expression else "",
|
||||||
|
f"framed as {composition}" if composition else "",
|
||||||
|
camera,
|
||||||
|
style if detail_level != "concise" else "",
|
||||||
|
]
|
||||||
|
return _paragraph(parts), "metadata(generic)"
|
||||||
|
|
||||||
|
|
||||||
|
def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str) -> tuple[str, str, str, str]:
|
||||||
|
descriptor = _clean(row.get("shared_descriptor"))
|
||||||
|
soft = row.get("softcore_row") if isinstance(row.get("softcore_row"), dict) else {}
|
||||||
|
hard = row.get("hardcore_row") if isinstance(row.get("hardcore_row"), dict) else {}
|
||||||
|
soft_camera = _clean(row.get("softcore_camera_directive")) or _camera_phrase(soft)
|
||||||
|
hard_camera = _clean(row.get("hardcore_camera_directive")) or _camera_phrase(hard)
|
||||||
|
soft_style = _style_phrase(soft, style_mode)
|
||||||
|
hard_style = _style_phrase(hard, style_mode)
|
||||||
|
options = row.get("options") if isinstance(row.get("options"), dict) else {}
|
||||||
|
soft_level = _clean(options.get("softcore_level")).replace("_", " ")
|
||||||
|
hard_level = _clean(options.get("hardcore_level")).replace("_", " ")
|
||||||
|
hard_cast = _clean(row.get("hardcore_women_count"))
|
||||||
|
hard_men = _clean(row.get("hardcore_men_count"))
|
||||||
|
hard_cast_text = _clean(hard.get("cast_summary")) or (
|
||||||
|
f"{hard_cast} adult women and {hard_men} adult men" if hard_cast or hard_men else ""
|
||||||
|
)
|
||||||
|
|
||||||
|
soft_parts = [
|
||||||
|
f"A visibly adult creator, {descriptor}",
|
||||||
|
f"shown in a {soft_level or 'softcore'} Insta/OF creator image",
|
||||||
|
f"wearing {soft.get('item')}" if soft.get("item") else "",
|
||||||
|
f"{soft.get('pose')}" if soft.get("pose") else "",
|
||||||
|
f"with {soft.get('expression')}" if soft.get("expression") else "",
|
||||||
|
f"in {soft.get('scene_text')}" if soft.get("scene_text") else "",
|
||||||
|
f"framed as {soft.get('composition')}" if soft.get("composition") else "",
|
||||||
|
soft_camera,
|
||||||
|
soft_style if detail_level != "concise" else "",
|
||||||
|
]
|
||||||
|
hard_parts = [
|
||||||
|
f"The same visibly adult creator, {descriptor}, is the visually central woman in a consensual explicit adult {hard_level or 'hardcore'} scene",
|
||||||
|
f"all participants are 21+ and visibly adult; the cast includes {hard_cast_text}" if hard_cast_text else "all participants are 21+ and visibly adult",
|
||||||
|
_clean(hard.get("role_graph")),
|
||||||
|
f"The sexual action is {hard.get('item')}" if hard.get("item") else "",
|
||||||
|
f"set in {row.get('hardcore_row', {}).get('scene_text') or hard.get('scene_text')}" if hard.get("scene_text") else "",
|
||||||
|
f"with {hard.get('expression')}" if hard.get("expression") else "",
|
||||||
|
f"framed as {hard.get('composition')}" if hard.get("composition") else "",
|
||||||
|
hard_camera,
|
||||||
|
hard_style if detail_level != "concise" else "",
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
_paragraph(soft_parts),
|
||||||
|
_combine_negative(row.get("softcore_negative_prompt")),
|
||||||
|
_paragraph(hard_parts),
|
||||||
|
_combine_negative(row.get("hardcore_negative_prompt")),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _fallback_text_to_krea(
|
||||||
|
source_text: str,
|
||||||
|
preserve_trigger: bool,
|
||||||
|
detail_level: str,
|
||||||
|
style_mode: str,
|
||||||
|
) -> tuple[str, str, str]:
|
||||||
|
positive, negative = _split_avoid(_strip_trigger(source_text, preserve_trigger))
|
||||||
|
positive = re.sub(r"\b(?:Scene|Setting):", "The setting is", positive)
|
||||||
|
positive = re.sub(r"\b(?:Pose|Sexual pose):", "The pose is", positive)
|
||||||
|
positive = re.sub(r"\bFacial expressions?:", "The facial expression is", positive)
|
||||||
|
positive = re.sub(r"\bComposition:", "The composition is", positive)
|
||||||
|
positive = re.sub(r"\bRole graph:", "The role choreography is", positive)
|
||||||
|
positive = re.sub(r"\bUse\b", "Use", positive)
|
||||||
|
positive = _clean(positive)
|
||||||
|
return _paragraph([positive]), negative, "text(fallback)"
|
||||||
|
|
||||||
|
|
||||||
|
def format_krea2_prompt(
|
||||||
|
source_text: str,
|
||||||
|
metadata_json: str = "",
|
||||||
|
negative_prompt: str = "",
|
||||||
|
input_hint: str = "auto",
|
||||||
|
target: str = "auto",
|
||||||
|
detail_level: str = "balanced",
|
||||||
|
style_mode: str = "preserve",
|
||||||
|
preserve_trigger: bool = False,
|
||||||
|
extra_positive: str = "",
|
||||||
|
extra_negative: str = "",
|
||||||
|
) -> dict[str, str]:
|
||||||
|
detail_level = detail_level if detail_level in ("concise", "balanced", "dense") else "balanced"
|
||||||
|
style_mode = style_mode if style_mode in ("preserve", "photographic", "minimal") else "preserve"
|
||||||
|
target = target if target in ("auto", "single", "softcore", "hardcore") else "auto"
|
||||||
|
row, method = _row_from_inputs(source_text, metadata_json, input_hint)
|
||||||
|
extracted_negative = ""
|
||||||
|
|
||||||
|
if row and row.get("mode") == "Insta/OF":
|
||||||
|
soft_prompt, soft_negative, hard_prompt, hard_negative = _insta_pair_to_krea(row, detail_level, style_mode)
|
||||||
|
selected = hard_prompt if target == "hardcore" else soft_prompt if target == "softcore" else soft_prompt
|
||||||
|
selected_negative = hard_negative if target == "hardcore" else soft_negative
|
||||||
|
if extra_positive.strip():
|
||||||
|
selected = f"{selected.rstrip()} {extra_positive.strip()}"
|
||||||
|
soft_prompt = f"{soft_prompt.rstrip()} {extra_positive.strip()}"
|
||||||
|
hard_prompt = f"{hard_prompt.rstrip()} {extra_positive.strip()}"
|
||||||
|
negative = _combine_negative(selected_negative, negative_prompt, extra_negative)
|
||||||
|
return {
|
||||||
|
"krea_prompt": selected,
|
||||||
|
"negative_prompt": negative,
|
||||||
|
"krea_softcore_prompt": soft_prompt,
|
||||||
|
"krea_hardcore_prompt": hard_prompt,
|
||||||
|
"softcore_negative_prompt": _combine_negative(soft_negative, extra_negative),
|
||||||
|
"hardcore_negative_prompt": _combine_negative(hard_negative, extra_negative),
|
||||||
|
"method": f"{method}:krea2(insta_of_pair)",
|
||||||
|
}
|
||||||
|
|
||||||
|
if row:
|
||||||
|
prompt, kind = _normal_row_to_krea(row, detail_level, style_mode)
|
||||||
|
extracted_negative = _clean(row.get("negative_prompt"))
|
||||||
|
method = f"{method}:krea2({kind})"
|
||||||
|
else:
|
||||||
|
prompt, extracted_negative, method = _fallback_text_to_krea(source_text, preserve_trigger, detail_level, style_mode)
|
||||||
|
|
||||||
|
if extra_positive.strip():
|
||||||
|
prompt = f"{prompt.rstrip()} {extra_positive.strip()}"
|
||||||
|
negative = _combine_negative(extracted_negative, negative_prompt, extra_negative)
|
||||||
|
return {
|
||||||
|
"krea_prompt": prompt,
|
||||||
|
"negative_prompt": negative,
|
||||||
|
"krea_softcore_prompt": "",
|
||||||
|
"krea_hardcore_prompt": "",
|
||||||
|
"softcore_negative_prompt": "",
|
||||||
|
"hardcore_negative_prompt": "",
|
||||||
|
"method": method,
|
||||||
|
}
|
||||||
+47
-1
@@ -202,6 +202,14 @@ def _list_from(value: Any) -> list[Any]:
|
|||||||
return [value]
|
return [value]
|
||||||
|
|
||||||
|
|
||||||
|
def _is_false(value: Any) -> bool:
|
||||||
|
if isinstance(value, bool):
|
||||||
|
return value is False
|
||||||
|
if isinstance(value, str):
|
||||||
|
return value.strip().lower() in ("false", "0", "no", "off")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _unique_extend(target: list[Any], additions: list[Any]) -> None:
|
def _unique_extend(target: list[Any], additions: list[Any]) -> None:
|
||||||
seen = set()
|
seen = set()
|
||||||
for item in target:
|
for item in target:
|
||||||
@@ -554,6 +562,24 @@ def load_category_library() -> list[dict[str, Any]]:
|
|||||||
return categories
|
return categories
|
||||||
|
|
||||||
|
|
||||||
|
def load_scene_pool_library() -> dict[str, list[Any]]:
|
||||||
|
pools: dict[str, list[Any]] = {}
|
||||||
|
for path in _json_files():
|
||||||
|
data = _read_json(path)
|
||||||
|
raw_pools = data.get("scene_pools", {})
|
||||||
|
if not raw_pools:
|
||||||
|
continue
|
||||||
|
if not isinstance(raw_pools, dict):
|
||||||
|
raise ValueError(f"scene_pools in {path} must be an object")
|
||||||
|
for name, entries in raw_pools.items():
|
||||||
|
pool_name = str(name).strip()
|
||||||
|
if not pool_name:
|
||||||
|
continue
|
||||||
|
pools.setdefault(pool_name, [])
|
||||||
|
_unique_extend(pools[pool_name], _list_from(entries))
|
||||||
|
return pools
|
||||||
|
|
||||||
|
|
||||||
def _extension_targets() -> dict[str, tuple[list[Any], bool]]:
|
def _extension_targets() -> dict[str, tuple[list[Any], bool]]:
|
||||||
return {
|
return {
|
||||||
"women_clothes": (g.WOMEN_CLOTHES, False),
|
"women_clothes": (g.WOMEN_CLOTHES, False),
|
||||||
@@ -1335,7 +1361,27 @@ def _subject_context(
|
|||||||
|
|
||||||
def _scene_pool(category: dict[str, Any], subcategory: dict[str, Any], item: Any, subject_type: str) -> list[Any]:
|
def _scene_pool(category: dict[str, Any], subcategory: dict[str, Any], item: Any, subject_type: str) -> list[Any]:
|
||||||
fallback = g.GROUP_SCENES if subject_type in ("group", "configured_cast") else g.SCENES
|
fallback = g.GROUP_SCENES if subject_type in ("group", "configured_cast") else g.SCENES
|
||||||
return _list_from(_merged_field(category, subcategory, item, "scenes", fallback))
|
scene_entries: list[Any] = []
|
||||||
|
scene_pools = load_scene_pool_library()
|
||||||
|
item_source = item if isinstance(item, dict) else None
|
||||||
|
if item_source is not None and _is_false(item_source.get("inherit_scenes")):
|
||||||
|
sources = (item_source,)
|
||||||
|
elif _is_false(subcategory.get("inherit_scenes")):
|
||||||
|
sources = (subcategory, item_source)
|
||||||
|
else:
|
||||||
|
sources = (category, subcategory, item_source)
|
||||||
|
for source in sources:
|
||||||
|
if not isinstance(source, dict):
|
||||||
|
continue
|
||||||
|
if "scenes" in source:
|
||||||
|
_unique_extend(scene_entries, _list_from(source["scenes"]))
|
||||||
|
refs = _list_from(source.get("scene_pool")) + _list_from(source.get("scene_pools"))
|
||||||
|
for ref in refs:
|
||||||
|
ref_name = str(ref).strip()
|
||||||
|
if ref_name not in scene_pools:
|
||||||
|
raise ValueError(f"Unknown scene pool '{ref_name}'")
|
||||||
|
_unique_extend(scene_entries, scene_pools[ref_name])
|
||||||
|
return scene_entries or fallback
|
||||||
|
|
||||||
|
|
||||||
def _pose_pool(category: dict[str, Any], subcategory: dict[str, Any], item: Any, subject_type: str, poses: str) -> list[Any]:
|
def _pose_pool(category: dict[str, Any], subcategory: dict[str, Any], item: Any, subject_type: str, poses: str) -> list[Any]:
|
||||||
|
|||||||
Reference in New Issue
Block a user