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
|
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.
|
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
|
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,
|
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:
|
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_expression_intensity`: `0.0` is controlled, `0.5` is balanced
|
||||||
hardcore, `1.0` strongly favors ahegao-style, drooling, fucked-out, climax,
|
hardcore, `1.0` strongly favors ahegao-style, drooling, fucked-out, climax,
|
||||||
and messy orgasm expressions.
|
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`.
|
- `platform_style`: `hybrid`, `instagram`, or `onlyfans`.
|
||||||
- `continuity`: `same_creator_same_room` keeps the scene aligned while each
|
- `continuity`: `same_creator_same_room` keeps the scene aligned while each
|
||||||
output keeps its own pose/composition; `same_creator_new_scene` keeps the same
|
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
|
`indigenous`, `mixed`, `asian`, or `white_asian`. Combined filter strings
|
||||||
such as `latina+south_asian` are also accepted in config JSON.
|
such as `latina+south_asian` are also accepted in config JSON.
|
||||||
- `poses`: `standard` or `evocative`.
|
- `poses`: `standard` or `evocative`.
|
||||||
|
- `expression_enabled`: disable facial-expression text entirely for this row.
|
||||||
- `expression_intensity`: `0.0` favors mild, neutral, controlled expressions;
|
- `expression_intensity`: `0.0` favors mild, neutral, controlled expressions;
|
||||||
`0.5` favors balanced category expressions; `1.0` strongly favors the most
|
`0.5` favors balanced category expressions; `1.0` strongly favors the most
|
||||||
intense expressions available in the selected category. This affects custom
|
intense expressions available in the selected category. This affects custom
|
||||||
|
|||||||
+32
-1
@@ -106,6 +106,7 @@ class SxCPPromptBuilder:
|
|||||||
"clothing": (["full", "minimal"], {"default": "full"}),
|
"clothing": (["full", "minimal"], {"default": "full"}),
|
||||||
"ethnicity": (ethnicity_choices(), {"default": "any"}),
|
"ethnicity": (ethnicity_choices(), {"default": "any"}),
|
||||||
"poses": (["standard", "evocative"], {"default": "standard"}),
|
"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}),
|
"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}),
|
"backside_bias": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||||
"figure": (["curvy", "balanced", "bombshell"], {"default": "curvy"}),
|
"figure": (["curvy", "balanced", "bombshell"], {"default": "curvy"}),
|
||||||
@@ -141,6 +142,7 @@ class SxCPPromptBuilder:
|
|||||||
clothing,
|
clothing,
|
||||||
ethnicity,
|
ethnicity,
|
||||||
poses,
|
poses,
|
||||||
|
expression_enabled,
|
||||||
expression_intensity,
|
expression_intensity,
|
||||||
backside_bias,
|
backside_bias,
|
||||||
figure,
|
figure,
|
||||||
@@ -168,6 +170,7 @@ class SxCPPromptBuilder:
|
|||||||
clothing=clothing,
|
clothing=clothing,
|
||||||
ethnicity=ethnicity,
|
ethnicity=ethnicity,
|
||||||
poses=poses,
|
poses=poses,
|
||||||
|
expression_enabled=expression_enabled,
|
||||||
expression_intensity=expression_intensity,
|
expression_intensity=expression_intensity,
|
||||||
backside_bias=backside_bias,
|
backside_bias=backside_bias,
|
||||||
figure=figure,
|
figure=figure,
|
||||||
@@ -418,6 +421,7 @@ class SxCPGenerationProfile:
|
|||||||
"profile": (generation_profile_choices(), {"default": "balanced"}),
|
"profile": (generation_profile_choices(), {"default": "balanced"}),
|
||||||
"clothing_override": (["profile_default", "full", "minimal"], {"default": "profile_default"}),
|
"clothing_override": (["profile_default", "full", "minimal"], {"default": "profile_default"}),
|
||||||
"poses_override": (["profile_default", "standard", "evocative"], {"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}),
|
"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}),
|
"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}),
|
"minimal_clothing_ratio": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||||
@@ -436,6 +440,7 @@ class SxCPGenerationProfile:
|
|||||||
profile,
|
profile,
|
||||||
clothing_override,
|
clothing_override,
|
||||||
poses_override,
|
poses_override,
|
||||||
|
expression_enabled,
|
||||||
expression_intensity,
|
expression_intensity,
|
||||||
backside_bias,
|
backside_bias,
|
||||||
minimal_clothing_ratio,
|
minimal_clothing_ratio,
|
||||||
@@ -446,6 +451,7 @@ class SxCPGenerationProfile:
|
|||||||
profile=profile,
|
profile=profile,
|
||||||
clothing_override=clothing_override,
|
clothing_override=clothing_override,
|
||||||
poses_override=poses_override,
|
poses_override=poses_override,
|
||||||
|
expression_enabled=expression_enabled,
|
||||||
expression_intensity=expression_intensity,
|
expression_intensity=expression_intensity,
|
||||||
backside_bias=backside_bias,
|
backside_bias=backside_bias,
|
||||||
minimal_clothing_ratio=minimal_clothing_ratio,
|
minimal_clothing_ratio=minimal_clothing_ratio,
|
||||||
@@ -453,7 +459,8 @@ class SxCPGenerationProfile:
|
|||||||
trigger_policy=trigger_policy,
|
trigger_policy=trigger_policy,
|
||||||
)
|
)
|
||||||
parsed = json.loads(config)
|
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
|
return config, summary
|
||||||
|
|
||||||
|
|
||||||
@@ -600,6 +607,8 @@ class SxCPCharacterSlot:
|
|||||||
"hair": ("STRING", {"default": ""}),
|
"hair": ("STRING", {"default": ""}),
|
||||||
"eyes": ("STRING", {"default": ""}),
|
"eyes": ("STRING", {"default": ""}),
|
||||||
"descriptor_detail": (character_descriptor_detail_choices(), {"default": "auto"}),
|
"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": {
|
"optional": {
|
||||||
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
||||||
@@ -627,6 +636,8 @@ class SxCPCharacterSlot:
|
|||||||
hair,
|
hair,
|
||||||
eyes,
|
eyes,
|
||||||
descriptor_detail="auto",
|
descriptor_detail="auto",
|
||||||
|
expression_enabled=True,
|
||||||
|
expression_intensity=-1.0,
|
||||||
character_cast="",
|
character_cast="",
|
||||||
):
|
):
|
||||||
result = build_character_slot_json(
|
result = build_character_slot_json(
|
||||||
@@ -643,6 +654,8 @@ class SxCPCharacterSlot:
|
|||||||
hair=hair,
|
hair=hair,
|
||||||
eyes=eyes,
|
eyes=eyes,
|
||||||
descriptor_detail=descriptor_detail,
|
descriptor_detail=descriptor_detail,
|
||||||
|
expression_enabled=expression_enabled,
|
||||||
|
expression_intensity=expression_intensity,
|
||||||
enabled=enabled,
|
enabled=enabled,
|
||||||
character_cast=character_cast or "",
|
character_cast=character_cast or "",
|
||||||
)
|
)
|
||||||
@@ -667,6 +680,8 @@ class SxCPWomanSlot:
|
|||||||
"hair": ("STRING", {"default": ""}),
|
"hair": ("STRING", {"default": ""}),
|
||||||
"eyes": ("STRING", {"default": ""}),
|
"eyes": ("STRING", {"default": ""}),
|
||||||
"descriptor_detail": (character_descriptor_detail_choices(), {"default": "auto"}),
|
"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": {
|
"optional": {
|
||||||
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
||||||
@@ -693,6 +708,8 @@ class SxCPWomanSlot:
|
|||||||
hair,
|
hair,
|
||||||
eyes,
|
eyes,
|
||||||
descriptor_detail="auto",
|
descriptor_detail="auto",
|
||||||
|
expression_enabled=True,
|
||||||
|
expression_intensity=-1.0,
|
||||||
character_cast="",
|
character_cast="",
|
||||||
):
|
):
|
||||||
result = build_character_slot_json(
|
result = build_character_slot_json(
|
||||||
@@ -709,6 +726,8 @@ class SxCPWomanSlot:
|
|||||||
hair=hair,
|
hair=hair,
|
||||||
eyes=eyes,
|
eyes=eyes,
|
||||||
descriptor_detail=descriptor_detail,
|
descriptor_detail=descriptor_detail,
|
||||||
|
expression_enabled=expression_enabled,
|
||||||
|
expression_intensity=expression_intensity,
|
||||||
enabled=enabled,
|
enabled=enabled,
|
||||||
character_cast=character_cast or "",
|
character_cast=character_cast or "",
|
||||||
)
|
)
|
||||||
@@ -732,6 +751,8 @@ class SxCPManSlot:
|
|||||||
"hair": ("STRING", {"default": ""}),
|
"hair": ("STRING", {"default": ""}),
|
||||||
"eyes": ("STRING", {"default": ""}),
|
"eyes": ("STRING", {"default": ""}),
|
||||||
"descriptor_detail": (character_descriptor_detail_choices(), {"default": "compact"}),
|
"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": {
|
"optional": {
|
||||||
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
||||||
@@ -757,6 +778,8 @@ class SxCPManSlot:
|
|||||||
hair,
|
hair,
|
||||||
eyes,
|
eyes,
|
||||||
descriptor_detail="compact",
|
descriptor_detail="compact",
|
||||||
|
expression_enabled=True,
|
||||||
|
expression_intensity=-1.0,
|
||||||
character_cast="",
|
character_cast="",
|
||||||
):
|
):
|
||||||
result = build_character_slot_json(
|
result = build_character_slot_json(
|
||||||
@@ -773,6 +796,8 @@ class SxCPManSlot:
|
|||||||
hair=hair,
|
hair=hair,
|
||||||
eyes=eyes,
|
eyes=eyes,
|
||||||
descriptor_detail=descriptor_detail,
|
descriptor_detail=descriptor_detail,
|
||||||
|
expression_enabled=expression_enabled,
|
||||||
|
expression_intensity=expression_intensity,
|
||||||
enabled=enabled,
|
enabled=enabled,
|
||||||
character_cast=character_cast or "",
|
character_cast=character_cast or "",
|
||||||
)
|
)
|
||||||
@@ -1005,6 +1030,8 @@ class SxCPInstaOFOptions:
|
|||||||
"hardcore_men_count": ("INT", {"default": 1, "min": 0, "max": 12, "step": 1}),
|
"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"}),
|
"softcore_level": (["social_tease", "lingerie_tease", "implied_nude", "explicit_tease", "explicit_nude"], {"default": "lingerie_tease"}),
|
||||||
"hardcore_level": (["explicit", "hardcore"], {"default": "hardcore"}),
|
"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}),
|
"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}),
|
"hardcore_expression_intensity": ("FLOAT", {"default": 0.85, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||||
"platform_style": (["hybrid", "instagram", "onlyfans"], {"default": "hybrid"}),
|
"platform_style": (["hybrid", "instagram", "onlyfans"], {"default": "hybrid"}),
|
||||||
@@ -1029,6 +1056,8 @@ class SxCPInstaOFOptions:
|
|||||||
hardcore_men_count,
|
hardcore_men_count,
|
||||||
softcore_level,
|
softcore_level,
|
||||||
hardcore_level,
|
hardcore_level,
|
||||||
|
softcore_expression_enabled,
|
||||||
|
hardcore_expression_enabled,
|
||||||
softcore_expression_intensity,
|
softcore_expression_intensity,
|
||||||
hardcore_expression_intensity,
|
hardcore_expression_intensity,
|
||||||
platform_style,
|
platform_style,
|
||||||
@@ -1046,6 +1075,8 @@ class SxCPInstaOFOptions:
|
|||||||
hardcore_men_count=hardcore_men_count,
|
hardcore_men_count=hardcore_men_count,
|
||||||
softcore_level=softcore_level,
|
softcore_level=softcore_level,
|
||||||
hardcore_level=hardcore_level,
|
hardcore_level=hardcore_level,
|
||||||
|
softcore_expression_enabled=softcore_expression_enabled,
|
||||||
|
hardcore_expression_enabled=hardcore_expression_enabled,
|
||||||
softcore_expression_intensity=softcore_expression_intensity,
|
softcore_expression_intensity=softcore_expression_intensity,
|
||||||
hardcore_expression_intensity=hardcore_expression_intensity,
|
hardcore_expression_intensity=hardcore_expression_intensity,
|
||||||
platform_style=platform_style,
|
platform_style=platform_style,
|
||||||
|
|||||||
+22
-4
@@ -49,6 +49,18 @@ def _clean_text(value: Any) -> str:
|
|||||||
return text
|
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:
|
def _cap_first(text: str) -> str:
|
||||||
text = _clean_text(text).strip(" ,")
|
text = _clean_text(text).strip(" ,")
|
||||||
return text[:1].upper() + text[1:] if text else ""
|
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")))
|
item = _clean_clothing(_row_value(row, "clothing", ("Clothing", "Erotic outfit")))
|
||||||
scene = _row_value(row, "scene_text", ("Scene", "Setting"))
|
scene = _row_value(row, "scene_text", ("Scene", "Setting"))
|
||||||
pose = _row_value(row, "pose", ("Pose",))
|
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",)))
|
composition = _normalize_composition(_row_value(row, "composition", ("Composition",)))
|
||||||
prop = _row_value(row, "prop", ("Prop/detail",))
|
prop = _row_value(row, "prop", ("Prop/detail",))
|
||||||
style = _row_value(row, "style") if keep_style else ""
|
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", "")
|
pose = pose.replace(", affectionate and flirtatious but non-explicit", "")
|
||||||
clothing = _clean_clothing(_row_value(row, "item", ITEM_LABELS) or _row_value(row, "clothing", ("Clothing",)))
|
clothing = _clean_clothing(_row_value(row, "item", ITEM_LABELS) or _row_value(row, "clothing", ("Clothing",)))
|
||||||
scene = _row_value(row, "scene_text", ("Scene", "Setting"))
|
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",)))
|
composition = _normalize_composition(_row_value(row, "composition", ("Composition",)))
|
||||||
style = _row_value(row, "style") if keep_style else ""
|
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",))
|
role_graph = _row_value(row, "role_graph", ("Role graph",))
|
||||||
item = _row_value(row, "item", ITEM_LABELS)
|
item = _row_value(row, "item", ITEM_LABELS)
|
||||||
scene = _row_value(row, "scene_text", ("Setting", "Scene"))
|
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",)))
|
composition = _normalize_composition(_row_value(row, "composition", ("Composition",)))
|
||||||
cast_descriptor_text = _row_value(row, "cast_descriptor_text", ("Characters", "Cast descriptors"))
|
cast_descriptor_text = _row_value(row, "cast_descriptor_text", ("Characters", "Cast descriptors"))
|
||||||
scene_kind = _row_value(row, "scene_kind") or "explicit adult sex scene"
|
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"))
|
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",)))
|
item = _clean_clothing(_row_value(row, "item", ITEM_LABELS) or _row_value(row, "clothing", ("Clothing",)))
|
||||||
scene = _row_value(row, "scene_text", ("Scene", "Setting"))
|
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",)))
|
composition = _normalize_composition(_row_value(row, "composition", ("Composition",)))
|
||||||
style = _row_value(row, "style") if keep_style else ""
|
style = _row_value(row, "style") if keep_style else ""
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
"id": 3,
|
"id": 3,
|
||||||
"type": "SxCPGenerationProfile",
|
"type": "SxCPGenerationProfile",
|
||||||
"pos": [-1220, -300],
|
"pos": [-1220, -300],
|
||||||
"size": [320, 226],
|
"size": [320, 250],
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"order": 2,
|
"order": 2,
|
||||||
"mode": 0,
|
"mode": 0,
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
{"name": "summary", "type": "STRING", "links": null, "slot_index": 1}
|
{"name": "summary", "type": "STRING", "links": null, "slot_index": 1}
|
||||||
],
|
],
|
||||||
"properties": {"Node name for S&R": "SxCPGenerationProfile"},
|
"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,
|
"id": 4,
|
||||||
@@ -210,7 +210,7 @@
|
|||||||
"id": 12,
|
"id": 12,
|
||||||
"type": "SxCPInstaOFOptions",
|
"type": "SxCPInstaOFOptions",
|
||||||
"pos": [-1220, 290],
|
"pos": [-1220, 290],
|
||||||
"size": [360, 318],
|
"size": [360, 366],
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"order": 11,
|
"order": 11,
|
||||||
"mode": 0,
|
"mode": 0,
|
||||||
@@ -219,7 +219,7 @@
|
|||||||
{"name": "options_json", "type": "STRING", "links": [10], "slot_index": 0}
|
{"name": "options_json", "type": "STRING", "links": [10], "slot_index": 0}
|
||||||
],
|
],
|
||||||
"properties": {"Node name for S&R": "SxCPInstaOFOptions"},
|
"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,
|
"id": 13,
|
||||||
|
|||||||
+41
-4
@@ -40,6 +40,18 @@ def _clean(value: Any) -> str:
|
|||||||
return text
|
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:
|
def _sentence(text: str) -> str:
|
||||||
text = _clean(text).strip(" ,;")
|
text = _clean(text).strip(" ,;")
|
||||||
if not text:
|
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))
|
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:
|
def _camera_phrase(row: dict[str, Any]) -> str:
|
||||||
directive = _clean(row.get("camera_directive"))
|
directive = _clean(row.get("camera_directive"))
|
||||||
if 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)
|
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"))
|
scene = _row_value(row, "scene_text", ("Setting", "Scene")) or _clean(row.get("scene"))
|
||||||
pose = _row_value(row, "pose", ("Sexual pose", "Pose"))
|
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)
|
composition = re.sub(r"^vertical\s+", "", _row_value(row, "composition", ("Composition",)), flags=re.IGNORECASE)
|
||||||
camera = _camera_phrase(row)
|
camera = _camera_phrase(row)
|
||||||
style = _style_phrase(row, style_mode)
|
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)
|
cast_prose, cast_labels = _cast_prose(cast_descriptor_text)
|
||||||
if not cast_labels and women_count == 1 and men_count == 1:
|
if not cast_labels and women_count == 1 and men_count == 1:
|
||||||
cast_labels = ["Woman A", "Man A"]
|
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)
|
role_graph = _sanitize_scene_text_for_cast(row.get("role_graph"), cast_labels)
|
||||||
item = _sanitize_scene_text_for_cast(item, cast_labels)
|
item = _sanitize_scene_text_for_cast(item, cast_labels)
|
||||||
role_graph = _natural_label_text(role_graph, 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"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 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"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"),
|
_composition_phrase(composition, action, "The image is framed as"),
|
||||||
camera,
|
camera,
|
||||||
style if detail_level != "concise" else "",
|
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_pose = ""
|
||||||
partner_outfit_text = _natural_label_text(partner_outfit_text, soft_labels)
|
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_parts = [
|
||||||
soft_cast_prose,
|
soft_cast_prose,
|
||||||
soft_cast_presence,
|
soft_cast_presence,
|
||||||
@@ -1235,7 +1272,7 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
|||||||
partner_pose,
|
partner_pose,
|
||||||
f"wearing {soft.get('item')}" if soft.get("item") else "",
|
f"wearing {soft.get('item')}" if soft.get("item") else "",
|
||||||
f"{soft.get('pose')}" if soft.get("pose") 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"in {soft.get('scene_text')}" if soft.get("scene_text") else "",
|
||||||
f"framed as {soft.get('composition')}" if soft.get("composition") else "",
|
f"framed as {soft.get('composition')}" if soft.get("composition") else "",
|
||||||
soft_camera,
|
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")),
|
_natural_clothing_state(row.get("hardcore_clothing_state")),
|
||||||
hard_cast_prose,
|
hard_cast_prose,
|
||||||
f"set in {hard_scene}" if hard_scene else "",
|
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),
|
_composition_phrase(hard_composition, hard_action),
|
||||||
hard_camera,
|
hard_camera,
|
||||||
hard_style if detail_level != "concise" else "",
|
hard_style if detail_level != "concise" else "",
|
||||||
|
|||||||
+292
-15
@@ -890,6 +890,7 @@ GENERATION_PROFILE_PRESETS = {
|
|||||||
"balanced": {
|
"balanced": {
|
||||||
"clothing": "full",
|
"clothing": "full",
|
||||||
"poses": "standard",
|
"poses": "standard",
|
||||||
|
"expression_enabled": True,
|
||||||
"expression_intensity": 0.5,
|
"expression_intensity": 0.5,
|
||||||
"backside_bias": 0.0,
|
"backside_bias": 0.0,
|
||||||
"minimal_clothing_ratio": -1.0,
|
"minimal_clothing_ratio": -1.0,
|
||||||
@@ -900,6 +901,7 @@ GENERATION_PROFILE_PRESETS = {
|
|||||||
"casual_clean": {
|
"casual_clean": {
|
||||||
"clothing": "full",
|
"clothing": "full",
|
||||||
"poses": "standard",
|
"poses": "standard",
|
||||||
|
"expression_enabled": True,
|
||||||
"expression_intensity": 0.35,
|
"expression_intensity": 0.35,
|
||||||
"backside_bias": 0.0,
|
"backside_bias": 0.0,
|
||||||
"minimal_clothing_ratio": -1.0,
|
"minimal_clothing_ratio": -1.0,
|
||||||
@@ -910,6 +912,7 @@ GENERATION_PROFILE_PRESETS = {
|
|||||||
"evocative_softcore": {
|
"evocative_softcore": {
|
||||||
"clothing": "minimal",
|
"clothing": "minimal",
|
||||||
"poses": "evocative",
|
"poses": "evocative",
|
||||||
|
"expression_enabled": True,
|
||||||
"expression_intensity": 0.65,
|
"expression_intensity": 0.65,
|
||||||
"backside_bias": 0.2,
|
"backside_bias": 0.2,
|
||||||
"minimal_clothing_ratio": -1.0,
|
"minimal_clothing_ratio": -1.0,
|
||||||
@@ -920,6 +923,7 @@ GENERATION_PROFILE_PRESETS = {
|
|||||||
"hardcore_intense": {
|
"hardcore_intense": {
|
||||||
"clothing": "minimal",
|
"clothing": "minimal",
|
||||||
"poses": "evocative",
|
"poses": "evocative",
|
||||||
|
"expression_enabled": True,
|
||||||
"expression_intensity": 0.9,
|
"expression_intensity": 0.9,
|
||||||
"backside_bias": 0.0,
|
"backside_bias": 0.0,
|
||||||
"minimal_clothing_ratio": -1.0,
|
"minimal_clothing_ratio": -1.0,
|
||||||
@@ -930,6 +934,7 @@ GENERATION_PROFILE_PRESETS = {
|
|||||||
"krea2_friendly": {
|
"krea2_friendly": {
|
||||||
"clothing": "full",
|
"clothing": "full",
|
||||||
"poses": "standard",
|
"poses": "standard",
|
||||||
|
"expression_enabled": True,
|
||||||
"expression_intensity": 0.55,
|
"expression_intensity": 0.55,
|
||||||
"backside_bias": 0.0,
|
"backside_bias": 0.0,
|
||||||
"minimal_clothing_ratio": -1.0,
|
"minimal_clothing_ratio": -1.0,
|
||||||
@@ -940,6 +945,7 @@ GENERATION_PROFILE_PRESETS = {
|
|||||||
"flux_original": {
|
"flux_original": {
|
||||||
"clothing": "full",
|
"clothing": "full",
|
||||||
"poses": "standard",
|
"poses": "standard",
|
||||||
|
"expression_enabled": True,
|
||||||
"expression_intensity": 0.5,
|
"expression_intensity": 0.5,
|
||||||
"backside_bias": 0.0,
|
"backside_bias": 0.0,
|
||||||
"minimal_clothing_ratio": -1.0,
|
"minimal_clothing_ratio": -1.0,
|
||||||
@@ -1039,6 +1045,7 @@ def build_generation_profile_json(
|
|||||||
minimal_clothing_ratio: float = -1.0,
|
minimal_clothing_ratio: float = -1.0,
|
||||||
standard_pose_ratio: float = -1.0,
|
standard_pose_ratio: float = -1.0,
|
||||||
trigger_policy: str = "profile_default",
|
trigger_policy: str = "profile_default",
|
||||||
|
expression_enabled: bool = True,
|
||||||
) -> str:
|
) -> str:
|
||||||
profile = profile if profile in GENERATION_PROFILE_PRESETS else "balanced"
|
profile = profile if profile in GENERATION_PROFILE_PRESETS else "balanced"
|
||||||
config = dict(GENERATION_PROFILE_PRESETS[profile])
|
config = dict(GENERATION_PROFILE_PRESETS[profile])
|
||||||
@@ -1046,6 +1053,7 @@ def build_generation_profile_json(
|
|||||||
config["clothing"] = clothing_override
|
config["clothing"] = clothing_override
|
||||||
if poses_override in ("standard", "evocative"):
|
if poses_override in ("standard", "evocative"):
|
||||||
config["poses"] = poses_override
|
config["poses"] = poses_override
|
||||||
|
config["expression_enabled"] = not _is_false(expression_enabled)
|
||||||
if float(expression_intensity) >= 0:
|
if float(expression_intensity) >= 0:
|
||||||
config["expression_intensity"] = _clamped_float(expression_intensity, config["expression_intensity"])
|
config["expression_intensity"] = _clamped_float(expression_intensity, config["expression_intensity"])
|
||||||
if float(backside_bias) >= 0:
|
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.update(raw)
|
||||||
parsed["clothing"] = parsed["clothing"] if parsed.get("clothing") in ("full", "minimal") else "full"
|
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["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["expression_intensity"] = _clamped_float(parsed.get("expression_intensity"), 0.5)
|
||||||
parsed["backside_bias"] = _clamped_float(parsed.get("backside_bias"), 0.0)
|
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)
|
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)
|
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:
|
def _prepend_trigger(prompt: str, trigger: str, enabled: bool) -> str:
|
||||||
trigger = trigger.strip()
|
trigger = trigger.strip()
|
||||||
if not enabled or not trigger:
|
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"
|
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:
|
def _descriptor_detail_for_subject(subject: Any, descriptor_detail: Any) -> str:
|
||||||
detail = _normalize_descriptor_detail(descriptor_detail)
|
detail = _normalize_descriptor_detail(descriptor_detail)
|
||||||
if detail != "auto":
|
if detail != "auto":
|
||||||
@@ -1883,6 +2045,8 @@ def _normalize_character_slot(slot: dict[str, Any]) -> dict[str, Any]:
|
|||||||
"hair": _slot_value(slot.get("hair")),
|
"hair": _slot_value(slot.get("hair")),
|
||||||
"eyes": _slot_value(slot.get("eyes")),
|
"eyes": _slot_value(slot.get("eyes")),
|
||||||
"descriptor_detail": _normalize_descriptor_detail(slot.get("descriptor_detail")),
|
"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)
|
normalized["summary"] = _character_slot_summary(normalized)
|
||||||
return normalized
|
return normalized
|
||||||
@@ -1927,6 +2091,12 @@ def _character_slot_summary(slot: dict[str, Any]) -> str:
|
|||||||
f"body={slot.get('body', 'random')}",
|
f"body={slot.get('body', 'random')}",
|
||||||
f"detail={slot.get('descriptor_detail', 'auto')}",
|
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"):
|
for key in ("body_phrase", "skin", "hair", "eyes"):
|
||||||
value = slot.get(key)
|
value = slot.get(key)
|
||||||
if value:
|
if value:
|
||||||
@@ -1948,6 +2118,8 @@ def build_character_slot_json(
|
|||||||
hair: str = "",
|
hair: str = "",
|
||||||
eyes: str = "",
|
eyes: str = "",
|
||||||
descriptor_detail: str = "auto",
|
descriptor_detail: str = "auto",
|
||||||
|
expression_enabled: bool = True,
|
||||||
|
expression_intensity: float = -1.0,
|
||||||
enabled: bool = True,
|
enabled: bool = True,
|
||||||
character_cast: str | dict[str, Any] | list[Any] | None = "",
|
character_cast: str | dict[str, Any] | list[Any] | None = "",
|
||||||
) -> dict[str, str]:
|
) -> dict[str, str]:
|
||||||
@@ -1967,6 +2139,8 @@ def build_character_slot_json(
|
|||||||
"hair": hair,
|
"hair": hair,
|
||||||
"eyes": eyes,
|
"eyes": eyes,
|
||||||
"descriptor_detail": descriptor_detail,
|
"descriptor_detail": descriptor_detail,
|
||||||
|
"expression_enabled": expression_enabled,
|
||||||
|
"expression_intensity": expression_intensity,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
slots = existing_slots + ([slot] if enabled else [])
|
slots = existing_slots + ([slot] if enabled else [])
|
||||||
@@ -2049,6 +2223,10 @@ def _context_from_character_slot(
|
|||||||
if value:
|
if value:
|
||||||
context[key] = value
|
context[key] = value
|
||||||
context["descriptor_detail"] = _normalize_descriptor_detail(slot.get("descriptor_detail"))
|
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_type"] = subject_type
|
||||||
context["subject"] = subject_type
|
context["subject"] = subject_type
|
||||||
context["subject_phrase"] = 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",
|
"eyes",
|
||||||
"figure",
|
"figure",
|
||||||
"descriptor_detail",
|
"descriptor_detail",
|
||||||
|
"expression_enabled",
|
||||||
|
"expression_intensity",
|
||||||
):
|
):
|
||||||
value = context.get(key)
|
value = context.get(key)
|
||||||
if value:
|
if value is not None and value != "":
|
||||||
row[key] = value
|
row[key] = value
|
||||||
if context.get("age"):
|
if context.get("age"):
|
||||||
row["age_band"] = context["age"]
|
row["age_band"] = context["age"]
|
||||||
@@ -2997,6 +3177,7 @@ def _build_custom_row(
|
|||||||
men_count: int,
|
men_count: int,
|
||||||
seed: int,
|
seed: int,
|
||||||
seed_config: dict[str, int],
|
seed_config: dict[str, int],
|
||||||
|
expression_enabled: bool,
|
||||||
expression_intensity: float,
|
expression_intensity: float,
|
||||||
character_profile: str | dict[str, Any] | None = None,
|
character_profile: str | dict[str, Any] | None = None,
|
||||||
character_cast: str | dict[str, Any] | list[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)
|
role_graph = _role_graph(role_rng, subcategory, context, item_axis_values)
|
||||||
cast_descriptors: list[str] = []
|
cast_descriptors: list[str] = []
|
||||||
cast_descriptor_text = ""
|
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:
|
if subject_type == "configured_cast" and character_slots:
|
||||||
cast_descriptors, _descriptor_slots = _cast_descriptor_entries(
|
cast_descriptors, _descriptor_slots = _cast_descriptor_entries(
|
||||||
seed_config,
|
seed_config,
|
||||||
@@ -3082,16 +3286,35 @@ def _build_custom_row(
|
|||||||
pose = str(_merged_field(category, subcategory, item, "pose", "") or context.get("fallback_pose") or _choose_text(
|
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)
|
pose_rng, _compatible_entries(_pose_pool(category, subcategory, item, subject_type, poses), women_count, men_count)
|
||||||
))
|
))
|
||||||
expression_entries = _compatible_entries(
|
expression_pool = _expression_pool(category, subcategory, item)
|
||||||
_expression_entries_for_intensity(_expression_pool(category, subcategory, item), expression_intensity),
|
if expression_disabled:
|
||||||
women_count,
|
expression = ""
|
||||||
men_count,
|
else:
|
||||||
)
|
expression_entries = _compatible_entries(
|
||||||
expression = _choose_text(expression_rng, expression_entries)
|
_expression_entries_for_intensity(expression_pool, expression_intensity),
|
||||||
if subject_type in ("couple", "group") and ";" not in expression:
|
women_count,
|
||||||
secondary_expression = _choose_distinct_text(expression_rng, expression_entries, expression)
|
men_count,
|
||||||
if secondary_expression:
|
)
|
||||||
expression = f"{expression}; {secondary_expression}"
|
expression = _choose_text(expression_rng, expression_entries)
|
||||||
|
if subject_type in ("couple", "group") and ";" not in expression:
|
||||||
|
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 = _choose_text(
|
||||||
composition_rng,
|
composition_rng,
|
||||||
_compatible_entries(_composition_pool(category, subcategory, item, subject_type), women_count, men_count),
|
_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,
|
"scene_slug": scene_slug,
|
||||||
"pose": pose,
|
"pose": pose,
|
||||||
"expression": expression,
|
"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": expression_intensity,
|
||||||
|
"expression_intensity_source": expression_intensity_source,
|
||||||
"composition": composition,
|
"composition": composition,
|
||||||
"composition_prompt": _composition_prompt(composition),
|
"composition_prompt": _composition_prompt(composition),
|
||||||
"role_graph": role_graph,
|
"role_graph": role_graph,
|
||||||
@@ -3191,6 +3420,11 @@ def _build_custom_row(
|
|||||||
"seed_config": seed_config,
|
"seed_config": seed_config,
|
||||||
"content_seed_axis": content_axis,
|
"content_seed_axis": content_axis,
|
||||||
"role_graph": role_graph,
|
"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_summary": context.get("cast_summary", ""),
|
||||||
"cast_descriptors": cast_descriptors,
|
"cast_descriptors": cast_descriptors,
|
||||||
"cast_descriptor_text": cast_descriptor_text,
|
"cast_descriptor_text": cast_descriptor_text,
|
||||||
@@ -3204,11 +3438,15 @@ def _build_custom_row(
|
|||||||
"character_slot": applied_slot,
|
"character_slot": applied_slot,
|
||||||
"character_slot_status": slot_status,
|
"character_slot_status": slot_status,
|
||||||
"character_cast_slots": character_slots,
|
"character_cast_slots": character_slots,
|
||||||
|
"expression_intensity": expression_intensity,
|
||||||
|
"expression_intensity_source": expression_intensity_source,
|
||||||
"source": "json_category",
|
"source": "json_category",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if context.get("figure"):
|
if context.get("figure"):
|
||||||
row["figure"] = context["figure"]
|
row["figure"] = context["figure"]
|
||||||
|
if expression_disabled:
|
||||||
|
row = _disable_row_expression(row, expression_intensity_source)
|
||||||
return row
|
return row
|
||||||
|
|
||||||
|
|
||||||
@@ -3238,6 +3476,7 @@ def build_prompt(
|
|||||||
expression_intensity: float = 0.5,
|
expression_intensity: float = 0.5,
|
||||||
character_profile: str | dict[str, Any] | None = None,
|
character_profile: str | dict[str, Any] | None = None,
|
||||||
character_cast: str | dict[str, Any] | list[Any] | None = None,
|
character_cast: str | dict[str, Any] | list[Any] | None = None,
|
||||||
|
expression_enabled: bool = True,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
apply_pool_extensions()
|
apply_pool_extensions()
|
||||||
row_number = max(1, int(row_number))
|
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"
|
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"
|
poses = poses if poses in ("standard", "evocative") else "standard"
|
||||||
figure = figure if figure in ("curvy", "balanced", "bombshell") else "curvy"
|
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)
|
minimal_ratio = _ratio_or_none(minimal_clothing_ratio)
|
||||||
pose_ratio = _ratio_or_none(standard_pose_ratio)
|
pose_ratio = _ratio_or_none(standard_pose_ratio)
|
||||||
expression_intensity = _clamped_float(expression_intensity, 0.5)
|
expression_intensity = _clamped_float(expression_intensity, 0.5)
|
||||||
@@ -3300,11 +3540,14 @@ def build_prompt(
|
|||||||
int(men_count),
|
int(men_count),
|
||||||
seed,
|
seed,
|
||||||
parsed_seed_config,
|
parsed_seed_config,
|
||||||
|
expression_enabled,
|
||||||
expression_intensity,
|
expression_intensity,
|
||||||
character_profile,
|
character_profile,
|
||||||
character_cast,
|
character_cast,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not expression_enabled:
|
||||||
|
row = _disable_row_expression(row, "disabled")
|
||||||
if extra_positive.strip():
|
if extra_positive.strip():
|
||||||
row["prompt"] = f"{row['prompt'].rstrip()} {extra_positive.strip()}"
|
row["prompt"] = f"{row['prompt'].rstrip()} {extra_positive.strip()}"
|
||||||
row = _apply_camera_config(row, camera_config)
|
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["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["negative_prompt"] = _combined_negative(row.get("negative_prompt", g.NEGATIVE_PROMPT), extra_negative)
|
||||||
row["trigger"] = active_trigger
|
row["trigger"] = active_trigger
|
||||||
row["expression_intensity"] = expression_intensity
|
row.setdefault("expression_intensity", expression_intensity)
|
||||||
|
row.setdefault("expression_intensity_source", "input")
|
||||||
return row
|
return row
|
||||||
|
|
||||||
|
|
||||||
@@ -3344,6 +3588,7 @@ def build_prompt_from_configs(
|
|||||||
clothing=profile["clothing"],
|
clothing=profile["clothing"],
|
||||||
ethnicity=filters["ethnicity"],
|
ethnicity=filters["ethnicity"],
|
||||||
poses=profile["poses"],
|
poses=profile["poses"],
|
||||||
|
expression_enabled=profile["expression_enabled"],
|
||||||
expression_intensity=profile["expression_intensity"],
|
expression_intensity=profile["expression_intensity"],
|
||||||
backside_bias=profile["backside_bias"],
|
backside_bias=profile["backside_bias"],
|
||||||
figure=filters["figure"],
|
figure=filters["figure"],
|
||||||
@@ -3538,6 +3783,8 @@ def build_insta_of_options_json(
|
|||||||
camera_detail: str = "compact",
|
camera_detail: str = "compact",
|
||||||
softcore_expression_intensity: float = 0.45,
|
softcore_expression_intensity: float = 0.45,
|
||||||
hardcore_expression_intensity: float = 0.85,
|
hardcore_expression_intensity: float = 0.85,
|
||||||
|
softcore_expression_enabled: bool = True,
|
||||||
|
hardcore_expression_enabled: bool = True,
|
||||||
) -> str:
|
) -> str:
|
||||||
return json.dumps(
|
return json.dumps(
|
||||||
{
|
{
|
||||||
@@ -3553,6 +3800,8 @@ def build_insta_of_options_json(
|
|||||||
"softcore_camera_mode": softcore_camera_mode,
|
"softcore_camera_mode": softcore_camera_mode,
|
||||||
"hardcore_camera_mode": hardcore_camera_mode,
|
"hardcore_camera_mode": hardcore_camera_mode,
|
||||||
"camera_detail": camera_detail,
|
"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),
|
"softcore_expression_intensity": _clamped_float(softcore_expression_intensity, 0.45),
|
||||||
"hardcore_expression_intensity": _clamped_float(hardcore_expression_intensity, 0.85),
|
"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",
|
"softcore_camera_mode": "handheld_selfie",
|
||||||
"hardcore_camera_mode": "from_camera_config",
|
"hardcore_camera_mode": "from_camera_config",
|
||||||
"camera_detail": "compact",
|
"camera_detail": "compact",
|
||||||
|
"softcore_expression_enabled": True,
|
||||||
|
"hardcore_expression_enabled": True,
|
||||||
"softcore_expression_intensity": 0.45,
|
"softcore_expression_intensity": 0.45,
|
||||||
"hardcore_expression_intensity": 0.85,
|
"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["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["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["softcore_expression_intensity"] = _clamped_float(
|
||||||
parsed.get("softcore_expression_intensity"),
|
parsed.get("softcore_expression_intensity"),
|
||||||
defaults["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_category, soft_subcategory = _insta_of_softcore_category(softcore_level_key)
|
||||||
soft_content_rng = _axis_rng(parsed_seed_config, "content", seed, row_number + 311)
|
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_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_context = None
|
||||||
primary_slot = character_slot_map.get("Woman A")
|
primary_slot = character_slot_map.get("Woman A")
|
||||||
if primary_slot:
|
if primary_slot:
|
||||||
@@ -3841,14 +4110,18 @@ def build_insta_of_pair(
|
|||||||
seed_config=parsed_seed_config,
|
seed_config=parsed_seed_config,
|
||||||
women_count=1,
|
women_count=1,
|
||||||
men_count=0,
|
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_profile="" if primary_slot else character_profile or "",
|
||||||
character_cast="",
|
character_cast="",
|
||||||
)
|
)
|
||||||
|
soft_row["expression_intensity_source"] = soft_expression_intensity_source
|
||||||
if primary_slot_context:
|
if primary_slot_context:
|
||||||
soft_row = _apply_character_context_to_row(soft_row, 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"] = primary_slot
|
||||||
soft_row["character_slot_status"] = "applied:Woman A"
|
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["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["pose"] = _insta_of_softcore_pose(soft_content_rng, softcore_level_key)
|
||||||
soft_row["item_label"] = "Insta/OF softcore outfit"
|
soft_row["item_label"] = "Insta/OF softcore outfit"
|
||||||
@@ -3876,6 +4149,7 @@ def build_insta_of_pair(
|
|||||||
seed_config=parsed_seed_config,
|
seed_config=parsed_seed_config,
|
||||||
women_count=hard_women_count,
|
women_count=hard_women_count,
|
||||||
men_count=hard_men_count,
|
men_count=hard_men_count,
|
||||||
|
expression_enabled=options["hardcore_expression_enabled"],
|
||||||
expression_intensity=options["hardcore_expression_intensity"],
|
expression_intensity=options["hardcore_expression_intensity"],
|
||||||
character_cast=character_cast or "",
|
character_cast=character_cast or "",
|
||||||
)
|
)
|
||||||
@@ -3959,7 +4233,8 @@ def build_insta_of_pair(
|
|||||||
f"{soft_cast_presence}"
|
f"{soft_cast_presence}"
|
||||||
f"{soft_cast_styling_sentence}"
|
f"{soft_cast_styling_sentence}"
|
||||||
f"Outfit: {soft_row['item']}. Pose: {soft_row['pose']}. Setting: {soft_row['scene_text']}. "
|
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}"
|
f"{soft_camera_sentence}"
|
||||||
"Keep the softcore version seductive, creator-shot, and non-explicit. "
|
"Keep the softcore version seductive, creator-shot, and non-explicit. "
|
||||||
f"{soft_row['positive_suffix']}."
|
f"{soft_row['positive_suffix']}."
|
||||||
@@ -3971,7 +4246,9 @@ def build_insta_of_pair(
|
|||||||
"Keep Woman A visually central. "
|
"Keep Woman A visually central. "
|
||||||
f"{hard_clothing_state} "
|
f"{hard_clothing_state} "
|
||||||
f"Role graph: {hard_row['role_graph']} Sexual scene: {hard_row['item']}. "
|
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_camera_sentence}"
|
||||||
f"{hard_row['positive_suffix']}."
|
f"{hard_row['positive_suffix']}."
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user