diff --git a/krea_formatter.py b/krea_formatter.py index 41b2951..0bedbfc 100644 --- a/krea_formatter.py +++ b/krea_formatter.py @@ -286,17 +286,29 @@ def _hardcore_pose_anchor(role_graph: str, hard_item: str, composition: str = "" if not text: return "" if "double penetration" in text or "vaginal and anal penetration" in text or "front-and-back" in text: + if "face-down ass-up" in text: + return "face-down rear-entry double-penetration pose" + if "doggy style" in text or "doggy-style" in text: + return "doggy-style double-penetration pose" + if "bent-over" in text: + return "bent-over double-penetration pose" + if "spooning anal" in text or "side-lying anal" in text: + return "side-lying double-penetration pose" if "bed-edge" in text or "edge-of-bed" in text: return "bed-edge front-and-back double-penetration pose" + if "standing anal" in text or "standing supported" in text: + return "standing supported front-and-back double-penetration pose" + if "kneeling anal" in text: + return "kneeling rear-entry double-penetration pose" if "standing supported" in text: return "standing supported front-and-back double-penetration pose" if "kneeling" in text: return "kneeling front-and-back double-penetration pose" return "front-and-back double-penetration pose" - if "face-sitting" in text: - return "face-sitting oral pose" if "sixty-nine" in text: return "sixty-nine oral pose" + if "face-sitting" in text: + return "face-sitting oral pose" if "cunnilingus" in text or "pussy licking" in text or "mouth on her pussy" in text: if "reclining" in text: return "reclining cunnilingus pose" @@ -328,17 +340,27 @@ def _hardcore_pose_anchor(role_graph: str, hard_item: str, composition: str = "" return "chair oral pose" return "mouth-to-genitals oral pose" if "anal" in text or "ass" in text or "rear-entry" in text: + if "face-down ass-up" in text: + return "face-down ass-up rear-entry anal pose" + if "doggy style" in text or "doggy-style" in text: + return "doggy-style anal pose" if "bed-edge" in text or "edge-of-bed" in text: return "bed-edge rear-entry anal pose" if "bent-over" in text: return "bent-over rear-entry anal pose" + if "spooning anal" in text or "side-lying anal" in text: + return "side-lying rear-entry anal pose" + if "kneeling anal" in text: + return "kneeling rear-entry anal pose" + if "standing anal" in text: + return "standing rear-entry anal pose" if "doggy" in text: return "doggy-style anal pose" return "rear-entry anal pose" positions = ( "missionary", - "cowgirl", "reverse cowgirl", + "cowgirl", "doggy style", "standing sex", "spooning sex", @@ -363,19 +385,175 @@ def _hardcore_pose_arrangement(anchor: str, role_graph: str, hard_item: str, com text = " ".join(_clean(part).lower() for part in (anchor, role_graph, hard_item, composition) if _clean(part)) if not text: return "" + mixed_woman_man = "the woman" in text and "the man" in text + is_double = "double-penetration" in text or "double penetration" in text + + def cast_phrase(mixed: str, generic: str) -> str: + return mixed if mixed_woman_man else generic + + def double_tail() -> str: + return ", with the toy aligned at the second penetration point" if "toy" in text else ", with the second penetration point aligned" + + if "sixty-nine" in text: + return cast_phrase( + "with the woman and man inverted head-to-hips so both mouths align with genitals", + "with both bodies inverted head-to-hips so both mouths align with genitals", + ) + if "face-sitting" in text: + return cast_phrase( + "with the man lying back while the woman straddles his face", + "with one partner lying back while the other straddles the face", + ) + 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: + return cast_phrase( + "with the man seated or lying back 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", + ) + return cast_phrase( + "with the woman lying back, thighs spread, and the man positioned between her legs", + "with the receiving partner lying back, thighs spread, and the giver positioned between the legs", + ) + if "straddled cunnilingus" in text or "straddled oral" in text: + return cast_phrase( + "with the woman straddling above the man's mouth and her thighs framing his face", + "with the receiver straddling above the giver's mouth", + ) + if "edge-of-bed oral" in text: + if "takes the man's penis" in text or "penis in her mouth" in text: + return cast_phrase( + "with the man at the bed edge and the woman kneeling at his hips", + "with the receiver at the bed edge and the giver positioned at hip height", + ) + return cast_phrase( + "with the woman lying at the bed edge and the man positioned between her open thighs", + "with the receiver lying at the bed edge and the giver positioned between open thighs", + ) + if "standing oral" in text: + if "takes the man's penis" in text or "penis in her mouth" in text: + return cast_phrase( + "with the man standing and the woman kneeling in front of his hips", + "with the receiver standing and the giver kneeling at hip height", + ) + return cast_phrase( + "with the woman standing braced and the man kneeling between her thighs", + "with the receiver standing braced and the giver kneeling between the thighs", + ) + if "chair oral" in text: + if "takes the man's penis" in text or "penis in her mouth" in text: + return cast_phrase( + "with the man seated in the chair and the woman kneeling between his legs at hip level", + "with the receiver seated in the chair and the giver kneeling between the legs at hip level", + ) + return cast_phrase( + "with one partner seated in a chair and the other kneeling between the open thighs", + "with the receiver seated in a chair and the giver kneeling between the open thighs", + ) + if "side-lying oral" in text: + return "with both bodies lying on their sides and mouth aligned to genitals" + if "kneeling oral" in text: + if "takes the man's penis" in text or "penis in her mouth" in text: + return cast_phrase( + "with the woman kneeling in front of the man's hips, her mouth at penis level", + "with the giver kneeling in front of the receiver's hips", + ) + if "mouth on her pussy" in text or "uses his mouth on" in text: + return cast_phrase( + "with the man kneeling between the woman's open thighs, his mouth at her pussy", + "with the giver kneeling between the receiver's open thighs", + ) + return "with the giver kneeling at the receiver's hips" + if "reverse cowgirl" in text: + return cast_phrase( + "with the man lying on his back under the woman while she straddles his hips facing away", + "with the lower partner lying on their back while the upper partner straddles them facing away", + ) + if "cowgirl" in text: + return cast_phrase( + "with the man lying on his back under the woman while she straddles his hips on top", + "with the lower partner lying on their back while the upper partner straddles their hips on top", + ) + if "missionary" in text: + return cast_phrase( + "with the woman lying on her back under the man, legs open around his hips", + "with the receiving partner lying on their back under the penetrating partner, legs open around the hips", + ) + if "lotus" in text: + return cast_phrase( + "with the man seated upright and the woman seated in his lap facing him, legs wrapped around his hips", + "with one partner seated upright and the other seated in their lap facing them, legs wrapped around the hips", + ) + if "kneeling straddle" in text: + return cast_phrase( + "with the woman straddling the man's kneeling lap, both torsos upright and hips pressed together", + "with one partner straddling the other's kneeling lap, torsos upright and hips pressed together", + ) + if "doggy-style" in text: + return cast_phrase( + f"with the woman on all fours and the man positioned behind her at hip level{double_tail() if is_double else ''}", + f"with the receiving partner on all fours and the penetrating partner positioned behind at hip level{double_tail() if is_double else ''}", + ) + if "face-down" in text: + return cast_phrase( + f"with the woman face-down, hips raised, and the man positioned behind her{double_tail() if is_double else ''}", + f"with the receiving partner face-down, hips raised, and the penetrating partner positioned behind{double_tail() if is_double else ''}", + ) + if "bent-over" in text: + return cast_phrase( + f"with the woman bent forward at the waist and the man positioned behind her{double_tail() if is_double else ''}", + f"with the receiving partner bent forward at the waist and the penetrating partner positioned behind{double_tail() if is_double else ''}", + ) + if "spooning" in text or ("side-lying" in text and "oral" not in text): + return cast_phrase( + f"with both lying on their sides and the man positioned behind the woman{double_tail() if is_double else ''}", + f"with both bodies lying on their sides and the penetrating partner positioned behind{double_tail() if is_double else ''}", + ) + if "edge-of-bed" in text or "bed-edge" in text: + 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 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 ''}", + ) + if "standing" in text: + return cast_phrase( + f"with the woman braced standing and the man aligned at her hips{double_tail() if is_double else ''}", + f"with both partners standing and the penetrating partner aligned at the receiver's hips{double_tail() if is_double else ''}", + ) + if "kneeling" in text and ("anal" in text or "rear-entry" in text): + return cast_phrase( + f"with the woman kneeling forward and the man positioned behind her{double_tail() if is_double else ''}", + f"with the receiving partner kneeling forward and the penetrating partner positioned behind{double_tail() if is_double else ''}", + ) if "double-penetration" in text or "double penetration" in text: if "toy" in text: - return "with the woman's hips spread and front-and-back alignment visible" - return "with the central body held between front-and-back contact" + 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 receiving body bent forward or on all fours and the toy aligned at the second penetration point", + ) + if "from the front" in text: + return cast_phrase( + "with the woman held between the man behind her and a second partner in front", + "with the receiving body held between one partner behind and a second partner in front", + ) + return cast_phrase( + "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", + ) if "anal" in text or "rear-entry" in text: - return "with the woman's hips raised, ass exposed, and penetration alignment visible" + return cast_phrase( + "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", + ) if "cunnilingus" in text or "mouth on her pussy" in text or "pussy licking" in text: - return "with the woman's thighs open and the giver's mouth pressed to her pussy" + return cast_phrase( + "with the woman's thighs open and the man's mouth pressed to her pussy", + "with the receiver's thighs open and the giver's mouth pressed to genitals", + ) if "oral" in text or "blowjob" in text or "penis in her mouth" in text or "penis in mouth" in text: - if "sixty-nine" in text: - return "with both bodies arranged mouth-to-genitals" if "takes the man's penis in her mouth" in text or "penis in her mouth" in text: - return "with the woman's mouth close to the man's hips" + return cast_phrase( + "with the woman's mouth at the man's hips", + "with the giver's mouth positioned at the receiver's hips", + ) return "with mouth and genitals aligned clearly" if "threesome" in text or "three-body" in text: return "with all three adult bodies clearly placed around the central subject" @@ -485,9 +663,10 @@ def _dedupe_hardcore_detail(detail: str, anchor: str) -> str: "straddled cunnilingus": (r"straddled oral position", r"straddled cunnilingus position"), "open-thigh cunnilingus": (r"reclining cunnilingus position", r"straddled cunnilingus position"), "bent-over": (r"bent-over position",), + "face-down": (r"face-down ass-up position",), "missionary": (r"missionary position",), - "cowgirl": (r"cowgirl position",), "reverse cowgirl": (r"reverse cowgirl position",), + "cowgirl": (r"cowgirl position",), "doggy-style": (r"doggy style position",), "edge-of-bed": (r"edge-of-bed position",), } @@ -553,6 +732,13 @@ def _hardcore_action_sentence(role_graph: str, hard_item: str, composition: str ) detail = _hardcore_item_detail(hard_item) anchor = _hardcore_pose_anchor(role_graph, hard_item, composition) + if "double-penetration" in anchor.lower() and "toy" in role_graph.lower(): + role_graph = re.sub( + r"\s+while a toy adds (?:the|a) second penetration point\b", + "", + role_graph, + flags=re.IGNORECASE, + ) detail = _dedupe_hardcore_detail(detail, anchor) if anchor else detail arrangement = _hardcore_pose_arrangement(anchor, role_graph, hard_item, composition) anchor_phrase = _with_indefinite_article(anchor) if anchor else "" diff --git a/prompt_builder.py b/prompt_builder.py index 13f2cb8..cbb6bb5 100644 --- a/prompt_builder.py +++ b/prompt_builder.py @@ -2530,7 +2530,17 @@ def _role_graph( if "oral" in slug: if "sixty-nine" in item_text or ("blowjob" in item_text and ("cunnilingus" in item_text or "pussy" in item_text)): graph = f"{woman} has {man}'s penis in her mouth while {man} uses his mouth on {woman}'s pussy, with both mouths pressed to genitals." - elif any(term in item_text for term in ("cunnilingus", "pussy licking", "tongue on pussy", "mouth on pussy")): + elif any( + term in item_text + for term in ( + "cunnilingus", + "pussy licking", + "tongue on pussy", + "mouth on pussy", + "pussy and tongue", + "tongue contact", + ) + ) or ("pussy" in item_text and "penis" not in item_text): graph = f"{man} gives oral to {woman}, mouth on her pussy while {woman}'s thighs are held open for the camera." else: graph = f"{woman} takes {man}'s penis in her mouth while {man} holds her hair and hips."