Extract Insta pair clothing routing
This commit is contained in:
@@ -0,0 +1,345 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import Any, Callable
|
||||
|
||||
|
||||
WOMAN_LOWER_ACCESS_TERMS = (
|
||||
"penetrat",
|
||||
"thrust",
|
||||
"vaginal",
|
||||
"anal",
|
||||
"rear-entry",
|
||||
"rear entry",
|
||||
"front-and-back",
|
||||
"front and back",
|
||||
"double",
|
||||
"doggy",
|
||||
"missionary",
|
||||
"cowgirl",
|
||||
"straddles",
|
||||
"hips aligned",
|
||||
"penis into",
|
||||
"penis inside",
|
||||
"penis entering",
|
||||
"mouth on her pussy",
|
||||
"mouth pressed to her pussy",
|
||||
"pussy licking",
|
||||
"cunnilingus",
|
||||
"thighs spread",
|
||||
"thighs open",
|
||||
"legs spread",
|
||||
"legs open",
|
||||
"cum on pussy",
|
||||
"cum across her pussy",
|
||||
"cum dripping from pussy",
|
||||
"cum dripping from ass",
|
||||
"cum on belly",
|
||||
"cum on thighs",
|
||||
"cum across her ass",
|
||||
"cum across her lower back",
|
||||
"toy aligned",
|
||||
"second penetration point",
|
||||
)
|
||||
|
||||
WOMAN_UPPER_ACCESS_TERMS = (
|
||||
"boobjob",
|
||||
"titjob",
|
||||
"breast sex",
|
||||
"breasts around",
|
||||
"breasts tightly",
|
||||
"hands pressing both breasts",
|
||||
"breasts together",
|
||||
"cum on breasts",
|
||||
"cum across her breasts",
|
||||
"cum on chest",
|
||||
)
|
||||
|
||||
MAN_LOWER_ACCESS_TERMS = (
|
||||
"penis",
|
||||
"glans",
|
||||
"testicle",
|
||||
"balls",
|
||||
"cumshot",
|
||||
"ejaculat",
|
||||
"semen",
|
||||
"boobjob",
|
||||
"titjob",
|
||||
"breast sex",
|
||||
"footjob",
|
||||
"handjob",
|
||||
"hand job",
|
||||
"hand wrapped",
|
||||
"hand stroking",
|
||||
"blowjob",
|
||||
"fellatio",
|
||||
"penis sucking",
|
||||
"penis in mouth",
|
||||
"mouth on penis",
|
||||
"penis licking",
|
||||
)
|
||||
|
||||
LOWER_BODY_CLOTHING_TERMS = (
|
||||
"panty",
|
||||
"panties",
|
||||
"brief",
|
||||
"briefs",
|
||||
"thong",
|
||||
"bottom",
|
||||
"bottoms",
|
||||
"bodysuit",
|
||||
"teddy",
|
||||
"dress",
|
||||
"skirt",
|
||||
"shorts",
|
||||
"jeans",
|
||||
"trousers",
|
||||
"pants",
|
||||
"bikini",
|
||||
"towel",
|
||||
"sheet",
|
||||
"blanket",
|
||||
)
|
||||
|
||||
UPPER_BODY_CLOTHING_TERMS = (
|
||||
"bra",
|
||||
"cup",
|
||||
"cups",
|
||||
"corset",
|
||||
"bodysuit",
|
||||
"bustier",
|
||||
"top",
|
||||
"camisole",
|
||||
"shirt",
|
||||
"blouse",
|
||||
"bodice",
|
||||
"dress",
|
||||
"robe",
|
||||
"jacket",
|
||||
"sweater",
|
||||
"harness",
|
||||
"chest",
|
||||
"cleavage",
|
||||
"panel",
|
||||
"panels",
|
||||
)
|
||||
|
||||
INSTA_OF_HARDCORE_MEN_CLOTHING_LOWER_ACCESS = [
|
||||
"wears an open button shirt with jeans lowered below the hips for genital access",
|
||||
"wears a fitted tee pushed up with trousers lowered below the hips",
|
||||
"keeps a dark shirt on while pants and underwear are pulled down below the hips",
|
||||
"wears an open overshirt with jeans pushed down at the thighs",
|
||||
"wears a hoodie lifted at the waist with sweatpants lowered below the hips",
|
||||
"wears gym shorts pulled down below the hips with his shirt still on",
|
||||
"keeps a casual shirt on with belt open and pants lowered below the hips",
|
||||
"wears a half-open shirt with lower garments pushed down below the hips",
|
||||
]
|
||||
|
||||
INSTA_OF_HARDCORE_MEN_CLOTHING_VISIBLE = [
|
||||
"wears an open button shirt with jeans unfastened",
|
||||
"wears a fitted tee with pants opened at the waist",
|
||||
"keeps a dark shirt on with trousers loosened",
|
||||
"wears an open overshirt with jeans partly lowered",
|
||||
"wears gym shorts loose at the waist with a towel nearby",
|
||||
"wears a hoodie lifted at the waist with sweatpants loosened",
|
||||
"wears a casual shirt with belt open and pants partly lowered",
|
||||
"wears a half-open shirt with dark trousers",
|
||||
]
|
||||
|
||||
|
||||
def hardcore_row_access_flags(row: dict[str, Any]) -> dict[str, bool]:
|
||||
axis_values = row.get("item_axis_values")
|
||||
axis_text = " ".join(str(value) for value in axis_values.values()) if isinstance(axis_values, dict) else ""
|
||||
role_text = " ".join(
|
||||
str(part or "")
|
||||
for part in (
|
||||
row.get("source_role_graph"),
|
||||
row.get("role_graph"),
|
||||
)
|
||||
).lower()
|
||||
detail_text = " ".join(
|
||||
str(part or "")
|
||||
for part in (
|
||||
row.get("item"),
|
||||
row.get("source_composition"),
|
||||
row.get("composition"),
|
||||
axis_text,
|
||||
)
|
||||
).lower()
|
||||
full_text = f"{role_text} {detail_text}"
|
||||
return {
|
||||
"woman_lower": any(term in role_text for term in WOMAN_LOWER_ACCESS_TERMS),
|
||||
"woman_upper": any(term in full_text for term in WOMAN_UPPER_ACCESS_TERMS),
|
||||
"man_lower": any(term in role_text for term in MAN_LOWER_ACCESS_TERMS),
|
||||
}
|
||||
|
||||
|
||||
def _outfit_without_lower_body_blockers(outfit: str) -> str:
|
||||
text = str(outfit or "").strip()
|
||||
if not text:
|
||||
return ""
|
||||
text = re.sub(r"\blingerie set\b", "lingerie top details", text, flags=re.IGNORECASE)
|
||||
text = re.sub(r"\bbrief set\b", "bra set", text, flags=re.IGNORECASE)
|
||||
text = re.sub(r"\bbodysuit with\b", "upper bodysuit detail with", text, flags=re.IGNORECASE)
|
||||
fragments = re.split(r"\s*,\s*|\s+\band\b\s+|\s+\bwith\b\s+|\s+\bunder\b\s+|\s+\bover\b\s+", text)
|
||||
kept = []
|
||||
for fragment in fragments:
|
||||
fragment = fragment.strip(" ,.;")
|
||||
fragment = re.sub(r"^(?:and|with|under|over)\s+", "", fragment, flags=re.IGNORECASE)
|
||||
if not fragment:
|
||||
continue
|
||||
lower = fragment.lower()
|
||||
if any(term in lower for term in LOWER_BODY_CLOTHING_TERMS):
|
||||
continue
|
||||
kept.append(fragment)
|
||||
if not kept:
|
||||
return ""
|
||||
deduped = []
|
||||
seen = set()
|
||||
for fragment in kept:
|
||||
key = re.sub(r"\W+", " ", fragment.lower()).strip()
|
||||
if key and key not in seen:
|
||||
deduped.append(fragment)
|
||||
seen.add(key)
|
||||
return ", ".join(deduped)
|
||||
|
||||
|
||||
def _outfit_without_upper_body_blockers(outfit: str) -> str:
|
||||
text = str(outfit or "").strip()
|
||||
if not text:
|
||||
return ""
|
||||
text = re.sub(r"\blingerie set\b", "lingerie styling", text, flags=re.IGNORECASE)
|
||||
text = re.sub(r"\bbalconette bra and brief set\b", "briefs and garter styling", text, flags=re.IGNORECASE)
|
||||
fragments = re.split(r"\s*,\s*|\s+\band\s+|\s+\bwith\s+|\s+\bunder\s+|\s+\bover\s+", text)
|
||||
kept = []
|
||||
for fragment in fragments:
|
||||
fragment = fragment.strip(" ,.;")
|
||||
fragment = re.sub(r"^(?:and|with|under|over)\s+", "", fragment, flags=re.IGNORECASE)
|
||||
if not fragment:
|
||||
continue
|
||||
lower = fragment.lower()
|
||||
if any(term in lower for term in UPPER_BODY_CLOTHING_TERMS):
|
||||
continue
|
||||
kept.append(fragment)
|
||||
if not kept:
|
||||
return ""
|
||||
deduped = []
|
||||
seen = set()
|
||||
for fragment in kept:
|
||||
key = re.sub(r"\W+", " ", fragment.lower()).strip()
|
||||
if key and key not in seen:
|
||||
deduped.append(fragment)
|
||||
seen.add(key)
|
||||
return ", ".join(deduped)
|
||||
|
||||
|
||||
def hardcore_clothing_state(
|
||||
mode: str,
|
||||
softcore_outfit: str,
|
||||
continuity_map: dict[str, str],
|
||||
woman_access: str = "",
|
||||
) -> str:
|
||||
mode = mode if mode in continuity_map else "none"
|
||||
outfit = str(softcore_outfit or "").strip()
|
||||
if mode == "none" or not outfit:
|
||||
return ""
|
||||
base = continuity_map[mode]
|
||||
if mode == "explicit_nude":
|
||||
return f"Body exposure: {base}."
|
||||
if mode == "implied_nude":
|
||||
return f"Body exposure: {base}."
|
||||
if mode == "partially_removed" and woman_access == "lower":
|
||||
detail = _outfit_without_lower_body_blockers(outfit)
|
||||
base = "Woman A's lower body is clear; any lower garment is pulled aside or removed below the hips"
|
||||
if detail:
|
||||
return f"Clothing state: {base}; visible remaining styling: {detail}."
|
||||
return f"Clothing state: {base}."
|
||||
if mode == "partially_removed" and woman_access == "upper":
|
||||
detail = _outfit_without_upper_body_blockers(outfit)
|
||||
base = "Woman A's breasts and upper body are clear; any bra cup, bodice, or top panel is pulled aside or removed"
|
||||
if detail:
|
||||
return f"Clothing state: {base}; visible remaining styling: {detail}."
|
||||
return f"Clothing state: {base}."
|
||||
if mode == "partially_removed":
|
||||
return f"Clothing state: Woman A keeps the outfit mostly on; teaser outfit detail: {outfit}."
|
||||
return f"Clothing state: {base}; teaser outfit detail: {outfit}."
|
||||
|
||||
|
||||
def default_man_hardcore_clothing_entries(
|
||||
men_count: int,
|
||||
pov_labels: list[str] | None,
|
||||
configured_entries: list[str],
|
||||
rng: Any,
|
||||
needs_lower_access: bool,
|
||||
choose: Callable[[Any, list[str]], str],
|
||||
sentence_builder: Callable[[str, str], str],
|
||||
) -> list[str]:
|
||||
pov_set = set(pov_labels or [])
|
||||
configured_labels = {
|
||||
match.group(1)
|
||||
for entry in configured_entries
|
||||
for match in [re.match(r"^\s*(Man [A-Z])\b", str(entry or ""))]
|
||||
if match
|
||||
}
|
||||
pool = INSTA_OF_HARDCORE_MEN_CLOTHING_LOWER_ACCESS if needs_lower_access else INSTA_OF_HARDCORE_MEN_CLOTHING_VISIBLE
|
||||
entries = []
|
||||
for index in range(max(0, int(men_count))):
|
||||
label = f"Man {chr(ord('A') + index)}"
|
||||
if label in pov_set or label in configured_labels:
|
||||
continue
|
||||
entries.append(sentence_builder(label, choose(rng, pool)))
|
||||
return entries
|
||||
|
||||
|
||||
def resolve_hardcore_pair_clothing(
|
||||
*,
|
||||
hard_row: dict[str, Any],
|
||||
mode: str,
|
||||
softcore_outfit: str,
|
||||
character_hardcore_clothing_entries: list[str],
|
||||
men_count: int,
|
||||
pov_labels: list[str] | None,
|
||||
rng: Any,
|
||||
continuity_map: dict[str, str],
|
||||
choose: Callable[[Any, list[str]], str],
|
||||
sentence_builder: Callable[[str, str], str],
|
||||
) -> dict[str, Any]:
|
||||
access_flags = hardcore_row_access_flags(hard_row)
|
||||
woman_access = "lower" if access_flags["woman_lower"] else "upper" if access_flags["woman_upper"] else ""
|
||||
default_man_entries = default_man_hardcore_clothing_entries(
|
||||
men_count,
|
||||
pov_labels,
|
||||
character_hardcore_clothing_entries,
|
||||
rng,
|
||||
access_flags["man_lower"],
|
||||
choose,
|
||||
sentence_builder,
|
||||
)
|
||||
has_primary_hardcore_clothing = any(entry.startswith("Woman A") for entry in character_hardcore_clothing_entries)
|
||||
fallback_state = "" if has_primary_hardcore_clothing else hardcore_clothing_state(
|
||||
mode,
|
||||
softcore_outfit,
|
||||
continuity_map,
|
||||
woman_access=woman_access,
|
||||
)
|
||||
hard_clothing_parts = [
|
||||
part.strip().rstrip(".")
|
||||
for part in (
|
||||
fallback_state,
|
||||
*character_hardcore_clothing_entries,
|
||||
*default_man_entries,
|
||||
)
|
||||
if str(part or "").strip()
|
||||
]
|
||||
hard_clothing_state = "; ".join(hard_clothing_parts)
|
||||
return {
|
||||
"access_flags": access_flags,
|
||||
"woman_access": woman_access,
|
||||
"default_man_hardcore_clothing": default_man_entries,
|
||||
"hardcore_clothing_state": hard_clothing_state,
|
||||
"hardcore_clothing_sentence": f"{hard_clothing_state}. " if hard_clothing_state else "",
|
||||
"requires_body_exposure_scene": (
|
||||
"body is fully exposed" in hard_clothing_state.lower()
|
||||
or "bare skin unobstructed" in hard_clothing_state.lower()
|
||||
),
|
||||
}
|
||||
Reference in New Issue
Block a user