Avoid clothing cues for nude mode

This commit is contained in:
2026-06-24 18:28:50 +02:00
parent e042960466
commit b539d8c5f0
2 changed files with 80 additions and 22 deletions
+18 -4
View File
@@ -375,6 +375,15 @@ def _natural_clothing_state(text: Any) -> str:
if not text:
return ""
text = re.sub(r"^Clothing state:\s*", "", text, flags=re.IGNORECASE)
body_exposure = re.match(r"^Body exposure:\s*(.*?)\.?$", text, flags=re.IGNORECASE)
if body_exposure:
return _clean(body_exposure.group(1)).rstrip(".")
if re.search(r"\bfully nude\b|\bbody is fully exposed\b|\bno clothing covering\b", text, flags=re.IGNORECASE):
owner = "the woman"
owner_match = re.match(r"^\s*((?:Woman|Man) [A-Z])\b", text)
if owner_match:
owner = _natural_label_text(owner_match.group(1), ["Woman A", "Man A"]) or owner
return f"{owner.capitalize()}'s body is fully exposed, bare skin unobstructed"
match = re.match(
r"^(.*?)\b(?:softcore|teaser) outfit is (.*?)(?: for the (?:hardcore|sex) scene)?;\s*(?:softcore visual reference|teaser outfit detail):\s*(.*?)\.?$",
text,
@@ -384,10 +393,10 @@ def _natural_clothing_state(text: Any) -> str:
owner = _natural_label_text(match.group(1).strip(" 's"), ["Woman A", "Man A"]).strip() or "the woman"
state = _clean(match.group(2)).lower()
outfit = _clean(match.group(3)).rstrip(".")
if "fully nude" in state:
return f"{owner.capitalize()} is fully nude, with the removed {outfit} visible nearby"
if "fully nude" in state or "fully exposed" in state or "no clothing covering" in state:
return f"{owner.capitalize()}'s body is fully exposed, bare skin unobstructed"
if "nude-adjacent" in state:
return f"{owner.capitalize()} is partly nude, with the {outfit} slipping off and no abstract clothing-reference wording"
return f"{owner.capitalize()}'s body is partly exposed"
if "partially removed" in state or "pushed aside" in state:
return f"{owner.capitalize()}'s {outfit} is pushed aside and partly removed, exposing the sexual contact clearly"
if "keeps" in state:
@@ -1589,6 +1598,11 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
hard_expression_source,
hard_labels,
)
soft_item = _clean(soft.get("item"))
soft_item_label = _clean(soft.get("softcore_item_prompt_label"))
soft_item_phrase = ""
if soft_item:
soft_item_phrase = f"body exposure: {soft_item}" if soft_item_label == "Body exposure" else f"wearing {soft_item}"
soft_parts = [
soft_cast_prose,
@@ -1596,7 +1610,7 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
partner_outfit_text,
partner_pose,
_pov_camera_phrase(pov_labels, softcore=True) if same_soft_cast else "",
f"wearing {soft.get('item')}" if soft.get("item") else "",
soft_item_phrase,
f"{soft.get('pose')}" if soft.get("pose") else "",
_expression_phrase(soft_expression),
f"in {soft.get('scene_text')}" if soft.get("scene_text") else "",
+62 -18
View File
@@ -2306,6 +2306,32 @@ def _pov_composition_prompt(composition: Any, pov_labels: list[str]) -> str:
return _clean_prompt_punctuation(text)
def _body_exposure_scene_text(scene: Any) -> str:
text = str(scene or "").strip()
if not text:
return ""
replacements = (
(r",?\s*\bscattered (?:clothes|clothing)\b", ""),
(r",?\s*\bfloor clothes\b", ""),
(r"\bclothes scattered\b", "soft floor shadows"),
(r",?\s*\bscattered lingerie\b", ""),
(r",?\s*\blingerie visible nearby\b", ""),
(r"\boutfit racks\b", "mirror shelves"),
(r"\bcostume racks\b", "mirror shelves"),
(r"\bhanging outfits\b", "hanging fabric"),
(r"\bclothing hooks\b", "wall hooks"),
(r"\boutfit-check\b", "creator-shot"),
(r"\boutfit framing\b", "body framing"),
(r"\bfull outfits\b", "full bodies"),
(r"\bcoordinated outfits\b", "coordinated posing"),
)
for pattern, replacement in replacements:
text = re.sub(pattern, replacement, text, flags=re.IGNORECASE)
text = re.sub(r"\bwith,\s*", "with ", text, flags=re.IGNORECASE)
text = re.sub(r",\s*,", ",", text)
return _clean_prompt_punctuation(text)
def _slot_softcore_outfit(slot: dict[str, Any] | None) -> str:
return _slot_value(slot.get("softcore_outfit")) if slot else ""
@@ -2329,10 +2355,12 @@ def _hardcore_clothing_sentence(label: str, clothing: str) -> str:
if not clothing:
return ""
lower = clothing.lower()
if lower.startswith(("fully nude", "nude")):
return f"{label}'s body is fully exposed, bare skin unobstructed"
if lower.startswith("partly nude"):
return f"{label}'s body is partly exposed"
if lower.startswith(("is ", "wears ", "wearing ", "keeps ", "has ", "with ")):
return f"{label} {clothing}"
if lower.startswith(("fully nude", "nude", "partly nude")):
return f"{label} is {clothing}"
return f"{label}'s clothing: {clothing}"
@@ -3863,8 +3891,8 @@ INSTA_OF_HARDCORE_CLOTHING_CONTINUITY = {
"none": "",
"same_outfit": "Woman A keeps her teaser outfit on, with sexual contact still clearly visible",
"partially_removed": "Woman A's teaser outfit is pushed aside and partly removed, exposing the sexual contact clearly",
"implied_nude": "Woman A is partly nude, with the teaser outfit slipping off or covering only part of the body",
"explicit_nude": "Woman A is fully nude, with the removed teaser outfit visible nearby",
"implied_nude": "Woman A's body is partly exposed, with fabric slipping off or covering only part of the body",
"explicit_nude": "Woman A's body is fully exposed, bare skin unobstructed",
}
INSTA_OF_NEGATIVE = (
@@ -3924,12 +3952,12 @@ INSTA_OF_SOFTCORE_OUTFITS = {
"strappy lingerie set with covered cups and high-waisted bottoms, styled as a stronger solo teaser",
],
"explicit_nude": [
"fully nude creator styling with jewelry, heels, and direct adult selfie confidence",
"fully nude mirror-selfie styling with jewelry only and bold creator-shot framing",
"nude-on-sheets creator pose with lingerie discarded nearby and direct eye contact",
"fully nude vanity-mirror pose with heels, necklace, and premium adult teaser styling",
"nude shower-afterglow creator pose with wet hair, skin highlights, and phone-shot framing",
"fully nude bedroom creator pose with one hand holding the phone and lingerie visible nearby",
"body fully exposed with jewelry accents and direct adult selfie confidence",
"mirror-selfie body exposure with jewelry accents and bold creator-shot framing",
"body fully exposed on soft sheets with direct eye contact",
"vanity-mirror body exposure with necklace detail and premium creator-shot styling",
"shower-afterglow body exposure with wet hair, skin highlights, and phone-shot framing",
"bedroom body exposure with one hand holding the phone and direct camera awareness",
],
}
@@ -3967,12 +3995,12 @@ INSTA_OF_SOFTCORE_POSES = {
"arching subtly in a solo adult tease while the styling keeps explicit anatomy obscured",
],
"explicit_nude": [
"taking a bold nude mirror selfie with direct eye contact and the body clearly framed",
"posing fully nude on the bed with jewelry and heels as the only styling",
"standing at the vanity fully nude in a premium creator-shot pose",
"reclining fully nude on soft sheets with the phone held close",
"turning slightly in a nude mirror pose with the body framed head-to-thigh",
"kneeling fully nude in a controlled adult teaser pose with direct phone-camera awareness",
"taking a bold mirror selfie with direct eye contact and the body clearly framed",
"posing on the bed with body fully exposed and jewelry accents as styling",
"standing at the vanity with body fully exposed in a premium creator-shot pose",
"reclining on soft sheets with body fully exposed and the phone held close",
"turning slightly in a mirror pose with the body framed head-to-thigh",
"kneeling in a controlled adult teaser pose with body fully exposed and direct phone-camera awareness",
],
}
@@ -4226,6 +4254,10 @@ def _insta_of_softcore_outfit(rng: random.Random, level: str) -> str:
return g.choose(rng, pool)
def _insta_of_softcore_item_prompt_label(level: str) -> str:
return "Body exposure" if level == "explicit_nude" else "Outfit"
def _insta_of_softcore_pose(rng: random.Random, level: str) -> str:
pool = INSTA_OF_SOFTCORE_POSES.get(level, INSTA_OF_SOFTCORE_POSES["lingerie_tease"])
return g.choose(rng, pool)
@@ -4237,6 +4269,10 @@ def _insta_of_hardcore_clothing_state(mode: str, softcore_outfit: str) -> str:
if mode == "none" or not outfit:
return ""
base = INSTA_OF_HARDCORE_CLOTHING_CONTINUITY[mode]
if mode == "explicit_nude":
return f"Body exposure: {base}."
if mode == "implied_nude":
return f"Body exposure: {base}."
return f"Clothing state: {base}; teaser outfit detail: {outfit}."
@@ -4382,9 +4418,13 @@ def build_insta_of_pair(
primary_softcore_outfit = _slot_softcore_outfit(primary_slot)
soft_row["item"] = primary_softcore_outfit or _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"
soft_row["item_label"] = "Insta/OF softcore body exposure" if softcore_level_key == "explicit_nude" else "Insta/OF softcore outfit"
soft_row["softcore_item_prompt_label"] = _insta_of_softcore_item_prompt_label(softcore_level_key)
soft_row["custom_item"] = "insta_of_softcore_outfit"
soft_row["softcore_outfit_policy"] = "character_slot:Woman A" if primary_softcore_outfit else "insta_of_safe_softcore"
if softcore_level_key == "explicit_nude":
soft_row["source_scene_text"] = soft_row.get("source_scene_text") or soft_row.get("scene_text", "")
soft_row["scene_text"] = _body_exposure_scene_text(soft_row.get("scene_text", ""))
soft_row["pov_character_labels"] = (
pov_character_labels
if options["softcore_cast"] == "same_as_hardcore"
@@ -4512,6 +4552,10 @@ def build_insta_of_pair(
)
hard_clothing_parts = [part for part in (fallback_hard_clothing_state, *character_hardcore_clothing_entries) if part]
hard_clothing_state = " ".join(hard_clothing_parts)
if "body is fully exposed" in hard_clothing_state.lower() or "bare skin unobstructed" in hard_clothing_state.lower():
hard_scene = _body_exposure_scene_text(hard_scene)
hard_row["source_scene_text"] = hard_row.get("source_scene_text") or hard_row.get("scene_text", "")
hard_row["scene_text"] = _body_exposure_scene_text(hard_row.get("scene_text", ""))
hard_detail_density = options["hardcore_detail_density"]
hard_detail_directive = {
"compact": "Use one compact position-first sexual action sentence; avoid repeated aftermath wording. ",
@@ -4531,7 +4575,7 @@ def build_insta_of_pair(
f"Softcore setup: {soft_level}. Cast: {soft_cast}. "
f"{soft_cast_presence}"
f"{soft_cast_styling_sentence}"
f"Outfit: {soft_row['item']}. Pose: {soft_row['pose']}. Setting: {soft_row['scene_text']}. "
f"{soft_row['softcore_item_prompt_label']}: {soft_row['item']}. Pose: {soft_row['pose']}. Setting: {soft_row['scene_text']}. "
f"{_labeled_expression_sentence('Facial expression', soft_row.get('expression'))}"
f"Composition: {soft_row['composition']}. "
f"{soft_camera_sentence}"