Improve Krea POV position formatting
This commit is contained in:
+268
-22
@@ -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 [])
|
||||
|
||||
Reference in New Issue
Block a user