Add chainable character slot controls
This commit is contained in:
+84
-13
@@ -14,6 +14,8 @@ PROMPT_FIELD_LABELS = (
|
||||
"Ages",
|
||||
"Body types",
|
||||
"Cast",
|
||||
"Cast descriptors",
|
||||
"Characters",
|
||||
"Scene",
|
||||
"Setting",
|
||||
"Pose",
|
||||
@@ -168,6 +170,65 @@ def _prompt_cast_descriptors(text: str) -> str:
|
||||
return _clean(text).replace("Woman A / primary creator:", "Woman A:")
|
||||
|
||||
|
||||
def _cast_entries(text: str) -> list[tuple[str, str]]:
|
||||
text = _prompt_cast_descriptors(text)
|
||||
entries: list[tuple[str, str]] = []
|
||||
for part in text.split(";"):
|
||||
part = _clean(part)
|
||||
match = re.match(r"^((?:Woman|Man) [A-Z]):\s*(.+)$", part)
|
||||
if match:
|
||||
entries.append((match.group(1), _clean(match.group(2))))
|
||||
return entries
|
||||
|
||||
|
||||
def _label_join(labels: list[str]) -> str:
|
||||
labels = [_clean(label) for label in labels if _clean(label)]
|
||||
if not labels:
|
||||
return "the named adults"
|
||||
if len(labels) == 1:
|
||||
return labels[0]
|
||||
if len(labels) == 2:
|
||||
return f"{labels[0]} and {labels[1]}"
|
||||
return f"{', '.join(labels[:-1])}, and {labels[-1]}"
|
||||
|
||||
|
||||
def _cast_prose(text: str, central_label: str = "Woman A") -> tuple[str, list[str]]:
|
||||
entries = _cast_entries(text)
|
||||
if not entries:
|
||||
return (f"{central_label} is {_clean(text)}" if _clean(text) else "", [])
|
||||
labels = [label for label, _descriptor in entries]
|
||||
count_phrase = "one named adult" if len(entries) == 1 else f"{len(entries)} named adults"
|
||||
sentences = [f"The scene contains {count_phrase}."]
|
||||
for label, descriptor in entries:
|
||||
sentences.append(f"{label} is {descriptor}.")
|
||||
if central_label in labels:
|
||||
sentences.append(f"{central_label} is the central subject.")
|
||||
return " ".join(sentences), labels
|
||||
|
||||
|
||||
def _sanitize_scene_text_for_cast(text: Any, labels: list[str]) -> str:
|
||||
text = _clean(text)
|
||||
if not text:
|
||||
return ""
|
||||
if len(labels) < 3:
|
||||
text = re.sub(r"\s*(?:while|as)\s+another partner watches\b", "", text, flags=re.IGNORECASE)
|
||||
text = re.sub(r"\banother partner watches\b", "", text, flags=re.IGNORECASE)
|
||||
text = re.sub(r"\bwhile blowjob\b", "during a blowjob", text, flags=re.IGNORECASE)
|
||||
text = re.sub(r"\bfeaturing blowjob\b", "featuring a blowjob", text, flags=re.IGNORECASE)
|
||||
text = re.sub(r"\s+,", ",", text)
|
||||
text = re.sub(r"\s{2,}", " ", text).strip(" ,")
|
||||
return text
|
||||
|
||||
|
||||
def _natural_clothing_state(text: Any) -> str:
|
||||
text = _clean(text)
|
||||
if not text:
|
||||
return ""
|
||||
text = re.sub(r"^Clothing state:\s*", "", text, flags=re.IGNORECASE)
|
||||
text = re.sub(r";\s*softcore visual reference:\s*", ". Softcore visual reference: ", text, flags=re.IGNORECASE)
|
||||
return text
|
||||
|
||||
|
||||
def _clean_age(age: Any) -> str:
|
||||
return _clean(age)
|
||||
|
||||
@@ -292,10 +353,17 @@ def _normal_row_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
||||
if subject_type == "configured_cast" or _clean(row.get("cast_summary")):
|
||||
subject = _clean(row.get("subject_phrase") or primary or "adult sexual scene")
|
||||
cast = _clean(row.get("cast_summary"))
|
||||
cast_descriptor_text = (
|
||||
_clean(row.get("cast_descriptor_text"))
|
||||
or _prompt_field(_clean(row.get("prompt")), "Characters")
|
||||
or _prompt_field(_clean(row.get("prompt")), "Cast descriptors")
|
||||
)
|
||||
cast_prose, _cast_labels = _cast_prose(cast_descriptor_text)
|
||||
role_graph = _clean(row.get("role_graph"))
|
||||
parts = [
|
||||
f"A consensual explicit adult scene with {subject}, all participants 21+ and visibly adult",
|
||||
f"The cast includes {cast}" if cast else "",
|
||||
f"A consensual explicit adult scene with {subject}",
|
||||
cast_prose,
|
||||
f"The cast includes {cast}" if cast and not cast_prose else "",
|
||||
role_graph,
|
||||
f"The sexual action is {item}" if item else "",
|
||||
f"The setting is {scene}" if scene else "",
|
||||
@@ -380,9 +448,13 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
||||
if options.get("softcore_cast") == "same_as_hardcore"
|
||||
else f"Woman A: {descriptor}"
|
||||
)
|
||||
soft_cast_prose, soft_labels = _cast_prose(soft_cast_descriptor_text)
|
||||
hard_cast_prose, hard_labels = _cast_prose(cast_descriptor_text)
|
||||
hard_item = _sanitize_scene_text_for_cast(hard.get("item"), hard_labels)
|
||||
hard_role_graph = _sanitize_scene_text_for_cast(hard.get("role_graph"), hard_labels)
|
||||
same_soft_cast = options.get("softcore_cast") == "same_as_hardcore"
|
||||
soft_cast_presence = (
|
||||
"Woman A and the listed partners are present together in a non-explicit teaser pose, with no sex act or genital contact"
|
||||
f"{_label_join(soft_labels)} are together in a non-explicit teaser pose, with no sex act or genital contact"
|
||||
if same_soft_cast
|
||||
else "The softcore version focuses on Woman A alone"
|
||||
)
|
||||
@@ -396,12 +468,11 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
||||
partner_pose = ""
|
||||
|
||||
soft_parts = [
|
||||
f"Cast descriptors: {soft_cast_descriptor_text}" if same_soft_cast and soft_cast_descriptor_text else "",
|
||||
soft_cast_descriptor_text if not same_soft_cast and soft_cast_descriptor_text else "",
|
||||
f"Softcore {soft_level or 'creator'} Insta/OF image",
|
||||
soft_cast_prose,
|
||||
soft_cast_presence,
|
||||
f"Partner softcore styling: {partner_outfit_text}" if partner_outfit_text else "",
|
||||
f"Cast pose: {partner_pose}" if partner_pose else "",
|
||||
f"shown in a {soft_level or 'softcore'} Insta/OF creator image",
|
||||
partner_outfit_text,
|
||||
f"The cast is {partner_pose}" if partner_pose else "",
|
||||
f"wearing {soft.get('item')}" if soft.get("item") else "",
|
||||
f"{soft.get('pose')}" if soft.get("pose") else "",
|
||||
f"with {soft.get('expression')}" if soft.get("expression") else "",
|
||||
@@ -411,11 +482,11 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
||||
soft_style if detail_level != "concise" else "",
|
||||
]
|
||||
hard_parts = [
|
||||
f"{hard_level or 'hardcore'} scene with Woman A visually central",
|
||||
f"Cast descriptors: {cast_descriptor_text}" if cast_descriptor_text else "",
|
||||
_clean(row.get("hardcore_clothing_state")),
|
||||
_clean(hard.get("role_graph")),
|
||||
f"The sexual action is {hard.get('item')}" if hard.get("item") else "",
|
||||
f"{hard_level or 'hardcore'} scene",
|
||||
hard_cast_prose,
|
||||
_natural_clothing_state(row.get("hardcore_clothing_state")),
|
||||
hard_role_graph,
|
||||
f"The explicit detail shows {hard_item}" if hard_item else "",
|
||||
f"set in {hard_scene}" if hard_scene else "",
|
||||
f"with {hard.get('expression')}" if hard.get("expression") else "",
|
||||
f"framed as {hard_composition}" if hard_composition else "",
|
||||
|
||||
Reference in New Issue
Block a user