197 lines
7.4 KiB
Python
197 lines
7.4 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,
|
|
normalize_hardcore_detail_density,
|
|
)
|
|
from .krea_detail import limit_detail_for_density
|
|
from .krea_action_positions import (
|
|
arrangement_duplicates_role,
|
|
hardcore_pose_anchor,
|
|
hardcore_pose_arrangement,
|
|
)
|
|
from .krea_action_details import (
|
|
dedupe_anchor_detail,
|
|
dedupe_oral_detail,
|
|
dedupe_outercourse_detail,
|
|
dedupe_penetration_detail,
|
|
dedupe_toy_double_detail,
|
|
hardcore_item_detail,
|
|
sanitize_foreplay_detail,
|
|
)
|
|
from .krea_action_climax import climax_role_graph, dedupe_climax_detail
|
|
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,
|
|
normalize_hardcore_detail_density,
|
|
)
|
|
from krea_detail import limit_detail_for_density
|
|
from krea_action_positions import (
|
|
arrangement_duplicates_role,
|
|
hardcore_pose_anchor,
|
|
hardcore_pose_arrangement,
|
|
)
|
|
from krea_action_details import (
|
|
dedupe_anchor_detail,
|
|
dedupe_oral_detail,
|
|
dedupe_outercourse_detail,
|
|
dedupe_penetration_detail,
|
|
dedupe_toy_double_detail,
|
|
hardcore_item_detail,
|
|
sanitize_foreplay_detail,
|
|
)
|
|
from krea_action_climax import climax_role_graph, dedupe_climax_detail
|
|
|
|
|
|
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 _lowercase_for_inline_join(text: str) -> str:
|
|
text = _clean(text)
|
|
return text[:1].lower() + text[1:] if text else text
|
|
|
|
|
|
def _with_indefinite_article(text: str) -> str:
|
|
text = _clean(text)
|
|
if not text or text.lower().startswith(("a ", "an ")):
|
|
return text
|
|
article = "an" if text[:1].lower() in "aeiou" else "a"
|
|
return f"{article} {text}"
|
|
|
|
|
|
def hardcore_action_sentence(
|
|
role_graph: str,
|
|
hard_item: str,
|
|
composition: str = "",
|
|
axis_values: Any = None,
|
|
detail_density: str = "balanced",
|
|
) -> str:
|
|
detail_density = normalize_hardcore_detail_density(detail_density)
|
|
role_graph = _clean(role_graph).rstrip(".")
|
|
hard_item = _clean(hard_item).rstrip(".")
|
|
role_graph = re.sub(
|
|
r"\bthe man penetrates the woman while a toy adds a second point of contact\b",
|
|
"the man's penis thrusts into the woman while a toy is positioned at the second penetration point",
|
|
role_graph,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
role_graph = re.sub(
|
|
r"\bthe man thrusts his penis into the woman while a toy adds a second penetration point\b",
|
|
"the man's penis thrusts into the woman while a toy is positioned at the second penetration point",
|
|
role_graph,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
role_graph = re.sub(
|
|
r"\bthe man thrusts his penis into the woman\b",
|
|
"the man's penis thrusts into the woman",
|
|
role_graph,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
role_graph = re.sub(
|
|
r"\bthe man penetrates the woman anally\b",
|
|
"the man's penis thrusts into the woman's ass",
|
|
role_graph,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
role_graph = re.sub(
|
|
r"\bthe man thrusts his penis into the woman's ass\b",
|
|
"the man's penis thrusts into the woman's ass",
|
|
role_graph,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
role_graph = re.sub(
|
|
r"\bthe man penetrates the woman\b",
|
|
"the man's penis thrusts into the woman",
|
|
role_graph,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
role_graph = re.sub(
|
|
r"\bthe woman and the man are in mutual oral contact with mouth-to-genital contact visible\b",
|
|
"the woman has the man's penis in her mouth while the man uses his mouth on her pussy",
|
|
role_graph,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
role_graph = re.sub(
|
|
r"\bthe woman gives oral to the man\b",
|
|
"the woman takes the man's penis in her mouth",
|
|
role_graph,
|
|
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)
|
|
anchor = hardcore_pose_anchor(role_graph, hard_item, composition, axis_values)
|
|
is_outercourse = is_outercourse_text(role_graph, hard_item, composition, axis_values_text(axis_values))
|
|
is_oral = is_oral_text(role_graph, hard_item, composition, axis_values_text(axis_values))
|
|
is_penetrative = is_vaginal_penetration_text(role_graph, hard_item, composition, axis_values_text(axis_values))
|
|
if is_toy_assisted_double_text(role_graph, hard_item, composition, axis_values_text(axis_values)):
|
|
role_graph = re.sub(
|
|
r"\s+while a toy adds (?:the|a) second penetration point\b",
|
|
" while a toy is positioned at the second penetration point",
|
|
role_graph,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
if is_climax:
|
|
anchor = ""
|
|
detail = dedupe_climax_detail(detail, role_graph, detail_density)
|
|
elif is_foreplay_text(role_graph, hard_item, composition, axis_values_text(axis_values)):
|
|
anchor = ""
|
|
detail = sanitize_foreplay_detail(detail, role_graph, composition)
|
|
detail = limit_detail_for_density(detail, detail_density, False)
|
|
elif is_outercourse:
|
|
anchor = ""
|
|
detail = dedupe_outercourse_detail(detail, role_graph, hard_item, axis_values)
|
|
detail = limit_detail_for_density(detail, detail_density, False)
|
|
elif is_oral and role_graph:
|
|
anchor = ""
|
|
detail = dedupe_oral_detail(detail, role_graph, hard_item, axis_values)
|
|
detail = limit_detail_for_density(detail, detail_density, False)
|
|
elif is_penetrative and role_graph:
|
|
anchor = ""
|
|
detail = dedupe_penetration_detail(detail, role_graph, hard_item, axis_values)
|
|
detail = limit_detail_for_density(detail, detail_density, False)
|
|
else:
|
|
detail = dedupe_anchor_detail(detail, anchor) if anchor else detail
|
|
if is_toy_assisted_double_text(role_graph, hard_item, composition, axis_values_text(axis_values)):
|
|
detail = dedupe_toy_double_detail(detail)
|
|
detail = limit_detail_for_density(detail, detail_density, False)
|
|
arrangement = hardcore_pose_arrangement(anchor, role_graph, hard_item, composition, axis_values)
|
|
anchor_phrase = _with_indefinite_article(anchor) if anchor else ""
|
|
if arrangement and anchor_phrase and not arrangement_duplicates_role(arrangement, role_graph):
|
|
anchor_phrase = f"{anchor_phrase} {arrangement}"
|
|
if role_graph and anchor_phrase:
|
|
sentence = f"In {anchor_phrase}, {_lowercase_for_inline_join(role_graph)}"
|
|
elif role_graph:
|
|
sentence = role_graph
|
|
elif detail and anchor_phrase:
|
|
sentence = f"In {anchor_phrase}, {detail}"
|
|
detail = ""
|
|
else:
|
|
sentence = detail or hard_item
|
|
detail = ""
|
|
if detail:
|
|
sentence = f"{sentence}; {detail}"
|
|
return sentence
|