143 lines
4.6 KiB
Python
143 lines
4.6 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
from typing import Any
|
|
|
|
try:
|
|
from .krea_action_context import (
|
|
axis_values_text,
|
|
is_climax_text,
|
|
is_foreplay_text,
|
|
is_oral_text,
|
|
is_outercourse_text,
|
|
is_toy_assisted_double_text,
|
|
is_vaginal_penetration_text,
|
|
)
|
|
except ImportError: # Allows local smoke tests with `python -c`.
|
|
from krea_action_context import (
|
|
axis_values_text,
|
|
is_climax_text,
|
|
is_foreplay_text,
|
|
is_oral_text,
|
|
is_outercourse_text,
|
|
is_toy_assisted_double_text,
|
|
is_vaginal_penetration_text,
|
|
)
|
|
|
|
|
|
ACTION_CLIMAX = "climax"
|
|
ACTION_FOREPLAY = "foreplay"
|
|
ACTION_OUTERCOURSE = "outercourse"
|
|
ACTION_ORAL = "oral"
|
|
ACTION_PENETRATION = "penetration"
|
|
ACTION_TOY_DOUBLE = "toy_double"
|
|
ACTION_DEFAULT = "default"
|
|
|
|
HARDCORE_ACTION_FAMILY_CHOICES = {
|
|
ACTION_CLIMAX,
|
|
ACTION_FOREPLAY,
|
|
ACTION_OUTERCOURSE,
|
|
ACTION_ORAL,
|
|
ACTION_PENETRATION,
|
|
ACTION_TOY_DOUBLE,
|
|
ACTION_DEFAULT,
|
|
}
|
|
|
|
|
|
def normalize_hardcore_action_family(value: Any, default: str = "") -> str:
|
|
text = re.sub(r"[^a-z0-9]+", "_", str(value or "").strip().lower()).strip("_")
|
|
aliases = {
|
|
"penetrative": ACTION_PENETRATION,
|
|
"penetrative_sex": ACTION_PENETRATION,
|
|
"penetration_sex": ACTION_PENETRATION,
|
|
"vaginal": ACTION_PENETRATION,
|
|
"vaginal_penetration": ACTION_PENETRATION,
|
|
"double": ACTION_TOY_DOUBLE,
|
|
"double_penetration": ACTION_TOY_DOUBLE,
|
|
"toy_double_penetration": ACTION_TOY_DOUBLE,
|
|
"toy_assisted_double": ACTION_TOY_DOUBLE,
|
|
"toy_assisted_double_penetration": ACTION_TOY_DOUBLE,
|
|
"outer_course": ACTION_OUTERCOURSE,
|
|
"outercourse_sex": ACTION_OUTERCOURSE,
|
|
"manual": ACTION_FOREPLAY,
|
|
"manual_stimulation": ACTION_FOREPLAY,
|
|
"interaction": ACTION_FOREPLAY,
|
|
"body_worship": ACTION_FOREPLAY,
|
|
"body_worship_touching": ACTION_FOREPLAY,
|
|
"foreplay_teasing": ACTION_FOREPLAY,
|
|
"cumshot": ACTION_CLIMAX,
|
|
"cumshot_climax": ACTION_CLIMAX,
|
|
"orgasm_aftermath": ACTION_CLIMAX,
|
|
"oral_sex": ACTION_ORAL,
|
|
}
|
|
text = aliases.get(text, text)
|
|
return text if text in HARDCORE_ACTION_FAMILY_CHOICES else default
|
|
|
|
|
|
def infer_hardcore_action_family(
|
|
role_graph: str,
|
|
hard_item: str,
|
|
composition: str = "",
|
|
axis_values: Any = None,
|
|
*,
|
|
is_climax: bool | None = None,
|
|
) -> str:
|
|
axis_text = axis_values_text(axis_values)
|
|
if is_climax is None:
|
|
is_climax = is_climax_text(role_graph, hard_item, composition, axis_text)
|
|
if is_climax:
|
|
return ACTION_CLIMAX
|
|
if is_foreplay_text(role_graph, hard_item, composition, axis_text):
|
|
return ACTION_FOREPLAY
|
|
if is_outercourse_text(role_graph, hard_item, composition, axis_text):
|
|
return ACTION_OUTERCOURSE
|
|
if is_oral_text(role_graph, hard_item, composition, axis_text):
|
|
return ACTION_ORAL
|
|
if is_vaginal_penetration_text(role_graph, hard_item, composition, axis_text):
|
|
return ACTION_PENETRATION
|
|
if is_toy_assisted_double_text(role_graph, hard_item, composition, axis_text):
|
|
return ACTION_TOY_DOUBLE
|
|
return ACTION_DEFAULT
|
|
|
|
|
|
def source_hardcore_action_family(
|
|
source_family: Any,
|
|
role_graph: str,
|
|
hard_item: str,
|
|
composition: str = "",
|
|
axis_values: Any = None,
|
|
) -> str:
|
|
inferred = infer_hardcore_action_family(role_graph, hard_item, composition, axis_values)
|
|
if inferred in (ACTION_CLIMAX, ACTION_TOY_DOUBLE):
|
|
return inferred
|
|
family = re.sub(r"[^a-z0-9]+", "_", str(source_family or "").strip().lower()).strip("_")
|
|
family = {
|
|
"penetration": "penetrative",
|
|
"penetrative_sex": "penetrative",
|
|
"outer_course": "outercourse",
|
|
"outercourse_sex": "outercourse",
|
|
"manual_stimulation": "manual",
|
|
"foreplay_teasing": "foreplay",
|
|
"body_worship": "interaction",
|
|
"body_worship_touching": "interaction",
|
|
"clothing_position_transitions": "interaction",
|
|
"dominant_guidance": "interaction",
|
|
"camera_performance": "interaction",
|
|
"group_coordination": "interaction",
|
|
"cumshot": "climax",
|
|
"cumshot_climax": "climax",
|
|
"oral_sex": "oral",
|
|
}.get(family, family)
|
|
source_mapping = {
|
|
"penetrative": ACTION_PENETRATION,
|
|
"foreplay": ACTION_FOREPLAY,
|
|
"interaction": ACTION_FOREPLAY,
|
|
"manual": ACTION_FOREPLAY,
|
|
"oral": ACTION_ORAL,
|
|
"outercourse": ACTION_OUTERCOURSE,
|
|
"climax": ACTION_CLIMAX,
|
|
}
|
|
if family == "anal":
|
|
return ACTION_DEFAULT
|
|
return source_mapping.get(family, inferred)
|