203 lines
7.2 KiB
Python
203 lines
7.2 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any, Callable
|
|
|
|
try:
|
|
from . import cast_context as cast_context_policy
|
|
from . import character_profile as character_profile_policy
|
|
from . import pair_clothing
|
|
from . import pair_options
|
|
except ImportError: # Allows local smoke tests with top-level imports.
|
|
import cast_context as cast_context_policy
|
|
import character_profile as character_profile_policy
|
|
import pair_clothing
|
|
import pair_options
|
|
|
|
|
|
CastDescriptors = Callable[..., list[str]]
|
|
AxisRng = Callable[[dict[str, int], str, int, int], Any]
|
|
Choose = Callable[[Any, list[str]], str]
|
|
SlotSoftcoreOutfit = Callable[[dict[str, Any] | None, Any], str]
|
|
|
|
|
|
def cast_summary_phrase(women_count: int, men_count: int) -> str:
|
|
return cast_context_policy.cast_summary_phrase(women_count, men_count)
|
|
|
|
|
|
def insta_descriptor_from_row(row: dict[str, Any]) -> str:
|
|
return character_profile_policy.descriptor_from_parts(
|
|
"woman",
|
|
row.get("age_band") or row.get("age"),
|
|
row.get("body_phrase"),
|
|
row.get("skin"),
|
|
row.get("hair"),
|
|
row.get("eyes"),
|
|
row.get("descriptor_detail"),
|
|
)
|
|
|
|
|
|
def insta_descriptor_from_context(context: dict[str, Any]) -> str:
|
|
subject = str(context.get("subject") or context.get("subject_type") or "person").strip()
|
|
return character_profile_policy.descriptor_from_parts(
|
|
subject,
|
|
context.get("age"),
|
|
context.get("body_phrase"),
|
|
context.get("skin"),
|
|
context.get("hair"),
|
|
context.get("eyes"),
|
|
context.get("descriptor_detail"),
|
|
)
|
|
|
|
|
|
def prompt_cast_descriptors(text: str) -> str:
|
|
return str(text or "").replace("Woman A / primary creator:", "Woman A:")
|
|
|
|
|
|
def softcore_partner_styling(
|
|
*,
|
|
seed_config: dict[str, int],
|
|
seed: int,
|
|
row_number: int,
|
|
women_count: int,
|
|
men_count: int,
|
|
pov_labels: list[str] | None,
|
|
label_map: dict[str, dict[str, Any]] | None,
|
|
axis_rng: AxisRng,
|
|
choose: Choose,
|
|
slot_softcore_outfit: SlotSoftcoreOutfit,
|
|
) -> dict[str, Any]:
|
|
content_rng = axis_rng(seed_config, "content", seed, row_number + 421)
|
|
pose_rng = axis_rng(seed_config, "pose", seed, row_number + 421)
|
|
pov_set = set(pov_labels or [])
|
|
outfits: list[str] = []
|
|
for index in range(max(0, women_count - 1)):
|
|
label = chr(ord("B") + index)
|
|
full_label = f"Woman {label}"
|
|
outfit = slot_softcore_outfit((label_map or {}).get(full_label), content_rng) or choose(
|
|
content_rng,
|
|
pair_options.INSTA_OF_SOFTCORE_PARTNER_WOMEN_OUTFITS,
|
|
)
|
|
sentence = pair_clothing.softcore_outfit_sentence(full_label, outfit)
|
|
if sentence:
|
|
outfits.append(sentence)
|
|
for index in range(max(0, men_count)):
|
|
label = chr(ord("A") + index)
|
|
full_label = f"Man {label}"
|
|
if full_label in pov_set:
|
|
continue
|
|
outfit = slot_softcore_outfit((label_map or {}).get(full_label), content_rng) or choose(
|
|
content_rng,
|
|
pair_options.INSTA_OF_SOFTCORE_PARTNER_MEN_OUTFITS,
|
|
)
|
|
sentence = pair_clothing.softcore_outfit_sentence(full_label, outfit)
|
|
if sentence:
|
|
outfits.append(sentence)
|
|
return {
|
|
"outfits": outfits,
|
|
"pose": choose(pose_rng, pair_options.SOFTCORE_CAST_POSES),
|
|
}
|
|
|
|
|
|
def resolve_insta_pair_cast_context(
|
|
*,
|
|
soft_row: dict[str, Any],
|
|
options: dict[str, Any],
|
|
parsed_seed_config: dict[str, int],
|
|
seed: int,
|
|
row_number: int,
|
|
ethnicity: str,
|
|
figure: str,
|
|
no_plus_women: bool,
|
|
no_black: bool,
|
|
hard_women_count: int,
|
|
hard_men_count: int,
|
|
character_slots: list[dict[str, Any]],
|
|
character_slot_map: dict[str, dict[str, Any]],
|
|
pov_character_labels: list[str],
|
|
platform_styles: dict[str, str],
|
|
soft_levels: dict[str, str],
|
|
hardcore_levels: dict[str, str],
|
|
build_cast_descriptors: CastDescriptors,
|
|
axis_rng: AxisRng,
|
|
choose: Choose,
|
|
slot_softcore_outfit: SlotSoftcoreOutfit,
|
|
) -> dict[str, Any]:
|
|
descriptor = insta_descriptor_from_row(soft_row)
|
|
cast_descriptors = build_cast_descriptors(
|
|
descriptor,
|
|
parsed_seed_config,
|
|
seed,
|
|
row_number,
|
|
ethnicity,
|
|
figure,
|
|
no_plus_women,
|
|
no_black,
|
|
hard_women_count,
|
|
hard_men_count,
|
|
character_slots,
|
|
)
|
|
cast_descriptor_text = prompt_cast_descriptors("; ".join(cast_descriptors))
|
|
same_softcore_cast = options["softcore_cast"] == "same_as_hardcore"
|
|
soft_cast_descriptor_text = cast_descriptor_text if same_softcore_cast else f"Woman A: {descriptor}"
|
|
soft_partner_styling = softcore_partner_styling(
|
|
seed_config=parsed_seed_config,
|
|
seed=seed,
|
|
row_number=row_number,
|
|
women_count=hard_women_count if same_softcore_cast else 1,
|
|
men_count=hard_men_count if same_softcore_cast else 0,
|
|
pov_labels=pov_character_labels if same_softcore_cast else [],
|
|
label_map=character_slot_map,
|
|
axis_rng=axis_rng,
|
|
choose=choose,
|
|
slot_softcore_outfit=slot_softcore_outfit,
|
|
)
|
|
if not same_softcore_cast:
|
|
soft_partner_styling = {"outfits": [], "pose": ""}
|
|
soft_partner_outfit_text = "; ".join(soft_partner_styling["outfits"])
|
|
|
|
soft_cast = (
|
|
"solo creator setup with Woman A alone"
|
|
if options["softcore_cast"] == "solo"
|
|
else f"soft creator-teaser setup with {cast_summary_phrase(hard_women_count, hard_men_count)}"
|
|
)
|
|
soft_cast_presence = (
|
|
(
|
|
"Frame Woman A from the POV participant's first-person camera in a soft creator-teaser setup; "
|
|
"keep the POV participant off-camera as the viewpoint and implied by camera perspective or foreground cues. "
|
|
)
|
|
if same_softcore_cast and pov_character_labels
|
|
else (
|
|
"Place Woman A and the listed partners together in a soft creator-teaser pose. "
|
|
if same_softcore_cast
|
|
else "Keep the softcore version focused on Woman A alone. "
|
|
)
|
|
)
|
|
soft_cast_styling_sentence = (
|
|
f"Partner softcore styling: {soft_partner_outfit_text}. Cast pose: {soft_partner_styling['pose']}. "
|
|
if same_softcore_cast and soft_partner_outfit_text
|
|
else ""
|
|
)
|
|
hard_cast = cast_summary_phrase(hard_women_count, hard_men_count)
|
|
soft_descriptor_sentence = (
|
|
f"Cast descriptors: {soft_cast_descriptor_text}. "
|
|
if same_softcore_cast
|
|
else f"Woman A: {descriptor}. "
|
|
)
|
|
|
|
return {
|
|
"descriptor": descriptor,
|
|
"cast_descriptors": cast_descriptors,
|
|
"cast_descriptor_text": cast_descriptor_text,
|
|
"soft_cast_descriptor_text": soft_cast_descriptor_text,
|
|
"soft_partner_styling": soft_partner_styling,
|
|
"soft_partner_outfit_text": soft_partner_outfit_text,
|
|
"platform_style": platform_styles[options["platform_style"]],
|
|
"soft_level": soft_levels[options["softcore_level"]],
|
|
"hard_level": hardcore_levels[options["hardcore_level"]],
|
|
"soft_cast": soft_cast,
|
|
"soft_cast_presence": soft_cast_presence,
|
|
"soft_cast_styling_sentence": soft_cast_styling_sentence,
|
|
"hard_cast": hard_cast,
|
|
"soft_descriptor_sentence": soft_descriptor_sentence,
|
|
}
|