Add expression enable controls
This commit is contained in:
@@ -98,6 +98,14 @@ is emitted into named-cast descriptors:
|
||||
couple/group prompts without turning every partner into a fully detailed primary
|
||||
character. Set a man slot to `full` when the partner needs exact hair/eye detail.
|
||||
|
||||
Slots also expose `expression_enabled` and `expression_intensity`. Disable
|
||||
`expression_enabled` when that character should not receive a face/expression
|
||||
directive. Leave `expression_intensity` at `-1` to use the generator or Insta/OF
|
||||
option fallback. Set it from `0.0` to `1.0` to make expression selection
|
||||
character-driven. For configured casts, matching enabled slots emit
|
||||
per-character expression text such as `Woman A has ...; Man A has ...`; Krea
|
||||
formatting naturalizes those labels in pair prompts.
|
||||
|
||||
Slots are chainable through the `character_cast` input/output. In automatic
|
||||
label mode, the slot closest to the final generator becomes `A` for its gender,
|
||||
the next upstream slot becomes `B`, then `C`, and so on. Example:
|
||||
@@ -293,6 +301,11 @@ Options:
|
||||
- `hardcore_expression_intensity`: `0.0` is controlled, `0.5` is balanced
|
||||
hardcore, `1.0` strongly favors ahegao-style, drooling, fucked-out, climax,
|
||||
and messy orgasm expressions.
|
||||
- `softcore_expression_enabled` and `hardcore_expression_enabled`: disable the
|
||||
expression sentence for that half of the Insta/OF pair. The intensity values
|
||||
are fallbacks; `SxCP Woman Slot` / `SxCP Man Slot` `expression_intensity`
|
||||
overrides them when character slots are connected, and a disabled slot omits
|
||||
that character's expression.
|
||||
- `platform_style`: `hybrid`, `instagram`, or `onlyfans`.
|
||||
- `continuity`: `same_creator_same_room` keeps the scene aligned while each
|
||||
output keeps its own pose/composition; `same_creator_new_scene` keeps the same
|
||||
@@ -320,6 +333,7 @@ The node keeps the original generator controls:
|
||||
`indigenous`, `mixed`, `asian`, or `white_asian`. Combined filter strings
|
||||
such as `latina+south_asian` are also accepted in config JSON.
|
||||
- `poses`: `standard` or `evocative`.
|
||||
- `expression_enabled`: disable facial-expression text entirely for this row.
|
||||
- `expression_intensity`: `0.0` favors mild, neutral, controlled expressions;
|
||||
`0.5` favors balanced category expressions; `1.0` strongly favors the most
|
||||
intense expressions available in the selected category. This affects custom
|
||||
|
||||
+32
-1
@@ -106,6 +106,7 @@ class SxCPPromptBuilder:
|
||||
"clothing": (["full", "minimal"], {"default": "full"}),
|
||||
"ethnicity": (ethnicity_choices(), {"default": "any"}),
|
||||
"poses": (["standard", "evocative"], {"default": "standard"}),
|
||||
"expression_enabled": ("BOOLEAN", {"default": True}),
|
||||
"expression_intensity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"backside_bias": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"figure": (["curvy", "balanced", "bombshell"], {"default": "curvy"}),
|
||||
@@ -141,6 +142,7 @@ class SxCPPromptBuilder:
|
||||
clothing,
|
||||
ethnicity,
|
||||
poses,
|
||||
expression_enabled,
|
||||
expression_intensity,
|
||||
backside_bias,
|
||||
figure,
|
||||
@@ -168,6 +170,7 @@ class SxCPPromptBuilder:
|
||||
clothing=clothing,
|
||||
ethnicity=ethnicity,
|
||||
poses=poses,
|
||||
expression_enabled=expression_enabled,
|
||||
expression_intensity=expression_intensity,
|
||||
backside_bias=backside_bias,
|
||||
figure=figure,
|
||||
@@ -418,6 +421,7 @@ class SxCPGenerationProfile:
|
||||
"profile": (generation_profile_choices(), {"default": "balanced"}),
|
||||
"clothing_override": (["profile_default", "full", "minimal"], {"default": "profile_default"}),
|
||||
"poses_override": (["profile_default", "standard", "evocative"], {"default": "profile_default"}),
|
||||
"expression_enabled": ("BOOLEAN", {"default": True}),
|
||||
"expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
"backside_bias": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
"minimal_clothing_ratio": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
@@ -436,6 +440,7 @@ class SxCPGenerationProfile:
|
||||
profile,
|
||||
clothing_override,
|
||||
poses_override,
|
||||
expression_enabled,
|
||||
expression_intensity,
|
||||
backside_bias,
|
||||
minimal_clothing_ratio,
|
||||
@@ -446,6 +451,7 @@ class SxCPGenerationProfile:
|
||||
profile=profile,
|
||||
clothing_override=clothing_override,
|
||||
poses_override=poses_override,
|
||||
expression_enabled=expression_enabled,
|
||||
expression_intensity=expression_intensity,
|
||||
backside_bias=backside_bias,
|
||||
minimal_clothing_ratio=minimal_clothing_ratio,
|
||||
@@ -453,7 +459,8 @@ class SxCPGenerationProfile:
|
||||
trigger_policy=trigger_policy,
|
||||
)
|
||||
parsed = json.loads(config)
|
||||
summary = f"{parsed['profile']}: {parsed['clothing']}, {parsed['poses']}, expression {parsed['expression_intensity']}"
|
||||
expression_summary = "expression disabled" if not parsed.get("expression_enabled", True) else f"expression {parsed['expression_intensity']}"
|
||||
summary = f"{parsed['profile']}: {parsed['clothing']}, {parsed['poses']}, {expression_summary}"
|
||||
return config, summary
|
||||
|
||||
|
||||
@@ -600,6 +607,8 @@ class SxCPCharacterSlot:
|
||||
"hair": ("STRING", {"default": ""}),
|
||||
"eyes": ("STRING", {"default": ""}),
|
||||
"descriptor_detail": (character_descriptor_detail_choices(), {"default": "auto"}),
|
||||
"expression_enabled": ("BOOLEAN", {"default": True}),
|
||||
"expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
},
|
||||
"optional": {
|
||||
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
||||
@@ -627,6 +636,8 @@ class SxCPCharacterSlot:
|
||||
hair,
|
||||
eyes,
|
||||
descriptor_detail="auto",
|
||||
expression_enabled=True,
|
||||
expression_intensity=-1.0,
|
||||
character_cast="",
|
||||
):
|
||||
result = build_character_slot_json(
|
||||
@@ -643,6 +654,8 @@ class SxCPCharacterSlot:
|
||||
hair=hair,
|
||||
eyes=eyes,
|
||||
descriptor_detail=descriptor_detail,
|
||||
expression_enabled=expression_enabled,
|
||||
expression_intensity=expression_intensity,
|
||||
enabled=enabled,
|
||||
character_cast=character_cast or "",
|
||||
)
|
||||
@@ -667,6 +680,8 @@ class SxCPWomanSlot:
|
||||
"hair": ("STRING", {"default": ""}),
|
||||
"eyes": ("STRING", {"default": ""}),
|
||||
"descriptor_detail": (character_descriptor_detail_choices(), {"default": "auto"}),
|
||||
"expression_enabled": ("BOOLEAN", {"default": True}),
|
||||
"expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
},
|
||||
"optional": {
|
||||
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
||||
@@ -693,6 +708,8 @@ class SxCPWomanSlot:
|
||||
hair,
|
||||
eyes,
|
||||
descriptor_detail="auto",
|
||||
expression_enabled=True,
|
||||
expression_intensity=-1.0,
|
||||
character_cast="",
|
||||
):
|
||||
result = build_character_slot_json(
|
||||
@@ -709,6 +726,8 @@ class SxCPWomanSlot:
|
||||
hair=hair,
|
||||
eyes=eyes,
|
||||
descriptor_detail=descriptor_detail,
|
||||
expression_enabled=expression_enabled,
|
||||
expression_intensity=expression_intensity,
|
||||
enabled=enabled,
|
||||
character_cast=character_cast or "",
|
||||
)
|
||||
@@ -732,6 +751,8 @@ class SxCPManSlot:
|
||||
"hair": ("STRING", {"default": ""}),
|
||||
"eyes": ("STRING", {"default": ""}),
|
||||
"descriptor_detail": (character_descriptor_detail_choices(), {"default": "compact"}),
|
||||
"expression_enabled": ("BOOLEAN", {"default": True}),
|
||||
"expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
},
|
||||
"optional": {
|
||||
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
||||
@@ -757,6 +778,8 @@ class SxCPManSlot:
|
||||
hair,
|
||||
eyes,
|
||||
descriptor_detail="compact",
|
||||
expression_enabled=True,
|
||||
expression_intensity=-1.0,
|
||||
character_cast="",
|
||||
):
|
||||
result = build_character_slot_json(
|
||||
@@ -773,6 +796,8 @@ class SxCPManSlot:
|
||||
hair=hair,
|
||||
eyes=eyes,
|
||||
descriptor_detail=descriptor_detail,
|
||||
expression_enabled=expression_enabled,
|
||||
expression_intensity=expression_intensity,
|
||||
enabled=enabled,
|
||||
character_cast=character_cast or "",
|
||||
)
|
||||
@@ -1005,6 +1030,8 @@ class SxCPInstaOFOptions:
|
||||
"hardcore_men_count": ("INT", {"default": 1, "min": 0, "max": 12, "step": 1}),
|
||||
"softcore_level": (["social_tease", "lingerie_tease", "implied_nude", "explicit_tease", "explicit_nude"], {"default": "lingerie_tease"}),
|
||||
"hardcore_level": (["explicit", "hardcore"], {"default": "hardcore"}),
|
||||
"softcore_expression_enabled": ("BOOLEAN", {"default": True}),
|
||||
"hardcore_expression_enabled": ("BOOLEAN", {"default": True}),
|
||||
"softcore_expression_intensity": ("FLOAT", {"default": 0.45, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"hardcore_expression_intensity": ("FLOAT", {"default": 0.85, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"platform_style": (["hybrid", "instagram", "onlyfans"], {"default": "hybrid"}),
|
||||
@@ -1029,6 +1056,8 @@ class SxCPInstaOFOptions:
|
||||
hardcore_men_count,
|
||||
softcore_level,
|
||||
hardcore_level,
|
||||
softcore_expression_enabled,
|
||||
hardcore_expression_enabled,
|
||||
softcore_expression_intensity,
|
||||
hardcore_expression_intensity,
|
||||
platform_style,
|
||||
@@ -1046,6 +1075,8 @@ class SxCPInstaOFOptions:
|
||||
hardcore_men_count=hardcore_men_count,
|
||||
softcore_level=softcore_level,
|
||||
hardcore_level=hardcore_level,
|
||||
softcore_expression_enabled=softcore_expression_enabled,
|
||||
hardcore_expression_enabled=hardcore_expression_enabled,
|
||||
softcore_expression_intensity=softcore_expression_intensity,
|
||||
hardcore_expression_intensity=hardcore_expression_intensity,
|
||||
platform_style=platform_style,
|
||||
|
||||
+22
-4
@@ -49,6 +49,18 @@ def _clean_text(value: Any) -> str:
|
||||
return text
|
||||
|
||||
|
||||
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 _expression_disabled(row: dict[str, Any]) -> bool:
|
||||
return bool(row.get("expression_disabled")) or _is_false(row.get("expression_enabled", True))
|
||||
|
||||
|
||||
def _cap_first(text: str) -> str:
|
||||
text = _clean_text(text).strip(" ,")
|
||||
return text[:1].upper() + text[1:] if text else ""
|
||||
@@ -362,7 +374,7 @@ def _single_from_row(row: dict[str, Any], detail_level: str, keep_style: bool) -
|
||||
item = _clean_clothing(_row_value(row, "clothing", ("Clothing", "Erotic outfit")))
|
||||
scene = _row_value(row, "scene_text", ("Scene", "Setting"))
|
||||
pose = _row_value(row, "pose", ("Pose",))
|
||||
expression = _row_value(row, "expression", ("Facial expression", "Facial expressions"))
|
||||
expression = "" if _expression_disabled(row) else _row_value(row, "expression", ("Facial expression", "Facial expressions"))
|
||||
composition = _normalize_composition(_row_value(row, "composition", ("Composition",)))
|
||||
prop = _row_value(row, "prop", ("Prop/detail",))
|
||||
style = _row_value(row, "style") if keep_style else ""
|
||||
@@ -431,7 +443,9 @@ def _couple_from_row(row: dict[str, Any], detail_level: str, keep_style: bool) -
|
||||
pose = pose.replace(", affectionate and flirtatious but non-explicit", "")
|
||||
clothing = _clean_clothing(_row_value(row, "item", ITEM_LABELS) or _row_value(row, "clothing", ("Clothing",)))
|
||||
scene = _row_value(row, "scene_text", ("Scene", "Setting"))
|
||||
expression = _row_value(row, "expression", ("Facial expressions", "Facial expression"))
|
||||
expression = ""
|
||||
if not _expression_disabled(row):
|
||||
expression = _row_value(row, "character_expression_text") or _row_value(row, "expression", ("Facial expressions", "Facial expression"))
|
||||
composition = _normalize_composition(_row_value(row, "composition", ("Composition",)))
|
||||
style = _row_value(row, "style") if keep_style else ""
|
||||
|
||||
@@ -466,7 +480,9 @@ def _configured_cast_from_row(row: dict[str, Any], detail_level: str, keep_style
|
||||
role_graph = _row_value(row, "role_graph", ("Role graph",))
|
||||
item = _row_value(row, "item", ITEM_LABELS)
|
||||
scene = _row_value(row, "scene_text", ("Setting", "Scene"))
|
||||
expression = _row_value(row, "expression", ("Facial expressions", "Facial expression"))
|
||||
expression = ""
|
||||
if not _expression_disabled(row):
|
||||
expression = _row_value(row, "character_expression_text") or _row_value(row, "expression", ("Facial expressions", "Facial expression"))
|
||||
composition = _normalize_composition(_row_value(row, "composition", ("Composition",)))
|
||||
cast_descriptor_text = _row_value(row, "cast_descriptor_text", ("Characters", "Cast descriptors"))
|
||||
scene_kind = _row_value(row, "scene_kind") or "explicit adult sex scene"
|
||||
@@ -504,7 +520,9 @@ def _group_or_layout_from_row(row: dict[str, Any], detail_level: str, keep_style
|
||||
age = _row_value(row, "age", ("Ages",)) or _clean_text(row.get("age_band"))
|
||||
item = _clean_clothing(_row_value(row, "item", ITEM_LABELS) or _row_value(row, "clothing", ("Clothing",)))
|
||||
scene = _row_value(row, "scene_text", ("Scene", "Setting"))
|
||||
expression = _row_value(row, "expression", ("Facial expressions", "Facial expression"))
|
||||
expression = ""
|
||||
if not _expression_disabled(row):
|
||||
expression = _row_value(row, "character_expression_text") or _row_value(row, "expression", ("Facial expressions", "Facial expression"))
|
||||
composition = _normalize_composition(_row_value(row, "composition", ("Composition",)))
|
||||
style = _row_value(row, "style") if keep_style else ""
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"id": 3,
|
||||
"type": "SxCPGenerationProfile",
|
||||
"pos": [-1220, -300],
|
||||
"size": [320, 226],
|
||||
"size": [320, 250],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
@@ -51,7 +51,7 @@
|
||||
{"name": "summary", "type": "STRING", "links": null, "slot_index": 1}
|
||||
],
|
||||
"properties": {"Node name for S&R": "SxCPGenerationProfile"},
|
||||
"widgets_values": ["casual_clean", "profile_default", "profile_default", -1.0, -1.0, -1.0, -1.0, "profile_default"]
|
||||
"widgets_values": ["casual_clean", "profile_default", "profile_default", true, -1.0, -1.0, -1.0, -1.0, "profile_default"]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
@@ -210,7 +210,7 @@
|
||||
"id": 12,
|
||||
"type": "SxCPInstaOFOptions",
|
||||
"pos": [-1220, 290],
|
||||
"size": [360, 318],
|
||||
"size": [360, 366],
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
@@ -219,7 +219,7 @@
|
||||
{"name": "options_json", "type": "STRING", "links": [10], "slot_index": 0}
|
||||
],
|
||||
"properties": {"Node name for S&R": "SxCPInstaOFOptions"},
|
||||
"widgets_values": ["same_as_hardcore", "couple", 1, 1, "lingerie_tease", "hardcore", 0.45, 0.85, "hybrid", "same_creator_same_room", "partially_removed", "handheld_selfie", "from_camera_config", "compact"]
|
||||
"widgets_values": ["same_as_hardcore", "couple", 1, 1, "lingerie_tease", "hardcore", true, true, 0.45, 0.85, "hybrid", "same_creator_same_room", "partially_removed", "handheld_selfie", "from_camera_config", "compact"]
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
|
||||
+41
-4
@@ -40,6 +40,18 @@ def _clean(value: Any) -> str:
|
||||
return text
|
||||
|
||||
|
||||
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 _expression_disabled(row: dict[str, Any]) -> bool:
|
||||
return bool(row.get("expression_disabled")) or _is_false(row.get("expression_enabled", True))
|
||||
|
||||
|
||||
def _sentence(text: str) -> str:
|
||||
text = _clean(text).strip(" ,;")
|
||||
if not text:
|
||||
@@ -1009,6 +1021,15 @@ def _appearance_phrase(row: dict[str, Any]) -> str:
|
||||
return ", ".join(_clean(part) for part in parts if _clean(part))
|
||||
|
||||
|
||||
def _expression_phrase(expression: Any) -> str:
|
||||
expression = _clean(expression)
|
||||
if not expression:
|
||||
return ""
|
||||
if ";" in expression or re.search(r"\b(?:Woman|Man) [A-Z] has\b|\bthe (?:woman|man) has\b", expression):
|
||||
return f"Expressions: {expression}"
|
||||
return f"with {expression}"
|
||||
|
||||
|
||||
def _camera_phrase(row: dict[str, Any]) -> str:
|
||||
directive = _clean(row.get("camera_directive"))
|
||||
if directive:
|
||||
@@ -1090,7 +1111,9 @@ def _normal_row_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
||||
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"))
|
||||
expression = ""
|
||||
if not _expression_disabled(row):
|
||||
expression = _row_value(row, "character_expression_text") or _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)
|
||||
@@ -1111,6 +1134,7 @@ def _normal_row_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
||||
cast_prose, cast_labels = _cast_prose(cast_descriptor_text)
|
||||
if not cast_labels and women_count == 1 and men_count == 1:
|
||||
cast_labels = ["Woman A", "Man A"]
|
||||
expression = _natural_label_text(expression, cast_labels)
|
||||
role_graph = _sanitize_scene_text_for_cast(row.get("role_graph"), cast_labels)
|
||||
item = _sanitize_scene_text_for_cast(item, cast_labels)
|
||||
role_graph = _natural_label_text(role_graph, cast_labels)
|
||||
@@ -1123,7 +1147,7 @@ def _normal_row_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
||||
f"A consensual explicit adult scene with {subject}" if not action else "",
|
||||
f"The cast includes {cast}" if cast and not cast_prose and not (women_count == 1 and men_count == 1) else "",
|
||||
f"The setting is {scene}" if scene else "",
|
||||
f"Facial expressions are {expression}" if expression else "",
|
||||
_expression_phrase(expression),
|
||||
_composition_phrase(composition, action, "The image is framed as"),
|
||||
camera,
|
||||
style if detail_level != "concise" else "",
|
||||
@@ -1228,6 +1252,19 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
||||
partner_pose = ""
|
||||
partner_outfit_text = _natural_label_text(partner_outfit_text, soft_labels)
|
||||
|
||||
soft_expression = ""
|
||||
if not _expression_disabled(soft):
|
||||
soft_expression = _natural_label_text(
|
||||
_clean(soft.get("character_expression_text")) or _clean(soft.get("expression")),
|
||||
soft_labels,
|
||||
)
|
||||
hard_expression = ""
|
||||
if not _expression_disabled(hard):
|
||||
hard_expression = _natural_label_text(
|
||||
_clean(hard.get("character_expression_text")) or _clean(hard.get("expression")),
|
||||
hard_labels,
|
||||
)
|
||||
|
||||
soft_parts = [
|
||||
soft_cast_prose,
|
||||
soft_cast_presence,
|
||||
@@ -1235,7 +1272,7 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
||||
partner_pose,
|
||||
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 "",
|
||||
_expression_phrase(soft_expression),
|
||||
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,
|
||||
@@ -1246,7 +1283,7 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
||||
_natural_clothing_state(row.get("hardcore_clothing_state")),
|
||||
hard_cast_prose,
|
||||
f"set in {hard_scene}" if hard_scene else "",
|
||||
f"with {hard.get('expression')}" if hard.get("expression") else "",
|
||||
_expression_phrase(hard_expression),
|
||||
_composition_phrase(hard_composition, hard_action),
|
||||
hard_camera,
|
||||
hard_style if detail_level != "concise" else "",
|
||||
|
||||
+283
-6
@@ -890,6 +890,7 @@ GENERATION_PROFILE_PRESETS = {
|
||||
"balanced": {
|
||||
"clothing": "full",
|
||||
"poses": "standard",
|
||||
"expression_enabled": True,
|
||||
"expression_intensity": 0.5,
|
||||
"backside_bias": 0.0,
|
||||
"minimal_clothing_ratio": -1.0,
|
||||
@@ -900,6 +901,7 @@ GENERATION_PROFILE_PRESETS = {
|
||||
"casual_clean": {
|
||||
"clothing": "full",
|
||||
"poses": "standard",
|
||||
"expression_enabled": True,
|
||||
"expression_intensity": 0.35,
|
||||
"backside_bias": 0.0,
|
||||
"minimal_clothing_ratio": -1.0,
|
||||
@@ -910,6 +912,7 @@ GENERATION_PROFILE_PRESETS = {
|
||||
"evocative_softcore": {
|
||||
"clothing": "minimal",
|
||||
"poses": "evocative",
|
||||
"expression_enabled": True,
|
||||
"expression_intensity": 0.65,
|
||||
"backside_bias": 0.2,
|
||||
"minimal_clothing_ratio": -1.0,
|
||||
@@ -920,6 +923,7 @@ GENERATION_PROFILE_PRESETS = {
|
||||
"hardcore_intense": {
|
||||
"clothing": "minimal",
|
||||
"poses": "evocative",
|
||||
"expression_enabled": True,
|
||||
"expression_intensity": 0.9,
|
||||
"backside_bias": 0.0,
|
||||
"minimal_clothing_ratio": -1.0,
|
||||
@@ -930,6 +934,7 @@ GENERATION_PROFILE_PRESETS = {
|
||||
"krea2_friendly": {
|
||||
"clothing": "full",
|
||||
"poses": "standard",
|
||||
"expression_enabled": True,
|
||||
"expression_intensity": 0.55,
|
||||
"backside_bias": 0.0,
|
||||
"minimal_clothing_ratio": -1.0,
|
||||
@@ -940,6 +945,7 @@ GENERATION_PROFILE_PRESETS = {
|
||||
"flux_original": {
|
||||
"clothing": "full",
|
||||
"poses": "standard",
|
||||
"expression_enabled": True,
|
||||
"expression_intensity": 0.5,
|
||||
"backside_bias": 0.0,
|
||||
"minimal_clothing_ratio": -1.0,
|
||||
@@ -1039,6 +1045,7 @@ def build_generation_profile_json(
|
||||
minimal_clothing_ratio: float = -1.0,
|
||||
standard_pose_ratio: float = -1.0,
|
||||
trigger_policy: str = "profile_default",
|
||||
expression_enabled: bool = True,
|
||||
) -> str:
|
||||
profile = profile if profile in GENERATION_PROFILE_PRESETS else "balanced"
|
||||
config = dict(GENERATION_PROFILE_PRESETS[profile])
|
||||
@@ -1046,6 +1053,7 @@ def build_generation_profile_json(
|
||||
config["clothing"] = clothing_override
|
||||
if poses_override in ("standard", "evocative"):
|
||||
config["poses"] = poses_override
|
||||
config["expression_enabled"] = not _is_false(expression_enabled)
|
||||
if float(expression_intensity) >= 0:
|
||||
config["expression_intensity"] = _clamped_float(expression_intensity, config["expression_intensity"])
|
||||
if float(backside_bias) >= 0:
|
||||
@@ -1079,6 +1087,7 @@ def _parse_generation_profile(profile_config: str | dict[str, Any] | None) -> di
|
||||
parsed.update(raw)
|
||||
parsed["clothing"] = parsed["clothing"] if parsed.get("clothing") in ("full", "minimal") else "full"
|
||||
parsed["poses"] = parsed["poses"] if parsed.get("poses") in ("standard", "evocative") else "standard"
|
||||
parsed["expression_enabled"] = not _is_false(parsed.get("expression_enabled", True))
|
||||
parsed["expression_intensity"] = _clamped_float(parsed.get("expression_intensity"), 0.5)
|
||||
parsed["backside_bias"] = _clamped_float(parsed.get("backside_bias"), 0.0)
|
||||
parsed["minimal_clothing_ratio"] = _clamped_float(parsed.get("minimal_clothing_ratio"), -1.0, -1.0, 1.0)
|
||||
@@ -1325,6 +1334,54 @@ def _format(template: str, context: dict[str, Any]) -> str:
|
||||
return template.format_map(safe_context)
|
||||
|
||||
|
||||
def _clean_prompt_punctuation(text: str) -> str:
|
||||
text = re.sub(r"\s+", " ", str(text or "")).strip()
|
||||
text = re.sub(r"\s+([,.;:])", r"\1", text)
|
||||
text = re.sub(r"(?:,\s*){2,}", ", ", text)
|
||||
text = re.sub(r"\.\s*\.", ".", text)
|
||||
text = re.sub(r":\s*\.", ".", text)
|
||||
return text.strip()
|
||||
|
||||
|
||||
def _strip_expression_text(text: str, expression: Any = "") -> str:
|
||||
text = str(text or "")
|
||||
if not text:
|
||||
return ""
|
||||
text = re.sub(r"\s*Facial expressions?:\s*[^.]*\.\s*", " ", text, flags=re.IGNORECASE)
|
||||
text = re.sub(r",\s*one with [^,]+ and the other with [^,]+(?=,)", "", text, flags=re.IGNORECASE)
|
||||
text = re.sub(r",\s*a lively mix of expressions from [^,]+(?=,)", "", text, flags=re.IGNORECASE)
|
||||
text = re.sub(r"\s+with\s+(?:an?|the)\s+[^,]*expression(?=,)", "", text, flags=re.IGNORECASE)
|
||||
expression_text = str(expression or "").strip()
|
||||
if expression_text:
|
||||
for part in [piece.strip() for piece in expression_text.split(";") if piece.strip()]:
|
||||
escaped = re.escape(part)
|
||||
text = re.sub(rf",\s*{escaped}(?=,)", "", text, flags=re.IGNORECASE)
|
||||
text = re.sub(rf"\s+with\s+(?:an?|the)?\s*{escaped}", "", text, flags=re.IGNORECASE)
|
||||
return _clean_prompt_punctuation(text)
|
||||
|
||||
|
||||
def _disable_row_expression(row: dict[str, Any], source: str = "disabled") -> dict[str, Any]:
|
||||
previous_expression = row.get("expression", "")
|
||||
row["prompt"] = _strip_expression_text(row.get("prompt", ""), previous_expression)
|
||||
row["caption"] = _strip_expression_text(row.get("caption", ""), previous_expression)
|
||||
row["expression"] = ""
|
||||
row["shared_expression"] = ""
|
||||
row["character_expressions"] = []
|
||||
row["character_expression_text"] = ""
|
||||
row["expression_enabled"] = False
|
||||
row["expression_disabled"] = True
|
||||
row["expression_intensity"] = None
|
||||
row["expression_intensity_source"] = source
|
||||
return row
|
||||
|
||||
|
||||
def _labeled_expression_sentence(label: str, expression: Any) -> str:
|
||||
expression = str(expression or "").strip()
|
||||
if not expression:
|
||||
return ""
|
||||
return f"{label}: {expression}. "
|
||||
|
||||
|
||||
def _prepend_trigger(prompt: str, trigger: str, enabled: bool) -> str:
|
||||
trigger = trigger.strip()
|
||||
if not enabled or not trigger:
|
||||
@@ -1801,6 +1858,111 @@ def _normalize_descriptor_detail(value: Any) -> str:
|
||||
return text if text in CHARACTER_DESCRIPTOR_DETAIL_CHOICES else "auto"
|
||||
|
||||
|
||||
def _normalize_slot_expression_intensity(value: Any) -> float:
|
||||
try:
|
||||
intensity = float(value)
|
||||
except (TypeError, ValueError):
|
||||
return -1.0
|
||||
if intensity < 0:
|
||||
return -1.0
|
||||
return _clamped_float(intensity, 0.5)
|
||||
|
||||
|
||||
def _slot_expression_enabled(slot: dict[str, Any] | None) -> bool:
|
||||
if not slot:
|
||||
return True
|
||||
return not _is_false(slot.get("expression_enabled", True))
|
||||
|
||||
|
||||
def _slot_expression_intensity(slot: dict[str, Any] | None) -> float | None:
|
||||
if not slot or not _slot_expression_enabled(slot):
|
||||
return None
|
||||
intensity = _normalize_slot_expression_intensity(slot.get("expression_intensity"))
|
||||
return intensity if intensity >= 0 else None
|
||||
|
||||
|
||||
def _mean(values: list[float]) -> float:
|
||||
return sum(values) / len(values)
|
||||
|
||||
|
||||
def _cast_expression_intensity_override(
|
||||
fallback: float,
|
||||
label_map: dict[str, dict[str, Any]],
|
||||
women_count: int,
|
||||
men_count: int,
|
||||
) -> tuple[float | None, str]:
|
||||
groups: list[tuple[str, list[str]]] = [
|
||||
("women", [f"Woman {chr(ord('A') + index)}" for index in range(max(0, women_count))]),
|
||||
("men", [f"Man {chr(ord('A') + index)}" for index in range(max(0, men_count))]),
|
||||
]
|
||||
all_values: list[float] = []
|
||||
matching_slots: list[dict[str, Any]] = []
|
||||
for group_name, labels in groups:
|
||||
values: list[float] = []
|
||||
value_labels: list[str] = []
|
||||
for label in labels:
|
||||
slot = label_map.get(label)
|
||||
if slot:
|
||||
matching_slots.append(slot)
|
||||
value = _slot_expression_intensity(slot)
|
||||
if value is not None:
|
||||
values.append(value)
|
||||
value_labels.append(label)
|
||||
all_values.append(value)
|
||||
if values:
|
||||
if len(values) == 1:
|
||||
return values[0], f"character_slot:{value_labels[0]}"
|
||||
return _mean(values), f"character_slots:{group_name}"
|
||||
if all_values:
|
||||
return _mean(all_values), "character_slots:cast"
|
||||
if matching_slots and all(not _slot_expression_enabled(slot) for slot in matching_slots):
|
||||
return None, "character_slots:disabled"
|
||||
return fallback, "input"
|
||||
|
||||
|
||||
def _character_expression_entries(
|
||||
rng: random.Random,
|
||||
expression_pool: list[Any],
|
||||
fallback_intensity: float,
|
||||
label_map: dict[str, dict[str, Any]],
|
||||
women_count: int,
|
||||
men_count: int,
|
||||
) -> list[str]:
|
||||
labels = [
|
||||
*[f"Woman {chr(ord('A') + index)}" for index in range(max(0, women_count))],
|
||||
*[f"Man {chr(ord('A') + index)}" for index in range(max(0, men_count))],
|
||||
]
|
||||
expressions: list[str] = []
|
||||
used: set[str] = set()
|
||||
for label in labels:
|
||||
slot = label_map.get(label)
|
||||
if not slot:
|
||||
continue
|
||||
if not _slot_expression_enabled(slot):
|
||||
continue
|
||||
intensity = _slot_expression_intensity(slot)
|
||||
if intensity is None:
|
||||
intensity = fallback_intensity
|
||||
entries = _compatible_entries(
|
||||
_expression_entries_for_intensity(expression_pool, intensity),
|
||||
women_count,
|
||||
men_count,
|
||||
)
|
||||
if not entries:
|
||||
continue
|
||||
choice = ""
|
||||
for _attempt in range(5):
|
||||
candidate = _choose_text(rng, entries)
|
||||
if candidate not in used:
|
||||
choice = candidate
|
||||
break
|
||||
if not choice:
|
||||
choice = _choose_text(rng, entries)
|
||||
used.add(choice)
|
||||
expressions.append(f"{label} has {choice}")
|
||||
return expressions
|
||||
|
||||
|
||||
def _descriptor_detail_for_subject(subject: Any, descriptor_detail: Any) -> str:
|
||||
detail = _normalize_descriptor_detail(descriptor_detail)
|
||||
if detail != "auto":
|
||||
@@ -1883,6 +2045,8 @@ def _normalize_character_slot(slot: dict[str, Any]) -> dict[str, Any]:
|
||||
"hair": _slot_value(slot.get("hair")),
|
||||
"eyes": _slot_value(slot.get("eyes")),
|
||||
"descriptor_detail": _normalize_descriptor_detail(slot.get("descriptor_detail")),
|
||||
"expression_enabled": not _is_false(slot.get("expression_enabled", True)),
|
||||
"expression_intensity": _normalize_slot_expression_intensity(slot.get("expression_intensity")),
|
||||
}
|
||||
normalized["summary"] = _character_slot_summary(normalized)
|
||||
return normalized
|
||||
@@ -1927,6 +2091,12 @@ def _character_slot_summary(slot: dict[str, Any]) -> str:
|
||||
f"body={slot.get('body', 'random')}",
|
||||
f"detail={slot.get('descriptor_detail', 'auto')}",
|
||||
]
|
||||
if not _slot_expression_enabled(slot):
|
||||
parts.append("expression=disabled")
|
||||
else:
|
||||
expression_intensity = _slot_expression_intensity(slot)
|
||||
if expression_intensity is not None:
|
||||
parts.append(f"expression={expression_intensity:.2f}")
|
||||
for key in ("body_phrase", "skin", "hair", "eyes"):
|
||||
value = slot.get(key)
|
||||
if value:
|
||||
@@ -1948,6 +2118,8 @@ def build_character_slot_json(
|
||||
hair: str = "",
|
||||
eyes: str = "",
|
||||
descriptor_detail: str = "auto",
|
||||
expression_enabled: bool = True,
|
||||
expression_intensity: float = -1.0,
|
||||
enabled: bool = True,
|
||||
character_cast: str | dict[str, Any] | list[Any] | None = "",
|
||||
) -> dict[str, str]:
|
||||
@@ -1967,6 +2139,8 @@ def build_character_slot_json(
|
||||
"hair": hair,
|
||||
"eyes": eyes,
|
||||
"descriptor_detail": descriptor_detail,
|
||||
"expression_enabled": expression_enabled,
|
||||
"expression_intensity": expression_intensity,
|
||||
}
|
||||
)
|
||||
slots = existing_slots + ([slot] if enabled else [])
|
||||
@@ -2049,6 +2223,10 @@ def _context_from_character_slot(
|
||||
if value:
|
||||
context[key] = value
|
||||
context["descriptor_detail"] = _normalize_descriptor_detail(slot.get("descriptor_detail"))
|
||||
context["expression_enabled"] = _slot_expression_enabled(slot)
|
||||
expression_intensity = _slot_expression_intensity(slot)
|
||||
if expression_intensity is not None:
|
||||
context["expression_intensity"] = expression_intensity
|
||||
context["subject_type"] = subject_type
|
||||
context["subject"] = subject_type
|
||||
context["subject_phrase"] = subject_type
|
||||
@@ -2084,9 +2262,11 @@ def _apply_character_context_to_row(row: dict[str, Any], context: dict[str, Any]
|
||||
"eyes",
|
||||
"figure",
|
||||
"descriptor_detail",
|
||||
"expression_enabled",
|
||||
"expression_intensity",
|
||||
):
|
||||
value = context.get(key)
|
||||
if value:
|
||||
if value is not None and value != "":
|
||||
row[key] = value
|
||||
if context.get("age"):
|
||||
row["age_band"] = context["age"]
|
||||
@@ -2997,6 +3177,7 @@ def _build_custom_row(
|
||||
men_count: int,
|
||||
seed: int,
|
||||
seed_config: dict[str, int],
|
||||
expression_enabled: bool,
|
||||
expression_intensity: float,
|
||||
character_profile: str | dict[str, Any] | None = None,
|
||||
character_cast: str | dict[str, Any] | list[Any] | None = None,
|
||||
@@ -3063,6 +3244,29 @@ def _build_custom_row(
|
||||
role_graph = _role_graph(role_rng, subcategory, context, item_axis_values)
|
||||
cast_descriptors: list[str] = []
|
||||
cast_descriptor_text = ""
|
||||
expression_intensity_source = "input"
|
||||
expression_disabled = not bool(expression_enabled)
|
||||
if expression_disabled:
|
||||
expression_intensity_source = "disabled"
|
||||
elif subject_type in ("woman", "man") and applied_slot:
|
||||
slot_label = "Woman A" if subject_type == "woman" else "Man A"
|
||||
if not _slot_expression_enabled(applied_slot):
|
||||
expression_disabled = True
|
||||
expression_intensity_source = f"character_slot:{slot_label}:disabled"
|
||||
else:
|
||||
slot_expression_intensity = _slot_expression_intensity(applied_slot)
|
||||
if slot_expression_intensity is not None:
|
||||
expression_intensity = slot_expression_intensity
|
||||
expression_intensity_source = f"character_slot:{slot_label}"
|
||||
elif subject_type == "configured_cast" and character_slots:
|
||||
expression_intensity, expression_intensity_source = _cast_expression_intensity_override(
|
||||
expression_intensity,
|
||||
character_slot_map,
|
||||
women_count,
|
||||
men_count,
|
||||
)
|
||||
if expression_intensity is None:
|
||||
expression_disabled = True
|
||||
if subject_type == "configured_cast" and character_slots:
|
||||
cast_descriptors, _descriptor_slots = _cast_descriptor_entries(
|
||||
seed_config,
|
||||
@@ -3082,8 +3286,12 @@ def _build_custom_row(
|
||||
pose = str(_merged_field(category, subcategory, item, "pose", "") or context.get("fallback_pose") or _choose_text(
|
||||
pose_rng, _compatible_entries(_pose_pool(category, subcategory, item, subject_type, poses), women_count, men_count)
|
||||
))
|
||||
expression_pool = _expression_pool(category, subcategory, item)
|
||||
if expression_disabled:
|
||||
expression = ""
|
||||
else:
|
||||
expression_entries = _compatible_entries(
|
||||
_expression_entries_for_intensity(_expression_pool(category, subcategory, item), expression_intensity),
|
||||
_expression_entries_for_intensity(expression_pool, expression_intensity),
|
||||
women_count,
|
||||
men_count,
|
||||
)
|
||||
@@ -3092,6 +3300,21 @@ def _build_custom_row(
|
||||
secondary_expression = _choose_distinct_text(expression_rng, expression_entries, expression)
|
||||
if secondary_expression:
|
||||
expression = f"{expression}; {secondary_expression}"
|
||||
shared_expression = expression
|
||||
character_expressions: list[str] = []
|
||||
character_expression_text = ""
|
||||
if not expression_disabled and subject_type == "configured_cast" and character_slots:
|
||||
character_expressions = _character_expression_entries(
|
||||
expression_rng,
|
||||
expression_pool,
|
||||
expression_intensity,
|
||||
character_slot_map,
|
||||
women_count,
|
||||
men_count,
|
||||
)
|
||||
character_expression_text = "; ".join(character_expressions)
|
||||
if character_expression_text:
|
||||
expression = character_expression_text
|
||||
composition = _choose_text(
|
||||
composition_rng,
|
||||
_compatible_entries(_composition_pool(category, subcategory, item, subject_type), women_count, men_count),
|
||||
@@ -3124,7 +3347,13 @@ def _build_custom_row(
|
||||
"scene_slug": scene_slug,
|
||||
"pose": pose,
|
||||
"expression": expression,
|
||||
"shared_expression": shared_expression,
|
||||
"character_expressions": character_expressions,
|
||||
"character_expression_text": character_expression_text,
|
||||
"expression_enabled": not expression_disabled,
|
||||
"expression_disabled": expression_disabled,
|
||||
"expression_intensity": expression_intensity,
|
||||
"expression_intensity_source": expression_intensity_source,
|
||||
"composition": composition,
|
||||
"composition_prompt": _composition_prompt(composition),
|
||||
"role_graph": role_graph,
|
||||
@@ -3191,6 +3420,11 @@ def _build_custom_row(
|
||||
"seed_config": seed_config,
|
||||
"content_seed_axis": content_axis,
|
||||
"role_graph": role_graph,
|
||||
"shared_expression": shared_expression,
|
||||
"character_expressions": character_expressions,
|
||||
"character_expression_text": character_expression_text,
|
||||
"expression_enabled": not expression_disabled,
|
||||
"expression_disabled": expression_disabled,
|
||||
"cast_summary": context.get("cast_summary", ""),
|
||||
"cast_descriptors": cast_descriptors,
|
||||
"cast_descriptor_text": cast_descriptor_text,
|
||||
@@ -3204,11 +3438,15 @@ def _build_custom_row(
|
||||
"character_slot": applied_slot,
|
||||
"character_slot_status": slot_status,
|
||||
"character_cast_slots": character_slots,
|
||||
"expression_intensity": expression_intensity,
|
||||
"expression_intensity_source": expression_intensity_source,
|
||||
"source": "json_category",
|
||||
}
|
||||
)
|
||||
if context.get("figure"):
|
||||
row["figure"] = context["figure"]
|
||||
if expression_disabled:
|
||||
row = _disable_row_expression(row, expression_intensity_source)
|
||||
return row
|
||||
|
||||
|
||||
@@ -3238,6 +3476,7 @@ def build_prompt(
|
||||
expression_intensity: float = 0.5,
|
||||
character_profile: str | dict[str, Any] | None = None,
|
||||
character_cast: str | dict[str, Any] | list[Any] | None = None,
|
||||
expression_enabled: bool = True,
|
||||
) -> dict[str, Any]:
|
||||
apply_pool_extensions()
|
||||
row_number = max(1, int(row_number))
|
||||
@@ -3247,6 +3486,7 @@ def build_prompt(
|
||||
ethnicity = ethnicity if ethnicity == "any" or ethnicity in ETHNICITY_FILTER_CHOICES or "+" in str(ethnicity) else "any"
|
||||
poses = poses if poses in ("standard", "evocative") else "standard"
|
||||
figure = figure if figure in ("curvy", "balanced", "bombshell") else "curvy"
|
||||
expression_enabled = not _is_false(expression_enabled)
|
||||
minimal_ratio = _ratio_or_none(minimal_clothing_ratio)
|
||||
pose_ratio = _ratio_or_none(standard_pose_ratio)
|
||||
expression_intensity = _clamped_float(expression_intensity, 0.5)
|
||||
@@ -3300,11 +3540,14 @@ def build_prompt(
|
||||
int(men_count),
|
||||
seed,
|
||||
parsed_seed_config,
|
||||
expression_enabled,
|
||||
expression_intensity,
|
||||
character_profile,
|
||||
character_cast,
|
||||
)
|
||||
|
||||
if not expression_enabled:
|
||||
row = _disable_row_expression(row, "disabled")
|
||||
if extra_positive.strip():
|
||||
row["prompt"] = f"{row['prompt'].rstrip()} {extra_positive.strip()}"
|
||||
row = _apply_camera_config(row, camera_config)
|
||||
@@ -3312,7 +3555,8 @@ def build_prompt(
|
||||
row["prompt"] = _prepend_trigger(row["prompt"], active_trigger, bool(prepend_trigger_to_prompt))
|
||||
row["negative_prompt"] = _combined_negative(row.get("negative_prompt", g.NEGATIVE_PROMPT), extra_negative)
|
||||
row["trigger"] = active_trigger
|
||||
row["expression_intensity"] = expression_intensity
|
||||
row.setdefault("expression_intensity", expression_intensity)
|
||||
row.setdefault("expression_intensity_source", "input")
|
||||
return row
|
||||
|
||||
|
||||
@@ -3344,6 +3588,7 @@ def build_prompt_from_configs(
|
||||
clothing=profile["clothing"],
|
||||
ethnicity=filters["ethnicity"],
|
||||
poses=profile["poses"],
|
||||
expression_enabled=profile["expression_enabled"],
|
||||
expression_intensity=profile["expression_intensity"],
|
||||
backside_bias=profile["backside_bias"],
|
||||
figure=filters["figure"],
|
||||
@@ -3538,6 +3783,8 @@ def build_insta_of_options_json(
|
||||
camera_detail: str = "compact",
|
||||
softcore_expression_intensity: float = 0.45,
|
||||
hardcore_expression_intensity: float = 0.85,
|
||||
softcore_expression_enabled: bool = True,
|
||||
hardcore_expression_enabled: bool = True,
|
||||
) -> str:
|
||||
return json.dumps(
|
||||
{
|
||||
@@ -3553,6 +3800,8 @@ def build_insta_of_options_json(
|
||||
"softcore_camera_mode": softcore_camera_mode,
|
||||
"hardcore_camera_mode": hardcore_camera_mode,
|
||||
"camera_detail": camera_detail,
|
||||
"softcore_expression_enabled": not _is_false(softcore_expression_enabled),
|
||||
"hardcore_expression_enabled": not _is_false(hardcore_expression_enabled),
|
||||
"softcore_expression_intensity": _clamped_float(softcore_expression_intensity, 0.45),
|
||||
"hardcore_expression_intensity": _clamped_float(hardcore_expression_intensity, 0.85),
|
||||
},
|
||||
@@ -3575,6 +3824,8 @@ def _parse_insta_of_options(options_json: str | dict[str, Any] | None) -> dict[s
|
||||
"softcore_camera_mode": "handheld_selfie",
|
||||
"hardcore_camera_mode": "from_camera_config",
|
||||
"camera_detail": "compact",
|
||||
"softcore_expression_enabled": True,
|
||||
"hardcore_expression_enabled": True,
|
||||
"softcore_expression_intensity": 0.45,
|
||||
"hardcore_expression_intensity": 0.85,
|
||||
}
|
||||
@@ -3608,6 +3859,8 @@ def _parse_insta_of_options(options_json: str | dict[str, Any] | None) -> dict[s
|
||||
):
|
||||
parsed["hardcore_camera_mode"] = defaults["hardcore_camera_mode"]
|
||||
parsed["camera_detail"] = parsed["camera_detail"] if parsed["camera_detail"] in CAMERA_DETAIL_CHOICES else defaults["camera_detail"]
|
||||
parsed["softcore_expression_enabled"] = not _is_false(parsed.get("softcore_expression_enabled", True))
|
||||
parsed["hardcore_expression_enabled"] = not _is_false(parsed.get("hardcore_expression_enabled", True))
|
||||
parsed["softcore_expression_intensity"] = _clamped_float(
|
||||
parsed.get("softcore_expression_intensity"),
|
||||
defaults["softcore_expression_intensity"],
|
||||
@@ -3806,6 +4059,22 @@ def build_insta_of_pair(
|
||||
soft_category, soft_subcategory = _insta_of_softcore_category(softcore_level_key)
|
||||
soft_content_rng = _axis_rng(parsed_seed_config, "content", seed, row_number + 311)
|
||||
soft_person_rng = _axis_rng(parsed_seed_config, "person", seed, row_number)
|
||||
soft_expression_women_count = hard_women_count if options["softcore_cast"] == "same_as_hardcore" else 1
|
||||
soft_expression_men_count = hard_men_count if options["softcore_cast"] == "same_as_hardcore" else 0
|
||||
soft_expression_enabled = bool(options["softcore_expression_enabled"])
|
||||
soft_expression_intensity = options["softcore_expression_intensity"]
|
||||
soft_expression_intensity_source = "input"
|
||||
if soft_expression_enabled:
|
||||
soft_expression_intensity, soft_expression_intensity_source = _cast_expression_intensity_override(
|
||||
options["softcore_expression_intensity"],
|
||||
character_slot_map,
|
||||
soft_expression_women_count,
|
||||
soft_expression_men_count,
|
||||
)
|
||||
if soft_expression_intensity is None:
|
||||
soft_expression_enabled = False
|
||||
else:
|
||||
soft_expression_intensity_source = "disabled"
|
||||
primary_slot_context = None
|
||||
primary_slot = character_slot_map.get("Woman A")
|
||||
if primary_slot:
|
||||
@@ -3841,14 +4110,18 @@ def build_insta_of_pair(
|
||||
seed_config=parsed_seed_config,
|
||||
women_count=1,
|
||||
men_count=0,
|
||||
expression_intensity=options["softcore_expression_intensity"],
|
||||
expression_enabled=soft_expression_enabled,
|
||||
expression_intensity=soft_expression_intensity,
|
||||
character_profile="" if primary_slot else character_profile or "",
|
||||
character_cast="",
|
||||
)
|
||||
soft_row["expression_intensity_source"] = soft_expression_intensity_source
|
||||
if primary_slot_context:
|
||||
soft_row = _apply_character_context_to_row(soft_row, primary_slot_context)
|
||||
soft_row["character_slot"] = primary_slot
|
||||
soft_row["character_slot_status"] = "applied:Woman A"
|
||||
if not soft_expression_enabled:
|
||||
soft_row = _disable_row_expression(soft_row, soft_expression_intensity_source)
|
||||
soft_row["item"] = _insta_of_softcore_outfit(soft_content_rng, softcore_level_key)
|
||||
soft_row["pose"] = _insta_of_softcore_pose(soft_content_rng, softcore_level_key)
|
||||
soft_row["item_label"] = "Insta/OF softcore outfit"
|
||||
@@ -3876,6 +4149,7 @@ def build_insta_of_pair(
|
||||
seed_config=parsed_seed_config,
|
||||
women_count=hard_women_count,
|
||||
men_count=hard_men_count,
|
||||
expression_enabled=options["hardcore_expression_enabled"],
|
||||
expression_intensity=options["hardcore_expression_intensity"],
|
||||
character_cast=character_cast or "",
|
||||
)
|
||||
@@ -3959,7 +4233,8 @@ def build_insta_of_pair(
|
||||
f"{soft_cast_presence}"
|
||||
f"{soft_cast_styling_sentence}"
|
||||
f"Outfit: {soft_row['item']}. Pose: {soft_row['pose']}. Setting: {soft_row['scene_text']}. "
|
||||
f"Facial expression: {soft_row['expression']}. Composition: {soft_row['composition']}. "
|
||||
f"{_labeled_expression_sentence('Facial expression', soft_row.get('expression'))}"
|
||||
f"Composition: {soft_row['composition']}. "
|
||||
f"{soft_camera_sentence}"
|
||||
"Keep the softcore version seductive, creator-shot, and non-explicit. "
|
||||
f"{soft_row['positive_suffix']}."
|
||||
@@ -3971,7 +4246,9 @@ def build_insta_of_pair(
|
||||
"Keep Woman A visually central. "
|
||||
f"{hard_clothing_state} "
|
||||
f"Role graph: {hard_row['role_graph']} Sexual scene: {hard_row['item']}. "
|
||||
f"Setting: {hard_scene}. Facial expressions: {hard_row['expression']}. Composition: {hard_composition}. "
|
||||
f"Setting: {hard_scene}. "
|
||||
f"{_labeled_expression_sentence('Facial expressions', hard_row.get('expression'))}"
|
||||
f"Composition: {hard_composition}. "
|
||||
f"{hard_camera_sentence}"
|
||||
f"{hard_row['positive_suffix']}."
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user