Add axis-aware hardcore position grammar
This commit is contained in:
@@ -967,11 +967,11 @@
|
|||||||
"item_templates": [
|
"item_templates": [
|
||||||
"{climax_act} with {fluid_location}, {body_position}, {expression_detail}, and {visibility}",
|
"{climax_act} with {fluid_location}, {body_position}, {expression_detail}, and {visibility}",
|
||||||
"{body_position} during {climax_act}, with {hand_detail}, {fluid_location}, and {fluid_detail}",
|
"{body_position} during {climax_act}, with {hand_detail}, {fluid_location}, and {fluid_detail}",
|
||||||
"{angle} climax view featuring {climax_act}, {body_contact}, {fluid_detail}, and {visibility}",
|
"{angle} climax view featuring {climax_act}, {body_position}, {body_contact}, {fluid_detail}, and {visibility}",
|
||||||
"hardcore post-climax scene with {fluid_location}, {body_position}, {expression_detail}, and {visibility}",
|
"hardcore post-climax scene with {fluid_location}, {body_position}, {expression_detail}, and {visibility}",
|
||||||
"{climax_act} on {surface}, with {body_contact}, {hand_detail}, and {fluid_detail}",
|
"{climax_act} on {surface}, with {body_position}, {body_contact}, {hand_detail}, and {fluid_detail}",
|
||||||
"{angle} view of {fluid_location}, {body_position}, {climax_act}, and {visibility}",
|
"{angle} view of {fluid_location}, {body_position}, {climax_act}, and {visibility}",
|
||||||
"explicit orgasm scene: {climax_act}, {fluid_detail}, {expression_detail}, and {body_contact}",
|
"explicit orgasm scene: {climax_act}, {body_position}, {fluid_detail}, {expression_detail}, and {body_contact}",
|
||||||
"{body_position} with {fluid_location}, {hand_detail}, {visibility}, and {climax_act}"
|
"{body_position} with {fluid_location}, {hand_detail}, {visibility}, and {climax_act}"
|
||||||
],
|
],
|
||||||
"item_axes": {
|
"item_axes": {
|
||||||
@@ -1001,15 +1001,31 @@
|
|||||||
],
|
],
|
||||||
"body_position": [
|
"body_position": [
|
||||||
"kneeling with mouth open",
|
"kneeling with mouth open",
|
||||||
"lying on the back with legs spread",
|
"lying on the back with legs spread and hips lifted",
|
||||||
"sitting on the edge of the bed",
|
"sitting on the edge of the bed",
|
||||||
"bent over with ass raised",
|
"bent over with ass raised",
|
||||||
"reclining with thighs open",
|
"reclining with thighs open",
|
||||||
"standing with cum on the body",
|
"standing with cum on the body",
|
||||||
"straddling a partner",
|
"straddling a partner's hips in cowgirl position",
|
||||||
|
"reverse cowgirl over a partner's hips",
|
||||||
|
"on all fours with hips raised",
|
||||||
|
"face-down ass-up on the mattress",
|
||||||
|
"side-lying with thighs parted",
|
||||||
|
"kneeling in front of a standing partner",
|
||||||
|
"seated in a partner's lap facing them",
|
||||||
|
"lying at the bed edge with thighs open",
|
||||||
|
"squatting on top of a partner",
|
||||||
{
|
{
|
||||||
"text": "lying between two partners",
|
"text": "lying between two partners",
|
||||||
"min_people": 3
|
"min_people": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "kneeling between standing partners",
|
||||||
|
"min_people": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "held between front-and-back partners",
|
||||||
|
"min_people": 3
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"climax_act": [
|
"climax_act": [
|
||||||
|
|||||||
+205
-18
@@ -280,9 +280,60 @@ def _natural_clothing_state(text: Any) -> str:
|
|||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
def _hardcore_pose_anchor(role_graph: str, hard_item: str, composition: str = "") -> str:
|
def _axis_values_text(axis_values: Any) -> str:
|
||||||
text = " ".join(_clean(part).lower() for part in (role_graph, hard_item, composition) if _clean(part))
|
if not isinstance(axis_values, dict):
|
||||||
item_text = _clean(hard_item).lower()
|
return ""
|
||||||
|
priority = (
|
||||||
|
"position",
|
||||||
|
"body_position",
|
||||||
|
"body_arrangement",
|
||||||
|
"arrangement",
|
||||||
|
"angle",
|
||||||
|
"surface",
|
||||||
|
"body_contact",
|
||||||
|
"leg_detail",
|
||||||
|
"oral_act",
|
||||||
|
"oral_detail",
|
||||||
|
"penetration_act",
|
||||||
|
"penetration_detail",
|
||||||
|
"anal_act",
|
||||||
|
"double_act",
|
||||||
|
"threesome_act",
|
||||||
|
"group_act",
|
||||||
|
)
|
||||||
|
parts = [_clean(axis_values.get(key)) for key in priority if _clean(axis_values.get(key))]
|
||||||
|
return " ".join(parts)
|
||||||
|
|
||||||
|
|
||||||
|
def _position_context_text(role_graph: str, hard_item: str, composition: str = "", axis_values: Any = None) -> str:
|
||||||
|
return " ".join(
|
||||||
|
_clean(part).lower()
|
||||||
|
for part in (role_graph, hard_item, composition, _axis_values_text(axis_values))
|
||||||
|
if _clean(part)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _mentions_ass(text: str) -> bool:
|
||||||
|
return bool(
|
||||||
|
re.search(
|
||||||
|
r"\bass\b|ass[- ](?:up|raised|exposed|lifted)|spread cheeks|lower back and ass|cum (?:on|dripping from) ass|pussy, ass|ass and",
|
||||||
|
text,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _mentions_rear_entry(text: str) -> bool:
|
||||||
|
return bool(
|
||||||
|
re.search(
|
||||||
|
r"ass[- ](?:up|raised|exposed|lifted|stretched)|penis entering ass|cum (?:on|dripping from) ass|spread cheeks|lower back and ass|pussy, ass|rear[- ]entry",
|
||||||
|
text,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _hardcore_pose_anchor(role_graph: str, hard_item: str, composition: str = "", axis_values: Any = None) -> str:
|
||||||
|
text = _position_context_text(role_graph, hard_item, composition, axis_values)
|
||||||
|
item_text = " ".join(part for part in (_clean(hard_item).lower(), _axis_values_text(axis_values).lower()) if part)
|
||||||
if not text:
|
if not text:
|
||||||
return ""
|
return ""
|
||||||
if "double penetration" in text or "vaginal and anal penetration" in text or "front-and-back" in text:
|
if "double penetration" in text or "vaginal and anal penetration" in text or "front-and-back" in text:
|
||||||
@@ -339,7 +390,7 @@ def _hardcore_pose_anchor(role_graph: str, hard_item: str, composition: str = ""
|
|||||||
if "chair oral" in text:
|
if "chair oral" in text:
|
||||||
return "chair oral pose"
|
return "chair oral pose"
|
||||||
return "mouth-to-genitals oral pose"
|
return "mouth-to-genitals oral pose"
|
||||||
if "anal" in text or "ass" in text or "rear-entry" in text:
|
if "anal" in text or _mentions_rear_entry(text) or "rear-entry" in text:
|
||||||
if "face-down ass-up" in text:
|
if "face-down ass-up" in text:
|
||||||
return "face-down ass-up rear-entry anal pose"
|
return "face-down ass-up rear-entry anal pose"
|
||||||
if "doggy style" in text or "doggy-style" in text:
|
if "doggy style" in text or "doggy-style" in text:
|
||||||
@@ -381,8 +432,8 @@ def _hardcore_pose_anchor(role_graph: str, hard_item: str, composition: str = ""
|
|||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def _hardcore_pose_arrangement(anchor: str, role_graph: str, hard_item: str, composition: str = "") -> str:
|
def _hardcore_pose_arrangement(anchor: str, role_graph: str, hard_item: str, composition: str = "", axis_values: Any = None) -> str:
|
||||||
text = " ".join(_clean(part).lower() for part in (anchor, role_graph, hard_item, composition) if _clean(part))
|
text = _position_context_text(anchor, f"{role_graph} {hard_item}", composition, axis_values)
|
||||||
if not text:
|
if not text:
|
||||||
return ""
|
return ""
|
||||||
mixed_woman_man = "the woman" in text and "the man" in text
|
mixed_woman_man = "the woman" in text and "the man" in text
|
||||||
@@ -407,8 +458,8 @@ def _hardcore_pose_arrangement(anchor: str, role_graph: str, hard_item: str, com
|
|||||||
if "reclining cunnilingus" in text or "spread-leg oral" in text:
|
if "reclining cunnilingus" in text or "spread-leg oral" in text:
|
||||||
if "takes the man's penis" in text or "penis in her mouth" in text:
|
if "takes the man's penis" in text or "penis in her mouth" in text:
|
||||||
return cast_phrase(
|
return cast_phrase(
|
||||||
"with the man seated or lying back with legs apart and the woman positioned at his hips",
|
"with the man seated with legs apart and the woman positioned at his hips",
|
||||||
"with the receiver seated or lying back with legs apart and the giver positioned at the hips",
|
"with the receiver seated with legs apart and the giver positioned at the hips",
|
||||||
)
|
)
|
||||||
return cast_phrase(
|
return cast_phrase(
|
||||||
"with the woman lying back, thighs spread, and the man positioned between her legs",
|
"with the woman lying back, thighs spread, and the man positioned between her legs",
|
||||||
@@ -510,8 +561,8 @@ def _hardcore_pose_arrangement(anchor: str, role_graph: str, hard_item: str, com
|
|||||||
)
|
)
|
||||||
if "edge-of-bed" in text or "bed-edge" in text:
|
if "edge-of-bed" in text or "bed-edge" in text:
|
||||||
return cast_phrase(
|
return cast_phrase(
|
||||||
f"with the woman lying at the bed edge, hips at the edge, and the man standing or kneeling between her legs{double_tail() if is_double else ''}",
|
f"with the woman lying at the bed edge, hips at the edge, and the man kneeling between her legs{double_tail() if is_double else ''}",
|
||||||
f"with the receiver lying at the bed edge, hips at the edge, and the penetrating partner standing or kneeling between the legs{double_tail() if is_double else ''}",
|
f"with the receiver lying at the bed edge, hips at the edge, and the penetrating partner kneeling between the legs{double_tail() if is_double else ''}",
|
||||||
)
|
)
|
||||||
if "standing" in text:
|
if "standing" in text:
|
||||||
return cast_phrase(
|
return cast_phrase(
|
||||||
@@ -526,8 +577,8 @@ def _hardcore_pose_arrangement(anchor: str, role_graph: str, hard_item: str, com
|
|||||||
if "double-penetration" in text or "double penetration" in text:
|
if "double-penetration" in text or "double penetration" in text:
|
||||||
if "toy" in text:
|
if "toy" in text:
|
||||||
return cast_phrase(
|
return cast_phrase(
|
||||||
"with the woman bent forward or on all fours, the man behind her, and the toy aligned at the second penetration point",
|
"with the woman on all fours, the man behind her, and the toy aligned at the second penetration point",
|
||||||
"with the receiving body bent forward or on all fours and the toy aligned at the second penetration point",
|
"with the receiving body on all fours and the toy aligned at the second penetration point",
|
||||||
)
|
)
|
||||||
if "from the front" in text:
|
if "from the front" in text:
|
||||||
return cast_phrase(
|
return cast_phrase(
|
||||||
@@ -538,7 +589,7 @@ def _hardcore_pose_arrangement(anchor: str, role_graph: str, hard_item: str, com
|
|||||||
"with the woman held in a front-and-back position so both contact points are visible",
|
"with the woman held in a front-and-back position so both contact points are visible",
|
||||||
"with the central body held in a front-and-back position so both contact points are visible",
|
"with the central body held in a front-and-back position so both contact points are visible",
|
||||||
)
|
)
|
||||||
if "anal" in text or "rear-entry" in text:
|
if "anal" in text or _mentions_rear_entry(text) or "rear-entry" in text:
|
||||||
return cast_phrase(
|
return cast_phrase(
|
||||||
"with the woman's hips raised, ass exposed, and the man positioned behind her",
|
"with the woman's hips raised, ass exposed, and the man positioned behind her",
|
||||||
"with the receiving partner's hips raised and the penetrating partner positioned behind",
|
"with the receiving partner's hips raised and the penetrating partner positioned behind",
|
||||||
@@ -570,6 +621,7 @@ def _hardcore_item_detail(hard_item: str) -> str:
|
|||||||
return ""
|
return ""
|
||||||
text = re.sub(r"^hardcore\s+", "", text, flags=re.IGNORECASE)
|
text = re.sub(r"^hardcore\s+", "", text, flags=re.IGNORECASE)
|
||||||
text = re.sub(r"^explicit\s+", "", text, flags=re.IGNORECASE)
|
text = re.sub(r"^explicit\s+", "", text, flags=re.IGNORECASE)
|
||||||
|
text = re.sub(r"^(?:orgasm|climax)\s+scene:\s*", "", text, flags=re.IGNORECASE)
|
||||||
text = re.sub(r"^(?:mouth-to-genitals|double-contact sex|adult group pile|sex pile)\s+pose:\s*", "", text, flags=re.IGNORECASE)
|
text = re.sub(r"^(?:mouth-to-genitals|double-contact sex|adult group pile|sex pile)\s+pose:\s*", "", text, flags=re.IGNORECASE)
|
||||||
text = re.sub(r"^(?:oral|threesome|orgy)\s+scene\s+with\s+", "", text, flags=re.IGNORECASE)
|
text = re.sub(r"^(?:oral|threesome|orgy)\s+scene\s+with\s+", "", text, flags=re.IGNORECASE)
|
||||||
text = re.sub(r"^(?:threesome|orgy)\s+pose:\s*", "", text, flags=re.IGNORECASE)
|
text = re.sub(r"^(?:threesome|orgy)\s+pose:\s*", "", text, flags=re.IGNORECASE)
|
||||||
@@ -591,6 +643,7 @@ def _hardcore_item_detail(hard_item: str) -> str:
|
|||||||
r"pussy stretched around a penis",
|
r"pussy stretched around a penis",
|
||||||
r"deep vaginal sex",
|
r"deep vaginal sex",
|
||||||
r"explicit penetrative sex",
|
r"explicit penetrative sex",
|
||||||
|
r"penetrative sex",
|
||||||
r"hardcore vaginal thrusting",
|
r"hardcore vaginal thrusting",
|
||||||
r"full-body penetrative sex",
|
r"full-body penetrative sex",
|
||||||
r"close-contact vaginal sex",
|
r"close-contact vaginal sex",
|
||||||
@@ -669,6 +722,9 @@ def _dedupe_hardcore_detail(detail: str, anchor: str) -> str:
|
|||||||
"cowgirl": (r"cowgirl position",),
|
"cowgirl": (r"cowgirl position",),
|
||||||
"doggy-style": (r"doggy style position",),
|
"doggy-style": (r"doggy style position",),
|
||||||
"edge-of-bed": (r"edge-of-bed position",),
|
"edge-of-bed": (r"edge-of-bed position",),
|
||||||
|
"lotus": (r"lotus sex position",),
|
||||||
|
"standing sex": (r"standing sex position",),
|
||||||
|
"spooning": (r"spooning sex position", r"spooning anal position"),
|
||||||
}
|
}
|
||||||
for anchor_token, phrases in duplicate_phrases.items():
|
for anchor_token, phrases in duplicate_phrases.items():
|
||||||
if anchor_token in anchor_lower:
|
if anchor_token in anchor_lower:
|
||||||
@@ -679,7 +735,129 @@ def _dedupe_hardcore_detail(detail: str, anchor: str) -> str:
|
|||||||
return _clean(detail).strip(" ,;")
|
return _clean(detail).strip(" ,;")
|
||||||
|
|
||||||
|
|
||||||
def _hardcore_action_sentence(role_graph: str, hard_item: str, composition: str = "") -> str:
|
def _is_climax_text(*parts: str) -> bool:
|
||||||
|
text = " ".join(_clean(part).lower() for part in parts if _clean(part))
|
||||||
|
return any(
|
||||||
|
token in text
|
||||||
|
for token in (
|
||||||
|
"cumshot",
|
||||||
|
"ejaculation",
|
||||||
|
"post-orgasm",
|
||||||
|
"post-climax",
|
||||||
|
"orgasm aftermath",
|
||||||
|
"orgasm scene",
|
||||||
|
"orgasm during",
|
||||||
|
"shared climax",
|
||||||
|
"hardcore climax",
|
||||||
|
"external cumshot",
|
||||||
|
"visible external ejaculation",
|
||||||
|
"climaxes on",
|
||||||
|
"climax lands",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _climax_role_graph(role_graph: str, hard_item: str, axis_values: Any = None) -> str:
|
||||||
|
role_graph = _clean(role_graph).rstrip(".")
|
||||||
|
text = " ".join(part.lower() for part in (role_graph, _clean(hard_item), _axis_values_text(axis_values)) if part)
|
||||||
|
if "the woman" not in text or "the man" not in text:
|
||||||
|
return role_graph
|
||||||
|
if "lying between two partners" in text or "lies between" in text:
|
||||||
|
return "the woman lies between two partners, the man under her hips and another partner over her torso as the climax lands on her body"
|
||||||
|
if "held between front-and-back partners" in text:
|
||||||
|
return "the woman is held between the man behind her and another partner in front of her as the climax lands across her body"
|
||||||
|
if "kneeling between standing partners" in text:
|
||||||
|
return "the woman kneels between standing partners gathered around her face and torso as the climax lands across her body"
|
||||||
|
if "side-lying with thighs parted" in text:
|
||||||
|
return "the woman lies on her side with thighs parted while the man kneels beside her hips as the climax lands across her thighs and pussy"
|
||||||
|
if "sitting on the edge of the bed" in text:
|
||||||
|
return "the woman sits on the edge of the bed with knees spread while the man stands close between her legs as the climax lands across her body"
|
||||||
|
if "lying at the bed edge with thighs open" in text:
|
||||||
|
return "the woman lies at the bed edge with thighs open while the man kneels between her legs as the climax lands across her pussy and thighs"
|
||||||
|
if "reclining with thighs open" in text or "lying on the back with legs spread" in text:
|
||||||
|
return "the woman lies on her back with thighs open while the man kneels between her legs as the climax lands across her pussy and thighs"
|
||||||
|
if "on all fours with hips raised" in text:
|
||||||
|
return "the woman is on all fours with hips raised while the man is positioned behind her as the climax lands across her ass, thighs, and lower back"
|
||||||
|
if "face-down ass-up" in text:
|
||||||
|
return "the woman lies face-down with ass raised while the man is positioned behind her as the climax lands across her lower back and ass"
|
||||||
|
if "bent over with ass raised" in text or "bent over" in text:
|
||||||
|
return "the woman bends forward with hips raised while the man stands behind her as the climax lands across her lower back, ass, and thighs"
|
||||||
|
if "kneeling with mouth open" in text:
|
||||||
|
return "the woman kneels in front of the man at hip height as the climax lands across her face, lips, and chest"
|
||||||
|
if "kneeling in front of a standing partner" in text:
|
||||||
|
return "the woman kneels in front of the man at hip height while he stands over her for the climax"
|
||||||
|
if "standing with cum on the body" in text:
|
||||||
|
return "the woman stands braced in front of the man while he stands close at hip level as the climax lands across her body"
|
||||||
|
if "squatting on top of a partner" in text:
|
||||||
|
return "the woman squats over the man's hips while the man lies on his back under her as the climax lands on her body"
|
||||||
|
if "reverse cowgirl over a partner's hips" in text:
|
||||||
|
return "the woman straddles the man's hips facing away while the man lies on his back under her as the climax lands on her body"
|
||||||
|
if "straddles" in text or "straddling a partner" in text or "straddling a partner's hips" in text or "shared climax after penetration" in text:
|
||||||
|
return "the woman straddles the man's hips while the man lies on his back under her as the climax lands on her body"
|
||||||
|
if "seated in a partner's lap facing them" in text:
|
||||||
|
return "the woman sits in the man's lap facing him, legs wrapped around his hips as the climax lands across her body"
|
||||||
|
if "lower back" in text or "cum dripping from ass" in text or "cum on lower back" in text or _mentions_rear_entry(text):
|
||||||
|
return "the woman bends forward with hips raised while the man stands behind her as the climax lands across her lower back, ass, and thighs"
|
||||||
|
if "cum on face" in text or "cum on tongue" in text or "cum on lips" in text or "cum on tongue and chin" in text:
|
||||||
|
return "the woman kneels in front of the man at hip height as the climax lands across her face, lips, and chest"
|
||||||
|
if (
|
||||||
|
"cum dripping from pussy" in text
|
||||||
|
or "arousal dripping from pussy" in text
|
||||||
|
or "open thighs" in text
|
||||||
|
):
|
||||||
|
return "the woman lies on her back with thighs open while the man kneels between her legs as the climax lands across her pussy and thighs"
|
||||||
|
if role_graph:
|
||||||
|
return role_graph
|
||||||
|
return "the woman lies on her back with thighs open while the man kneels between her legs as the climax lands across her body"
|
||||||
|
|
||||||
|
|
||||||
|
def _dedupe_climax_detail(detail: str, role_graph: str) -> str:
|
||||||
|
detail = _clean(detail)
|
||||||
|
lower = role_graph.lower()
|
||||||
|
patterns: list[str] = []
|
||||||
|
if "lies on her back" in lower:
|
||||||
|
patterns.extend((r"lying on the back with legs spread and hips lifted", r"reclining with thighs open", r"lying on the back with legs spread"))
|
||||||
|
detail = re.sub(r"\bcum on lower back and ass\b", "cum across thighs and pussy", detail, flags=re.IGNORECASE)
|
||||||
|
detail = re.sub(r"\bcum (?:on|dripping from) ass\b", "cum across thighs and pussy", detail, flags=re.IGNORECASE)
|
||||||
|
if "straddles" in lower:
|
||||||
|
patterns.extend(
|
||||||
|
(
|
||||||
|
r"straddling a partner's hips in cowgirl position",
|
||||||
|
r"reverse cowgirl over a partner's hips",
|
||||||
|
r"straddling a partner",
|
||||||
|
r"squatting on top of a partner",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if "squats over" in lower:
|
||||||
|
patterns.append(r"squatting on top of a partner")
|
||||||
|
if "sits in the man's lap" in lower:
|
||||||
|
patterns.append(r"seated in a partner's lap facing them")
|
||||||
|
if "bends forward" in lower:
|
||||||
|
patterns.append(r"bent over with ass raised")
|
||||||
|
if "on all fours" in lower:
|
||||||
|
patterns.append(r"on all fours with hips raised")
|
||||||
|
if "face-down" in lower:
|
||||||
|
patterns.append(r"face-down ass-up on the mattress")
|
||||||
|
if "lies on her side" in lower:
|
||||||
|
patterns.append(r"side-lying with thighs parted")
|
||||||
|
if "sits on the edge" in lower:
|
||||||
|
patterns.append(r"sitting on the edge of the bed")
|
||||||
|
if "bed edge" in lower:
|
||||||
|
patterns.append(r"lying at the bed edge with thighs open")
|
||||||
|
if "kneels in front" in lower:
|
||||||
|
patterns.extend((r"kneeling with mouth open", r"kneeling in front of a standing partner"))
|
||||||
|
if "stands braced" in lower:
|
||||||
|
patterns.append(r"standing with cum on the body")
|
||||||
|
for pattern in patterns:
|
||||||
|
detail = re.sub(rf"\b{pattern}\b,?\s*", "", detail, flags=re.IGNORECASE)
|
||||||
|
detail = re.sub(r",\s*,", ",", detail)
|
||||||
|
detail = re.sub(r"\bwith\s*,\s*", "with ", detail, flags=re.IGNORECASE)
|
||||||
|
detail = re.sub(r"^with\s+", "", detail, flags=re.IGNORECASE)
|
||||||
|
detail = re.sub(r"^and\s+", "", detail, flags=re.IGNORECASE)
|
||||||
|
return _clean(detail).strip(" ,;")
|
||||||
|
|
||||||
|
|
||||||
|
def _hardcore_action_sentence(role_graph: str, hard_item: str, composition: str = "", axis_values: Any = None) -> str:
|
||||||
role_graph = _clean(role_graph).rstrip(".")
|
role_graph = _clean(role_graph).rstrip(".")
|
||||||
hard_item = _clean(hard_item).rstrip(".")
|
hard_item = _clean(hard_item).rstrip(".")
|
||||||
role_graph = re.sub(
|
role_graph = re.sub(
|
||||||
@@ -730,8 +908,11 @@ def _hardcore_action_sentence(role_graph: str, hard_item: str, composition: str
|
|||||||
role_graph,
|
role_graph,
|
||||||
flags=re.IGNORECASE,
|
flags=re.IGNORECASE,
|
||||||
)
|
)
|
||||||
|
is_climax = _is_climax_text(role_graph, hard_item, composition, _axis_values_text(axis_values))
|
||||||
|
if is_climax:
|
||||||
|
role_graph = _climax_role_graph(role_graph, hard_item, axis_values)
|
||||||
detail = _hardcore_item_detail(hard_item)
|
detail = _hardcore_item_detail(hard_item)
|
||||||
anchor = _hardcore_pose_anchor(role_graph, hard_item, composition)
|
anchor = _hardcore_pose_anchor(role_graph, hard_item, composition, axis_values)
|
||||||
if "double-penetration" in anchor.lower() and "toy" in role_graph.lower():
|
if "double-penetration" in anchor.lower() and "toy" in role_graph.lower():
|
||||||
role_graph = re.sub(
|
role_graph = re.sub(
|
||||||
r"\s+while a toy adds (?:the|a) second penetration point\b",
|
r"\s+while a toy adds (?:the|a) second penetration point\b",
|
||||||
@@ -739,8 +920,12 @@ def _hardcore_action_sentence(role_graph: str, hard_item: str, composition: str
|
|||||||
role_graph,
|
role_graph,
|
||||||
flags=re.IGNORECASE,
|
flags=re.IGNORECASE,
|
||||||
)
|
)
|
||||||
|
if is_climax:
|
||||||
|
anchor = ""
|
||||||
|
detail = _dedupe_climax_detail(detail, role_graph)
|
||||||
|
else:
|
||||||
detail = _dedupe_hardcore_detail(detail, anchor) if anchor else detail
|
detail = _dedupe_hardcore_detail(detail, anchor) if anchor else detail
|
||||||
arrangement = _hardcore_pose_arrangement(anchor, role_graph, hard_item, composition)
|
arrangement = _hardcore_pose_arrangement(anchor, role_graph, hard_item, composition, axis_values)
|
||||||
anchor_phrase = _with_indefinite_article(anchor) if anchor else ""
|
anchor_phrase = _with_indefinite_article(anchor) if anchor else ""
|
||||||
if arrangement and anchor_phrase:
|
if arrangement and anchor_phrase:
|
||||||
anchor_phrase = f"{anchor_phrase} {arrangement}"
|
anchor_phrase = f"{anchor_phrase} {arrangement}"
|
||||||
@@ -926,7 +1111,8 @@ def _normal_row_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
|||||||
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)
|
||||||
item = _natural_label_text(item, cast_labels)
|
item = _natural_label_text(item, cast_labels)
|
||||||
action = _hardcore_action_sentence(role_graph, item, composition)
|
axis_values = row.get("item_axis_values") if isinstance(row.get("item_axis_values"), dict) else {}
|
||||||
|
action = _hardcore_action_sentence(role_graph, item, composition, axis_values)
|
||||||
parts = [
|
parts = [
|
||||||
action,
|
action,
|
||||||
cast_prose,
|
cast_prose,
|
||||||
@@ -1020,7 +1206,8 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
|||||||
hard_role_graph = _sanitize_scene_text_for_cast(hard.get("role_graph"), hard_labels)
|
hard_role_graph = _sanitize_scene_text_for_cast(hard.get("role_graph"), hard_labels)
|
||||||
hard_item = _natural_label_text(hard_item, hard_labels)
|
hard_item = _natural_label_text(hard_item, hard_labels)
|
||||||
hard_role_graph = _natural_label_text(hard_role_graph, hard_labels)
|
hard_role_graph = _natural_label_text(hard_role_graph, hard_labels)
|
||||||
hard_action = _hardcore_action_sentence(hard_role_graph, hard_item, hard_composition)
|
hard_axis_values = hard.get("item_axis_values") if isinstance(hard.get("item_axis_values"), dict) else {}
|
||||||
|
hard_action = _hardcore_action_sentence(hard_role_graph, hard_item, hard_composition, hard_axis_values)
|
||||||
same_soft_cast = options.get("softcore_cast") == "same_as_hardcore"
|
same_soft_cast = options.get("softcore_cast") == "same_as_hardcore"
|
||||||
soft_cast_presence = (
|
soft_cast_presence = (
|
||||||
f"{_label_join(soft_labels)} are together in a non-explicit teaser pose, with no sex act or genital contact"
|
f"{_label_join(soft_labels)} are together in a non-explicit teaser pose, with no sex act or genital contact"
|
||||||
|
|||||||
+55
-4
@@ -2477,6 +2477,57 @@ def _role_graph(
|
|||||||
]
|
]
|
||||||
return f" {extra} {rng.choice(actions)}."
|
return f" {extra} {rng.choice(actions)}."
|
||||||
|
|
||||||
|
def mentions_ass(text: str) -> bool:
|
||||||
|
return bool(
|
||||||
|
re.search(
|
||||||
|
r"\bass\b|ass[- ](?:up|raised|exposed|lifted)|spread cheeks|lower back and ass|cum (?:on|dripping from) ass|pussy, ass|ass and",
|
||||||
|
text,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def climax_position_graph(woman: str, man: str, third: str = "") -> str:
|
||||||
|
if "lying between two partners" in item_text and third:
|
||||||
|
return f"{woman} lies between {man} and {third}, with {man} under her hips and {third} positioned above her torso as the climax lands on her body."
|
||||||
|
if "held between front-and-back partners" in item_text and third:
|
||||||
|
return f"{woman} is held between {man} behind her and {third} in front of her as the climax lands across her body."
|
||||||
|
if "kneeling between standing partners" in item_text and third:
|
||||||
|
return f"{woman} kneels between {man} and {third} while both stand close around her face and torso for the climax."
|
||||||
|
if "side-lying with thighs parted" in item_text:
|
||||||
|
return f"{woman} lies on her side with thighs parted while {man} kneels beside her hips as the climax lands across her thighs and pussy."
|
||||||
|
if "sitting on the edge of the bed" in item_text:
|
||||||
|
return f"{woman} sits on the edge of the bed with knees spread while {man} stands close between her legs as the climax lands across her body."
|
||||||
|
if "lying at the bed edge with thighs open" in item_text:
|
||||||
|
return f"{woman} lies at the bed edge with thighs open while {man} kneels between her legs as the climax lands across her pussy and thighs."
|
||||||
|
if "reclining with thighs open" in item_text or "lying on the back with legs spread" in item_text:
|
||||||
|
return f"{woman} lies on her back with thighs open while {man} kneels between her legs as the climax lands across her pussy and thighs."
|
||||||
|
if "on all fours with hips raised" in item_text:
|
||||||
|
return f"{woman} is on all fours with hips raised while {man} is positioned behind her as the climax lands across her ass, thighs, and lower back."
|
||||||
|
if "face-down ass-up" in item_text:
|
||||||
|
return f"{woman} lies face-down with ass raised while {man} is positioned behind her as the climax lands across her lower back and ass."
|
||||||
|
if "bent over with ass raised" in item_text or "bent over" in item_text:
|
||||||
|
return f"{woman} is bent forward with hips raised while {man} is positioned behind her, climax visible across her lower back, ass, and thighs."
|
||||||
|
if "kneeling with mouth open" in item_text:
|
||||||
|
return f"{woman} kneels in front of {man} at hip height while {man} climaxes toward her face, lips, and chest."
|
||||||
|
if "kneeling in front of a standing partner" in item_text:
|
||||||
|
return f"{woman} kneels in front of {man} at hip height while {man} stands over her for the climax."
|
||||||
|
if "standing with cum on the body" in item_text:
|
||||||
|
return f"{woman} stands braced in front of {man} while he stays close at hip level and the climax lands across her body."
|
||||||
|
if "squatting on top of a partner" in item_text:
|
||||||
|
return f"{woman} squats over {man}'s hips while {man} lies on his back under her as the climax lands on her body."
|
||||||
|
if "reverse cowgirl over a partner's hips" in item_text:
|
||||||
|
return f"{woman} straddles {man}'s hips facing away while {man} lies on his back under her as the climax lands on her body."
|
||||||
|
if any(term in item_text for term in ("straddling a partner", "straddling a partner's hips", "shared climax after penetration", "orgasm during penetration")):
|
||||||
|
return f"{woman} straddles {man}'s hips while {man} lies on his back under her, their bodies still aligned from penetration as the climax lands on her body."
|
||||||
|
if "seated in a partner's lap facing them" in item_text:
|
||||||
|
return f"{woman} sits in {man}'s lap facing him, legs wrapped around his hips as the climax lands across her body."
|
||||||
|
if any(term in item_text for term in ("lower back", "cum dripping from ass", "cum on lower back")) or mentions_ass(item_text):
|
||||||
|
return f"{woman} is bent forward with hips raised while {man} is positioned behind her, climax visible across her lower back, ass, and thighs."
|
||||||
|
if any(term in item_text for term in ("cum on face", "cum on tongue", "cum on lips", "cum on face and lips", "cum on tongue and chin")):
|
||||||
|
if third:
|
||||||
|
return f"{woman} kneels in the center while {man} and {third} stand close around her face and torso for the climax."
|
||||||
|
return f"{woman} kneels in front of {man} at hip height while {man} climaxes toward her face, lips, and chest."
|
||||||
|
return f"{woman} lies on her back with thighs open while {man} kneels between her legs as the climax lands on her body."
|
||||||
|
|
||||||
if people_count == 1:
|
if people_count == 1:
|
||||||
solo = people[0]
|
solo = people[0]
|
||||||
if women_count == 1:
|
if women_count == 1:
|
||||||
@@ -2494,7 +2545,7 @@ def _role_graph(
|
|||||||
if "oral" in slug:
|
if "oral" in slug:
|
||||||
graph = f"{a} kneels between {b}'s spread thighs and uses tongue and fingers on her pussy."
|
graph = f"{a} kneels between {b}'s spread thighs and uses tongue and fingers on her pussy."
|
||||||
elif "anal" in slug or "double" in slug:
|
elif "anal" in slug or "double" in slug:
|
||||||
graph = f"{a} uses a strap-on or toy on {b} while keeping her hips held open."
|
graph = f"{a} uses a strap-on on {b} while keeping her hips held open."
|
||||||
elif "threesome" in slug or "group" in slug or "orgy" in slug:
|
elif "threesome" in slug or "group" in slug or "orgy" in slug:
|
||||||
helper = c or any_woman({a})
|
helper = c or any_woman({a})
|
||||||
graph = f"{a} uses a strap-on on {b} while {helper} gives oral contact and touches both bodies."
|
graph = f"{a} uses a strap-on on {b} while {helper} gives oral contact and touches both bodies."
|
||||||
@@ -2502,7 +2553,7 @@ def _role_graph(
|
|||||||
elif "cumshot" in slug or "climax" in slug:
|
elif "cumshot" in slug or "climax" in slug:
|
||||||
graph = f"{a} brings {b} to orgasm with mouth and fingers while wetness is visible on thighs and sheets."
|
graph = f"{a} brings {b} to orgasm with mouth and fingers while wetness is visible on thighs and sheets."
|
||||||
else:
|
else:
|
||||||
graph = f"{a} uses a strap-on or toy on {b} while their bodies stay pressed together."
|
graph = f"{a} uses a strap-on on {b} while their bodies stay pressed together."
|
||||||
return graph + support_sentence(used)
|
return graph + support_sentence(used)
|
||||||
|
|
||||||
if men_count > 0 and women_count == 0:
|
if men_count > 0 and women_count == 0:
|
||||||
@@ -2555,11 +2606,11 @@ def _role_graph(
|
|||||||
else:
|
else:
|
||||||
graph = f"{man} thrusts his penis into {woman}'s ass while keeping her hips held open."
|
graph = f"{man} thrusts his penis into {woman}'s ass while keeping her hips held open."
|
||||||
elif "threesome" in slug:
|
elif "threesome" in slug:
|
||||||
graph = f"{man} thrusts his penis into {woman} while {third or any_person({woman, man})} uses mouth or hands on the exposed body."
|
graph = f"{man} thrusts his penis into {woman} while {third or any_person({woman, man})} uses mouth and hands on the exposed body."
|
||||||
elif "group" in slug or "orgy" in slug:
|
elif "group" in slug or "orgy" in slug:
|
||||||
graph = f"{man} thrusts his penis into {woman} while surrounding partners give oral contact and keep hands on hips, breasts, and thighs."
|
graph = f"{man} thrusts his penis into {woman} while surrounding partners give oral contact and keep hands on hips, breasts, and thighs."
|
||||||
elif "cumshot" in slug or "climax" in slug:
|
elif "cumshot" in slug or "climax" in slug:
|
||||||
graph = f"{man} climaxes on {woman}'s body while {woman} stays posed with thighs open and direct eye contact."
|
graph = climax_position_graph(woman, man, third)
|
||||||
else:
|
else:
|
||||||
graph = f"{man}'s penis thrusts into {woman} with penetration and body contact visible."
|
graph = f"{man}'s penis thrusts into {woman} with penetration and body contact visible."
|
||||||
return graph + support_sentence({woman, man, third} if third else {woman, man})
|
return graph + support_sentence({woman, man, third} if third else {woman, man})
|
||||||
|
|||||||
Reference in New Issue
Block a user