Extract Krea action context helpers

This commit is contained in:
2026-06-26 15:36:43 +02:00
parent 031223255d
commit 659a730169
4 changed files with 330 additions and 288 deletions
+301
View File
@@ -0,0 +1,301 @@
from __future__ import annotations
import re
from typing import Any
HARDCORE_DETAIL_DENSITY_CHOICES = {"compact", "balanced", "dense"}
def _clean(value: Any) -> str:
text = "" if value is None else str(value)
text = text.replace("\n", " ")
text = re.sub(r"\s+", " ", text).strip()
text = re.sub(r"\s+([,.;:])", r"\1", text)
return text
def normalize_hardcore_detail_density(value: Any) -> str:
text = _clean(value).lower()
return text if text in HARDCORE_DETAIL_DENSITY_CHOICES else "balanced"
def axis_values_text(axis_values: Any) -> str:
if not isinstance(axis_values, dict):
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 is_outercourse_text(*parts: Any) -> bool:
text = " ".join(_clean(part).lower() for part in parts if _clean(part))
return any(
term in text
for term in (
"outercourse",
"non-penetrative",
"boobjob",
"titjob",
"breast sex",
"breast-sex",
"testicle",
"balls",
"balls licking",
"balls-licking",
"breasts tightly around",
"breasts around",
"penis licking",
"penis-licking",
"tongue along",
"tongue runs along",
"tongue running along",
"handjob",
"hand job",
"hand wrapped",
"hand stroking",
"hand wraps around",
"manual stimulation",
"fingering",
"fingers inside",
"fingers in pussy",
"hand on pussy",
"fingers on pussy",
"fingers sliding against the pussy",
"open-thigh manual",
"clit rubbing",
"clit",
"clitoris",
"mutual masturbation",
"footjob",
"soles wrap around",
"soles",
"toes curled",
"feet stroking",
)
)
def is_oral_text(*parts: Any) -> bool:
text = " ".join(_clean(part).lower() for part in parts if _clean(part))
return any(
term in text
for term in (
"oral",
"fellatio",
"blowjob",
"deepthroat",
"penis sucking",
"penis in her mouth",
"penis in mouth",
"takes the man's penis",
"takes his penis",
"mouth at penis level",
"mouth on his penis",
"lips wrapped",
"cunnilingus",
"pussy licking",
"mouth on her pussy",
"mouth pressed to her pussy",
"face-sitting",
"sixty-nine",
)
)
def is_foreplay_text(*parts: Any) -> bool:
text = " ".join(_clean(part).lower() for part in parts if _clean(part))
if not text:
return False
return any(
term in text
for term in (
"foreplay",
"pre-sex",
"before sex",
"before penetration",
"kissing",
"deep kiss",
"mouth-to-mouth",
"lips pressed",
"caressing",
"hands roaming",
"stroking skin",
"touching breasts",
"cupping a breast",
"hand on the cheek",
"cheek and jaw",
"fingers under the chin",
"undressing",
"removing clothing",
"removing clothes",
"pulling clothing",
"sliding straps",
"unbuttoning",
"body worship",
"nipple",
"mouth on skin",
"kissing down",
"ass grabbing",
"gripping the ass",
"thigh kissing",
"inner thighs",
"hair held",
"holding hair",
"hair pulled back",
"wrist",
"wrists",
"pinning",
"guided",
"guiding",
"turning the body",
"position transition",
"pulling onto the bed",
"lifting and spreading",
"spreading thighs",
"dirty talk",
"whispering",
"camera performance",
"presented directly to the camera",
"present her body",
"showing to camera",
"spread open for the camera",
"watching partner",
"waiting turn",
"group coordination",
"aftercare",
"cleanup",
"wiping",
"towel",
"post-sex",
"fingering",
"fingers inside",
"hand on pussy",
"fingers on pussy",
"clit rubbing",
"clit",
"clitoris",
"manual stimulation",
"mutual masturbation",
)
)
def is_close_foreplay_text(*parts: Any) -> bool:
text = " ".join(_clean(part).lower() for part in parts if _clean(part))
if not text or not is_foreplay_text(text):
return False
return any(
term in text
for term in (
"stand close",
"stand face-to-face",
"press their bodies",
"bodies pressed close",
"hips pressed close",
"mouth-to-mouth",
"deep kissing",
"heated kiss",
"hands pull clothing",
"pull clothing aside",
"clothing being removed",
)
)
def is_vaginal_penetration_text(*parts: Any) -> bool:
text = " ".join(_clean(part).lower() for part in parts if _clean(part))
if not text or is_outercourse_text(text) or is_oral_text(text) or is_foreplay_text(text):
return False
if any(term in text for term in ("anal", "double penetration", "double-penetration", "toy-assisted", "strap-on")):
return False
return any(
term in text
for term in (
"vaginal penetration",
"deep vaginal sex",
"explicit penetrative sex",
"penetrative sex",
"penis entering pussy",
"penis thrusts into her pussy",
"penis thrusts into the woman",
"pussy stretched around a penis",
"hardcore vaginal thrusting",
"full-body penetrative sex",
"close-contact vaginal sex",
"missionary position",
"cowgirl position",
"reverse cowgirl position",
"doggy style position",
"standing sex position",
"spooning sex position",
"edge-of-bed position",
"kneeling straddle position",
"lotus sex position",
"bent-over position",
)
)
def is_toy_assisted_double_text(*parts: Any) -> bool:
text = " ".join(_clean(part).lower() for part in parts if _clean(part))
if "toy" not in text:
return False
return any(
token in text
for token in (
"double penetration",
"double-penetration",
"vaginal and anal penetration",
"second penetration point",
"second point of contact",
"second contact",
)
)
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",
)
)