Extract Krea action context helpers
This commit is contained in:
@@ -146,6 +146,9 @@ Already isolated:
|
|||||||
natural label replacement for formatter routes.
|
natural label replacement for formatter routes.
|
||||||
- `krea_clothing.py` owns clothing-state cleanup and action-aware body-access
|
- `krea_clothing.py` owns clothing-state cleanup and action-aware body-access
|
||||||
wording for formatter routes.
|
wording for formatter routes.
|
||||||
|
- `krea_action_context.py` owns shared action-family predicates, axis context
|
||||||
|
text, climax detection, and detail-density normalization used by action and
|
||||||
|
POV formatter routes.
|
||||||
|
|
||||||
Improve later:
|
Improve later:
|
||||||
|
|
||||||
|
|||||||
@@ -547,6 +547,7 @@ Key Krea2 ownership:
|
|||||||
|
|
||||||
- Cast descriptor naturalization: `krea_cast.cast_prose`,
|
- Cast descriptor naturalization: `krea_cast.cast_prose`,
|
||||||
`krea_cast.natural_label_text`.
|
`krea_cast.natural_label_text`.
|
||||||
|
- Action context and family predicates: `krea_action_context.py`.
|
||||||
- Hardcore action sentence: `_hardcore_action_sentence`.
|
- Hardcore action sentence: `_hardcore_action_sentence`.
|
||||||
- POV hardcore sentence: `_pov_hardcore_pose_sentence`, `_pov_action_phrase`.
|
- POV hardcore sentence: `_pov_hardcore_pose_sentence`, `_pov_action_phrase`.
|
||||||
- Clothing state cleanup: `krea_clothing.natural_clothing_state`.
|
- Clothing state cleanup: `krea_clothing.natural_clothing_state`.
|
||||||
@@ -733,6 +734,7 @@ Use these traces to narrow a problem in one pass.
|
|||||||
3. Inspect `categories/sexual_poses.json` for the selected subcategory,
|
3. Inspect `categories/sexual_poses.json` for the selected subcategory,
|
||||||
`item_templates`, `axes`, and `weight`.
|
`item_templates`, `axes`, and `weight`.
|
||||||
4. If raw `item` differs but Krea output looks identical, inspect
|
4. If raw `item` differs but Krea output looks identical, inspect
|
||||||
|
`krea_action_context.py` family predicates first, then
|
||||||
`_hardcore_pose_anchor`, `_hardcore_pose_arrangement`,
|
`_hardcore_pose_anchor`, `_hardcore_pose_arrangement`,
|
||||||
`_hardcore_item_detail`, and `_hardcore_action_sentence`.
|
`_hardcore_item_detail`, and `_hardcore_action_sentence`.
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
|
)
|
||||||
|
)
|
||||||
+24
-288
@@ -5,6 +5,18 @@ import re
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
from .krea_action_context import (
|
||||||
|
axis_values_text as _axis_values_text,
|
||||||
|
is_climax_text as _is_climax_text,
|
||||||
|
is_close_foreplay_text as _is_close_foreplay_text,
|
||||||
|
is_foreplay_text as _is_foreplay_text,
|
||||||
|
is_oral_text as _is_oral_text,
|
||||||
|
is_outercourse_text as _is_outercourse_text,
|
||||||
|
is_toy_assisted_double_text as _is_toy_assisted_double_text,
|
||||||
|
is_vaginal_penetration_text as _is_vaginal_penetration_text,
|
||||||
|
normalize_hardcore_detail_density as _normalize_hardcore_detail_density,
|
||||||
|
position_context_text as _position_context_text,
|
||||||
|
)
|
||||||
from .hardcore_text_cleanup import (
|
from .hardcore_text_cleanup import (
|
||||||
sanitize_hardcore_axis_values as _sanitize_hardcore_axis_values,
|
sanitize_hardcore_axis_values as _sanitize_hardcore_axis_values,
|
||||||
sanitize_hardcore_environment_anchors as _sanitize_hardcore_environment_anchors,
|
sanitize_hardcore_environment_anchors as _sanitize_hardcore_environment_anchors,
|
||||||
@@ -19,6 +31,18 @@ try:
|
|||||||
from .krea_clothing import natural_clothing_state as _natural_clothing_state
|
from .krea_clothing import natural_clothing_state as _natural_clothing_state
|
||||||
from .prompt_hygiene import sanitize_negative_text, sanitize_prose_text
|
from .prompt_hygiene import sanitize_negative_text, sanitize_prose_text
|
||||||
except ImportError: # Allows local smoke tests with `python -c`.
|
except ImportError: # Allows local smoke tests with `python -c`.
|
||||||
|
from krea_action_context import (
|
||||||
|
axis_values_text as _axis_values_text,
|
||||||
|
is_climax_text as _is_climax_text,
|
||||||
|
is_close_foreplay_text as _is_close_foreplay_text,
|
||||||
|
is_foreplay_text as _is_foreplay_text,
|
||||||
|
is_oral_text as _is_oral_text,
|
||||||
|
is_outercourse_text as _is_outercourse_text,
|
||||||
|
is_toy_assisted_double_text as _is_toy_assisted_double_text,
|
||||||
|
is_vaginal_penetration_text as _is_vaginal_penetration_text,
|
||||||
|
normalize_hardcore_detail_density as _normalize_hardcore_detail_density,
|
||||||
|
position_context_text as _position_context_text,
|
||||||
|
)
|
||||||
from hardcore_text_cleanup import (
|
from hardcore_text_cleanup import (
|
||||||
sanitize_hardcore_axis_values as _sanitize_hardcore_axis_values,
|
sanitize_hardcore_axis_values as _sanitize_hardcore_axis_values,
|
||||||
sanitize_hardcore_environment_anchors as _sanitize_hardcore_environment_anchors,
|
sanitize_hardcore_environment_anchors as _sanitize_hardcore_environment_anchors,
|
||||||
@@ -38,8 +62,6 @@ TRIGGER_CANDIDATES = (
|
|||||||
"sxcpinup_coloredpencil",
|
"sxcpinup_coloredpencil",
|
||||||
"sxcppnl7",
|
"sxcppnl7",
|
||||||
)
|
)
|
||||||
HARDCORE_DETAIL_DENSITY_CHOICES = {"compact", "balanced", "dense"}
|
|
||||||
|
|
||||||
PROMPT_FIELD_LABELS = (
|
PROMPT_FIELD_LABELS = (
|
||||||
"Ages",
|
"Ages",
|
||||||
"Body types",
|
"Body types",
|
||||||
@@ -82,11 +104,6 @@ def _expression_disabled(row: dict[str, Any]) -> bool:
|
|||||||
return bool(row.get("expression_disabled")) or _is_false(row.get("expression_enabled", True))
|
return bool(row.get("expression_disabled")) or _is_false(row.get("expression_enabled", True))
|
||||||
|
|
||||||
|
|
||||||
def _normalize_hardcore_detail_density(value: Any) -> str:
|
|
||||||
text = _clean(value).lower()
|
|
||||||
return text if text in HARDCORE_DETAIL_DENSITY_CHOICES else "balanced"
|
|
||||||
|
|
||||||
|
|
||||||
def _sentence(text: str) -> str:
|
def _sentence(text: str) -> str:
|
||||||
text = _clean(text).strip(" ,;")
|
text = _clean(text).strip(" ,;")
|
||||||
if not text:
|
if not text:
|
||||||
@@ -595,214 +612,6 @@ def _sanitize_scene_text_for_cast(text: Any, labels: list[str]) -> str:
|
|||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
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 _sanitize_foreplay_detail(detail: str, role_graph: str = "", composition: str = "") -> str:
|
def _sanitize_foreplay_detail(detail: str, role_graph: str = "", composition: str = "") -> str:
|
||||||
detail = _clean(detail)
|
detail = _clean(detail)
|
||||||
if not detail:
|
if not detail:
|
||||||
@@ -832,57 +641,6 @@ def _sanitize_foreplay_detail(detail: str, role_graph: str = "", composition: st
|
|||||||
return _clean(detail)
|
return _clean(detail)
|
||||||
|
|
||||||
|
|
||||||
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 _mentions_ass(text: str) -> bool:
|
def _mentions_ass(text: str) -> bool:
|
||||||
return bool(
|
return bool(
|
||||||
re.search(
|
re.search(
|
||||||
@@ -1729,28 +1487,6 @@ def _limit_detail_for_density(detail: str, density: str, is_climax: bool) -> str
|
|||||||
return _join_detail_clauses(clauses[:limit])
|
return _join_detail_clauses(clauses[:limit])
|
||||||
|
|
||||||
|
|
||||||
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:
|
def _climax_role_graph(role_graph: str, hard_item: str, axis_values: Any = None) -> str:
|
||||||
role_graph = _clean(role_graph).rstrip(".")
|
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)
|
text = " ".join(part.lower() for part in (role_graph, _clean(hard_item), _axis_values_text(axis_values)) if part)
|
||||||
|
|||||||
Reference in New Issue
Block a user