79 lines
2.8 KiB
Python
79 lines
2.8 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
from typing import Any
|
|
|
|
|
|
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 pov_labels_from_value(value: Any) -> list[str]:
|
|
labels: list[str] = []
|
|
if isinstance(value, list):
|
|
candidates = value
|
|
else:
|
|
candidates = re.split(r"[,;]\s*", _clean(value)) if _clean(value) else []
|
|
for candidate in candidates:
|
|
label = _clean(candidate)
|
|
if re.match(r"^Man [A-Z]$", label) and label not in labels:
|
|
labels.append(label)
|
|
return labels
|
|
|
|
|
|
def merge_labels(*groups: list[str]) -> list[str]:
|
|
merged: list[str] = []
|
|
for group in groups:
|
|
for label in group:
|
|
if label and label not in merged:
|
|
merged.append(label)
|
|
return merged
|
|
|
|
|
|
def filter_pov_labeled_clauses(text: Any, pov_labels: list[str]) -> str:
|
|
rendered = _clean(text)
|
|
if not rendered or not pov_labels:
|
|
return rendered
|
|
clauses = [clause.strip() for clause in rendered.split(";") if clause.strip()]
|
|
filtered = [
|
|
clause
|
|
for clause in clauses
|
|
if not any(re.match(rf"^{re.escape(label)}\b", clause) for label in pov_labels)
|
|
]
|
|
return "; ".join(filtered)
|
|
|
|
|
|
def pov_camera_phrase(pov_labels: list[str], softcore: bool = False) -> str:
|
|
if not pov_labels:
|
|
return ""
|
|
if softcore:
|
|
return (
|
|
"Camera is the male participant's first-person creator view in one continuous frame, with him implied by perspective or foreground cues"
|
|
)
|
|
return (
|
|
"Camera is the male participant's first-person view in one continuous frame; only his foreground hands or body cues appear"
|
|
)
|
|
|
|
|
|
def pov_composition_text(composition: Any, pov_labels: list[str]) -> str:
|
|
text = _clean(composition)
|
|
if not text or not pov_labels:
|
|
return text
|
|
text = re.sub(r"\ball participants visible\b", "visible partners readable", text, flags=re.IGNORECASE)
|
|
text = re.sub(r"\ball adult bodies visible\b", "visible partners readable", text, flags=re.IGNORECASE)
|
|
text = re.sub(r"\ball bodies visible\b", "visible partners readable", text, flags=re.IGNORECASE)
|
|
text = re.sub(r"\ball three bodies readable\b", "visible partner bodies readable", text, flags=re.IGNORECASE)
|
|
text = re.sub(r"\bwide group-sex composition\b", "first-person group-sex POV composition", text, flags=re.IGNORECASE)
|
|
text = re.sub(
|
|
r",?\s*adapted for first-person POV with the POV participant kept off-camera\b",
|
|
"",
|
|
text,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
text = re.sub(r",?\s*with the POV participant kept off-camera\b", "", text, flags=re.IGNORECASE)
|
|
return _clean(text)
|