Add prompt control and filter options
This commit is contained in:
@@ -7,6 +7,7 @@ The node is registered as:
|
|||||||
|
|
||||||
- `prompt_builder / SxCP Prompt Builder`
|
- `prompt_builder / SxCP Prompt Builder`
|
||||||
- `prompt_builder / SxCP Seed Control`
|
- `prompt_builder / SxCP Seed Control`
|
||||||
|
- `prompt_builder / SxCP Seed Locker`
|
||||||
- `prompt_builder / SxCP Camera Control`
|
- `prompt_builder / SxCP Camera Control`
|
||||||
- `prompt_builder / SxCP Category Preset`
|
- `prompt_builder / SxCP Category Preset`
|
||||||
- `prompt_builder / SxCP Cast Control`
|
- `prompt_builder / SxCP Cast Control`
|
||||||
@@ -41,8 +42,8 @@ node. For cleaner workflows, use the split nodes:
|
|||||||
- `SxCP Generation Profile` outputs `generation_profile` for common behavior
|
- `SxCP Generation Profile` outputs `generation_profile` for common behavior
|
||||||
presets such as casual-clean, evocative-softcore, hardcore-intense,
|
presets such as casual-clean, evocative-softcore, hardcore-intense,
|
||||||
Krea2-friendly, or Flux-original.
|
Krea2-friendly, or Flux-original.
|
||||||
- `SxCP Advanced Filters` outputs `filter_config` for ethnicity, figure, and
|
- `SxCP Advanced Filters` outputs `filter_config` for appearance include
|
||||||
exclusion filters.
|
checkboxes, figure, and plus-size inclusion.
|
||||||
- `SxCP Prompt Builder From Configs` consumes those config outputs and produces
|
- `SxCP Prompt Builder From Configs` consumes those config outputs and produces
|
||||||
the same prompt, negative, caption, metadata, category, and subcategory
|
the same prompt, negative, caption, metadata, category, and subcategory
|
||||||
outputs as the full builder.
|
outputs as the full builder.
|
||||||
@@ -50,7 +51,7 @@ node. For cleaner workflows, use the split nodes:
|
|||||||
The practical compact workflow is:
|
The practical compact workflow is:
|
||||||
|
|
||||||
`Category Preset` + `Cast Control` + `Generation Profile` + optional
|
`Category Preset` + `Cast Control` + `Generation Profile` + optional
|
||||||
`Advanced Filters`, `Seed Control`, `Camera Control`, and `Character Profile`
|
`Advanced Filters`, `Seed Locker` or `Seed Control`, `Camera Control`, and `Character Profile`
|
||||||
into `Prompt Builder From Configs`.
|
into `Prompt Builder From Configs`.
|
||||||
|
|
||||||
An importable default workflow is included at
|
An importable default workflow is included at
|
||||||
@@ -99,13 +100,18 @@ appearance remains stable.
|
|||||||
`SxCP Seed Control` outputs `seed_config`, which can be connected to the prompt
|
`SxCP Seed Control` outputs `seed_config`, which can be connected to the prompt
|
||||||
builder's optional `seed_config` input.
|
builder's optional `seed_config` input.
|
||||||
|
|
||||||
|
`SxCP Seed Locker` is the fast version for iteration. Set `base_seed` to a seed
|
||||||
|
you like, choose one `reroll_axis`, and connect its `seed_config`. All other
|
||||||
|
axes stay frozen to `base_seed`; the rerolled axis follows `reroll_seed`, or the
|
||||||
|
main prompt seed when `reroll_seed=-1`.
|
||||||
|
|
||||||
`SxCP Camera Control` outputs `camera_config`, which can be connected to the
|
`SxCP Camera Control` outputs `camera_config`, which can be connected to the
|
||||||
prompt builder or the Insta/OF pair node. It makes camera/framing first-class
|
prompt builder or the Insta/OF pair node. It makes camera/framing first-class
|
||||||
instead of relying on a weak phrase inside the prompt.
|
instead of relying on a weak phrase inside the prompt.
|
||||||
|
|
||||||
Camera controls:
|
Camera controls:
|
||||||
|
|
||||||
- `camera_mode`: `standard`, `handheld_selfie`, `mirror_selfie`,
|
- `camera_mode`: `disabled`, `standard`, `handheld_selfie`, `mirror_selfie`,
|
||||||
`phone_tripod`, `creator_pov`, `bed_selfie`, `bathroom_mirror`,
|
`phone_tripod`, `creator_pov`, `bed_selfie`, `bathroom_mirror`,
|
||||||
`phone_flash`, or `action_cam`.
|
`phone_flash`, or `action_cam`.
|
||||||
- `shot_size`: `auto`, `full_body`, `three_quarter`, `waist_up`, `close_up`,
|
- `shot_size`: `auto`, `full_body`, `three_quarter`, `waist_up`, `close_up`,
|
||||||
@@ -119,6 +125,8 @@ Camera controls:
|
|||||||
- `phone_visibility`: `auto`, `phone_visible`, `phone_hidden`,
|
- `phone_visibility`: `auto`, `phone_visible`, `phone_hidden`,
|
||||||
`screen_reflection`, or `ring_light_visible`.
|
`screen_reflection`, or `ring_light_visible`.
|
||||||
- `priority`: `soft_hint`, `strong`, or `locked`.
|
- `priority`: `soft_hint`, `strong`, or `locked`.
|
||||||
|
- `camera_detail`: `off` emits no camera sentence, `compact` emits one short
|
||||||
|
camera sentence, and `full` emits the full detailed camera constraint.
|
||||||
|
|
||||||
`SxCP Caption Naturalizer` rewrites tag-like captions or labeled prompts into
|
`SxCP Caption Naturalizer` rewrites tag-like captions or labeled prompts into
|
||||||
more natural language. Connect the prompt builder's `metadata_json` output to
|
more natural language. Connect the prompt builder's `metadata_json` output to
|
||||||
@@ -177,18 +185,17 @@ It outputs:
|
|||||||
- `method`
|
- `method`
|
||||||
|
|
||||||
`SxCP Insta/OF Prompt Pair` is a special paired-output mode. It creates one
|
`SxCP Insta/OF Prompt Pair` is a special paired-output mode. It creates one
|
||||||
shared primary creator descriptor, then returns both a softcore prompt and a
|
primary creator descriptor internally, then returns both a softcore prompt and a
|
||||||
hardcore prompt from that same descriptor. This is useful when you want the same
|
hardcore prompt from that descriptor. This is useful when you want matching
|
||||||
person/look/scene continuity but need two different prompt strengths.
|
person/look/scene continuity but need two different prompt strengths.
|
||||||
|
|
||||||
When the hardcore cast includes partners, pair mode also creates deterministic
|
When the hardcore cast includes partners, pair mode also creates deterministic
|
||||||
shared cast descriptors such as `woman A / primary creator` and `man A`. Use
|
cast descriptors such as `Woman A` and `Man A`. Use
|
||||||
`softcore_cast=same_as_hardcore`, `hardcore_cast=couple`, and
|
`softcore_cast=same_as_hardcore`, `hardcore_cast=couple`, and
|
||||||
`continuity=same_creator_same_room` when you want a soft prompt with the same
|
`continuity=same_creator_same_room` when you want both outputs to reuse the cast
|
||||||
woman, man, and location as the hardcore prompt. In that setup, the softcore
|
and location. The generated positive prompts are still standalone: each output
|
||||||
prompt keeps the same listed adult cast physically present together in a
|
lists the relevant cast descriptors directly and does not depend on the image
|
||||||
non-explicit teaser pose, with deterministic non-explicit partner outfits and a
|
model carrying context from another prompt.
|
||||||
shared cast pose.
|
|
||||||
|
|
||||||
It outputs:
|
It outputs:
|
||||||
|
|
||||||
@@ -217,10 +224,11 @@ Options:
|
|||||||
- `hardcore_women_count` and `hardcore_men_count`: used when `hardcore_cast` is
|
- `hardcore_women_count` and `hardcore_men_count`: used when `hardcore_cast` is
|
||||||
`use_counts`. The pair mode always keeps at least one adult woman as the
|
`use_counts`. The pair mode always keeps at least one adult woman as the
|
||||||
primary creator so the shared descriptor remains valid.
|
primary creator so the shared descriptor remains valid.
|
||||||
- `softcore_level`: `social_tease`, `lingerie_tease`, `implied_nude`, or
|
- `softcore_level`: `social_tease`, `lingerie_tease`, `implied_nude`,
|
||||||
`explicit_tease`. Insta/OF softcore uses dedicated outfit pools so teaser
|
`explicit_tease`, or `explicit_nude`. Insta/OF softcore uses dedicated outfit
|
||||||
prompts do not randomly pull hardcore-adjacent harness, nude accessory,
|
pools so teaser prompts do not randomly pull hardcore-adjacent harness,
|
||||||
microwear, or shirtless partner styling.
|
microwear, or shirtless partner styling. `explicit_nude` is available when
|
||||||
|
you want visible nude creator-shot framing without a sex act.
|
||||||
- `hardcore_level`: `explicit` or `hardcore`.
|
- `hardcore_level`: `explicit` or `hardcore`.
|
||||||
- `softcore_expression_intensity`: `0.0` is mild/controlled, `0.5` is sensual,
|
- `softcore_expression_intensity`: `0.0` is mild/controlled, `0.5` is sensual,
|
||||||
`1.0` strongly favors more heated softcore faces.
|
`1.0` strongly favors more heated softcore faces.
|
||||||
@@ -228,12 +236,17 @@ Options:
|
|||||||
hardcore, `1.0` strongly favors ahegao-style, drooling, fucked-out, climax,
|
hardcore, `1.0` strongly favors ahegao-style, drooling, fucked-out, climax,
|
||||||
and messy orgasm expressions.
|
and messy orgasm expressions.
|
||||||
- `platform_style`: `hybrid`, `instagram`, or `onlyfans`.
|
- `platform_style`: `hybrid`, `instagram`, or `onlyfans`.
|
||||||
- `continuity`: `same_creator_same_room` keeps the scene/composition aligned;
|
- `continuity`: `same_creator_same_room` keeps the scene aligned while each
|
||||||
`same_creator_new_scene` keeps the same creator descriptor but lets the
|
output keeps its own pose/composition; `same_creator_new_scene` keeps the same
|
||||||
hardcore scene use its own setting.
|
creator descriptor but lets the hardcore scene use its own setting.
|
||||||
|
- `hardcore_clothing_continuity`: `none`, `same_outfit`, `partially_removed`,
|
||||||
|
`implied_nude`, or `explicit_nude`. This controls whether the hardcore prompt
|
||||||
|
references the softcore outfit, uses it displaced/removed, or makes Woman A
|
||||||
|
explicitly nude.
|
||||||
- `softcore_camera_mode`: base camera mode for the softcore output.
|
- `softcore_camera_mode`: base camera mode for the softcore output.
|
||||||
- `hardcore_camera_mode`: `same_as_softcore` or a separate base camera mode for
|
- `hardcore_camera_mode`: `same_as_softcore` or a separate base camera mode for
|
||||||
the hardcore output.
|
the hardcore output.
|
||||||
|
- `camera_detail`: `off`, `compact`, or `full` for the pair prompt camera text.
|
||||||
|
|
||||||
## Built-In Categories
|
## Built-In Categories
|
||||||
|
|
||||||
@@ -242,7 +255,10 @@ The node keeps the original generator controls:
|
|||||||
- `category`: `auto_weighted`, `woman`, `man`, `couple`, `group_or_layout`, or a custom JSON category.
|
- `category`: `auto_weighted`, `woman`, `man`, `couple`, `group_or_layout`, or a custom JSON category.
|
||||||
- `clothing`: `full` or `minimal`.
|
- `clothing`: `full` or `minimal`.
|
||||||
- `minimal_clothing_ratio`: `-1` disables mixing; `0.0` to `1.0` mixes minimal/full clothing.
|
- `minimal_clothing_ratio`: `-1` disables mixing; `0.0` to `1.0` mixes minimal/full clothing.
|
||||||
- `ethnicity`: `any`, `asian`, `white_asian`.
|
- `ethnicity`: `any`, `european`, `mediterranean_mena`, `latina`,
|
||||||
|
`east_asian`, `southeast_asian`, `south_asian`, `black_african`,
|
||||||
|
`indigenous`, `mixed`, `asian`, or `white_asian`. Combined filter strings
|
||||||
|
such as `latina+south_asian` are also accepted in config JSON.
|
||||||
- `poses`: `standard` or `evocative`.
|
- `poses`: `standard` or `evocative`.
|
||||||
- `expression_intensity`: `0.0` favors mild, neutral, controlled expressions;
|
- `expression_intensity`: `0.0` favors mild, neutral, controlled expressions;
|
||||||
`0.5` favors balanced category expressions; `1.0` strongly favors the most
|
`0.5` favors balanced category expressions; `1.0` strongly favors the most
|
||||||
@@ -252,8 +268,8 @@ The node keeps the original generator controls:
|
|||||||
- `standard_pose_ratio`: `-1` disables mixing; `0.0` to `1.0` mixes standard/evocative poses.
|
- `standard_pose_ratio`: `-1` disables mixing; `0.0` to `1.0` mixes standard/evocative poses.
|
||||||
- `backside_bias`: `0.0` to `1.0`, applies to evocative single-subject poses.
|
- `backside_bias`: `0.0` to `1.0`, applies to evocative single-subject poses.
|
||||||
- `figure`: `curvy`, `balanced`, `bombshell`.
|
- `figure`: `curvy`, `balanced`, `bombshell`.
|
||||||
- `no_plus_women`: excludes plus-size women.
|
- In split workflows, use `SxCP Advanced Filters` checkboxes instead of negative
|
||||||
- `no_black`: excludes Black/African-coded women from women-focused pools.
|
toggles. Black/African and plus-size are positive include choices there.
|
||||||
- Optional `camera_config`: connect `SxCP Camera Control` to force selfie,
|
- Optional `camera_config`: connect `SxCP Camera Control` to force selfie,
|
||||||
phone, lens, angle, distance, crop, and camera-priority behavior. This applies
|
phone, lens, angle, distance, crop, and camera-priority behavior. This applies
|
||||||
to custom categories too, including `Hardcore sexual poses`.
|
to custom categories too, including `Hardcore sexual poses`.
|
||||||
@@ -475,6 +491,14 @@ alone.
|
|||||||
The main `seed` input is still the default master seed. Connect `SxCP Seed
|
The main `seed` input is still the default master seed. Connect `SxCP Seed
|
||||||
Control` to `seed_config` when you want to lock or vary specific axes.
|
Control` to `seed_config` when you want to lock or vary specific axes.
|
||||||
|
|
||||||
|
For normal prompt iteration, `SxCP Seed Locker` is usually simpler:
|
||||||
|
|
||||||
|
- `base_seed`: the seed whose character/location/etc. you want to keep.
|
||||||
|
- `reroll_axis`: `none`, `content`, `person`, `scene`, `pose`, `role`,
|
||||||
|
`expression`, `composition`, `content_pose`, or `scene_pose`.
|
||||||
|
- `reroll_seed`: `-1` makes the selected axis follow the main prompt seed;
|
||||||
|
`0` or higher pins that selected axis to a specific seed.
|
||||||
|
|
||||||
Seed values:
|
Seed values:
|
||||||
|
|
||||||
- `-1`: follow the main seed.
|
- `-1`: follow the main seed.
|
||||||
|
|||||||
+101
-18
@@ -15,7 +15,9 @@ try:
|
|||||||
build_prompt,
|
build_prompt,
|
||||||
build_prompt_from_configs,
|
build_prompt_from_configs,
|
||||||
build_seed_config_json,
|
build_seed_config_json,
|
||||||
|
build_seed_lock_config_json,
|
||||||
camera_angle_choices,
|
camera_angle_choices,
|
||||||
|
camera_detail_choices,
|
||||||
camera_distance_choices,
|
camera_distance_choices,
|
||||||
camera_lens_choices,
|
camera_lens_choices,
|
||||||
camera_mode_choices,
|
camera_mode_choices,
|
||||||
@@ -27,6 +29,7 @@ try:
|
|||||||
category_preset_choices,
|
category_preset_choices,
|
||||||
category_choices,
|
category_choices,
|
||||||
character_profile_choices,
|
character_profile_choices,
|
||||||
|
ethnicity_choices,
|
||||||
generation_profile_choices,
|
generation_profile_choices,
|
||||||
load_character_profile_json,
|
load_character_profile_json,
|
||||||
subcategory_choices,
|
subcategory_choices,
|
||||||
@@ -46,7 +49,9 @@ except ImportError:
|
|||||||
build_prompt,
|
build_prompt,
|
||||||
build_prompt_from_configs,
|
build_prompt_from_configs,
|
||||||
build_seed_config_json,
|
build_seed_config_json,
|
||||||
|
build_seed_lock_config_json,
|
||||||
camera_angle_choices,
|
camera_angle_choices,
|
||||||
|
camera_detail_choices,
|
||||||
camera_distance_choices,
|
camera_distance_choices,
|
||||||
camera_lens_choices,
|
camera_lens_choices,
|
||||||
camera_mode_choices,
|
camera_mode_choices,
|
||||||
@@ -58,6 +63,7 @@ except ImportError:
|
|||||||
category_preset_choices,
|
category_preset_choices,
|
||||||
category_choices,
|
category_choices,
|
||||||
character_profile_choices,
|
character_profile_choices,
|
||||||
|
ethnicity_choices,
|
||||||
generation_profile_choices,
|
generation_profile_choices,
|
||||||
load_character_profile_json,
|
load_character_profile_json,
|
||||||
subcategory_choices,
|
subcategory_choices,
|
||||||
@@ -77,13 +83,11 @@ class SxCPPromptBuilder:
|
|||||||
"start_index": ("INT", {"default": 41, "min": 1, "max": 1000000, "step": 1}),
|
"start_index": ("INT", {"default": 41, "min": 1, "max": 1000000, "step": 1}),
|
||||||
"seed": ("INT", {"default": 20260614, "min": 0, "max": 0xFFFFFFFF, "step": 1}),
|
"seed": ("INT", {"default": 20260614, "min": 0, "max": 0xFFFFFFFF, "step": 1}),
|
||||||
"clothing": (["full", "minimal"], {"default": "full"}),
|
"clothing": (["full", "minimal"], {"default": "full"}),
|
||||||
"ethnicity": (["any", "asian", "white_asian"], {"default": "any"}),
|
"ethnicity": (ethnicity_choices(), {"default": "any"}),
|
||||||
"poses": (["standard", "evocative"], {"default": "standard"}),
|
"poses": (["standard", "evocative"], {"default": "standard"}),
|
||||||
"expression_intensity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
|
"expression_intensity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||||
"backside_bias": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
"backside_bias": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||||
"figure": (["curvy", "balanced", "bombshell"], {"default": "curvy"}),
|
"figure": (["curvy", "balanced", "bombshell"], {"default": "curvy"}),
|
||||||
"no_plus_women": ("BOOLEAN", {"default": False}),
|
|
||||||
"no_black": ("BOOLEAN", {"default": False}),
|
|
||||||
"women_count": ("INT", {"default": 1, "min": 0, "max": 12, "step": 1}),
|
"women_count": ("INT", {"default": 1, "min": 0, "max": 12, "step": 1}),
|
||||||
"men_count": ("INT", {"default": 1, "min": 0, "max": 12, "step": 1}),
|
"men_count": ("INT", {"default": 1, "min": 0, "max": 12, "step": 1}),
|
||||||
"minimal_clothing_ratio": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
"minimal_clothing_ratio": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||||
@@ -118,8 +122,6 @@ class SxCPPromptBuilder:
|
|||||||
expression_intensity,
|
expression_intensity,
|
||||||
backside_bias,
|
backside_bias,
|
||||||
figure,
|
figure,
|
||||||
no_plus_women,
|
|
||||||
no_black,
|
|
||||||
women_count,
|
women_count,
|
||||||
men_count,
|
men_count,
|
||||||
minimal_clothing_ratio,
|
minimal_clothing_ratio,
|
||||||
@@ -131,6 +133,8 @@ class SxCPPromptBuilder:
|
|||||||
character_profile="",
|
character_profile="",
|
||||||
extra_positive="",
|
extra_positive="",
|
||||||
extra_negative="",
|
extra_negative="",
|
||||||
|
no_plus_women=False,
|
||||||
|
no_black=False,
|
||||||
):
|
):
|
||||||
row = build_prompt(
|
row = build_prompt(
|
||||||
category=category,
|
category=category,
|
||||||
@@ -218,6 +222,46 @@ class SxCPSeedControl:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SxCPSeedLocker:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
seed_spec = {"default": 20260614, "min": 0, "max": 0xFFFFFFFF, "step": 1}
|
||||||
|
reroll_seed_spec = {"default": -1, "min": -1, "max": 0xFFFFFFFF, "step": 1}
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"base_seed": ("INT", seed_spec),
|
||||||
|
"reroll_axis": (
|
||||||
|
[
|
||||||
|
"none",
|
||||||
|
"category",
|
||||||
|
"subcategory",
|
||||||
|
"content",
|
||||||
|
"person",
|
||||||
|
"scene",
|
||||||
|
"pose",
|
||||||
|
"role",
|
||||||
|
"expression",
|
||||||
|
"composition",
|
||||||
|
"content_pose",
|
||||||
|
"scene_pose",
|
||||||
|
],
|
||||||
|
{"default": "none"},
|
||||||
|
),
|
||||||
|
"reroll_seed": ("INT", reroll_seed_spec),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING", "STRING")
|
||||||
|
RETURN_NAMES = ("seed_config", "summary")
|
||||||
|
FUNCTION = "build"
|
||||||
|
CATEGORY = "prompt_builder"
|
||||||
|
|
||||||
|
def build(self, base_seed, reroll_axis, reroll_seed):
|
||||||
|
config = build_seed_lock_config_json(base_seed=base_seed, reroll_axis=reroll_axis, reroll_seed=reroll_seed)
|
||||||
|
summary = f"base {base_seed}; reroll {reroll_axis} with {'main seed' if int(reroll_seed) < 0 else reroll_seed}"
|
||||||
|
return config, summary
|
||||||
|
|
||||||
|
|
||||||
class SxCPCameraControl:
|
class SxCPCameraControl:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
@@ -231,6 +275,7 @@ class SxCPCameraControl:
|
|||||||
"orientation": (camera_orientation_choices(), {"default": "vertical_story"}),
|
"orientation": (camera_orientation_choices(), {"default": "vertical_story"}),
|
||||||
"phone_visibility": (camera_phone_choices(), {"default": "phone_visible"}),
|
"phone_visibility": (camera_phone_choices(), {"default": "phone_visible"}),
|
||||||
"priority": (camera_priority_choices(), {"default": "locked"}),
|
"priority": (camera_priority_choices(), {"default": "locked"}),
|
||||||
|
"camera_detail": (camera_detail_choices(), {"default": "compact"}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,6 +294,7 @@ class SxCPCameraControl:
|
|||||||
orientation,
|
orientation,
|
||||||
phone_visibility,
|
phone_visibility,
|
||||||
priority,
|
priority,
|
||||||
|
camera_detail,
|
||||||
):
|
):
|
||||||
return (
|
return (
|
||||||
build_camera_config_json(
|
build_camera_config_json(
|
||||||
@@ -260,6 +306,7 @@ class SxCPCameraControl:
|
|||||||
orientation=orientation,
|
orientation=orientation,
|
||||||
phone_visibility=phone_visibility,
|
phone_visibility=phone_visibility,
|
||||||
priority=priority,
|
priority=priority,
|
||||||
|
camera_detail=camera_detail,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -360,10 +407,17 @@ class SxCPAdvancedFilters:
|
|||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"ethnicity": (["any", "asian", "white_asian"], {"default": "any"}),
|
"include_european": ("BOOLEAN", {"default": True}),
|
||||||
|
"include_mediterranean_mena": ("BOOLEAN", {"default": True}),
|
||||||
|
"include_latina": ("BOOLEAN", {"default": True}),
|
||||||
|
"include_east_asian": ("BOOLEAN", {"default": True}),
|
||||||
|
"include_southeast_asian": ("BOOLEAN", {"default": True}),
|
||||||
|
"include_south_asian": ("BOOLEAN", {"default": True}),
|
||||||
|
"include_black_african": ("BOOLEAN", {"default": True}),
|
||||||
|
"include_indigenous": ("BOOLEAN", {"default": True}),
|
||||||
|
"include_mixed": ("BOOLEAN", {"default": True}),
|
||||||
|
"include_plus_size": ("BOOLEAN", {"default": True}),
|
||||||
"figure": (["curvy", "balanced", "bombshell"], {"default": "curvy"}),
|
"figure": (["curvy", "balanced", "bombshell"], {"default": "curvy"}),
|
||||||
"no_plus_women": ("BOOLEAN", {"default": False}),
|
|
||||||
"no_black": ("BOOLEAN", {"default": False}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,13 +426,33 @@ class SxCPAdvancedFilters:
|
|||||||
FUNCTION = "build"
|
FUNCTION = "build"
|
||||||
CATEGORY = "prompt_builder"
|
CATEGORY = "prompt_builder"
|
||||||
|
|
||||||
def build(self, ethnicity, figure, no_plus_women, no_black):
|
def build(
|
||||||
|
self,
|
||||||
|
include_european,
|
||||||
|
include_mediterranean_mena,
|
||||||
|
include_latina,
|
||||||
|
include_east_asian,
|
||||||
|
include_southeast_asian,
|
||||||
|
include_south_asian,
|
||||||
|
include_black_african,
|
||||||
|
include_indigenous,
|
||||||
|
include_mixed,
|
||||||
|
include_plus_size,
|
||||||
|
figure,
|
||||||
|
):
|
||||||
return (
|
return (
|
||||||
build_filter_config_json(
|
build_filter_config_json(
|
||||||
ethnicity=ethnicity,
|
|
||||||
figure=figure,
|
figure=figure,
|
||||||
no_plus_women=no_plus_women,
|
include_european=include_european,
|
||||||
no_black=no_black,
|
include_mediterranean_mena=include_mediterranean_mena,
|
||||||
|
include_latina=include_latina,
|
||||||
|
include_east_asian=include_east_asian,
|
||||||
|
include_southeast_asian=include_southeast_asian,
|
||||||
|
include_south_asian=include_south_asian,
|
||||||
|
include_black_african=include_black_african,
|
||||||
|
include_indigenous=include_indigenous,
|
||||||
|
include_mixed=include_mixed,
|
||||||
|
include_plus_size=include_plus_size,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -673,14 +747,16 @@ class SxCPInstaOFOptions:
|
|||||||
"hardcore_cast": (["use_counts", "couple", "threesome", "group"], {"default": "use_counts"}),
|
"hardcore_cast": (["use_counts", "couple", "threesome", "group"], {"default": "use_counts"}),
|
||||||
"hardcore_women_count": ("INT", {"default": 1, "min": 0, "max": 12, "step": 1}),
|
"hardcore_women_count": ("INT", {"default": 1, "min": 0, "max": 12, "step": 1}),
|
||||||
"hardcore_men_count": ("INT", {"default": 1, "min": 0, "max": 12, "step": 1}),
|
"hardcore_men_count": ("INT", {"default": 1, "min": 0, "max": 12, "step": 1}),
|
||||||
"softcore_level": (["social_tease", "lingerie_tease", "implied_nude", "explicit_tease"], {"default": "lingerie_tease"}),
|
"softcore_level": (["social_tease", "lingerie_tease", "implied_nude", "explicit_tease", "explicit_nude"], {"default": "lingerie_tease"}),
|
||||||
"hardcore_level": (["explicit", "hardcore"], {"default": "hardcore"}),
|
"hardcore_level": (["explicit", "hardcore"], {"default": "hardcore"}),
|
||||||
"softcore_expression_intensity": ("FLOAT", {"default": 0.45, "min": 0.0, "max": 1.0, "step": 0.01}),
|
"softcore_expression_intensity": ("FLOAT", {"default": 0.45, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||||
"hardcore_expression_intensity": ("FLOAT", {"default": 0.85, "min": 0.0, "max": 1.0, "step": 0.01}),
|
"hardcore_expression_intensity": ("FLOAT", {"default": 0.85, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||||
"platform_style": (["hybrid", "instagram", "onlyfans"], {"default": "hybrid"}),
|
"platform_style": (["hybrid", "instagram", "onlyfans"], {"default": "hybrid"}),
|
||||||
"continuity": (["same_creator_same_room", "same_creator_new_scene"], {"default": "same_creator_same_room"}),
|
"continuity": (["same_creator_same_room", "same_creator_new_scene"], {"default": "same_creator_same_room"}),
|
||||||
|
"hardcore_clothing_continuity": (["none", "same_outfit", "partially_removed", "implied_nude", "explicit_nude"], {"default": "partially_removed"}),
|
||||||
"softcore_camera_mode": (camera_mode_choices(), {"default": "handheld_selfie"}),
|
"softcore_camera_mode": (camera_mode_choices(), {"default": "handheld_selfie"}),
|
||||||
"hardcore_camera_mode": (["same_as_softcore"] + camera_mode_choices(), {"default": "same_as_softcore"}),
|
"hardcore_camera_mode": (["same_as_softcore"] + camera_mode_choices(), {"default": "same_as_softcore"}),
|
||||||
|
"camera_detail": (camera_detail_choices(), {"default": "compact"}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -701,8 +777,10 @@ class SxCPInstaOFOptions:
|
|||||||
hardcore_expression_intensity,
|
hardcore_expression_intensity,
|
||||||
platform_style,
|
platform_style,
|
||||||
continuity,
|
continuity,
|
||||||
|
hardcore_clothing_continuity,
|
||||||
softcore_camera_mode,
|
softcore_camera_mode,
|
||||||
hardcore_camera_mode,
|
hardcore_camera_mode,
|
||||||
|
camera_detail,
|
||||||
):
|
):
|
||||||
return (
|
return (
|
||||||
build_insta_of_options_json(
|
build_insta_of_options_json(
|
||||||
@@ -716,8 +794,10 @@ class SxCPInstaOFOptions:
|
|||||||
hardcore_expression_intensity=hardcore_expression_intensity,
|
hardcore_expression_intensity=hardcore_expression_intensity,
|
||||||
platform_style=platform_style,
|
platform_style=platform_style,
|
||||||
continuity=continuity,
|
continuity=continuity,
|
||||||
|
hardcore_clothing_continuity=hardcore_clothing_continuity,
|
||||||
softcore_camera_mode=softcore_camera_mode,
|
softcore_camera_mode=softcore_camera_mode,
|
||||||
hardcore_camera_mode=hardcore_camera_mode,
|
hardcore_camera_mode=hardcore_camera_mode,
|
||||||
|
camera_detail=camera_detail,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -730,16 +810,15 @@ class SxCPInstaOFPromptPair:
|
|||||||
"row_number": ("INT", {"default": 1, "min": 1, "max": 1000000, "step": 1}),
|
"row_number": ("INT", {"default": 1, "min": 1, "max": 1000000, "step": 1}),
|
||||||
"start_index": ("INT", {"default": 41, "min": 1, "max": 1000000, "step": 1}),
|
"start_index": ("INT", {"default": 41, "min": 1, "max": 1000000, "step": 1}),
|
||||||
"seed": ("INT", {"default": 20260614, "min": 0, "max": 0xFFFFFFFF, "step": 1}),
|
"seed": ("INT", {"default": 20260614, "min": 0, "max": 0xFFFFFFFF, "step": 1}),
|
||||||
"ethnicity": (["any", "asian", "white_asian"], {"default": "any"}),
|
"ethnicity": (ethnicity_choices(), {"default": "any"}),
|
||||||
"figure": (["curvy", "balanced", "bombshell"], {"default": "curvy"}),
|
"figure": (["curvy", "balanced", "bombshell"], {"default": "curvy"}),
|
||||||
"no_plus_women": ("BOOLEAN", {"default": False}),
|
|
||||||
"no_black": ("BOOLEAN", {"default": False}),
|
|
||||||
"trigger": ("STRING", {"default": "sxcpinup_coloredpencil"}),
|
"trigger": ("STRING", {"default": "sxcpinup_coloredpencil"}),
|
||||||
"prepend_trigger_to_prompt": ("BOOLEAN", {"default": True}),
|
"prepend_trigger_to_prompt": ("BOOLEAN", {"default": True}),
|
||||||
},
|
},
|
||||||
"optional": {
|
"optional": {
|
||||||
"seed_config": ("STRING", {"default": "", "multiline": True}),
|
"seed_config": ("STRING", {"default": "", "multiline": True}),
|
||||||
"options_json": ("STRING", {"default": "", "multiline": True}),
|
"options_json": ("STRING", {"default": "", "multiline": True}),
|
||||||
|
"filter_config": ("STRING", {"default": "", "multiline": True}),
|
||||||
"camera_config": ("STRING", {"default": "", "multiline": True}),
|
"camera_config": ("STRING", {"default": "", "multiline": True}),
|
||||||
"character_profile": ("STRING", {"default": "", "multiline": True}),
|
"character_profile": ("STRING", {"default": "", "multiline": True}),
|
||||||
"extra_positive": ("STRING", {"default": "", "multiline": True}),
|
"extra_positive": ("STRING", {"default": "", "multiline": True}),
|
||||||
@@ -768,16 +847,17 @@ class SxCPInstaOFPromptPair:
|
|||||||
seed,
|
seed,
|
||||||
ethnicity,
|
ethnicity,
|
||||||
figure,
|
figure,
|
||||||
no_plus_women,
|
|
||||||
no_black,
|
|
||||||
trigger,
|
trigger,
|
||||||
prepend_trigger_to_prompt,
|
prepend_trigger_to_prompt,
|
||||||
seed_config="",
|
seed_config="",
|
||||||
options_json="",
|
options_json="",
|
||||||
|
filter_config="",
|
||||||
camera_config="",
|
camera_config="",
|
||||||
character_profile="",
|
character_profile="",
|
||||||
extra_positive="",
|
extra_positive="",
|
||||||
extra_negative="",
|
extra_negative="",
|
||||||
|
no_plus_women=False,
|
||||||
|
no_black=False,
|
||||||
):
|
):
|
||||||
row = build_insta_of_pair(
|
row = build_insta_of_pair(
|
||||||
row_number=row_number,
|
row_number=row_number,
|
||||||
@@ -791,6 +871,7 @@ class SxCPInstaOFPromptPair:
|
|||||||
prepend_trigger_to_prompt=prepend_trigger_to_prompt,
|
prepend_trigger_to_prompt=prepend_trigger_to_prompt,
|
||||||
seed_config=seed_config or "",
|
seed_config=seed_config or "",
|
||||||
options_json=options_json or "",
|
options_json=options_json or "",
|
||||||
|
filter_config=filter_config or "",
|
||||||
camera_config=camera_config or "",
|
camera_config=camera_config or "",
|
||||||
character_profile=character_profile or "",
|
character_profile=character_profile or "",
|
||||||
extra_positive=extra_positive or "",
|
extra_positive=extra_positive or "",
|
||||||
@@ -811,6 +892,7 @@ class SxCPInstaOFPromptPair:
|
|||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
"SxCPPromptBuilder": SxCPPromptBuilder,
|
"SxCPPromptBuilder": SxCPPromptBuilder,
|
||||||
"SxCPSeedControl": SxCPSeedControl,
|
"SxCPSeedControl": SxCPSeedControl,
|
||||||
|
"SxCPSeedLocker": SxCPSeedLocker,
|
||||||
"SxCPCameraControl": SxCPCameraControl,
|
"SxCPCameraControl": SxCPCameraControl,
|
||||||
"SxCPCategoryPreset": SxCPCategoryPreset,
|
"SxCPCategoryPreset": SxCPCategoryPreset,
|
||||||
"SxCPCastControl": SxCPCastControl,
|
"SxCPCastControl": SxCPCastControl,
|
||||||
@@ -828,6 +910,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
"SxCPPromptBuilder": "SxCP Prompt Builder",
|
"SxCPPromptBuilder": "SxCP Prompt Builder",
|
||||||
"SxCPSeedControl": "SxCP Seed Control",
|
"SxCPSeedControl": "SxCP Seed Control",
|
||||||
|
"SxCPSeedLocker": "SxCP Seed Locker",
|
||||||
"SxCPCameraControl": "SxCP Camera Control",
|
"SxCPCameraControl": "SxCP Camera Control",
|
||||||
"SxCPCategoryPreset": "SxCP Category Preset",
|
"SxCPCategoryPreset": "SxCP Category Preset",
|
||||||
"SxCPCastControl": "SxCP Cast Control",
|
"SxCPCastControl": "SxCP Cast Control",
|
||||||
|
|||||||
+111
-14
@@ -2782,21 +2782,122 @@ WHITE_KEYWORDS = (
|
|||||||
"french",
|
"french",
|
||||||
"mediterranean",
|
"mediterranean",
|
||||||
)
|
)
|
||||||
|
EAST_ASIAN_KEYWORDS = (
|
||||||
|
"east asian",
|
||||||
|
"japanese",
|
||||||
|
"korean",
|
||||||
|
"chinese",
|
||||||
|
"taiwanese",
|
||||||
|
"mongolian",
|
||||||
|
"tibetan",
|
||||||
|
"manchu",
|
||||||
|
"okinawan",
|
||||||
|
)
|
||||||
|
SOUTHEAST_ASIAN_KEYWORDS = (
|
||||||
|
"southeast asian",
|
||||||
|
"vietnamese",
|
||||||
|
"thai",
|
||||||
|
"filipina",
|
||||||
|
"filipino",
|
||||||
|
"indonesian",
|
||||||
|
"malay",
|
||||||
|
"cambodian",
|
||||||
|
"lao",
|
||||||
|
"burmese",
|
||||||
|
"singaporean",
|
||||||
|
"hmong",
|
||||||
|
"balinese",
|
||||||
|
)
|
||||||
|
SOUTH_ASIAN_KEYWORDS = (
|
||||||
|
"south asian",
|
||||||
|
"indian",
|
||||||
|
"punjabi",
|
||||||
|
"tamil",
|
||||||
|
"bengali",
|
||||||
|
"sri lankan",
|
||||||
|
"nepali",
|
||||||
|
"pakistani",
|
||||||
|
"gujarati",
|
||||||
|
"bangladeshi",
|
||||||
|
"malayali",
|
||||||
|
"kashmiri",
|
||||||
|
)
|
||||||
|
MEDITERRANEAN_MENA_KEYWORDS = (
|
||||||
|
"mediterranean",
|
||||||
|
"greek",
|
||||||
|
"italian",
|
||||||
|
"spanish",
|
||||||
|
"portuguese",
|
||||||
|
"turkish",
|
||||||
|
"persian",
|
||||||
|
"levantine",
|
||||||
|
"maghrebi",
|
||||||
|
"egyptian",
|
||||||
|
"moroccan",
|
||||||
|
"amazigh",
|
||||||
|
"kurdish",
|
||||||
|
"middle-eastern",
|
||||||
|
"middle eastern",
|
||||||
|
"mena",
|
||||||
|
"olive",
|
||||||
|
)
|
||||||
|
LATINA_KEYWORDS = (
|
||||||
|
"latina",
|
||||||
|
"latino",
|
||||||
|
"mexican",
|
||||||
|
"chicana",
|
||||||
|
"colombian",
|
||||||
|
"brazilian",
|
||||||
|
"puerto rican",
|
||||||
|
"cuban",
|
||||||
|
"dominican",
|
||||||
|
"venezuelan",
|
||||||
|
"peruvian",
|
||||||
|
"chilean",
|
||||||
|
"argentine",
|
||||||
|
"uruguayan",
|
||||||
|
"ecuadorian",
|
||||||
|
)
|
||||||
|
BLACK_AFRICAN_KEYWORDS = ("african", "african-diaspora", "cape verdean")
|
||||||
|
INDIGENOUS_KEYWORDS = ("indigenous", "amazigh")
|
||||||
|
MIXED_KEYWORDS = ("mixed",)
|
||||||
|
ETHNICITY_KEYWORD_GROUPS = {
|
||||||
|
"asian": ASIAN_KEYWORDS,
|
||||||
|
"white_asian": WHITE_KEYWORDS + ASIAN_KEYWORDS,
|
||||||
|
"european": WHITE_KEYWORDS,
|
||||||
|
"mediterranean_mena": MEDITERRANEAN_MENA_KEYWORDS,
|
||||||
|
"latina": LATINA_KEYWORDS,
|
||||||
|
"east_asian": EAST_ASIAN_KEYWORDS,
|
||||||
|
"southeast_asian": SOUTHEAST_ASIAN_KEYWORDS,
|
||||||
|
"south_asian": SOUTH_ASIAN_KEYWORDS,
|
||||||
|
"black_african": BLACK_AFRICAN_KEYWORDS,
|
||||||
|
"indigenous": INDIGENOUS_KEYWORDS,
|
||||||
|
"mixed": MIXED_KEYWORDS,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def by_ethnicity(pool: list, ethnicity: str) -> list:
|
def by_ethnicity(pool: list, ethnicity: str) -> list:
|
||||||
"""Filter an appearance pool by heritage keywords found in the skin field.
|
"""Filter an appearance pool by heritage keywords found in the skin field.
|
||||||
'asian' = East/Southeast/South/Central Asian; 'white_asian' = white/European + Asian;
|
'asian' = East/Southeast/South/Central Asian; 'white_asian' = white/European + Asian;
|
||||||
'any' returns the full pool."""
|
'any' returns the full pool."""
|
||||||
|
ethnicity = str(ethnicity or "any").strip().lower()
|
||||||
if ethnicity == "any":
|
if ethnicity == "any":
|
||||||
return pool
|
return pool
|
||||||
if ethnicity == "asian":
|
tokens = [token.strip() for token in re.split(r"[,+|;/\s]+", ethnicity) if token.strip()]
|
||||||
kws = ASIAN_KEYWORDS
|
kws: list[str] = []
|
||||||
elif ethnicity == "white_asian":
|
exclude_kws: list[str] = []
|
||||||
kws = WHITE_KEYWORDS + ASIAN_KEYWORDS
|
for token in tokens:
|
||||||
else:
|
if token.startswith("exclude_"):
|
||||||
kws = (ethnicity,)
|
exclude_key = token.removeprefix("exclude_")
|
||||||
|
exclude_kws.extend(ETHNICITY_KEYWORD_GROUPS.get(exclude_key, (exclude_key,)))
|
||||||
|
continue
|
||||||
|
if token in ("not_mixed", "no_mixed"):
|
||||||
|
exclude_kws.extend(MIXED_KEYWORDS)
|
||||||
|
continue
|
||||||
|
kws.extend(ETHNICITY_KEYWORD_GROUPS.get(token, (token,)))
|
||||||
filtered = [e for e in pool if any(k in e[3].lower() for k in kws)]
|
filtered = [e for e in pool if any(k in e[3].lower() for k in kws)]
|
||||||
|
if exclude_kws:
|
||||||
|
filtered = [e for e in filtered if not any(k in e[3].lower() for k in exclude_kws)]
|
||||||
return filtered or pool
|
return filtered or pool
|
||||||
|
|
||||||
|
|
||||||
@@ -2894,10 +2995,7 @@ def make_single(index: int, batch: int, rng: random.Random, gender: str, expr_de
|
|||||||
clothes = choose(rng, WOMEN_CLOTHES_MINIMAL if minimal else WOMEN_CLOTHES)
|
clothes = choose(rng, WOMEN_CLOTHES_MINIMAL if minimal else WOMEN_CLOTHES)
|
||||||
figure_note = choose(rng, figure_pool(figure))
|
figure_note = choose(rng, figure_pool(figure))
|
||||||
else:
|
else:
|
||||||
# The ethnicity bias targets women; men stay any-heritage unless the
|
men_pool = by_ethnicity(MEN, ethnicity)
|
||||||
# batch is a fully-themed 'asian' batch.
|
|
||||||
men_eth = ethnicity if ethnicity == "asian" else "any"
|
|
||||||
men_pool = by_ethnicity(MEN, men_eth)
|
|
||||||
subject, age, body, skin, hair, eyes = choose(rng, men_pool)
|
subject, age, body, skin, hair, eyes = choose(rng, men_pool)
|
||||||
clothes = choose(rng, MEN_CLOTHES_MINIMAL if minimal else MEN_CLOTHES)
|
clothes = choose(rng, MEN_CLOTHES_MINIMAL if minimal else MEN_CLOTHES)
|
||||||
figure_note = ""
|
figure_note = ""
|
||||||
@@ -3247,11 +3345,10 @@ def main() -> None:
|
|||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--ethnicity",
|
"--ethnicity",
|
||||||
choices=["any", "asian", "white_asian"],
|
choices=["any", *ETHNICITY_KEYWORD_GROUPS.keys()],
|
||||||
default="any",
|
default="any",
|
||||||
help="'any' = balanced heritage mix (default); "
|
help="'any' = balanced heritage mix (default). Other values restrict the appearance pool "
|
||||||
"'asian' = restrict all subjects to Asian (East/Southeast/South/Central Asian); "
|
"by heritage keywords, e.g. east_asian, latina, black_african, mixed, asian, or white_asian.",
|
||||||
"'white_asian' = bias women to white/European + Asian (men stay any heritage).",
|
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--poses",
|
"--poses",
|
||||||
|
|||||||
+52
-18
@@ -164,6 +164,10 @@ def _combine_negative(*parts: str) -> str:
|
|||||||
return ", ".join(cleaned)
|
return ", ".join(cleaned)
|
||||||
|
|
||||||
|
|
||||||
|
def _prompt_cast_descriptors(text: str) -> str:
|
||||||
|
return _clean(text).replace("Woman A / primary creator:", "Woman A:")
|
||||||
|
|
||||||
|
|
||||||
def _clean_age(age: Any) -> str:
|
def _clean_age(age: Any) -> str:
|
||||||
return _clean(age)
|
return _clean(age)
|
||||||
|
|
||||||
@@ -205,15 +209,50 @@ def _camera_phrase(row: dict[str, Any]) -> str:
|
|||||||
return directive
|
return directive
|
||||||
config = row.get("camera_config")
|
config = row.get("camera_config")
|
||||||
if isinstance(config, dict):
|
if isinstance(config, dict):
|
||||||
|
detail = _clean(config.get("camera_detail"))
|
||||||
|
if detail == "off" or _clean(config.get("camera_mode")) == "disabled":
|
||||||
|
return ""
|
||||||
mode = _clean(config.get("camera_mode")).replace("_", " ")
|
mode = _clean(config.get("camera_mode")).replace("_", " ")
|
||||||
shot = _clean(config.get("shot_size")).replace("_", " ")
|
shot = _clean(config.get("shot_size")).replace("_", " ")
|
||||||
angle = _clean(config.get("angle")).replace("_", " ")
|
angle = _clean(config.get("angle")).replace("_", " ")
|
||||||
pieces = [piece for piece in (mode, shot, angle) if piece and piece != "auto" and piece != "standard"]
|
pieces = [piece for piece in (mode, shot, angle) if piece and piece != "auto" and piece != "standard"]
|
||||||
if pieces:
|
if pieces:
|
||||||
return "Camera framing uses " + ", ".join(pieces)
|
return "Camera: " + ", ".join(pieces)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def _camera_phrase_from_config(config: Any) -> str:
|
||||||
|
if not isinstance(config, dict):
|
||||||
|
return ""
|
||||||
|
detail = _clean(config.get("camera_detail"))
|
||||||
|
if detail == "off" or _clean(config.get("camera_mode")) == "disabled":
|
||||||
|
return ""
|
||||||
|
values = [
|
||||||
|
_clean(config.get("camera_mode")).replace("_", " "),
|
||||||
|
_clean(config.get("shot_size")).replace("_", " "),
|
||||||
|
_clean(config.get("angle")).replace("_", " "),
|
||||||
|
_clean(config.get("lens")).replace("_", " "),
|
||||||
|
_clean(config.get("distance")).replace("_", " "),
|
||||||
|
_clean(config.get("orientation")).replace("_", " "),
|
||||||
|
_clean(config.get("phone_visibility")).replace("_", " "),
|
||||||
|
]
|
||||||
|
pieces = [value for value in values if value and value not in ("auto", "standard")]
|
||||||
|
if not pieces:
|
||||||
|
return ""
|
||||||
|
return "Camera: " + ", ".join(pieces)
|
||||||
|
|
||||||
|
|
||||||
|
def _pair_camera_phrase(directive: Any, config: Any, row: dict[str, Any]) -> str:
|
||||||
|
directive_text = _clean(directive)
|
||||||
|
if directive_text:
|
||||||
|
return directive_text
|
||||||
|
if isinstance(config, dict) and (
|
||||||
|
_clean(config.get("camera_detail")) == "off" or _clean(config.get("camera_mode")) == "disabled"
|
||||||
|
):
|
||||||
|
return ""
|
||||||
|
return _camera_phrase_from_config(config) or _camera_phrase(row)
|
||||||
|
|
||||||
|
|
||||||
def _style_phrase(row: dict[str, Any], style_mode: str) -> str:
|
def _style_phrase(row: dict[str, Any], style_mode: str) -> str:
|
||||||
if style_mode == "minimal":
|
if style_mode == "minimal":
|
||||||
return ""
|
return ""
|
||||||
@@ -323,31 +362,27 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
|||||||
cast_descriptor_text = "; ".join(_clean(item) for item in cast_descriptors if _clean(item))
|
cast_descriptor_text = "; ".join(_clean(item) for item in cast_descriptors if _clean(item))
|
||||||
else:
|
else:
|
||||||
cast_descriptor_text = _clean(cast_descriptors)
|
cast_descriptor_text = _clean(cast_descriptors)
|
||||||
|
cast_descriptor_text = _prompt_cast_descriptors(cast_descriptor_text)
|
||||||
soft = row.get("softcore_row") if isinstance(row.get("softcore_row"), dict) else {}
|
soft = row.get("softcore_row") if isinstance(row.get("softcore_row"), dict) else {}
|
||||||
hard = row.get("hardcore_row") if isinstance(row.get("hardcore_row"), dict) else {}
|
hard = row.get("hardcore_row") if isinstance(row.get("hardcore_row"), dict) else {}
|
||||||
soft_camera = _clean(row.get("softcore_camera_directive")) or _camera_phrase(soft)
|
soft_camera = _pair_camera_phrase(row.get("softcore_camera_directive"), row.get("softcore_camera_config"), soft)
|
||||||
hard_camera = _clean(row.get("hardcore_camera_directive")) or _camera_phrase(hard)
|
hard_camera = _pair_camera_phrase(row.get("hardcore_camera_directive"), row.get("hardcore_camera_config"), hard)
|
||||||
soft_style = _style_phrase(soft, style_mode)
|
soft_style = _style_phrase(soft, style_mode)
|
||||||
hard_style = _style_phrase(hard, style_mode)
|
hard_style = _style_phrase(hard, style_mode)
|
||||||
options = row.get("options") if isinstance(row.get("options"), dict) else {}
|
options = row.get("options") if isinstance(row.get("options"), dict) else {}
|
||||||
soft_level = _clean(options.get("softcore_level")).replace("_", " ")
|
soft_level = _clean(options.get("softcore_level")).replace("_", " ")
|
||||||
hard_level = _clean(options.get("hardcore_level")).replace("_", " ")
|
hard_level = _clean(options.get("hardcore_level")).replace("_", " ")
|
||||||
hard_cast = _clean(row.get("hardcore_women_count"))
|
|
||||||
hard_men = _clean(row.get("hardcore_men_count"))
|
|
||||||
hard_cast_text = _clean(hard.get("cast_summary")) or (
|
|
||||||
f"{hard_cast} adult women and {hard_men} adult men" if hard_cast or hard_men else ""
|
|
||||||
)
|
|
||||||
same_room = options.get("continuity") == "same_creator_same_room"
|
same_room = options.get("continuity") == "same_creator_same_room"
|
||||||
hard_scene = soft.get("scene_text") if same_room and soft.get("scene_text") else hard.get("scene_text")
|
hard_scene = soft.get("scene_text") if same_room and soft.get("scene_text") else hard.get("scene_text")
|
||||||
hard_composition = soft.get("composition") if same_room and soft.get("composition") else hard.get("composition")
|
hard_composition = hard.get("composition")
|
||||||
soft_cast_descriptor_text = (
|
soft_cast_descriptor_text = (
|
||||||
cast_descriptor_text
|
cast_descriptor_text
|
||||||
if options.get("softcore_cast") == "same_as_hardcore"
|
if options.get("softcore_cast") == "same_as_hardcore"
|
||||||
else f"Woman A / primary creator: {descriptor}"
|
else f"Woman A: {descriptor}"
|
||||||
)
|
)
|
||||||
same_soft_cast = options.get("softcore_cast") == "same_as_hardcore"
|
same_soft_cast = options.get("softcore_cast") == "same_as_hardcore"
|
||||||
soft_cast_presence = (
|
soft_cast_presence = (
|
||||||
"The same cast is present together in a non-explicit teaser pose, with no sex act or genital contact"
|
"Woman A and the listed partners are present together in a non-explicit teaser pose, with no sex act or genital contact"
|
||||||
if same_soft_cast
|
if same_soft_cast
|
||||||
else "The softcore version focuses on Woman A alone"
|
else "The softcore version focuses on Woman A alone"
|
||||||
)
|
)
|
||||||
@@ -361,12 +396,11 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
|||||||
partner_pose = ""
|
partner_pose = ""
|
||||||
|
|
||||||
soft_parts = [
|
soft_parts = [
|
||||||
descriptor,
|
f"Cast descriptors: {soft_cast_descriptor_text}" if same_soft_cast and soft_cast_descriptor_text else "",
|
||||||
f"Shared 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 primary creator descriptor: {soft_cast_descriptor_text}" if not same_soft_cast and soft_cast_descriptor_text else "",
|
|
||||||
soft_cast_presence,
|
soft_cast_presence,
|
||||||
f"Partner softcore styling: {partner_outfit_text}" if partner_outfit_text else "",
|
f"Partner softcore styling: {partner_outfit_text}" if partner_outfit_text else "",
|
||||||
f"The shared softcore cast pose is {partner_pose}" if partner_pose else "",
|
f"Cast pose: {partner_pose}" if partner_pose else "",
|
||||||
f"shown in a {soft_level or 'softcore'} Insta/OF creator image",
|
f"shown in a {soft_level or 'softcore'} Insta/OF creator image",
|
||||||
f"wearing {soft.get('item')}" if soft.get("item") else "",
|
f"wearing {soft.get('item')}" if soft.get("item") else "",
|
||||||
f"{soft.get('pose')}" if soft.get("pose") else "",
|
f"{soft.get('pose')}" if soft.get("pose") else "",
|
||||||
@@ -377,9 +411,9 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
|||||||
soft_style if detail_level != "concise" else "",
|
soft_style if detail_level != "concise" else "",
|
||||||
]
|
]
|
||||||
hard_parts = [
|
hard_parts = [
|
||||||
f"The primary creator remains {descriptor}, visually central in a {hard_level or 'hardcore'} scene",
|
f"{hard_level or 'hardcore'} scene with Woman A visually central",
|
||||||
f"{'Shared' if same_soft_cast else 'Hardcore'} cast descriptors: {cast_descriptor_text}" if cast_descriptor_text else "",
|
f"Cast descriptors: {cast_descriptor_text}" if cast_descriptor_text else "",
|
||||||
f"The cast includes {hard_cast_text}" if hard_cast_text else "",
|
_clean(row.get("hardcore_clothing_state")),
|
||||||
_clean(hard.get("role_graph")),
|
_clean(hard.get("role_graph")),
|
||||||
f"The sexual action is {hard.get('item')}" if hard.get("item") else "",
|
f"The sexual action is {hard.get('item')}" if hard.get("item") else "",
|
||||||
f"set in {hard_scene}" if hard_scene else "",
|
f"set in {hard_scene}" if hard_scene else "",
|
||||||
|
|||||||
+282
-27
@@ -49,6 +49,35 @@ SEED_AXIS_ALIASES = {
|
|||||||
"composition": ("composition_seed", "camera_seed", "composition"),
|
"composition": ("composition_seed", "camera_seed", "composition"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SEED_LOCK_AXES = (
|
||||||
|
"category",
|
||||||
|
"subcategory",
|
||||||
|
"content",
|
||||||
|
"person",
|
||||||
|
"scene",
|
||||||
|
"pose",
|
||||||
|
"role",
|
||||||
|
"expression",
|
||||||
|
"composition",
|
||||||
|
)
|
||||||
|
|
||||||
|
ETHNICITY_FILTER_CHOICES = [
|
||||||
|
"any",
|
||||||
|
"european",
|
||||||
|
"mediterranean_mena",
|
||||||
|
"latina",
|
||||||
|
"east_asian",
|
||||||
|
"southeast_asian",
|
||||||
|
"south_asian",
|
||||||
|
"black_african",
|
||||||
|
"indigenous",
|
||||||
|
"mixed",
|
||||||
|
"asian",
|
||||||
|
"white_asian",
|
||||||
|
]
|
||||||
|
|
||||||
|
CAMERA_DETAIL_CHOICES = ["off", "compact", "full"]
|
||||||
|
|
||||||
GENERIC_POSITIVE_SUFFIX = (
|
GENERIC_POSITIVE_SUFFIX = (
|
||||||
"Use crisp clean comic linework, detailed hatching, soft blended shading, "
|
"Use crisp clean comic linework, detailed hatching, soft blended shading, "
|
||||||
"pastel skin tones, muted blues and pinks, warm sensual lighting, and tactile textured paper."
|
"pastel skin tones, muted blues and pinks, warm sensual lighting, and tactile textured paper."
|
||||||
@@ -79,6 +108,7 @@ LAYOUT_TEMPLATE = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
CAMERA_MODE_PROMPTS = {
|
CAMERA_MODE_PROMPTS = {
|
||||||
|
"disabled": "",
|
||||||
"standard": "",
|
"standard": "",
|
||||||
"handheld_selfie": (
|
"handheld_selfie": (
|
||||||
"Camera mode: handheld smartphone selfie, close arm-length framing, visible creator-shot perspective, "
|
"Camera mode: handheld smartphone selfie, close arm-length framing, visible creator-shot perspective, "
|
||||||
@@ -109,6 +139,47 @@ CAMERA_MODE_PROMPTS = {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CAMERA_COMPACT_LABELS = {
|
||||||
|
"disabled": "",
|
||||||
|
"standard": "",
|
||||||
|
"handheld_selfie": "handheld smartphone selfie",
|
||||||
|
"mirror_selfie": "mirror selfie",
|
||||||
|
"phone_tripod": "phone tripod / ring-light setup",
|
||||||
|
"creator_pov": "creator-held POV",
|
||||||
|
"bed_selfie": "bed selfie",
|
||||||
|
"bathroom_mirror": "bathroom mirror selfie",
|
||||||
|
"phone_flash": "phone-flash selfie",
|
||||||
|
"action_cam": "handheld action-camera view",
|
||||||
|
"full_body": "full body",
|
||||||
|
"three_quarter": "three-quarter body",
|
||||||
|
"waist_up": "waist-up",
|
||||||
|
"close_up": "close-up",
|
||||||
|
"extreme_close_up": "extreme close-up",
|
||||||
|
"eye_level": "eye-level",
|
||||||
|
"high_angle": "high-angle",
|
||||||
|
"low_angle": "low-angle",
|
||||||
|
"overhead": "overhead",
|
||||||
|
"side_profile": "side-profile",
|
||||||
|
"rear_view": "rear-view",
|
||||||
|
"mirror_reflection": "mirror reflection",
|
||||||
|
"smartphone_wide": "smartphone wide-angle",
|
||||||
|
"ultra_wide": "ultra-wide",
|
||||||
|
"portrait_lens": "phone portrait lens",
|
||||||
|
"telephoto": "telephoto-style",
|
||||||
|
"macro_detail": "macro detail",
|
||||||
|
"arm_length": "arm-length",
|
||||||
|
"near_body": "near-body",
|
||||||
|
"bedside": "bedside phone",
|
||||||
|
"room_corner": "room-corner phone",
|
||||||
|
"vertical_story": "vertical 9:16",
|
||||||
|
"square_feed": "square feed",
|
||||||
|
"horizontal": "horizontal",
|
||||||
|
"phone_visible": "phone visible",
|
||||||
|
"phone_hidden": "phone hidden",
|
||||||
|
"screen_reflection": "screen reflection",
|
||||||
|
"ring_light_visible": "ring light visible",
|
||||||
|
}
|
||||||
|
|
||||||
CAMERA_SHOT_PROMPTS = {
|
CAMERA_SHOT_PROMPTS = {
|
||||||
"auto": "",
|
"auto": "",
|
||||||
"full_body": "Shot size: full body visible, head-to-toe framing, no important body parts cropped out.",
|
"full_body": "Shot size: full body visible, head-to-toe framing, no important body parts cropped out.",
|
||||||
@@ -917,13 +988,46 @@ def build_filter_config_json(
|
|||||||
figure: str = "curvy",
|
figure: str = "curvy",
|
||||||
no_plus_women: bool = False,
|
no_plus_women: bool = False,
|
||||||
no_black: bool = False,
|
no_black: bool = False,
|
||||||
|
include_european: bool = True,
|
||||||
|
include_mediterranean_mena: bool = True,
|
||||||
|
include_latina: bool = True,
|
||||||
|
include_east_asian: bool = True,
|
||||||
|
include_southeast_asian: bool = True,
|
||||||
|
include_south_asian: bool = True,
|
||||||
|
include_black_african: bool = True,
|
||||||
|
include_indigenous: bool = True,
|
||||||
|
include_mixed: bool = True,
|
||||||
|
include_plus_size: bool = True,
|
||||||
) -> str:
|
) -> str:
|
||||||
|
include_flags = {
|
||||||
|
"european": include_european,
|
||||||
|
"mediterranean_mena": include_mediterranean_mena,
|
||||||
|
"latina": include_latina,
|
||||||
|
"east_asian": include_east_asian,
|
||||||
|
"southeast_asian": include_southeast_asian,
|
||||||
|
"south_asian": include_south_asian,
|
||||||
|
"black_african": include_black_african,
|
||||||
|
"indigenous": include_indigenous,
|
||||||
|
"mixed": include_mixed,
|
||||||
|
}
|
||||||
|
selected_ethnicities = [key for key, enabled in include_flags.items() if enabled]
|
||||||
|
disabled_ethnicities = [key for key, enabled in include_flags.items() if not enabled]
|
||||||
|
enabled_ethnicities = list(selected_ethnicities)
|
||||||
|
if enabled_ethnicities:
|
||||||
|
enabled_ethnicities.extend(f"exclude_{key}" for key in disabled_ethnicities)
|
||||||
|
if 0 < len(selected_ethnicities) < len(include_flags):
|
||||||
|
ethnicity = "+".join(enabled_ethnicities)
|
||||||
|
elif ethnicity not in ETHNICITY_FILTER_CHOICES:
|
||||||
|
ethnicity = "any"
|
||||||
return json.dumps(
|
return json.dumps(
|
||||||
{
|
{
|
||||||
"ethnicity": ethnicity if ethnicity in ("any", "asian", "white_asian") else "any",
|
"ethnicity": ethnicity,
|
||||||
|
"ethnicity_includes": selected_ethnicities,
|
||||||
"figure": figure if figure in ("curvy", "balanced", "bombshell") else "curvy",
|
"figure": figure if figure in ("curvy", "balanced", "bombshell") else "curvy",
|
||||||
"no_plus_women": bool(no_plus_women),
|
"include_plus_size": bool(include_plus_size),
|
||||||
"no_black": bool(no_black),
|
"include_black_african": bool(include_black_african),
|
||||||
|
"no_plus_women": not bool(include_plus_size) or bool(no_plus_women),
|
||||||
|
"no_black": not bool(include_black_african) or bool(no_black),
|
||||||
},
|
},
|
||||||
ensure_ascii=True,
|
ensure_ascii=True,
|
||||||
sort_keys=True,
|
sort_keys=True,
|
||||||
@@ -931,7 +1035,14 @@ def build_filter_config_json(
|
|||||||
|
|
||||||
|
|
||||||
def _parse_filter_config(filter_config: str | dict[str, Any] | None) -> dict[str, Any]:
|
def _parse_filter_config(filter_config: str | dict[str, Any] | None) -> dict[str, Any]:
|
||||||
defaults = {"ethnicity": "any", "figure": "curvy", "no_plus_women": False, "no_black": False}
|
defaults = {
|
||||||
|
"ethnicity": "any",
|
||||||
|
"figure": "curvy",
|
||||||
|
"no_plus_women": False,
|
||||||
|
"no_black": False,
|
||||||
|
"include_plus_size": True,
|
||||||
|
"include_black_african": True,
|
||||||
|
}
|
||||||
if not filter_config:
|
if not filter_config:
|
||||||
return defaults
|
return defaults
|
||||||
if isinstance(filter_config, dict):
|
if isinstance(filter_config, dict):
|
||||||
@@ -944,8 +1055,11 @@ def _parse_filter_config(filter_config: str | dict[str, Any] | None) -> dict[str
|
|||||||
if not isinstance(raw, dict):
|
if not isinstance(raw, dict):
|
||||||
raise ValueError("filter_config must be a JSON object")
|
raise ValueError("filter_config must be a JSON object")
|
||||||
parsed = {**defaults, **raw}
|
parsed = {**defaults, **raw}
|
||||||
parsed["ethnicity"] = parsed["ethnicity"] if parsed.get("ethnicity") in ("any", "asian", "white_asian") else "any"
|
ethnicity = str(parsed.get("ethnicity") or "any")
|
||||||
|
parsed["ethnicity"] = ethnicity if ethnicity == "any" or ethnicity in ETHNICITY_FILTER_CHOICES or "+" in ethnicity else "any"
|
||||||
parsed["figure"] = parsed["figure"] if parsed.get("figure") in ("curvy", "balanced", "bombshell") else "curvy"
|
parsed["figure"] = parsed["figure"] if parsed.get("figure") in ("curvy", "balanced", "bombshell") else "curvy"
|
||||||
|
parsed["include_plus_size"] = bool(parsed.get("include_plus_size"))
|
||||||
|
parsed["include_black_african"] = bool(parsed.get("include_black_african"))
|
||||||
parsed["no_plus_women"] = bool(parsed.get("no_plus_women"))
|
parsed["no_plus_women"] = bool(parsed.get("no_plus_women"))
|
||||||
parsed["no_black"] = bool(parsed.get("no_black"))
|
parsed["no_black"] = bool(parsed.get("no_black"))
|
||||||
return parsed
|
return parsed
|
||||||
@@ -997,6 +1111,34 @@ def build_seed_config_json(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_seed_lock_config_json(
|
||||||
|
base_seed: int = 20260614,
|
||||||
|
reroll_axis: str = "none",
|
||||||
|
reroll_seed: int = -1,
|
||||||
|
) -> str:
|
||||||
|
base_seed = int(base_seed)
|
||||||
|
reroll_seed = int(reroll_seed)
|
||||||
|
reroll_groups = {
|
||||||
|
"none": (),
|
||||||
|
"category": ("category",),
|
||||||
|
"subcategory": ("subcategory",),
|
||||||
|
"content": ("content",),
|
||||||
|
"person": ("person",),
|
||||||
|
"scene": ("scene",),
|
||||||
|
"pose": ("pose", "role"),
|
||||||
|
"role": ("role",),
|
||||||
|
"expression": ("expression",),
|
||||||
|
"composition": ("composition",),
|
||||||
|
"content_pose": ("content", "pose", "role"),
|
||||||
|
"scene_pose": ("scene", "pose", "role"),
|
||||||
|
}
|
||||||
|
reroll = set(reroll_groups.get(str(reroll_axis or "none"), ()))
|
||||||
|
config: dict[str, int] = {}
|
||||||
|
for axis in SEED_LOCK_AXES:
|
||||||
|
config[f"{axis}_seed"] = reroll_seed if axis in reroll else base_seed
|
||||||
|
return json.dumps(config, ensure_ascii=True, sort_keys=True)
|
||||||
|
|
||||||
|
|
||||||
def _parse_seed_config(seed_config: str | dict[str, Any] | None) -> dict[str, int]:
|
def _parse_seed_config(seed_config: str | dict[str, Any] | None) -> dict[str, int]:
|
||||||
if not seed_config:
|
if not seed_config:
|
||||||
return {}
|
return {}
|
||||||
@@ -1075,6 +1217,14 @@ def camera_mode_choices() -> list[str]:
|
|||||||
return list(CAMERA_MODE_PROMPTS)
|
return list(CAMERA_MODE_PROMPTS)
|
||||||
|
|
||||||
|
|
||||||
|
def ethnicity_choices() -> list[str]:
|
||||||
|
return list(ETHNICITY_FILTER_CHOICES)
|
||||||
|
|
||||||
|
|
||||||
|
def camera_detail_choices() -> list[str]:
|
||||||
|
return list(CAMERA_DETAIL_CHOICES)
|
||||||
|
|
||||||
|
|
||||||
def camera_shot_choices() -> list[str]:
|
def camera_shot_choices() -> list[str]:
|
||||||
return list(CAMERA_SHOT_PROMPTS)
|
return list(CAMERA_SHOT_PROMPTS)
|
||||||
|
|
||||||
@@ -1112,6 +1262,7 @@ def build_camera_config_json(
|
|||||||
orientation: str = "auto",
|
orientation: str = "auto",
|
||||||
phone_visibility: str = "auto",
|
phone_visibility: str = "auto",
|
||||||
priority: str = "strong",
|
priority: str = "strong",
|
||||||
|
camera_detail: str = "compact",
|
||||||
) -> str:
|
) -> str:
|
||||||
return json.dumps(
|
return json.dumps(
|
||||||
{
|
{
|
||||||
@@ -1123,6 +1274,7 @@ def build_camera_config_json(
|
|||||||
"orientation": orientation,
|
"orientation": orientation,
|
||||||
"phone_visibility": phone_visibility,
|
"phone_visibility": phone_visibility,
|
||||||
"priority": priority,
|
"priority": priority,
|
||||||
|
"camera_detail": camera_detail,
|
||||||
},
|
},
|
||||||
ensure_ascii=True,
|
ensure_ascii=True,
|
||||||
sort_keys=True,
|
sort_keys=True,
|
||||||
@@ -1144,6 +1296,7 @@ def _parse_camera_config(camera_config: str | dict[str, Any] | None) -> dict[str
|
|||||||
"orientation": "auto",
|
"orientation": "auto",
|
||||||
"phone_visibility": "auto",
|
"phone_visibility": "auto",
|
||||||
"priority": "strong",
|
"priority": "strong",
|
||||||
|
"camera_detail": "compact",
|
||||||
}
|
}
|
||||||
if not camera_config:
|
if not camera_config:
|
||||||
return defaults
|
return defaults
|
||||||
@@ -1166,6 +1319,9 @@ def _parse_camera_config(camera_config: str | dict[str, Any] | None) -> dict[str
|
|||||||
"orientation": _choice(parsed.get("orientation"), CAMERA_ORIENTATION_PROMPTS, defaults["orientation"]),
|
"orientation": _choice(parsed.get("orientation"), CAMERA_ORIENTATION_PROMPTS, defaults["orientation"]),
|
||||||
"phone_visibility": _choice(parsed.get("phone_visibility"), CAMERA_PHONE_PROMPTS, defaults["phone_visibility"]),
|
"phone_visibility": _choice(parsed.get("phone_visibility"), CAMERA_PHONE_PROMPTS, defaults["phone_visibility"]),
|
||||||
"priority": _choice(parsed.get("priority"), CAMERA_PRIORITY_PROMPTS, defaults["priority"]),
|
"priority": _choice(parsed.get("priority"), CAMERA_PRIORITY_PROMPTS, defaults["priority"]),
|
||||||
|
"camera_detail": str(parsed.get("camera_detail") or defaults["camera_detail"])
|
||||||
|
if str(parsed.get("camera_detail") or defaults["camera_detail"]) in CAMERA_DETAIL_CHOICES
|
||||||
|
else defaults["camera_detail"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1178,6 +1334,26 @@ def _camera_config_with_mode(camera_config: str | dict[str, Any] | None, camera_
|
|||||||
|
|
||||||
def _camera_directive(camera_config: str | dict[str, Any] | None) -> tuple[str, dict[str, str]]:
|
def _camera_directive(camera_config: str | dict[str, Any] | None) -> tuple[str, dict[str, str]]:
|
||||||
parsed = _parse_camera_config(camera_config)
|
parsed = _parse_camera_config(camera_config)
|
||||||
|
if parsed["camera_detail"] == "off" or parsed["camera_mode"] == "disabled":
|
||||||
|
return "", parsed
|
||||||
|
if parsed["camera_detail"] == "compact":
|
||||||
|
values = [
|
||||||
|
parsed["camera_mode"],
|
||||||
|
parsed["shot_size"],
|
||||||
|
parsed["angle"],
|
||||||
|
parsed["lens"],
|
||||||
|
parsed["distance"],
|
||||||
|
parsed["orientation"],
|
||||||
|
parsed["phone_visibility"],
|
||||||
|
]
|
||||||
|
labels = [CAMERA_COMPACT_LABELS.get(value, value.replace("_", " ")) for value in values]
|
||||||
|
labels = [label for value, label in zip(values, labels) if label and value != "auto"]
|
||||||
|
if not labels:
|
||||||
|
return "", parsed
|
||||||
|
directive = "Camera: " + ", ".join(labels) + "."
|
||||||
|
if parsed["priority"] == "locked":
|
||||||
|
directive += " Keep this camera framing."
|
||||||
|
return directive, parsed
|
||||||
parts = [
|
parts = [
|
||||||
CAMERA_MODE_PROMPTS[parsed["camera_mode"]],
|
CAMERA_MODE_PROMPTS[parsed["camera_mode"]],
|
||||||
CAMERA_SHOT_PROMPTS[parsed["shot_size"]],
|
CAMERA_SHOT_PROMPTS[parsed["shot_size"]],
|
||||||
@@ -1679,7 +1855,7 @@ def _appearance_for_subject(
|
|||||||
subject_type = "woman" if rng.random() < 0.82 else "man"
|
subject_type = "woman" if rng.random() < 0.82 else "man"
|
||||||
|
|
||||||
if subject_type == "man":
|
if subject_type == "man":
|
||||||
men_ethnicity = ethnicity if ethnicity == "asian" else "any"
|
men_ethnicity = ethnicity if ethnicity else "any"
|
||||||
subject, age, body, skin, hair, eyes = g.choose(rng, g.by_ethnicity(g.MEN, men_ethnicity))
|
subject, age, body, skin, hair, eyes = g.choose(rng, g.by_ethnicity(g.MEN, men_ethnicity))
|
||||||
return {
|
return {
|
||||||
"subject_type": "man",
|
"subject_type": "man",
|
||||||
@@ -2452,7 +2628,7 @@ def build_prompt(
|
|||||||
start_index = max(1, int(start_index))
|
start_index = max(1, int(start_index))
|
||||||
seed = int(seed)
|
seed = int(seed)
|
||||||
clothing = clothing if clothing in ("full", "minimal") else "full"
|
clothing = clothing if clothing in ("full", "minimal") else "full"
|
||||||
ethnicity = ethnicity if ethnicity in ("any", "asian", "white_asian") else "any"
|
ethnicity = ethnicity if ethnicity == "any" or ethnicity in ETHNICITY_FILTER_CHOICES or "+" in str(ethnicity) else "any"
|
||||||
poses = poses if poses in ("standard", "evocative") else "standard"
|
poses = poses if poses in ("standard", "evocative") else "standard"
|
||||||
figure = figure if figure in ("curvy", "balanced", "bombshell") else "curvy"
|
figure = figure if figure in ("curvy", "balanced", "bombshell") else "curvy"
|
||||||
minimal_ratio = _ratio_or_none(minimal_clothing_ratio)
|
minimal_ratio = _ratio_or_none(minimal_clothing_ratio)
|
||||||
@@ -2574,6 +2750,7 @@ INSTA_OF_SOFT_LEVELS = {
|
|||||||
"lingerie_tease": "premium OF teaser set, lingerie-focused, sensual and intimate",
|
"lingerie_tease": "premium OF teaser set, lingerie-focused, sensual and intimate",
|
||||||
"implied_nude": "implied nude creator set, strategically covered body and intimate teaser framing",
|
"implied_nude": "implied nude creator set, strategically covered body and intimate teaser framing",
|
||||||
"explicit_tease": "stronger adult teaser set with bolder nude-adjacent styling and solo-tease framing",
|
"explicit_tease": "stronger adult teaser set with bolder nude-adjacent styling and solo-tease framing",
|
||||||
|
"explicit_nude": "explicit nude creator set with fully nude solo-tease framing",
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTA_OF_HARDCORE_LEVELS = {
|
INSTA_OF_HARDCORE_LEVELS = {
|
||||||
@@ -2587,6 +2764,14 @@ INSTA_OF_PLATFORM_STYLES = {
|
|||||||
"onlyfans": "OnlyFans-inspired creator shoot, intimate subscriber-view camera and candid premium-content framing",
|
"onlyfans": "OnlyFans-inspired creator shoot, intimate subscriber-view camera and candid premium-content framing",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INSTA_OF_HARDCORE_CLOTHING_CONTINUITY = {
|
||||||
|
"none": "",
|
||||||
|
"same_outfit": "Woman A keeps the softcore outfit in the hardcore scene",
|
||||||
|
"partially_removed": "Woman A's softcore outfit is partially removed or pushed aside for the hardcore scene",
|
||||||
|
"implied_nude": "Woman A is nude-adjacent in the hardcore scene, with the softcore outfit slipping off or covering only part of the body",
|
||||||
|
"explicit_nude": "Woman A is fully nude in the hardcore scene, with the removed softcore outfit visible nearby",
|
||||||
|
}
|
||||||
|
|
||||||
INSTA_OF_NEGATIVE = (
|
INSTA_OF_NEGATIVE = (
|
||||||
"minors, childlike appearance, teen, underage, schoolgirl, non-consensual, coercion, rape, "
|
"minors, childlike appearance, teen, underage, schoolgirl, non-consensual, coercion, rape, "
|
||||||
"violence, injury, blood, gore, incest, bestiality, watermark, logo, readable username, social media UI"
|
"violence, injury, blood, gore, incest, bestiality, watermark, logo, readable username, social media UI"
|
||||||
@@ -2603,6 +2788,7 @@ INSTA_OF_SOFTCORE_SUBCATEGORY_BY_LEVEL = {
|
|||||||
"lingerie_tease": "Provocative erotic clothes / Provocative lingerie",
|
"lingerie_tease": "Provocative erotic clothes / Provocative lingerie",
|
||||||
"implied_nude": "Provocative erotic clothes / Provocative lingerie",
|
"implied_nude": "Provocative erotic clothes / Provocative lingerie",
|
||||||
"explicit_tease": "Provocative erotic clothes / Sheer exposed",
|
"explicit_tease": "Provocative erotic clothes / Sheer exposed",
|
||||||
|
"explicit_nude": "Provocative erotic clothes / Nude accessories",
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTA_OF_SOFTCORE_OUTFITS = {
|
INSTA_OF_SOFTCORE_OUTFITS = {
|
||||||
@@ -2642,6 +2828,14 @@ INSTA_OF_SOFTCORE_OUTFITS = {
|
|||||||
"bare-shoulder robe opened around covered lingerie, explicit adult tease without partnered contact",
|
"bare-shoulder robe opened around covered lingerie, explicit adult tease without partnered contact",
|
||||||
"strappy lingerie set with covered cups and high-waisted bottoms, styled as a stronger solo teaser",
|
"strappy lingerie set with covered cups and high-waisted bottoms, styled as a stronger solo teaser",
|
||||||
],
|
],
|
||||||
|
"explicit_nude": [
|
||||||
|
"fully nude creator styling with jewelry, heels, and direct adult selfie confidence",
|
||||||
|
"fully nude mirror-selfie styling with jewelry only and bold creator-shot framing",
|
||||||
|
"nude-on-sheets creator pose with lingerie discarded nearby and direct eye contact",
|
||||||
|
"fully nude vanity-mirror pose with heels, necklace, and premium adult teaser styling",
|
||||||
|
"nude shower-afterglow creator pose with wet hair, skin highlights, and phone-shot framing",
|
||||||
|
"fully nude bedroom creator pose with one hand holding the phone and lingerie visible nearby",
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTA_OF_SOFTCORE_POSES = {
|
INSTA_OF_SOFTCORE_POSES = {
|
||||||
@@ -2677,6 +2871,14 @@ INSTA_OF_SOFTCORE_POSES = {
|
|||||||
"sitting at the vanity in a bolder covered lingerie pose with direct eye contact",
|
"sitting at the vanity in a bolder covered lingerie pose with direct eye contact",
|
||||||
"arching subtly in a solo adult tease while the styling keeps explicit anatomy obscured",
|
"arching subtly in a solo adult tease while the styling keeps explicit anatomy obscured",
|
||||||
],
|
],
|
||||||
|
"explicit_nude": [
|
||||||
|
"taking a bold nude mirror selfie with direct eye contact and the body clearly framed",
|
||||||
|
"posing fully nude on the bed with jewelry and heels as the only styling",
|
||||||
|
"standing at the vanity fully nude in a premium creator-shot pose",
|
||||||
|
"reclining fully nude on soft sheets with the phone held close",
|
||||||
|
"turning slightly in a nude mirror pose with the body framed head-to-thigh",
|
||||||
|
"kneeling fully nude in a controlled adult teaser pose with direct phone-camera awareness",
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTA_OF_SOFTCORE_PARTNER_WOMEN_OUTFITS = [
|
INSTA_OF_SOFTCORE_PARTNER_WOMEN_OUTFITS = [
|
||||||
@@ -2711,8 +2913,10 @@ def build_insta_of_options_json(
|
|||||||
hardcore_level: str = "hardcore",
|
hardcore_level: str = "hardcore",
|
||||||
platform_style: str = "hybrid",
|
platform_style: str = "hybrid",
|
||||||
continuity: str = "same_creator_same_room",
|
continuity: str = "same_creator_same_room",
|
||||||
|
hardcore_clothing_continuity: str = "partially_removed",
|
||||||
softcore_camera_mode: str = "handheld_selfie",
|
softcore_camera_mode: str = "handheld_selfie",
|
||||||
hardcore_camera_mode: str = "same_as_softcore",
|
hardcore_camera_mode: str = "same_as_softcore",
|
||||||
|
camera_detail: str = "compact",
|
||||||
softcore_expression_intensity: float = 0.45,
|
softcore_expression_intensity: float = 0.45,
|
||||||
hardcore_expression_intensity: float = 0.85,
|
hardcore_expression_intensity: float = 0.85,
|
||||||
) -> str:
|
) -> str:
|
||||||
@@ -2726,8 +2930,10 @@ def build_insta_of_options_json(
|
|||||||
"hardcore_level": hardcore_level,
|
"hardcore_level": hardcore_level,
|
||||||
"platform_style": platform_style,
|
"platform_style": platform_style,
|
||||||
"continuity": continuity,
|
"continuity": continuity,
|
||||||
|
"hardcore_clothing_continuity": hardcore_clothing_continuity,
|
||||||
"softcore_camera_mode": softcore_camera_mode,
|
"softcore_camera_mode": softcore_camera_mode,
|
||||||
"hardcore_camera_mode": hardcore_camera_mode,
|
"hardcore_camera_mode": hardcore_camera_mode,
|
||||||
|
"camera_detail": camera_detail,
|
||||||
"softcore_expression_intensity": _clamped_float(softcore_expression_intensity, 0.45),
|
"softcore_expression_intensity": _clamped_float(softcore_expression_intensity, 0.45),
|
||||||
"hardcore_expression_intensity": _clamped_float(hardcore_expression_intensity, 0.85),
|
"hardcore_expression_intensity": _clamped_float(hardcore_expression_intensity, 0.85),
|
||||||
},
|
},
|
||||||
@@ -2746,8 +2952,10 @@ def _parse_insta_of_options(options_json: str | dict[str, Any] | None) -> dict[s
|
|||||||
"hardcore_level": "hardcore",
|
"hardcore_level": "hardcore",
|
||||||
"platform_style": "hybrid",
|
"platform_style": "hybrid",
|
||||||
"continuity": "same_creator_same_room",
|
"continuity": "same_creator_same_room",
|
||||||
|
"hardcore_clothing_continuity": "partially_removed",
|
||||||
"softcore_camera_mode": "handheld_selfie",
|
"softcore_camera_mode": "handheld_selfie",
|
||||||
"hardcore_camera_mode": "same_as_softcore",
|
"hardcore_camera_mode": "same_as_softcore",
|
||||||
|
"camera_detail": "compact",
|
||||||
"softcore_expression_intensity": 0.45,
|
"softcore_expression_intensity": 0.45,
|
||||||
"hardcore_expression_intensity": 0.85,
|
"hardcore_expression_intensity": 0.85,
|
||||||
}
|
}
|
||||||
@@ -2769,9 +2977,15 @@ def _parse_insta_of_options(options_json: str | dict[str, Any] | None) -> dict[s
|
|||||||
parsed["hardcore_level"] = parsed["hardcore_level"] if parsed["hardcore_level"] in INSTA_OF_HARDCORE_LEVELS else defaults["hardcore_level"]
|
parsed["hardcore_level"] = parsed["hardcore_level"] if parsed["hardcore_level"] in INSTA_OF_HARDCORE_LEVELS else defaults["hardcore_level"]
|
||||||
parsed["platform_style"] = parsed["platform_style"] if parsed["platform_style"] in INSTA_OF_PLATFORM_STYLES else defaults["platform_style"]
|
parsed["platform_style"] = parsed["platform_style"] if parsed["platform_style"] in INSTA_OF_PLATFORM_STYLES else defaults["platform_style"]
|
||||||
parsed["continuity"] = parsed["continuity"] if parsed["continuity"] in ("same_creator_same_room", "same_creator_new_scene") else defaults["continuity"]
|
parsed["continuity"] = parsed["continuity"] if parsed["continuity"] in ("same_creator_same_room", "same_creator_new_scene") else defaults["continuity"]
|
||||||
|
parsed["hardcore_clothing_continuity"] = (
|
||||||
|
parsed["hardcore_clothing_continuity"]
|
||||||
|
if parsed["hardcore_clothing_continuity"] in INSTA_OF_HARDCORE_CLOTHING_CONTINUITY
|
||||||
|
else defaults["hardcore_clothing_continuity"]
|
||||||
|
)
|
||||||
parsed["softcore_camera_mode"] = parsed["softcore_camera_mode"] if parsed["softcore_camera_mode"] in CAMERA_MODE_PROMPTS else defaults["softcore_camera_mode"]
|
parsed["softcore_camera_mode"] = parsed["softcore_camera_mode"] if parsed["softcore_camera_mode"] in CAMERA_MODE_PROMPTS else defaults["softcore_camera_mode"]
|
||||||
if parsed["hardcore_camera_mode"] not in CAMERA_MODE_PROMPTS and parsed["hardcore_camera_mode"] != "same_as_softcore":
|
if parsed["hardcore_camera_mode"] not in CAMERA_MODE_PROMPTS and parsed["hardcore_camera_mode"] != "same_as_softcore":
|
||||||
parsed["hardcore_camera_mode"] = defaults["hardcore_camera_mode"]
|
parsed["hardcore_camera_mode"] = defaults["hardcore_camera_mode"]
|
||||||
|
parsed["camera_detail"] = parsed["camera_detail"] if parsed["camera_detail"] in CAMERA_DETAIL_CHOICES else defaults["camera_detail"]
|
||||||
parsed["softcore_expression_intensity"] = _clamped_float(
|
parsed["softcore_expression_intensity"] = _clamped_float(
|
||||||
parsed.get("softcore_expression_intensity"),
|
parsed.get("softcore_expression_intensity"),
|
||||||
defaults["softcore_expression_intensity"],
|
defaults["softcore_expression_intensity"],
|
||||||
@@ -2865,13 +3079,17 @@ def _insta_of_cast_phrase(women_count: int, men_count: int) -> str:
|
|||||||
return context["cast_summary"]
|
return context["cast_summary"]
|
||||||
|
|
||||||
|
|
||||||
|
def _insta_of_prompt_cast_descriptors(text: str) -> str:
|
||||||
|
return str(text or "").replace("Woman A / primary creator:", "Woman A:")
|
||||||
|
|
||||||
|
|
||||||
SOFTCORE_CAST_POSES = [
|
SOFTCORE_CAST_POSES = [
|
||||||
"standing together for a mirror selfie with bodies close but no sexual contact",
|
"standing together for a mirror selfie with bodies close but no sexual contact",
|
||||||
"posing shoulder-to-shoulder in a creator-shot group teaser",
|
"posing shoulder-to-shoulder in a creator-shot group teaser",
|
||||||
"leaning together on the bed in a non-explicit subscriber preview",
|
"leaning together on the bed in a non-explicit subscriber preview",
|
||||||
"sitting close together with hands kept above clothing",
|
"sitting close together with hands kept above clothing",
|
||||||
"arranged around Woman A in a flirtatious non-explicit teaser pose",
|
"arranged around Woman A in a flirtatious non-explicit teaser pose",
|
||||||
"posing in the same room as a coordinated adult creator set",
|
"posing together as a coordinated adult creator set",
|
||||||
"standing near the phone tripod with relaxed teasing body language",
|
"standing near the phone tripod with relaxed teasing body language",
|
||||||
"framed together in a softcore cast reveal with no sex act",
|
"framed together in a softcore cast reveal with no sex act",
|
||||||
]
|
]
|
||||||
@@ -2896,6 +3114,15 @@ def _insta_of_softcore_pose(rng: random.Random, level: str) -> str:
|
|||||||
return g.choose(rng, pool)
|
return g.choose(rng, pool)
|
||||||
|
|
||||||
|
|
||||||
|
def _insta_of_hardcore_clothing_state(mode: str, softcore_outfit: str) -> str:
|
||||||
|
mode = mode if mode in INSTA_OF_HARDCORE_CLOTHING_CONTINUITY else "none"
|
||||||
|
outfit = str(softcore_outfit or "").strip()
|
||||||
|
if mode == "none" or not outfit:
|
||||||
|
return ""
|
||||||
|
base = INSTA_OF_HARDCORE_CLOTHING_CONTINUITY[mode]
|
||||||
|
return f"Clothing state: {base}; softcore visual reference: {outfit}."
|
||||||
|
|
||||||
|
|
||||||
def _insta_of_partner_styling(
|
def _insta_of_partner_styling(
|
||||||
seed_config: dict[str, int],
|
seed_config: dict[str, int],
|
||||||
seed: int,
|
seed: int,
|
||||||
@@ -2934,12 +3161,19 @@ def build_insta_of_pair(
|
|||||||
prepend_trigger_to_prompt: bool,
|
prepend_trigger_to_prompt: bool,
|
||||||
seed_config: str | dict[str, Any] | None = None,
|
seed_config: str | dict[str, Any] | None = None,
|
||||||
options_json: str | dict[str, Any] | None = None,
|
options_json: str | dict[str, Any] | None = None,
|
||||||
|
filter_config: str | dict[str, Any] | None = None,
|
||||||
camera_config: str | dict[str, Any] | None = None,
|
camera_config: str | dict[str, Any] | None = None,
|
||||||
character_profile: str | dict[str, Any] | None = "",
|
character_profile: str | dict[str, Any] | None = "",
|
||||||
extra_positive: str = "",
|
extra_positive: str = "",
|
||||||
extra_negative: str = "",
|
extra_negative: str = "",
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
options = _parse_insta_of_options(options_json)
|
options = _parse_insta_of_options(options_json)
|
||||||
|
if filter_config:
|
||||||
|
filters = _parse_filter_config(filter_config)
|
||||||
|
ethnicity = filters["ethnicity"]
|
||||||
|
figure = filters["figure"]
|
||||||
|
no_plus_women = filters["no_plus_women"]
|
||||||
|
no_black = filters["no_black"]
|
||||||
hard_women_count, hard_men_count = _insta_of_hardcore_counts(options)
|
hard_women_count, hard_men_count = _insta_of_hardcore_counts(options)
|
||||||
active_trigger = trigger.strip() or g.TRIGGER
|
active_trigger = trigger.strip() or g.TRIGGER
|
||||||
parsed_seed_config = _parse_seed_config(seed_config)
|
parsed_seed_config = _parse_seed_config(seed_config)
|
||||||
@@ -3015,11 +3249,11 @@ def build_insta_of_pair(
|
|||||||
hard_women_count,
|
hard_women_count,
|
||||||
hard_men_count,
|
hard_men_count,
|
||||||
)
|
)
|
||||||
cast_descriptor_text = "; ".join(cast_descriptors)
|
cast_descriptor_text = _insta_of_prompt_cast_descriptors("; ".join(cast_descriptors))
|
||||||
soft_cast_descriptor_text = (
|
soft_cast_descriptor_text = (
|
||||||
cast_descriptor_text
|
cast_descriptor_text
|
||||||
if options["softcore_cast"] == "same_as_hardcore"
|
if options["softcore_cast"] == "same_as_hardcore"
|
||||||
else f"Woman A / primary creator: {descriptor}"
|
else f"Woman A: {descriptor}"
|
||||||
)
|
)
|
||||||
soft_partner_styling = _insta_of_partner_styling(
|
soft_partner_styling = _insta_of_partner_styling(
|
||||||
parsed_seed_config,
|
parsed_seed_config,
|
||||||
@@ -3039,33 +3273,44 @@ def build_insta_of_pair(
|
|||||||
hard_camera_mode = options["softcore_camera_mode"]
|
hard_camera_mode = options["softcore_camera_mode"]
|
||||||
soft_camera_config = _camera_config_with_mode(camera_config, options["softcore_camera_mode"])
|
soft_camera_config = _camera_config_with_mode(camera_config, options["softcore_camera_mode"])
|
||||||
hard_camera_config = _camera_config_with_mode(camera_config, hard_camera_mode)
|
hard_camera_config = _camera_config_with_mode(camera_config, hard_camera_mode)
|
||||||
|
soft_camera_config["camera_detail"] = options["camera_detail"]
|
||||||
|
hard_camera_config["camera_detail"] = options["camera_detail"]
|
||||||
soft_camera_directive, soft_camera_config = _camera_directive(soft_camera_config)
|
soft_camera_directive, soft_camera_config = _camera_directive(soft_camera_config)
|
||||||
hard_camera_directive, hard_camera_config = _camera_directive(hard_camera_config)
|
hard_camera_directive, hard_camera_config = _camera_directive(hard_camera_config)
|
||||||
soft_camera_sentence = f"Camera control: {soft_camera_directive} " if soft_camera_directive else ""
|
soft_camera_sentence = f"Camera control: {soft_camera_directive} " if soft_camera_directive else ""
|
||||||
hard_camera_sentence = f"Camera control: {hard_camera_directive} " if hard_camera_directive else ""
|
hard_camera_sentence = f"Camera control: {hard_camera_directive} " if hard_camera_directive else ""
|
||||||
hard_scene = soft_row["scene_text"] if options["continuity"] == "same_creator_same_room" else hard_row["scene_text"]
|
hard_scene = soft_row["scene_text"] if options["continuity"] == "same_creator_same_room" else hard_row["scene_text"]
|
||||||
hard_composition = soft_row["composition"] if options["continuity"] == "same_creator_same_room" else hard_row["composition"]
|
hard_composition = hard_row["composition"]
|
||||||
soft_cast = (
|
soft_cast = (
|
||||||
"solo creator setup; the primary creator is alone in the softcore version"
|
"solo creator setup with Woman A alone"
|
||||||
if options["softcore_cast"] == "solo"
|
if options["softcore_cast"] == "solo"
|
||||||
else f"non-explicit teaser setup with the same adult cast as the hardcore version: {_insta_of_cast_phrase(hard_women_count, hard_men_count)}"
|
else f"non-explicit teaser setup with {_insta_of_cast_phrase(hard_women_count, hard_men_count)}"
|
||||||
)
|
)
|
||||||
soft_cast_presence = (
|
soft_cast_presence = (
|
||||||
"Keep the same cast together in the softcore version in a non-explicit teaser pose with no sex act or genital contact. "
|
"Place Woman A and the listed partners together in a non-explicit teaser pose with no sex act or genital contact. "
|
||||||
if options["softcore_cast"] == "same_as_hardcore"
|
if options["softcore_cast"] == "same_as_hardcore"
|
||||||
else "Keep the softcore version focused on Woman A alone. "
|
else "Keep the softcore version focused on Woman A alone. "
|
||||||
)
|
)
|
||||||
soft_cast_styling_sentence = (
|
soft_cast_styling_sentence = (
|
||||||
f"Partner softcore styling: {soft_partner_outfit_text}. Shared softcore cast pose: {soft_partner_styling['pose']}. "
|
f"Partner softcore styling: {soft_partner_outfit_text}. Cast pose: {soft_partner_styling['pose']}. "
|
||||||
if options["softcore_cast"] == "same_as_hardcore" and soft_partner_outfit_text
|
if options["softcore_cast"] == "same_as_hardcore" and soft_partner_outfit_text
|
||||||
else ""
|
else ""
|
||||||
)
|
)
|
||||||
hard_cast = _insta_of_cast_phrase(hard_women_count, hard_men_count)
|
hard_cast = _insta_of_cast_phrase(hard_women_count, hard_men_count)
|
||||||
|
hard_clothing_state = _insta_of_hardcore_clothing_state(
|
||||||
|
options["hardcore_clothing_continuity"],
|
||||||
|
soft_row["item"],
|
||||||
|
)
|
||||||
|
soft_descriptor_sentence = (
|
||||||
|
f"Cast descriptors: {soft_cast_descriptor_text}. "
|
||||||
|
if options["softcore_cast"] == "same_as_hardcore"
|
||||||
|
else f"Woman A: {descriptor}. "
|
||||||
|
)
|
||||||
|
|
||||||
soft_prompt = (
|
soft_prompt = (
|
||||||
f"Insta/OF softcore mode: {platform_style}. Shared primary creator descriptor: {descriptor}. "
|
f"Insta/OF softcore mode: {platform_style}. "
|
||||||
f"Softcore setup: {soft_level}. Cast continuity: {soft_cast}. "
|
f"{soft_descriptor_sentence}"
|
||||||
f"Shared cast descriptors: {soft_cast_descriptor_text}. "
|
f"Softcore setup: {soft_level}. Cast: {soft_cast}. "
|
||||||
f"{soft_cast_presence}"
|
f"{soft_cast_presence}"
|
||||||
f"{soft_cast_styling_sentence}"
|
f"{soft_cast_styling_sentence}"
|
||||||
f"Outfit: {soft_row['item']}. Pose: {soft_row['pose']}. Setting: {soft_row['scene_text']}. "
|
f"Outfit: {soft_row['item']}. Pose: {soft_row['pose']}. Setting: {soft_row['scene_text']}. "
|
||||||
@@ -3075,10 +3320,11 @@ def build_insta_of_pair(
|
|||||||
f"{soft_row['positive_suffix']}."
|
f"{soft_row['positive_suffix']}."
|
||||||
)
|
)
|
||||||
hard_prompt = (
|
hard_prompt = (
|
||||||
f"Insta/OF hardcore mode: {platform_style}. Shared primary creator descriptor: {descriptor}. "
|
f"Insta/OF hardcore mode: {platform_style}. "
|
||||||
f"Hardcore setup: {hard_level}. Cast: {hard_cast}. "
|
f"Hardcore setup: {hard_level}. Cast: {hard_cast}. "
|
||||||
f"Shared cast descriptors: {cast_descriptor_text}. "
|
f"Cast descriptors: {cast_descriptor_text}. "
|
||||||
"Apply the shared descriptor to the most visually central woman, keeping her continuous with the softcore version. "
|
"Keep Woman A visually central. "
|
||||||
|
f"{hard_clothing_state} "
|
||||||
f"Role graph: {hard_row['role_graph']} Sexual scene: {hard_row['item']}. "
|
f"Role graph: {hard_row['role_graph']} Sexual scene: {hard_row['item']}. "
|
||||||
f"Setting: {hard_scene}. Facial expressions: {hard_row['expression']}. Composition: {hard_composition}. "
|
f"Setting: {hard_scene}. Facial expressions: {hard_row['expression']}. Composition: {hard_composition}. "
|
||||||
f"{hard_camera_sentence}"
|
f"{hard_camera_sentence}"
|
||||||
@@ -3103,20 +3349,29 @@ def build_insta_of_pair(
|
|||||||
soft_partner_styling["pose"],
|
soft_partner_styling["pose"],
|
||||||
soft_row["scene_text"],
|
soft_row["scene_text"],
|
||||||
soft_row["composition"],
|
soft_row["composition"],
|
||||||
f"{soft_camera_config['camera_mode'].replace('_', ' ')} camera",
|
f"{soft_camera_config['camera_mode'].replace('_', ' ')} camera" if soft_camera_directive else "",
|
||||||
]
|
]
|
||||||
soft_caption = ", ".join(str(part).strip() for part in soft_caption_parts if str(part).strip())
|
soft_caption = ", ".join(str(part).strip() for part in soft_caption_parts if str(part).strip())
|
||||||
hard_caption = (
|
hard_caption_parts = [
|
||||||
f"{active_trigger}, Insta/OF hardcore mode, same primary creator descriptor, {descriptor}, "
|
active_trigger,
|
||||||
f"{hard_cast}, {hard_row['role_graph']}, {hard_row['item']}, {hard_scene}, {hard_composition}, "
|
"Insta/OF hardcore mode",
|
||||||
f"{hard_camera_config['camera_mode'].replace('_', ' ')} camera"
|
"Woman A",
|
||||||
)
|
descriptor,
|
||||||
|
hard_cast,
|
||||||
|
hard_row["role_graph"],
|
||||||
|
hard_row["item"],
|
||||||
|
hard_scene,
|
||||||
|
hard_composition,
|
||||||
|
f"{hard_camera_config['camera_mode'].replace('_', ' ')} camera" if hard_camera_directive else "",
|
||||||
|
]
|
||||||
|
hard_caption = ", ".join(str(part).strip() for part in hard_caption_parts if str(part).strip())
|
||||||
metadata = {
|
metadata = {
|
||||||
"mode": "Insta/OF",
|
"mode": "Insta/OF",
|
||||||
"options": options,
|
"options": options,
|
||||||
"shared_descriptor": descriptor,
|
"shared_descriptor": descriptor,
|
||||||
"shared_cast_descriptors": cast_descriptors,
|
"shared_cast_descriptors": cast_descriptors,
|
||||||
"softcore_partner_styling": soft_partner_styling,
|
"softcore_partner_styling": soft_partner_styling,
|
||||||
|
"hardcore_clothing_state": hard_clothing_state,
|
||||||
"softcore_prompt": soft_prompt,
|
"softcore_prompt": soft_prompt,
|
||||||
"hardcore_prompt": hard_prompt,
|
"hardcore_prompt": hard_prompt,
|
||||||
"softcore_negative_prompt": soft_negative,
|
"softcore_negative_prompt": soft_negative,
|
||||||
|
|||||||
Reference in New Issue
Block a user