from __future__ import annotations import re from dataclasses import dataclass from typing import Any, Callable try: from . import formatter_input as input_policy from . import formatter_target as target_policy except ImportError: # pragma: no cover - plain-script smoke tests import formatter_input as input_policy import formatter_target as target_policy @dataclass(frozen=True) class CaptionMetadataRouteRequest: row: dict[str, Any] detail_level: str keep_style: bool target: str = "auto" @dataclass(frozen=True) class CaptionMetadataRoute: prose: str method: str def as_tuple(self) -> tuple[str, str]: return self.prose, self.method @dataclass(frozen=True) class CaptionMetadataRouteDependencies: item_labels: tuple[str, ...] clean_text: Callable[[Any], str] row_value: Callable[[dict[str, Any], str, tuple[str, ...]], str] field_row_value: Callable[[dict[str, Any], str], str] clean_clothing: Callable[[str], str] normalize_composition: Callable[[str], str] expression_disabled: Callable[[dict[str, Any]], bool] detail_allows: Callable[..., bool] join_sentences: Callable[[list[str]], str] human_join: Callable[[list[str]], str] article: Callable[[str], str] cap_first: Callable[[str], str] body_phrase: Callable[[Any, Any], str] single_caption_front: Callable[[dict[str, Any]], dict[str, str]] pose_clause: Callable[[str], str] age_subject: Callable[[str, str], str] clean_age_phrase: Callable[[str], str] subject_phrase_from_counts: Callable[[dict[str, Any]], str] verb_for_row: Callable[[dict[str, Any]], str] metadata_action_label: Callable[[dict[str, Any]], str] natural_cast_descriptor_text: Callable[[str], str] cast_labels: Callable[[str], list[str]] natural_label_text: Callable[[Any, list[str]], str] softcore_caption_setup_phrase: Callable[..., str] metadata_to_prose: Callable[..., tuple[str, str]] def pronoun(subject: str) -> str: return "She" if subject == "woman" else "He" def possessive_pronoun(subject: str) -> str: return "Her" if subject == "woman" else "His" def couple_clothing_sentence(clothing: str, clean_text: Callable[[Any], str]) -> str: clothing = clean_text(clothing) lower = clothing.lower() partner_text = re.sub(r"\bPartner ([AB]) wears\b", r"Partner \1 wearing", clothing) partner_text = re.sub(r"\bPartner ([AB]) has\b", r"Partner \1 with", partner_text) if lower.startswith("partner a "): return f"The outfits show {partner_text}" if lower.startswith(("two ", "paired ", "coordinated ")): return f"The outfits are {partner_text}" return f"They wear {clothing}" def couple_subject_sentence( subject: str, ages: str, cap_first: Callable[[str], str], clean_age_phrase: Callable[[str], str], ) -> str: subject = cap_first(subject or "adult couple") ages = clean_age_phrase(ages) if ages: return f"{subject}, {ages}" if subject.lower() == "adult couple": return subject return f"{subject} are adults" def single_from_row_result( request: CaptionMetadataRouteRequest, deps: CaptionMetadataRouteDependencies, ) -> CaptionMetadataRoute | None: row = request.row detail_level = request.detail_level keep_style = request.keep_style subject = deps.clean_text(row.get("primary_subject") or row.get("subject") or "") if subject not in ("woman", "man"): return None caption_front = deps.single_caption_front(row) age = deps.clean_text(row.get("age") or row.get("age_band") or caption_front.get("caption_age") or "") body_phrase = deps.field_row_value(row, "body_phrase") or caption_front.get("caption_body_phrase", "") if not body_phrase: body = deps.clean_text(row.get("body_type") or row.get("body") or "") figure = deps.clean_text(row.get("figure")) body_phrase = deps.body_phrase(body, figure) skin = deps.field_row_value(row, "skin") or caption_front.get("caption_skin", "") hair = deps.field_row_value(row, "hair") or caption_front.get("caption_hair", "") eyes = deps.field_row_value(row, "eyes") or caption_front.get("caption_eyes", "") item = deps.row_value(row, "item", deps.item_labels) if item: item = deps.clean_clothing(item) if not item: item = deps.clean_clothing(deps.row_value(row, "clothing", ("Clothing", "Erotic outfit"))) scene = deps.row_value(row, "scene_text", ("Scene", "Setting")) pose = deps.row_value(row, "pose", ("Pose",)) expression = "" if deps.expression_disabled(row) else deps.row_value(row, "expression", ("Facial expression", "Facial expressions")) composition = deps.normalize_composition(deps.row_value(row, "composition", ("Composition",))) camera_scene = deps.clean_text(row.get("camera_scene_directive")) prop = deps.row_value(row, "prop", ("Prop/detail",)) style = deps.field_row_value(row, "style") if keep_style else "" parts = [] opener = deps.age_subject(age, subject) appearance_details = [piece for piece in (skin, hair, eyes) if piece] if body_phrase: parts.append(f"{opener} has {deps.article(body_phrase)} {body_phrase}") elif appearance_details: parts.append(f"{opener} has {deps.human_join(appearance_details)}") else: parts.append(opener) if body_phrase and appearance_details: parts.append(f"{pronoun(subject)} has {deps.human_join(appearance_details)}") if item: verb = "wears" if subject == "woman" else "is dressed in" parts.append(f"{pronoun(subject)} {verb} {item}") if prop: parts.append(f"{pronoun(subject)} is {prop}") if pose: parts.append(f"{pronoun(subject)} is {deps.pose_clause(pose)}") if expression: parts.append(f"{possessive_pronoun(subject)} expression is {expression}") if scene: parts.append(f"The setting is {scene}") if deps.detail_allows(detail_level) and camera_scene: parts.append(camera_scene) if deps.detail_allows(detail_level) and composition: parts.append(f"The composition is {composition}") if keep_style and style: parts.append(f"The visual style is {style}") return CaptionMetadataRoute(deps.join_sentences(parts), "metadata(single)") def couple_from_row_result( request: CaptionMetadataRouteRequest, deps: CaptionMetadataRouteDependencies, ) -> CaptionMetadataRoute | None: row = request.row detail_level = request.detail_level keep_style = request.keep_style subject = deps.clean_text(row.get("subject_phrase") or row.get("primary_subject")) primary = deps.clean_text(row.get("primary_subject")) if "couple" not in primary and subject not in ("two women", "two men", "a woman and a man"): if not primary.startswith("two ") and " and " not in subject: return None if subject == "woman and man": subject = "a woman and a man" ages = deps.row_value(row, "age", ("Ages",)) or deps.clean_text(row.get("age_band")) body = deps.row_value(row, "body", ("Body types",)) or deps.clean_text(row.get("body_type")) pose = deps.row_value(row, "pose", ("Pose",)) pose = pose.replace(", affectionate and flirtatious but non-explicit", "") clothing = deps.clean_clothing(deps.row_value(row, "item", deps.item_labels) or deps.row_value(row, "clothing", ("Clothing",))) scene = deps.row_value(row, "scene_text", ("Scene", "Setting")) expression = "" if not deps.expression_disabled(row): expression = deps.row_value(row, "character_expression_text") or deps.row_value( row, "expression", ("Facial expressions", "Facial expression"), ) composition = deps.normalize_composition(deps.row_value(row, "composition", ("Composition",))) camera_scene = deps.clean_text(row.get("camera_scene_directive")) style = deps.field_row_value(row, "style") if keep_style else "" parts = [couple_subject_sentence(subject, ages, deps.cap_first, deps.clean_age_phrase)] if body: parts.append(f"Their body types are {body}") if clothing: parts.append(couple_clothing_sentence(clothing, deps.clean_text)) if pose: parts.append(f"The pose is {pose}") if scene: parts.append(f"The setting is {scene}") if deps.detail_allows(detail_level) and camera_scene: parts.append(camera_scene) if expression: parts.append(f"Their expressions are {expression}") if deps.detail_allows(detail_level) and composition: parts.append(f"The composition is {composition}") if keep_style and style: parts.append(f"The visual style is {style}") return CaptionMetadataRoute(deps.join_sentences(parts), "metadata(couple)") def configured_cast_from_row_result( request: CaptionMetadataRouteRequest, deps: CaptionMetadataRouteDependencies, ) -> CaptionMetadataRoute | None: row = request.row detail_level = request.detail_level keep_style = request.keep_style if deps.clean_text(row.get("subject_type")) != "configured_cast": if "hardcore sexual poses" not in deps.clean_text(row.get("main_category")).lower(): return None subject = deps.subject_phrase_from_counts(row) verb = deps.verb_for_row(row) cast = deps.row_value(row, "cast_summary", ("Cast",)) role_graph = deps.row_value(row, "role_graph", ("Role graph",)) item = deps.row_value(row, "item", deps.item_labels) scene = deps.row_value(row, "scene_text", ("Setting", "Scene")) expression = "" if not deps.expression_disabled(row): expression = deps.row_value(row, "character_expression_text") or deps.row_value( row, "expression", ("Facial expressions", "Facial expression"), ) composition = deps.normalize_composition(deps.row_value(row, "composition", ("Composition",))) camera_scene = deps.clean_text(row.get("camera_scene_directive")) cast_descriptor_text = deps.row_value(row, "cast_descriptor_text", ("Characters", "Cast descriptors")) scene_kind = deps.field_row_value(row, "scene_kind") or "explicit adult sex scene" style = deps.field_row_value(row, "style") if keep_style else "" parts = [f"{deps.cap_first(subject)} {verb} shown as a consensual {scene_kind}"] if cast_descriptor_text: parts.append(deps.natural_cast_descriptor_text(cast_descriptor_text)) if cast and not cast_descriptor_text: parts.append(f"The cast is {cast}") if role_graph: parts.append(role_graph) if item: parts.append(f"The {deps.metadata_action_label(row)} is {item}") scene_bits = [] if scene: scene_bits.append(f"set in {scene}") if expression: scene_bits.append(f"with {expression}") if composition: scene_bits.append(f"framed as {composition}") if scene_bits and deps.detail_allows(detail_level): parts.append(", ".join(scene_bits)) if deps.detail_allows(detail_level) and camera_scene: parts.append(camera_scene) if keep_style and style: parts.append(f"The visual style is {style}") return CaptionMetadataRoute(deps.join_sentences(parts), "metadata(configured_cast)") def group_or_layout_from_row_result( request: CaptionMetadataRouteRequest, deps: CaptionMetadataRouteDependencies, ) -> CaptionMetadataRoute | None: row = request.row detail_level = request.detail_level keep_style = request.keep_style primary = deps.clean_text(row.get("primary_subject")) if "group" not in primary and primary != "layout scene": return None subject = deps.field_row_value(row, "subject_phrase") or primary age = deps.row_value(row, "age", ("Ages",)) or deps.clean_text(row.get("age_band")) item = deps.clean_clothing(deps.row_value(row, "item", deps.item_labels) or deps.row_value(row, "clothing", ("Clothing",))) scene = deps.row_value(row, "scene_text", ("Scene", "Setting")) expression = "" if not deps.expression_disabled(row): expression = deps.row_value(row, "character_expression_text") or deps.row_value( row, "expression", ("Facial expressions", "Facial expression"), ) composition = deps.normalize_composition(deps.row_value(row, "composition", ("Composition",))) camera_scene = deps.clean_text(row.get("camera_scene_directive")) style = deps.field_row_value(row, "style") if keep_style else "" if primary == "layout scene": parts = [f"{deps.cap_first(subject)} is arranged as an adults-only designed illustration layout"] if expression: parts.append(f"The featured expression is {expression}") else: parts = [f"{deps.cap_first(subject)} includes adults"] if age: parts[0] += f" ages {age}" if item: parts.append(f"They wear {item}") if expression: parts.append(f"They show {expression}") if scene: parts.append(f"The setting is {scene}") if deps.detail_allows(detail_level) and camera_scene: parts.append(camera_scene) if deps.detail_allows(detail_level) and composition: parts.append(f"The composition is {composition}") if keep_style and style: parts.append(f"The visual style is {style}") return CaptionMetadataRoute(deps.join_sentences(parts), "metadata(group_layout)") def insta_of_pair_from_row_result( request: CaptionMetadataRouteRequest, deps: CaptionMetadataRouteDependencies, ) -> CaptionMetadataRoute | None: row = request.row detail_level = request.detail_level keep_style = request.keep_style pair_target = target_policy.pair_policy(request.target) target = pair_target.pair_target if not input_policy.is_pair_metadata(row): return None soft_row = row.get("softcore_row") hard_row = row.get("hardcore_row") if not isinstance(soft_row, dict) or not isinstance(hard_row, dict): return None hard_row_for_text = dict(hard_row) options = row.get("options") if isinstance(options, dict) and options.get("continuity") == "same_creator_same_room": if not hard_row_for_text.get("scene_text") and soft_row.get("scene_text"): hard_row_for_text["scene_text"] = soft_row["scene_text"] if not hard_row_for_text.get("composition") and soft_row.get("composition"): hard_row_for_text["composition"] = soft_row["composition"] include_soft = pair_target.include_softcore include_hard = pair_target.include_hardcore soft_text = "" hard_text = "" if include_soft: soft_text, _soft_method = deps.metadata_to_prose(soft_row, detail_level, keep_style, "single") if include_hard: hard_text, _hard_method = deps.metadata_to_prose(hard_row_for_text, detail_level, keep_style, "single") descriptor = deps.clean_text(row.get("shared_descriptor")) options = row.get("options") if isinstance(row.get("options"), dict) else {} cast_descriptors = row.get("shared_cast_descriptors") if isinstance(cast_descriptors, list): cast_descriptor_text = "; ".join(deps.clean_text(item) for item in cast_descriptors if deps.clean_text(item)) else: cast_descriptor_text = deps.clean_text(cast_descriptors) labels = deps.cast_labels(cast_descriptor_text) same_soft_cast = options.get("softcore_cast") == "same_as_hardcore" parts = [] if cast_descriptor_text and same_soft_cast: parts.append(deps.natural_cast_descriptor_text(cast_descriptor_text)) elif descriptor: parts.append(f"A {descriptor}") if cast_descriptor_text and not same_soft_cast: parts.append(deps.natural_cast_descriptor_text(cast_descriptor_text)) if same_soft_cast and include_soft: parts.append( deps.softcore_caption_setup_phrase( same_cast=True, target_auto=target == "auto", ) ) partner_styling = row.get("softcore_partner_styling") if isinstance(partner_styling, dict): outfits = partner_styling.get("outfits") if isinstance(outfits, list): outfit_text = deps.human_join([deps.clean_text(item) for item in outfits if deps.clean_text(item)]) outfit_text = deps.natural_label_text(outfit_text, labels) if outfit_text: parts.append(f"Softcore partner styling: {outfit_text}") pose = deps.clean_text(partner_styling.get("pose")) if pose: parts.append(f"The shared softcore cast pose is {pose}") if soft_text: parts.append(f"Softcore side: {soft_text}" if target == "auto" else soft_text) if hard_text: parts.append(f"Hardcore side: {hard_text}" if target == "auto" else hard_text) if not parts: return None return CaptionMetadataRoute(deps.join_sentences(parts), "metadata(insta_of_pair)") def single_from_row(request: CaptionMetadataRouteRequest, deps: CaptionMetadataRouteDependencies) -> tuple[str, str] | None: result = single_from_row_result(request, deps) return result.as_tuple() if result else None def couple_from_row(request: CaptionMetadataRouteRequest, deps: CaptionMetadataRouteDependencies) -> tuple[str, str] | None: result = couple_from_row_result(request, deps) return result.as_tuple() if result else None def configured_cast_from_row( request: CaptionMetadataRouteRequest, deps: CaptionMetadataRouteDependencies, ) -> tuple[str, str] | None: result = configured_cast_from_row_result(request, deps) return result.as_tuple() if result else None def group_or_layout_from_row( request: CaptionMetadataRouteRequest, deps: CaptionMetadataRouteDependencies, ) -> tuple[str, str] | None: result = group_or_layout_from_row_result(request, deps) return result.as_tuple() if result else None def insta_of_pair_from_row( request: CaptionMetadataRouteRequest, deps: CaptionMetadataRouteDependencies, ) -> tuple[str, str] | None: result = insta_of_pair_from_row_result(request, deps) return result.as_tuple() if result else None