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: if not text:
return "" return ""
text = re.sub(r"^Clothing state:\s*", "", text, flags=re.IGNORECASE) 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( match = re.match(
r"^(.*?)\b(?:softcore|teaser) outfit is (.*?)(?: for the (?:hardcore|sex) scene)?;\s*(?:softcore visual reference|teaser outfit detail):\s*(.*?)\.?$", r"^(.*?)\b(?:softcore|teaser) outfit is (.*?)(?: for the (?:hardcore|sex) scene)?;\s*(?:softcore visual reference|teaser outfit detail):\s*(.*?)\.?$",
text, 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" owner = _natural_label_text(match.group(1).strip(" 's"), ["Woman A", "Man A"]).strip() or "the woman"
state = _clean(match.group(2)).lower() state = _clean(match.group(2)).lower()
outfit = _clean(match.group(3)).rstrip(".") outfit = _clean(match.group(3)).rstrip(".")
if "fully nude" in state: if "fully nude" in state or "fully exposed" in state or "no clothing covering" in state:
return f"{owner.capitalize()} is fully nude, with the removed {outfit} visible nearby" return f"{owner.capitalize()}'s body is fully exposed, bare skin unobstructed"
if "nude-adjacent" in state: 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: 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" return f"{owner.capitalize()}'s {outfit} is pushed aside and partly removed, exposing the sexual contact clearly"
if "keeps" in state: 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_expression_source,
hard_labels, 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_parts = [
soft_cast_prose, 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_outfit_text,
partner_pose, partner_pose,
_pov_camera_phrase(pov_labels, softcore=True) if same_soft_cast else "", _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 "", f"{soft.get('pose')}" if soft.get("pose") else "",
_expression_phrase(soft_expression), _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 "",
+62 -18
View File
@@ -2306,6 +2306,32 @@ def _pov_composition_prompt(composition: Any, pov_labels: list[str]) -> str:
return _clean_prompt_punctuation(text) 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: def _slot_softcore_outfit(slot: dict[str, Any] | None) -> str:
return _slot_value(slot.get("softcore_outfit")) if slot else "" 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: if not clothing:
return "" return ""
lower = clothing.lower() 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 ")): if lower.startswith(("is ", "wears ", "wearing ", "keeps ", "has ", "with ")):
return f"{label} {clothing}" return f"{label} {clothing}"
if lower.startswith(("fully nude", "nude", "partly nude")):
return f"{label} is {clothing}"
return f"{label}'s clothing: {clothing}" return f"{label}'s clothing: {clothing}"
@@ -3863,8 +3891,8 @@ INSTA_OF_HARDCORE_CLOTHING_CONTINUITY = {
"none": "", "none": "",
"same_outfit": "Woman A keeps her teaser outfit on, with sexual contact still clearly visible", "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", "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", "implied_nude": "Woman A's body is partly exposed, with fabric slipping off or covering only part of the body",
"explicit_nude": "Woman A is fully nude, with the removed teaser outfit visible nearby", "explicit_nude": "Woman A's body is fully exposed, bare skin unobstructed",
} }
INSTA_OF_NEGATIVE = ( 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", "strappy lingerie set with covered cups and high-waisted bottoms, styled as a stronger solo teaser",
], ],
"explicit_nude": [ "explicit_nude": [
"fully nude creator styling with jewelry, heels, and direct adult selfie confidence", "body fully exposed with jewelry accents and direct adult selfie confidence",
"fully nude mirror-selfie styling with jewelry only and bold creator-shot framing", "mirror-selfie body exposure with jewelry accents and bold creator-shot framing",
"nude-on-sheets creator pose with lingerie discarded nearby and direct eye contact", "body fully exposed on soft sheets with direct eye contact",
"fully nude vanity-mirror pose with heels, necklace, and premium adult teaser styling", "vanity-mirror body exposure with necklace detail and premium creator-shot styling",
"nude shower-afterglow creator pose with wet hair, skin highlights, and phone-shot framing", "shower-afterglow body exposure with wet hair, skin highlights, and phone-shot framing",
"fully nude bedroom creator pose with one hand holding the phone and lingerie visible nearby", "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", "arching subtly in a solo adult tease while the styling keeps explicit anatomy obscured",
], ],
"explicit_nude": [ "explicit_nude": [
"taking a bold nude mirror selfie with direct eye contact and the body clearly framed", "taking a bold 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", "posing on the bed with body fully exposed and jewelry accents as styling",
"standing at the vanity fully nude in a premium creator-shot pose", "standing at the vanity with body fully exposed in a premium creator-shot pose",
"reclining fully nude on soft sheets with the phone held close", "reclining on soft sheets with body fully exposed and the phone held close",
"turning slightly in a nude mirror pose with the body framed head-to-thigh", "turning slightly in a mirror pose with the body framed head-to-thigh",
"kneeling fully nude in a controlled adult teaser pose with direct phone-camera awareness", "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) 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: def _insta_of_softcore_pose(rng: random.Random, level: str) -> str:
pool = INSTA_OF_SOFTCORE_POSES.get(level, INSTA_OF_SOFTCORE_POSES["lingerie_tease"]) pool = INSTA_OF_SOFTCORE_POSES.get(level, INSTA_OF_SOFTCORE_POSES["lingerie_tease"])
return g.choose(rng, pool) 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: if mode == "none" or not outfit:
return "" return ""
base = INSTA_OF_HARDCORE_CLOTHING_CONTINUITY[mode] 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}." 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) 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["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["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["custom_item"] = "insta_of_softcore_outfit"
soft_row["softcore_outfit_policy"] = "character_slot:Woman A" if primary_softcore_outfit else "insta_of_safe_softcore" 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"] = ( soft_row["pov_character_labels"] = (
pov_character_labels pov_character_labels
if options["softcore_cast"] == "same_as_hardcore" 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_parts = [part for part in (fallback_hard_clothing_state, *character_hardcore_clothing_entries) if part]
hard_clothing_state = " ".join(hard_clothing_parts) 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_density = options["hardcore_detail_density"]
hard_detail_directive = { hard_detail_directive = {
"compact": "Use one compact position-first sexual action sentence; avoid repeated aftermath wording. ", "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"Softcore setup: {soft_level}. Cast: {soft_cast}. "
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"{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"{_labeled_expression_sentence('Facial expression', soft_row.get('expression'))}"
f"Composition: {soft_row['composition']}. " f"Composition: {soft_row['composition']}. "
f"{soft_camera_sentence}" f"{soft_camera_sentence}"