Improve Krea POV position formatting

This commit is contained in:
2026-06-25 02:33:02 +02:00
parent 71f4f162eb
commit e65b67fe85
+268 -22
View File
@@ -358,33 +358,239 @@ def _filter_pov_labeled_clauses(text: Any, pov_labels: list[str]) -> str:
return "; ".join(filtered)
def _pov_action_phrase(action: Any, pov_labels: list[str]) -> str:
def _pov_ejaculation_target(context: str) -> str:
if any(token in context for token in ("face", "mouth", "lips", "tongue", "chin")):
return "onto her face and chest"
if any(token in context for token in ("lower back", "ass", "rear-entry", "face-down", "bent-over", "doggy")):
return "across her ass, thighs, and lower back"
if any(token in context for token in ("pussy", "open thighs", "thighs", "legs open")):
return "across her pussy and thighs"
return "onto her body"
def _pov_contact_clause(
action: Any,
role_graph: Any,
hard_item: Any,
axis_values: Any,
context: str,
) -> str:
is_climax = _is_climax_text(action, role_graph, hard_item, _axis_values_text(axis_values))
if is_climax:
return f"as he ejaculates semen {_pov_ejaculation_target(context)}"
is_anal = any(
token in context
for token in (
"anal",
"into her ass",
"penis entering ass",
"ass stretched",
"thrusts into her ass",
)
)
contact = "as his penis thrusts into her ass" if is_anal else "as his penis thrusts into her"
if _is_toy_assisted_double_text(action, role_graph, hard_item, _axis_values_text(axis_values)):
contact = f"{contact} while a toy is positioned at the second penetration point"
return contact
def _pov_clean_detail(detail: Any, context: str, detail_density: str) -> str:
detail = _clean(detail).strip(" .;")
if not detail:
return ""
detail = re.sub(r"\bthe POV viewer\b", "the viewer", detail, flags=re.IGNORECASE)
detail = re.sub(r"\bthe man's\b", "the viewer's", detail, flags=re.IGNORECASE)
detail = re.sub(r"\bthe man\b", "the viewer", detail, flags=re.IGNORECASE)
detail = re.sub(
r"^(?:missionary|cowgirl|reverse cowgirl|doggy style|standing sex|spooning sex|edge-supported|edge-of-bed|raised edge|kneeling straddle|lotus sex|bent-over|face-down ass-up|side-lying|kneeling rear-entry)\s+(?:position|pose)\s+(?:featuring|with|while|,)?\s*",
"",
detail,
flags=re.IGNORECASE,
)
detail = re.sub(r"^(?:featuring|with)\s+", "", detail, flags=re.IGNORECASE)
detail = re.sub(
r"^(?:full-body|explicit|close-contact|deep|hardcore|vaginal|anal)?\s*(?:penetrative sex|vaginal sex|anal sex|penetration with visible genital contact|hardcore vaginal thrusting|hardcore anal thrusting),?\s*",
"",
detail,
flags=re.IGNORECASE,
)
detail = re.sub(
r"\b(?:front-facing|close-up|wide full-body|wide|overhead|mirror-reflected|low-angle|side-profile|bed-level)\s+view of\b",
"visible",
detail,
flags=re.IGNORECASE,
)
if "toy is positioned at the second penetration point" in context:
detail = re.sub(
r",?\s*\b(?:toy aligned for a second penetration point|toy-assisted second contact aligned behind the body)\b",
"",
detail,
flags=re.IGNORECASE,
)
detail = re.sub(r"\bwith with\b", "with", detail, flags=re.IGNORECASE)
detail = re.sub(r"\s*,\s*", ", ", detail)
detail = re.sub(r",\s*,", ",", detail).strip(" ,;")
return _limit_detail_for_density(detail, _normalize_hardcore_detail_density(detail_density), _is_climax_text(context, detail))
def _pov_hardcore_pose_sentence(
action: Any,
role_graph: Any,
hard_item: Any,
composition: Any = "",
axis_values: Any = None,
detail_density: str = "balanced",
) -> str:
context = _position_context_text(role_graph, hard_item, composition, axis_values)
action_text = _clean(action)
action_lower = action_text.lower()
if not context:
context = action_lower
penetrative_tokens = (
"penetrat",
"thrust",
"penis",
"anal",
"cowgirl",
"missionary",
"doggy",
"rear-entry",
"spooning",
"side-lying",
"bent-over",
"face-down",
"ejaculat",
"semen",
"cumshot",
"climax",
)
if not any(token in context or token in action_lower for token in penetrative_tokens):
return ""
oral_only = any(token in context for token in ("oral", "blowjob", "cunnilingus", "mouth on", "penis in her mouth"))
if oral_only and not any(token in context for token in ("penetrat", "thrust", "anal", "ejaculat", "semen", "cumshot", "climax")):
return ""
contact = _pov_contact_clause(action, role_graph, hard_item, axis_values, context)
def sentence(base: str) -> str:
details = ""
if ";" in action_text:
details = _pov_clean_detail(action_text.split(";", 1)[1], f"{context} {base}", detail_density)
return f"{base}; {details}" if details else base
if "reverse cowgirl" in context:
return sentence(
"POV reverse cowgirl position: the viewer lies on his back while the woman straddles his hips facing away; "
f"her back, ass, thighs, and the viewer's foreground legs are visible {contact}"
)
if "cowgirl" in context or "straddling a partner" in context or "squatting on top" in context:
return sentence(
"POV cowgirl position: the viewer lies on his back while the woman straddles his hips facing him; "
f"her torso, hips, and open thighs fill the frame from below {contact}"
)
if "lotus" in context or "seated in a partner's lap" in context:
return sentence(
"POV lotus position: the viewer sits upright while the woman sits in his lap facing him with her legs around his hips; "
f"her torso and hips stay close to the viewer {contact}"
)
if "kneeling straddle" in context:
return sentence(
"POV kneeling straddle position: the viewer kneels upright while the woman straddles his hips facing him; "
f"both torsos are upright and her hips press directly against him {contact}"
)
if "face-down" in context or "face down" in context:
return sentence(
"POV face-down rear-entry position: the woman lies face-down in front of the viewer with ass raised toward him; "
f"the viewer is behind her at hip level with foreground hands on her hips {contact}"
)
if "bent-over" in context or "bent over" in context or "bent forward" in context:
return sentence(
"POV bent-over rear-entry position: the woman bends forward at the waist in front of the viewer with hips raised and head turned back; "
f"the viewer stands behind her at hip level {contact}"
)
if "doggy" in context or "all fours" in context or "rear-entry" in context:
return sentence(
"POV doggy position: the woman is on all fours directly in front of the viewer with hips raised and back arched; "
f"the viewer is behind her at hip level with his hands on her hips in the foreground {contact}"
)
if "standing" in context:
return sentence(
"POV standing rear-entry position: the woman stands braced in front of the viewer with hips angled back and legs steady; "
f"the viewer stands behind her at hip level {contact}"
)
if "spooning" in context or "side-lying" in context or "lies on her side" in context:
return sentence(
"POV side-lying sex position: the woman lies on her side in front of the viewer with thighs parted; "
f"the viewer is behind her along the same body line {contact}"
)
if (
"edge-supported" in context
or "raised edge" in context
or "edge of bed" in context
or "bed edge" in context
or "kneels between her legs" in context
):
return sentence(
"POV raised-edge penetration position: the woman reclines at the raised edge with thighs open toward the viewer; "
f"the viewer kneels between her legs with his hands near her hips {contact}"
)
if "missionary" in context or ("lies on her back" in context and ("legs open" in context or "thighs open" in context)):
return sentence(
"POV missionary position: the woman lies on her back with legs open around the viewer's hips; "
f"the viewer is above her with foreground arms braced beside her body {contact}"
)
return sentence(
"POV penetrative sex position: the woman is directly in front of the viewer with legs open around his hips; "
f"the viewer's foreground hands and body position define the first-person angle {contact}"
)
def _pov_action_phrase(
action: Any,
pov_labels: list[str],
role_graph: Any = "",
hard_item: Any = "",
composition: Any = "",
axis_values: Any = None,
detail_density: str = "balanced",
) -> str:
rendered = _clean(action)
if not rendered or not pov_labels:
return rendered
if "Man A" in pov_labels:
pov_sentence = _pov_hardcore_pose_sentence(
rendered,
role_graph,
hard_item,
composition,
axis_values,
detail_density,
)
if pov_sentence:
return pov_sentence
for label in sorted(pov_labels, key=len, reverse=True):
escaped = re.escape(label)
rendered = re.sub(rf"\b{escaped}'s\b", "the POV viewer's", rendered)
rendered = re.sub(rf"\b{escaped}\b", "the POV viewer", rendered)
rendered = re.sub(rf"\b{escaped}'s\b", "the viewer's", rendered)
rendered = re.sub(rf"\b{escaped}\b", "the viewer", rendered)
if "Man A" in pov_labels:
rendered = re.sub(r"\bthe man's\b", "the POV viewer's", rendered, flags=re.IGNORECASE)
rendered = re.sub(r"\bthe man\b", "the POV viewer", rendered, flags=re.IGNORECASE)
rendered = re.sub(r"\bhe\b", "the POV viewer", rendered, flags=re.IGNORECASE)
rendered = re.sub(r"\bhim\b", "the POV viewer", rendered, flags=re.IGNORECASE)
rendered = re.sub(r"\bhis\b", "the POV viewer's", rendered, flags=re.IGNORECASE)
rendered = re.sub(r"\bthe man's\b", "the viewer's", rendered, flags=re.IGNORECASE)
rendered = re.sub(r"\bthe man\b", "the viewer", rendered, flags=re.IGNORECASE)
rendered = re.sub(r"\bhe\b", "the viewer", rendered, flags=re.IGNORECASE)
rendered = re.sub(r"\bhim\b", "the viewer", rendered, flags=re.IGNORECASE)
rendered = re.sub(r"\bhis\b", "the viewer's", rendered, flags=re.IGNORECASE)
rendered = re.sub(
r"\bthe POV viewer lies on the POV viewer's back under her\b",
"the POV viewer reclines underneath her",
r"\bthe viewer lies on the viewer's back under her\b",
"the viewer reclines underneath her",
rendered,
flags=re.IGNORECASE,
)
rendered = re.sub(
r"\bthe POV viewer lies on the POV viewer's back\b",
"the POV viewer reclines",
r"\bthe viewer lies on the viewer's back\b",
"the viewer reclines",
rendered,
flags=re.IGNORECASE,
)
rendered = re.sub(r"\bthe POV viewer is positioned\b", "the POV camera is positioned", rendered, flags=re.IGNORECASE)
rendered = re.sub(r"\bthe viewer is positioned\b", "the POV camera is positioned", rendered, flags=re.IGNORECASE)
return rendered
@@ -393,12 +599,10 @@ def _pov_camera_phrase(pov_labels: list[str], softcore: bool = False) -> str:
return ""
if softcore:
return (
"first-person POV creator framing from the male participant; "
"the woman is the visible subject, and the participant is implied by camera position or foreground cues"
"Camera is the male participant's first-person creator view, with him implied by perspective or foreground cues"
)
return (
"first-person POV from the male participant; keep the visible partners readable from the viewer's position, "
"using only foreground hands, body position, or camera perspective cues for the POV participant"
"Camera is the male participant's first-person view; only his foreground hands or body cues appear"
)
@@ -411,8 +615,13 @@ def _pov_composition_text(composition: Any, pov_labels: list[str]) -> str:
text = re.sub(r"\ball bodies visible\b", "visible partners readable", text, flags=re.IGNORECASE)
text = re.sub(r"\ball three bodies readable\b", "visible partner bodies readable", text, flags=re.IGNORECASE)
text = re.sub(r"\bwide group-sex composition\b", "first-person group-sex POV composition", text, flags=re.IGNORECASE)
if "pov" not in text.lower() and "first-person" not in text.lower():
text = f"{text}, adapted for first-person POV with the POV participant kept off-camera"
text = re.sub(
r",?\s*adapted for first-person POV with the POV participant kept off-camera\b",
"",
text,
flags=re.IGNORECASE,
)
text = re.sub(r",?\s*with the POV participant kept off-camera\b", "", text, flags=re.IGNORECASE)
return _clean(text)
@@ -657,6 +866,8 @@ def _hardcore_pose_anchor(role_graph: str, hard_item: str, composition: str = ""
if "doggy" in text:
return "doggy-style anal pose"
return "rear-entry anal pose"
if "edge-supported" in text or "raised edge" in text or "edge-of-bed" in text or "bed-edge" in text:
return "edge-supported penetrative sex pose"
positions = (
"missionary",
"reverse cowgirl",
@@ -1014,7 +1225,8 @@ def _dedupe_hardcore_detail(detail: str, anchor: str) -> str:
"reverse cowgirl": (r"reverse cowgirl position",),
"cowgirl": (r"cowgirl position",),
"doggy-style": (r"doggy style position",),
"edge-of-bed": (r"edge-of-bed position",),
"edge-supported": (r"edge-of-bed position", r"edge-supported position", r"raised edge position"),
"edge-of-bed": (r"edge-of-bed position", r"edge-supported position"),
"lotus": (r"lotus sex position",),
"standing sex": (r"standing sex position",),
"spooning": (r"spooning sex position", r"spooning anal position"),
@@ -1084,6 +1296,26 @@ def _join_detail_clauses(clauses: list[str]) -> str:
def _action_position_phrase(action: str) -> str:
action = _clean(action).lower()
if "pov reverse cowgirl" in action:
return "reverse-cowgirl first-person position"
if "pov cowgirl" in action:
return "cowgirl first-person position"
if "pov missionary" in action:
return "missionary first-person position"
if "pov raised-edge" in action or "raised edge" in action:
return "raised-edge open-thigh position"
if "pov doggy" in action or "on all fours" in action:
return "all-fours rear-entry position"
if "pov bent-over" in action or "bent forward" in action:
return "bent-over rear-entry position"
if "pov face-down" in action:
return "face-down rear-entry position"
if "pov standing" in action:
return "standing rear-entry position"
if "pov side-lying" in action:
return "side-lying position"
if "pov lotus" in action:
return "lap-straddling position"
if "face-down" in action and "ass raised" in action:
return "face-down raised-hip position"
if "on all fours" in action:
@@ -1606,6 +1838,8 @@ def _normal_row_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
or _prompt_field(_clean(row.get("prompt")), "Cast descriptors")
)
pov_labels = _pov_labels_from_value(row.get("pov_character_labels"))
if pov_labels:
camera = ""
cast_prose, cast_labels = _cast_prose(cast_descriptor_text, omit_labels=pov_labels)
if not cast_labels and women_count == 1 and men_count == 1:
cast_labels = ["Woman A", "Man A"]
@@ -1624,7 +1858,7 @@ def _normal_row_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
axis_values = _sanitize_hardcore_axis_values(row.get("item_axis_values"))
detail_density = _normalize_hardcore_detail_density(row.get("hardcore_detail_density"))
action = _hardcore_action_sentence(role_graph, item, source_composition, axis_values, detail_density)
action = _pov_action_phrase(action, pov_labels)
action = _pov_action_phrase(action, pov_labels, role_graph, item, source_composition, axis_values, detail_density)
output_composition = _pov_composition_text(composition, pov_labels)
parts = [
action,
@@ -1715,6 +1949,10 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
_pov_labels_from_value(soft.get("pov_character_labels")),
_pov_labels_from_value(hard.get("pov_character_labels")),
)
if pov_labels:
hard_camera = ""
if options.get("softcore_cast") == "same_as_hardcore":
soft_camera = ""
soft_cast_descriptor_text = (
cast_descriptor_text
if options.get("softcore_cast") == "same_as_hardcore"
@@ -1745,7 +1983,15 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
hard_axis_values,
hard_detail_density,
)
hard_action = _pov_action_phrase(hard_action, pov_labels)
hard_action = _pov_action_phrase(
hard_action,
pov_labels,
hard_role_graph,
hard_item,
hard_source_composition,
hard_axis_values,
hard_detail_density,
)
hard_output_composition = _pov_composition_text(hard_composition, pov_labels)
same_soft_cast = options.get("softcore_cast") == "same_as_hardcore"
soft_output_composition = _pov_composition_text(soft.get("composition"), pov_labels if same_soft_cast else [])