Add expression intensity controls

This commit is contained in:
2026-06-24 10:44:13 +02:00
parent 89af926a5a
commit a1c6dc2391
7 changed files with 792 additions and 46 deletions
+38 -10
View File
@@ -127,6 +127,11 @@ Options:
- `softcore_level`: `social_tease`, `lingerie_tease`, `implied_nude`, or - `softcore_level`: `social_tease`, `lingerie_tease`, `implied_nude`, or
`explicit_tease`. `explicit_tease`.
- `hardcore_level`: `explicit` or `hardcore`. - `hardcore_level`: `explicit` or `hardcore`.
- `softcore_expression_intensity`: `0.0` is mild/controlled, `0.5` is sensual,
`1.0` strongly favors more heated softcore faces.
- `hardcore_expression_intensity`: `0.0` is controlled, `0.5` is balanced
hardcore, `1.0` strongly favors ahegao-style, drooling, fucked-out, climax,
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/composition aligned;
`same_creator_new_scene` keeps the same creator descriptor but lets the `same_creator_new_scene` keeps the same creator descriptor but lets the
@@ -144,6 +149,11 @@ The node keeps the original generator controls:
- `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`, `asian`, `white_asian`.
- `poses`: `standard` or `evocative`. - `poses`: `standard` or `evocative`.
- `expression_intensity`: `0.0` favors mild, neutral, controlled expressions;
`0.5` favors balanced category expressions; `1.0` strongly favors the most
intense expressions available in the selected category. This affects custom
JSON categories such as `Provocative erotic clothes` and `Hardcore sexual
poses`.
- `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`.
@@ -210,22 +220,36 @@ provided, the node uses a generic composer that selects subject appearance,
scene, pose, expression, composition, and a random item from the selected scene, pose, expression, composition, and a random item from the selected
subcategory. subcategory.
Reusable location banks can be defined with top-level `scene_pools` in any Reusable banks can be defined with top-level `scene_pools`,
`categories/*.json` file. Categories, subcategories, and items can reference `expression_pools`, and `composition_pools` in any `categories/*.json` file.
them with `scene_pools`; referenced pools are merged with any local `scenes`. Categories, subcategories, and items can reference them with `scene_pools`,
This keeps location expansion scalable without duplicating the same bedroom, `expression_pools`, and `composition_pools`; referenced pools are merged with
selfie, mirror, creator, or group-sex locations across every subcategory. any local `scenes`, `expressions`, or `compositions`. This keeps expansion
scalable without duplicating the same bedroom, selfie, mirror, creator,
expression, camera, or group-sex framing across every subcategory.
Set `"inherit_scenes": false` on a subcategory or item when it should use only Set `"inherit_scenes": false`, `"inherit_expressions": false`, or
its own `scenes` and `scene_pools` instead of also inheriting parent category `"inherit_compositions": false` on a subcategory or item when it should use only
locations. This is useful for narrow subcategories such as group scenes, vehicle its own pools instead of also inheriting parent category values. This is useful
sets, outdoor-only sets, or any category where a generic parent room would be a for narrow subcategories such as group scenes, fetish sets, outdoor-only sets,
bad match. or any category where generic parent wording would be a bad match.
Example: Example:
```json ```json
{ {
"expression_pools": {
"creator_tease_faces": [
"direct creator-shot eye contact",
"heavy-lidded bedroom gaze"
]
},
"composition_pools": {
"creator_selfie_frames": [
"handheld selfie crop from face to hips",
"mirror selfie with phone visible and body framed clearly"
]
},
"scene_pools": { "scene_pools": {
"creator_selfie_rooms": [ "creator_selfie_rooms": [
{ {
@@ -241,7 +265,11 @@ Example:
{ {
"name": "Selfie set", "name": "Selfie set",
"inherit_scenes": false, "inherit_scenes": false,
"inherit_expressions": false,
"inherit_compositions": false,
"scene_pools": ["creator_selfie_rooms"], "scene_pools": ["creator_selfie_rooms"],
"expression_pools": ["creator_tease_faces"],
"composition_pools": ["creator_selfie_frames"],
"items": ["simple outfit prompt"] "items": ["simple outfit prompt"]
} }
] ]
+9
View File
@@ -57,6 +57,7 @@ class SxCPPromptBuilder:
"clothing": (["full", "minimal"], {"default": "full"}), "clothing": (["full", "minimal"], {"default": "full"}),
"ethnicity": (["any", "asian", "white_asian"], {"default": "any"}), "ethnicity": (["any", "asian", "white_asian"], {"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}),
"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_plus_women": ("BOOLEAN", {"default": False}),
@@ -91,6 +92,7 @@ class SxCPPromptBuilder:
clothing, clothing,
ethnicity, ethnicity,
poses, poses,
expression_intensity,
backside_bias, backside_bias,
figure, figure,
no_plus_women, no_plus_women,
@@ -115,6 +117,7 @@ class SxCPPromptBuilder:
clothing=clothing, clothing=clothing,
ethnicity=ethnicity, ethnicity=ethnicity,
poses=poses, poses=poses,
expression_intensity=expression_intensity,
backside_bias=backside_bias, backside_bias=backside_bias,
figure=figure, figure=figure,
no_plus_women=no_plus_women, no_plus_women=no_plus_women,
@@ -359,6 +362,8 @@ class SxCPInstaOFOptions:
"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"], {"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}),
"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"}),
"softcore_camera_mode": (camera_mode_choices(), {"default": "handheld_selfie"}), "softcore_camera_mode": (camera_mode_choices(), {"default": "handheld_selfie"}),
@@ -379,6 +384,8 @@ class SxCPInstaOFOptions:
hardcore_men_count, hardcore_men_count,
softcore_level, softcore_level,
hardcore_level, hardcore_level,
softcore_expression_intensity,
hardcore_expression_intensity,
platform_style, platform_style,
continuity, continuity,
softcore_camera_mode, softcore_camera_mode,
@@ -392,6 +399,8 @@ class SxCPInstaOFOptions:
hardcore_men_count=hardcore_men_count, hardcore_men_count=hardcore_men_count,
softcore_level=softcore_level, softcore_level=softcore_level,
hardcore_level=hardcore_level, hardcore_level=hardcore_level,
softcore_expression_intensity=softcore_expression_intensity,
hardcore_expression_intensity=hardcore_expression_intensity,
platform_style=platform_style, platform_style=platform_style,
continuity=continuity, continuity=continuity,
softcore_camera_mode=softcore_camera_mode, softcore_camera_mode=softcore_camera_mode,
+14
View File
@@ -9,12 +9,18 @@
"item_label": "Clothing", "item_label": "Clothing",
"style": "tasteful adult fashion-editorial coloured-pencil comic illustration with casual everyday styling", "style": "tasteful adult fashion-editorial coloured-pencil comic illustration with casual everyday styling",
"positive_suffix": "Use crisp clean comic linework, soft fabric texture, detailed hatching, warm natural light, and tactile textured paper.", "positive_suffix": "Use crisp clean comic linework, soft fabric texture, detailed hatching, warm natural light, and tactile textured paper.",
"expression_pools": ["casual_observational_expressions"],
"composition_pools": ["casual_fashion_compositions"],
"subcategories": [ "subcategories": [
{ {
"name": "Streetwear", "name": "Streetwear",
"slug": "streetwear", "slug": "streetwear",
"weight": 1.0, "weight": 1.0,
"inherit_expressions": false,
"inherit_compositions": false,
"scene_pools": ["casual_urban_scenes"], "scene_pools": ["casual_urban_scenes"],
"expression_pools": ["streetwear_attitude_expressions"],
"composition_pools": ["streetwear_phone_compositions"],
"items": ["streetwear outfit generator"], "items": ["streetwear outfit generator"],
"item_templates": [ "item_templates": [
"{outerwear} layered over {top}, with {bottom}, {footwear}, and {accessory}", "{outerwear} layered over {top}, with {bottom}, {footwear}, and {accessory}",
@@ -169,7 +175,11 @@
"name": "Summer casual", "name": "Summer casual",
"slug": "summer_casual", "slug": "summer_casual",
"weight": 1.0, "weight": 1.0,
"inherit_expressions": false,
"inherit_compositions": false,
"scene_pools": ["casual_summer_scenes"], "scene_pools": ["casual_summer_scenes"],
"expression_pools": ["summer_candid_expressions"],
"composition_pools": ["summer_lifestyle_compositions"],
"items": ["summer casual outfit generator"], "items": ["summer casual outfit generator"],
"item_templates": [ "item_templates": [
"{dress} with {footwear}, {accessory}, and {fabric_detail}", "{dress} with {footwear}, {accessory}, and {fabric_detail}",
@@ -342,7 +352,11 @@
"name": "Cozy lounge", "name": "Cozy lounge",
"slug": "cozy_lounge", "slug": "cozy_lounge",
"weight": 1.0, "weight": 1.0,
"inherit_expressions": false,
"inherit_compositions": false,
"scene_pools": ["casual_lounge_scenes"], "scene_pools": ["casual_lounge_scenes"],
"expression_pools": ["cozy_lounge_expressions"],
"composition_pools": ["cozy_home_compositions"],
"items": ["cozy lounge outfit generator"], "items": ["cozy lounge outfit generator"],
"item_templates": [ "item_templates": [
"{layer} over {top}, with {bottom}, {footwear}, and {texture_detail}", "{layer} over {top}, with {bottom}, {footwear}, and {texture_detail}",
+6
View File
@@ -11,6 +11,8 @@
"positive_suffix": "Use crisp clean comic linework, detailed hatching, soft skin shading, tactile fabric texture, warm intimate lighting, and textured paper.", "positive_suffix": "Use crisp clean comic linework, detailed hatching, soft skin shading, tactile fabric texture, warm intimate lighting, and textured paper.",
"negative_prompt": "minors, childlike appearance, schoolgirl, childlike costume, non-consensual, coercion, violence, injury, watermark", "negative_prompt": "minors, childlike appearance, schoolgirl, childlike costume, non-consensual, coercion, violence, injury, watermark",
"scene_pools": ["softcore_creator_scenes", "mirror_scenes"], "scene_pools": ["softcore_creator_scenes", "mirror_scenes"],
"expression_pools": ["softcore_creator_expressions", "erotic_inviting_expressions"],
"composition_pools": ["softcore_creator_compositions", "boudoir_body_compositions"],
"expressions": [ "expressions": [
"heavy-lidded seductive gaze", "heavy-lidded seductive gaze",
"direct erotic stare", "direct erotic stare",
@@ -552,7 +554,11 @@
"name": "Fetish inspired", "name": "Fetish inspired",
"slug": "fetish_inspired", "slug": "fetish_inspired",
"weight": 1.0, "weight": 1.0,
"inherit_expressions": false,
"inherit_compositions": false,
"scene_pools": ["fetish_studio_scenes"], "scene_pools": ["fetish_studio_scenes"],
"expression_pools": ["fetish_control_expressions"],
"composition_pools": ["fetish_studio_compositions"],
"item_templates": [ "item_templates": [
"{color} {material} {latex_piece} with {zipper_detail}, {boot_style}, and {glove_style}", "{color} {material} {latex_piece} with {zipper_detail}, {boot_style}, and {glove_style}",
"{color} {harness_style} over {breast_exposure} with {bottom_detail}, {hardware}, and {shoe_style}", "{color} {harness_style} over {breast_exposure} with {bottom_detail}, {hardware}, and {shoe_style}",
@@ -0,0 +1,435 @@
{
"version": 1,
"expression_pools": {
"casual_observational_expressions": [
"neutral relaxed face with direct eye contact",
"quiet focused gaze",
"slightly aloof editorial stare",
"calm unsmiling expression",
"reserved half-smile",
"thoughtful side glance",
"candid mid-walk look",
"subtle skeptical eyebrow raise",
"composed fashion-model stare",
"soft tired morning expression",
"mildly distracted street-photo look",
"relaxed closed-mouth smile"
],
"streetwear_attitude_expressions": [
"cool unsmiling streetwear stare",
"confident sideways glance",
"detached outfit-check expression",
"small knowing smirk",
"phone-camera look with raised brow",
"focused city-walk expression",
"low-key bored fashion face",
"direct lens stare under sunglasses",
"casual closed-mouth grin",
"serious model-off-duty expression"
],
"summer_candid_expressions": [
"squinting softly in bright sun",
"windblown candid smile",
"relaxed vacation gaze",
"half-lidded warm-weather expression",
"calm look toward the horizon",
"soft open-air market glance",
"subtle smile while turning away",
"sunlit thoughtful expression",
"quiet contented look",
"fresh-faced candid expression"
],
"cozy_lounge_expressions": [
"sleepy relaxed expression",
"quiet morning stare",
"soft unfocused gaze",
"warm but tired half-smile",
"barely awake expression",
"peaceful closed-mouth smile",
"intimate home-camera gaze",
"relaxed thoughtful look",
"softly serious window-light expression",
"calm private-moment face"
],
"softcore_creator_expressions": [
"direct creator-shot eye contact",
"heavy-lidded bedroom gaze",
"teasing open-mouthed breath",
"parted lips with flushed cheeks",
"knowing subscriber-camera stare",
"playful tongue touching the upper lip",
"slow inviting half-smile",
"soft aroused gaze",
"confident mirror-selfie stare",
"breathy expression with relaxed jaw",
"shameless teasing grin",
"dreamy post-tease expression",
"subtle lip bite",
"intense close-camera stare",
"flushed skin and glossy eyes",
"smirking phone-camera expression"
],
"erotic_inviting_expressions": [
"wet parted lips and heavy-lidded eyes",
"bold lustful eye contact",
"open-mouthed sensual gasp",
"slow predatory smile",
"flushed cheeks with a shameless stare",
"soft moaning expression without a sex act",
"inviting gaze with relaxed mouth",
"bedroom eyes and a teasing smirk",
"dreamy aroused expression",
"confident erotic glare",
"mouth slightly open with glossy lips",
"intimate close-lens stare",
"playful tongue-out tease",
"commanding sensual expression",
"heated side glance",
"after-dark seduction face"
],
"fetish_control_expressions": [
"strict dominant stare",
"cold confident gaze",
"smirking control expression",
"commanding eye contact",
"calm predatory smile",
"unblinking fetish-studio stare",
"chin-raised confident expression",
"sharp side-eye under dramatic light",
"teasingly stern face",
"intense latex-gloss stare",
"detached dominant expression",
"slow controlled smile"
],
"hardcore_orgasm_expressions": [
"controlled intense eye contact during sex",
"focused adult pleasure face",
"bitten-lip near-orgasm expression",
"steady flushed gaze with held breath",
"adult ahegao-style crossed eyes with tongue out",
"drooling open-mouthed orgasm face",
"eyes rolled upward in climax",
"tongue out with flushed cheeks",
"overwhelmed orgasmic grimace",
"wide open mouth mid-moan",
"glassy unfocused climax stare",
"sweaty breathless face with parted lips",
"jaw slack from intense pleasure",
"eyes squeezed shut during orgasm",
"wild aroused expression with messy hair",
"near-climax trembling lips",
"desperate open-mouth pleasure face",
"flushed face with saliva at the lips",
"shameless fucked-out expression",
"dazed post-orgasm stare",
"heavy-lidded exhausted pleasure face",
"raw orgasmic face with wet lips"
],
"hardcore_messy_expressions": [
"controlled sweaty eye contact",
"focused heavy-breathing expression",
"bitten-lip messy pleasure face",
"steady post-sex stare",
"messy drooling pleasure expression",
"cum-smeared lips with dazed eye contact",
"saliva-shiny tongue-out expression",
"sweaty flushed face and open mouth",
"mascara-smudged adult climax face",
"fucked-out stare with slack jaw",
"wild tongue-out expression",
"wet lips and unfocused eyes",
"breathless panting expression",
"overheated flushed cheeks",
"messy post-climax grin",
"shaking near-orgasm expression",
"glossy eyes and parted lips",
"drool at the corner of the mouth",
"intense shameless eye contact",
"spent satisfied expression"
],
"hardcore_penetration_expressions": [
"controlled eye contact during penetration",
"focused adult pleasure face during thrusting",
"bitten-lip concentration under penetration",
"braced expression with held breath",
"open-mouthed moan during penetration",
"eyes rolled back from deep penetration",
"bitten-lip expression under hard thrusting",
"desperate eye contact during penetration",
"jaw slack from deep thrusts",
"flushed face with a strained moan",
"adult ahegao-style penetration face",
"breathless grimace from intense pressure",
"fucked-out gaze while hips are pinned",
"tongue out during hard penetration",
"sweaty orgasm-building expression",
"eyes squeezed shut as penetration deepens",
"wild pleasure face during thrusting",
"near-climax pleading stare"
],
"hardcore_oral_expressions": [
"focused eye contact during oral sex",
"controlled oral-sex expression",
"steady gaze while mouth is close to genitals",
"bitten-lip anticipation before oral contact",
"wet open-mouth oral expression",
"drooling tongue-out oral face",
"glossy eyes while mouth is occupied",
"messy lips during oral sex",
"gagging-but-consensual adult oral expression",
"hungry eye contact during oral sex",
"saliva strings at the lips",
"sex-drunk dazed expression",
"tongue extended toward genitals",
"cum-smeared mouth and heavy-lidded eyes",
"breathless oral-sex face",
"wide open mouth ready for oral contact",
"wet lips pressed around a partner",
"shameless oral-service stare"
],
"hardcore_anal_dp_expressions": [
"controlled braced expression during anal sex",
"focused eye contact while hips are held open",
"bitten-lip pressure expression",
"steady concentrated pleasure face",
"overwhelmed face during anal penetration",
"eyes rolled back from double penetration",
"strained open-mouthed pleasure expression",
"adult ahegao-style double-penetration face",
"fucked-out expression between two partners",
"breathless face under intense pressure",
"tongue out with flushed cheeks",
"near-orgasm grimace during anal sex",
"wide-eyed pleasure shock",
"sweaty clenched-jaw moan",
"dazed expression while hips are held open",
"desperate heavy-lidded stare",
"open mouth and trembling lips",
"messy post-anal climax face"
],
"hardcore_group_expressions": [
"controlled aroused expressions across the group",
"focused eye contact between several partners",
"steady concentrated pleasure faces",
"bitten-lip group-sex expressions",
"multiple adults with open-mouthed orgasm faces",
"mixed dazed and hungry faces across the group",
"several tongue-out ahegao-style adult expressions",
"flushed sweaty faces around the pile",
"one partner drooling while others moan",
"overwhelmed group-sex expressions",
"fucked-out faces across several bodies",
"heavy-lidded eye contact between partners",
"wild orgasmic faces in the group",
"messy saliva-shiny mouths across the scene",
"shameless aroused grins and moans",
"near-climax expressions on multiple adults",
"spent post-orgy faces",
"open mouths and glossy eyes across the group",
"dazed pleasure expressions from every visible adult",
"heated direct eye contact between participants"
],
"hardcore_climax_expressions": [
"controlled post-climax stare",
"focused breathless aftermath expression",
"bitten-lip satisfied expression",
"steady flushed face after orgasm",
"eyes rolled back during climax",
"mouth open as orgasm hits",
"cum-smeared dazed expression",
"tongue out with visible post-climax mess",
"breathless post-orgasm stare",
"spent fucked-out face",
"shaking orgasm expression",
"wet lips and flushed cheeks after climax",
"heavy-lidded satisfied stare",
"dazed expression under phone flash",
"open-mouthed post-climax panting",
"messy satisfied grin",
"adult ahegao-style climax face",
"glassy eyes and slack jaw after release"
]
},
"composition_pools": {
"casual_fashion_compositions": [
"vertical outfit-check composition with the full silhouette readable",
"three-quarter fashion crop with face, outfit, and shoes visible",
"street-style editorial frame with environmental context",
"candid phone-photo composition with natural body spacing",
"mirror outfit-check view with the room visible",
"full-body standing frame with no cropped shoes",
"mid-distance lifestyle frame with relaxed negative space",
"clean fashion catalog composition",
"slightly low phone angle emphasizing outfit shape",
"waist-up crop focused on layering and accessories",
"over-the-shoulder fashion pose with background context",
"side-profile frame showing the outfit silhouette"
],
"streetwear_phone_compositions": [
"low-angle streetwear phone shot with sneakers near the foreground",
"mirror elevator outfit-check composition",
"crosswalk mid-step vertical frame",
"wall-leaning full-body street portrait",
"storefront reflection composition",
"wide sidewalk frame with city lines behind the body",
"seated curb-level streetwear pose",
"phone-snapshot crop from head to shoes",
"three-quarter frame emphasizing jacket layers",
"rear three-quarter streetwear turn"
],
"summer_lifestyle_compositions": [
"sunlit full-body summer lifestyle frame",
"promenade walking shot with fabric movement",
"market-stall candid composition",
"seated low-wall frame with legs and sandals visible",
"balcony outfit-check with plants and skyline",
"bright high-angle phone photo",
"soft side-profile summer silhouette",
"beach-cafe seated composition",
"wide resort-day frame with clear warm light",
"close crop on face, neckline, and fabric texture"
],
"cozy_home_compositions": [
"soft window-light home portrait",
"sofa-corner seated vertical frame",
"bedroom-floor lounge composition",
"mirror view showing cozy layers and room context",
"kneeling rug-level composition",
"waist-up intimate home-camera crop",
"full-body barefoot frame near curtains",
"reading-nook composition with relaxed negative space",
"overhead sofa composition",
"side-profile lounge silhouette"
],
"softcore_creator_compositions": [
"handheld selfie crop from face to hips",
"mirror selfie with phone visible and body framed clearly",
"phone-tripod vertical creator frame",
"bed selfie with sheets around the body",
"bathroom mirror crop with phone and torso visible",
"close subscriber-view crop from thighs to face",
"ring-light creator setup with full body visible",
"over-the-shoulder mirror view",
"kneeling bed-frame creator composition",
"side-profile body curve composition",
"low-angle phone shot emphasizing legs and hips",
"full-body lingerie outfit-check frame",
"tight crop on face, chest, hands, and fabric",
"vertical story-frame creator shot"
],
"boudoir_body_compositions": [
"reclining boudoir full-body composition",
"arched-back side-profile lingerie frame",
"mirror-view bedroom composition",
"kneeling bed composition with face and torso visible",
"standing center-frame boudoir poster crop",
"seated legs-forward lingerie frame",
"close crop on lace, skin, and facial expression",
"over-the-shoulder rear-view boudoir frame",
"bed-edge full-body composition",
"soft overhead bed composition",
"low-angle glamour frame emphasizing hips and thighs",
"three-quarter body editorial lingerie pose"
],
"fetish_studio_compositions": [
"low-angle fetish-studio dominance frame",
"centered full-body latex poster composition",
"mirror-wall fetish composition with hard rim light",
"kneeling front-facing studio frame",
"standing wide-leg dominance composition",
"close crop on harness, expression, and hands",
"rear three-quarter fetish silhouette",
"glossy floor reflection composition",
"symmetrical private-studio frame",
"side-profile body-line composition",
"dramatic overhead studio crop",
"tight vertical frame with props and body centered"
],
"hardcore_explicit_compositions": [
{"text": "full-body explicit sex frame with all adult bodies visible", "min_people": 2},
{"text": "bed-level camera angle focused on genital contact and faces", "min_people": 2, "max_people": 3},
{"text": "side-profile view showing penetration alignment clearly", "min_people": 2, "max_people": 3},
{"text": "mirror-reflected explicit sex composition with direct and reflected views", "min_people": 2, "max_people": 3},
{"text": "overhead view of intertwined adult bodies", "min_people": 2},
{"text": "low-angle explicit body-contact frame", "min_people": 2},
{"text": "close crop on hips, thighs, hands, genitals, and faces", "min_people": 2, "max_people": 3},
{"text": "wide full-body explicit composition with readable anatomy", "min_people": 2},
{"text": "creator-shot vertical frame with phone-camera intimacy", "min_people": 2},
{"text": "front-facing explicit frame with body contact centered", "min_people": 2},
{"text": "floor-level sex composition with sheets and bodies filling the frame", "min_people": 2},
{"text": "tight subscriber-view crop with face and explicit contact visible", "min_people": 2, "max_people": 3}
],
"penetration_compositions": [
{"text": "low-angle penetration view with hips near the camera", "min_people": 2, "max_people": 3},
{"text": "side-profile penetration frame with genital contact readable", "min_people": 2, "max_people": 3},
{"text": "bed-edge explicit penetration composition", "min_people": 2, "max_people": 3},
{"text": "overhead penetration frame with legs and hips visible", "min_people": 2, "max_people": 3},
{"text": "mirror-reflected penetration view", "min_people": 2, "max_people": 3},
{"text": "close crop on penetration, hands, and orgasmic face", "min_people": 2, "max_people": 3},
{"text": "wide full-body penetration frame", "min_people": 2, "max_people": 3},
{"text": "rear three-quarter penetration composition", "min_people": 2, "max_people": 3},
{"text": "kneeling penetration frame with bodies stacked vertically", "min_people": 2, "max_people": 3},
{"text": "front-facing explicit thrusting composition", "min_people": 2, "max_people": 3}
],
"oral_compositions": [
{"text": "mouth-level oral-sex close-up with eyes and hands visible", "min_people": 2, "max_people": 3},
{"text": "kneeling oral composition with genitals and face centered", "min_people": 2, "max_people": 3},
{"text": "front-view oral-sex frame from bed height", "min_people": 2, "max_people": 3},
{"text": "low phone-camera angle focused on mouth contact", "min_people": 2, "max_people": 3},
{"text": "mirror-reflected oral scene with face and body visible", "min_people": 2, "max_people": 3},
{"text": "overhead oral-sex frame with thighs and mouth contact readable", "min_people": 2, "max_people": 3},
{"text": "side-profile oral composition with saliva and expression visible", "min_people": 2, "max_people": 3},
{"text": "tight crop on mouth, hands, genitals, and eyes", "min_people": 2, "max_people": 3},
{"text": "wide full-body oral-sex composition", "min_people": 2, "max_people": 3},
{"text": "bedside kneeling oral frame with phone-shot intimacy", "min_people": 2, "max_people": 3}
],
"anal_dp_compositions": [
{"text": "rear-view anal composition with hips and face visible", "min_people": 2, "max_people": 3},
{"text": "side-profile anal penetration frame", "min_people": 2, "max_people": 3},
{"text": "front-and-back double-penetration composition", "min_people": 3},
{"text": "mirror-reflected double-penetration view", "min_people": 3},
{"text": "low-angle bent-over explicit frame", "min_people": 2, "max_people": 3},
{"text": "bed-edge rear-entry composition", "min_people": 2, "max_people": 3},
{"text": "close crop on hips, hands, and overwhelmed face", "min_people": 2, "max_people": 3},
{"text": "wide frame with all penetration points visible", "min_people": 3},
{"text": "kneeling double-contact composition", "min_people": 3},
{"text": "overhead tangled-body anal frame", "min_people": 2, "max_people": 3}
],
"threesome_compositions": [
{"text": "centered threesome composition with all three adult bodies visible", "min_people": 3, "max_people": 3},
{"text": "front-and-back threesome frame", "min_people": 3, "max_people": 3},
{"text": "overhead three-body composition", "min_people": 3, "max_people": 3},
{"text": "wide bed threesome frame with faces and genitals visible", "min_people": 3, "max_people": 3},
{"text": "mirror-wall threesome composition", "min_people": 3, "max_people": 3},
{"text": "low mattress three-person frame", "min_people": 3, "max_people": 3},
{"text": "side-profile threesome alignment view", "min_people": 3, "max_people": 3},
{"text": "tight center-body crop with both partners visible", "min_people": 3, "max_people": 3},
{"text": "floor-level three-body explicit frame", "min_people": 3, "max_people": 3},
{"text": "standing and kneeling threesome composition", "min_people": 3, "max_people": 3}
],
"group_sex_compositions": [
{"text": "wide orgy composition with every visible adult body readable", "min_people": 4},
{"text": "overhead group-sex frame with bodies arranged in layers", "min_people": 4},
{"text": "floor-level group pile with multiple acts visible", "min_people": 4},
{"text": "wide mirror-wall group-sex composition", "min_people": 4},
{"text": "multi-couch group scene with all participants in frame", "min_people": 4},
{"text": "centered group pile with penetration and oral contact visible", "min_people": 4},
{"text": "wide private-suite orgy frame with readable body positions", "min_people": 4},
{"text": "high-angle group-sex composition showing the whole arrangement", "min_people": 4},
{"text": "long vertical creator-shot group frame", "min_people": 4},
{"text": "full-room group-sex composition with clear adult spacing", "min_people": 4}
],
"climax_compositions": [
{"text": "tight post-climax crop on face, body, hands, and visible fluids", "min_people": 1},
{"text": "phone-flash close-up of orgasm aftermath", "min_people": 1},
{"text": "bed-level climax frame with thighs, face, and fluid detail visible", "min_people": 1},
{"text": "mirror-reflected post-climax composition", "min_people": 1},
{"text": "front-facing cumshot composition with body and expression centered", "min_people": 1},
{"text": "overhead post-orgasm frame on rumpled sheets", "min_people": 1},
{"text": "kneeling climax frame with open mouth and body visible", "min_people": 1},
{"text": "wide aftermath composition with all adult bodies visible", "min_people": 2},
{"text": "tight subscriber-view crop of climax expression and fluid detail", "min_people": 1},
{"text": "side-profile post-climax body-line composition", "min_people": 1}
]
}
}
+55 -27
View File
@@ -11,39 +11,43 @@
"positive_suffix": "Use clear adult anatomy, visible sexual contact, intense body language, crisp comic linework, detailed hatching, warm erotic lighting, and tactile textured paper.", "positive_suffix": "Use clear adult anatomy, visible sexual contact, intense body language, crisp comic linework, detailed hatching, warm erotic lighting, and tactile textured paper.",
"negative_prompt": "minors, childlike appearance, teen, schoolgirl, incest, bestiality, non-consensual, coercion, rape, violence, injury, blood, gore, watermark", "negative_prompt": "minors, childlike appearance, teen, schoolgirl, incest, bestiality, non-consensual, coercion, rape, violence, injury, blood, gore, watermark",
"scene_pools": ["hardcore_private_scenes"], "scene_pools": ["hardcore_private_scenes"],
"expression_pools": ["hardcore_orgasm_expressions", "hardcore_messy_expressions"],
"composition_pools": ["hardcore_explicit_compositions"],
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Sexual pose: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit, hardcore, and anatomically clear, with visible genital contact and adult bodies only. {positive_suffix} Avoid: {negative_prompt}.", "prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Sexual pose: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit, hardcore, and anatomically clear, with visible genital contact and adult bodies only. {positive_suffix} Avoid: {negative_prompt}.",
"caption_template": "{trigger}, {scene_kind}, {cast_summary}, {role_graph}, {item}, {scene}, {composition}, explicit consensual adult hardcore sex illustration", "caption_template": "{trigger}, {scene_kind}, {cast_summary}, {role_graph}, {item}, {scene}, {composition}, explicit consensual adult hardcore sex illustration",
"expressions": [ "expressions": [
"open-mouthed orgasmic expression", "adult ahegao-style orgasm face",
"heavy-lidded lustful gaze", "eyes rolled back with tongue out",
"flushed faces and parted lips", "drooling open-mouthed moan",
"intense eye contact during sex", "fucked-out dazed stare",
"breathless pleasure expression", "sweaty flushed orgasm expression",
"commanding erotic stare", "jaw slack from intense pleasure",
"shameless aroused grin", "messy saliva-shiny lips",
"eyes closed in climax", "desperate near-climax gaze",
"teasing dominant smile", "wild tongue-out arousal",
"desperate hungry gaze", "breathless post-orgasm face",
"sweaty post-orgasmic glow", "cum-smeared lips and glossy eyes",
"bitten-lip pleasure expression", "open mouth and trembling lips",
"wild aroused expression", "heavy-lidded exhausted pleasure stare",
"soft moaning expression", "raw overwhelmed climax expression",
"focused intimate eye contact", "shameless messy aroused grin",
"overwhelmed climax expression" "eyes squeezed shut during orgasm",
"dazed sex-drunk expression",
"heated direct eye contact during sex"
], ],
"compositions": [ "compositions": [
"full-body bed scene with all bodies visible", "full-body explicit sex frame with all adult bodies visible",
"low-angle explicit penetration view", "low-angle view focused on genital contact",
"overhead view of intertwined bodies", "overhead view of intertwined adult bodies",
"side-profile view showing genital contact", "side-profile view showing penetration alignment",
"mirror-view sex composition", "mirror-view explicit sex composition",
"close crop on hips, thighs, and faces", "close crop on hips, thighs, hands, genitals, and faces",
"wide orgy composition with all participants visible", "wide group-sex composition with all participants visible",
"centered threesome composition", "centered threesome composition with all three bodies readable",
"kneeling and standing composition", "kneeling and standing explicit body-contact frame",
"reclining full-body composition", "reclining full-body sex composition",
"floor-level explicit sex composition", "floor-level explicit sex composition",
"front-facing explicit pin-up composition" "front-facing hardcore creator-shot composition"
], ],
"scenes": [ "scenes": [
{ {
@@ -84,8 +88,12 @@
"name": "Penetrative sex", "name": "Penetrative sex",
"slug": "penetrative_sex", "slug": "penetrative_sex",
"min_people": 2, "min_people": 2,
"inherit_expressions": false,
"inherit_compositions": false,
"weight": 1.0, "weight": 1.0,
"scene_pools": ["hardcore_penetrative_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"], "scene_pools": ["hardcore_penetrative_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
"expression_pools": ["hardcore_penetration_expressions"],
"composition_pools": ["penetration_compositions"],
"item_templates": [ "item_templates": [
"{penetration_act} in {position}, with {body_contact}, {intensity}, and {visibility}", "{penetration_act} in {position}, with {body_contact}, {intensity}, and {visibility}",
"{position} while {penetration_act}, {hand_detail}, {mouth_detail}, and {visibility}", "{position} while {penetration_act}, {hand_detail}, {mouth_detail}, and {visibility}",
@@ -249,8 +257,12 @@
"name": "Oral sex", "name": "Oral sex",
"slug": "oral_sex", "slug": "oral_sex",
"min_people": 2, "min_people": 2,
"inherit_expressions": false,
"inherit_compositions": false,
"weight": 1.0, "weight": 1.0,
"scene_pools": ["hardcore_oral_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"], "scene_pools": ["hardcore_oral_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
"expression_pools": ["hardcore_oral_expressions"],
"composition_pools": ["oral_compositions"],
"item_templates": [ "item_templates": [
"{oral_act} in {position}, with {hand_detail}, {expression_detail}, and {visibility}", "{oral_act} in {position}, with {hand_detail}, {expression_detail}, and {visibility}",
"{position} featuring {oral_act}, {body_contact}, {saliva_detail}, and {climax_hint}", "{position} featuring {oral_act}, {body_contact}, {saliva_detail}, and {climax_hint}",
@@ -382,8 +394,12 @@
"name": "Anal and double penetration", "name": "Anal and double penetration",
"slug": "anal_double_penetration", "slug": "anal_double_penetration",
"min_people": 2, "min_people": 2,
"inherit_expressions": false,
"inherit_compositions": false,
"weight": 1.0, "weight": 1.0,
"scene_pools": ["hardcore_anal_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"], "scene_pools": ["hardcore_anal_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
"expression_pools": ["hardcore_anal_dp_expressions"],
"composition_pools": ["anal_dp_compositions"],
"item_templates": [ "item_templates": [
"{anal_act} in {position}, with {leg_detail}, {hand_detail}, and {visibility}", "{anal_act} in {position}, with {leg_detail}, {hand_detail}, and {visibility}",
"{double_act} with {body_arrangement}, {intensity}, {mouth_detail}, and {visibility}", "{double_act} with {body_arrangement}, {intensity}, {mouth_detail}, and {visibility}",
@@ -587,8 +603,12 @@
"name": "Threesomes", "name": "Threesomes",
"slug": "threesomes", "slug": "threesomes",
"min_people": 3, "min_people": 3,
"inherit_expressions": false,
"inherit_compositions": false,
"weight": 1.0, "weight": 1.0,
"scene_pools": ["hardcore_threesome_scenes", "hardcore_group_scenes", "hardcore_mirror_scenes"], "scene_pools": ["hardcore_threesome_scenes", "hardcore_group_scenes", "hardcore_mirror_scenes"],
"expression_pools": ["hardcore_group_expressions"],
"composition_pools": ["threesome_compositions"],
"item_templates": [ "item_templates": [
"{threesome_act} with {body_arrangement}, {oral_detail}, {penetration_detail}, and {visibility}", "{threesome_act} with {body_arrangement}, {oral_detail}, {penetration_detail}, and {visibility}",
"{body_arrangement} while {threesome_act}, with {hand_detail}, {mouth_detail}, and {climax_hint}", "{body_arrangement} while {threesome_act}, with {hand_detail}, {mouth_detail}, and {climax_hint}",
@@ -766,8 +786,12 @@
"slug": "group_sex_orgy", "slug": "group_sex_orgy",
"min_people": 4, "min_people": 4,
"inherit_scenes": false, "inherit_scenes": false,
"inherit_expressions": false,
"inherit_compositions": false,
"weight": 1.0, "weight": 1.0,
"scene_pools": ["hardcore_group_scenes"], "scene_pools": ["hardcore_group_scenes"],
"expression_pools": ["hardcore_group_expressions"],
"composition_pools": ["group_sex_compositions"],
"item_templates": [ "item_templates": [
"{group_act} with {arrangement}, {contact_detail}, {fluid_detail}, and {visibility}", "{group_act} with {arrangement}, {contact_detail}, {fluid_detail}, and {visibility}",
"{arrangement} featuring {group_act}, {oral_detail}, {penetration_detail}, and {intensity}", "{arrangement} featuring {group_act}, {oral_detail}, {penetration_detail}, and {intensity}",
@@ -934,8 +958,12 @@
"name": "Cumshot and climax", "name": "Cumshot and climax",
"slug": "cumshot_climax", "slug": "cumshot_climax",
"min_people": 1, "min_people": 1,
"inherit_expressions": false,
"inherit_compositions": false,
"weight": 1.0, "weight": 1.0,
"scene_pools": ["hardcore_climax_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"], "scene_pools": ["hardcore_climax_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
"expression_pools": ["hardcore_climax_expressions"],
"composition_pools": ["climax_compositions"],
"item_templates": [ "item_templates": [
"{climax_act} with {fluid_location}, {body_position}, {expression_detail}, and {visibility}", "{climax_act} with {fluid_location}, {body_position}, {expression_detail}, and {visibility}",
"{body_position} during {climax_act}, with {hand_detail}, {fluid_location}, and {fluid_detail}", "{body_position} during {climax_act}, with {hand_detail}, {fluid_location}, and {fluid_detail}",
+235 -9
View File
@@ -562,15 +562,15 @@ def load_category_library() -> list[dict[str, Any]]:
return categories return categories
def load_scene_pool_library() -> dict[str, list[Any]]: def _load_named_pool_library(key: str) -> dict[str, list[Any]]:
pools: dict[str, list[Any]] = {} pools: dict[str, list[Any]] = {}
for path in _json_files(): for path in _json_files():
data = _read_json(path) data = _read_json(path)
raw_pools = data.get("scene_pools", {}) raw_pools = data.get(key, {})
if not raw_pools: if not raw_pools:
continue continue
if not isinstance(raw_pools, dict): if not isinstance(raw_pools, dict):
raise ValueError(f"scene_pools in {path} must be an object") raise ValueError(f"{key} in {path} must be an object")
for name, entries in raw_pools.items(): for name, entries in raw_pools.items():
pool_name = str(name).strip() pool_name = str(name).strip()
if not pool_name: if not pool_name:
@@ -580,6 +580,18 @@ def load_scene_pool_library() -> dict[str, list[Any]]:
return pools return pools
def load_scene_pool_library() -> dict[str, list[Any]]:
return _load_named_pool_library("scene_pools")
def load_expression_pool_library() -> dict[str, list[Any]]:
return _load_named_pool_library("expression_pools")
def load_composition_pool_library() -> dict[str, list[Any]]:
return _load_named_pool_library("composition_pools")
def _extension_targets() -> dict[str, tuple[list[Any], bool]]: def _extension_targets() -> dict[str, tuple[list[Any], bool]]:
return { return {
"women_clothes": (g.WOMEN_CLOTHES, False), "women_clothes": (g.WOMEN_CLOTHES, False),
@@ -654,6 +666,14 @@ def _ratio_or_none(value: float) -> float | None:
return max(0.0, min(1.0, ratio)) return max(0.0, min(1.0, ratio))
def _clamped_float(value: Any, default: float = 0.5, min_value: float = 0.0, max_value: float = 1.0) -> float:
try:
number = float(value)
except (TypeError, ValueError):
return default
return max(min_value, min(max_value, number))
def build_seed_config_json( def build_seed_config_json(
category_seed: int = -1, category_seed: int = -1,
subcategory_seed: int = -1, subcategory_seed: int = -1,
@@ -1384,6 +1404,180 @@ def _scene_pool(category: dict[str, Any], subcategory: dict[str, Any], item: Any
return scene_entries or fallback return scene_entries or fallback
def _sources_with_inheritance(
category: dict[str, Any],
subcategory: dict[str, Any],
item: Any,
inherit_key: str,
) -> tuple[Any, ...]:
item_source = item if isinstance(item, dict) else None
if item_source is not None and _is_false(item_source.get(inherit_key)):
return (item_source,)
if _is_false(subcategory.get(inherit_key)):
return (subcategory, item_source)
return (category, subcategory, item_source)
def _configured_pool(
category: dict[str, Any],
subcategory: dict[str, Any],
item: Any,
direct_key: str,
pool_key: str,
pool_library: dict[str, list[Any]],
inherit_key: str,
) -> list[Any]:
entries: list[Any] = []
singular_pool_key = pool_key[:-1] if pool_key.endswith("s") else pool_key
for source in _sources_with_inheritance(category, subcategory, item, inherit_key):
if not isinstance(source, dict):
continue
if direct_key in source:
_unique_extend(entries, _list_from(source[direct_key]))
refs = _list_from(source.get(singular_pool_key)) + _list_from(source.get(pool_key))
for ref in refs:
ref_name = str(ref).strip()
if ref_name not in pool_library:
raise ValueError(f"Unknown {singular_pool_key} '{ref_name}'")
_unique_extend(entries, pool_library[ref_name])
return entries
def _expression_pool(category: dict[str, Any], subcategory: dict[str, Any], item: Any) -> list[Any]:
return _configured_pool(
category,
subcategory,
item,
"expressions",
"expression_pools",
load_expression_pool_library(),
"inherit_expressions",
) or g.EXPRESSIONS
def _expression_intensity_hint(entry: Any) -> float:
if isinstance(entry, dict):
for key in ("expression_intensity", "intensity"):
if key in entry:
return _clamped_float(entry[key], 0.5)
text = _entry_text(entry).lower()
high_terms = (
"ahegao",
"orgasm",
"climax",
"drool",
"drooling",
"tongue out",
"eyes rolled",
"fucked-out",
"cum-smeared",
"saliva",
"gagging",
"slack jaw",
"jaw slack",
"slack-jawed",
"sex-drunk",
"overwhelmed",
"strained",
"messy",
"panting",
"trembling",
"shaking",
"wide open mouth",
"raw ",
"wild ",
"dazed",
"spent",
)
if any(term in text for term in high_terms):
return 0.9
medium_terms = (
"seductive",
"teasing",
"lustful",
"aroused",
"bedroom",
"dominant",
"predatory",
"control",
"stern",
"strict",
"smirk",
"parted lips",
"open-mouthed",
"heated",
"hungry",
"inviting",
"sensual",
"fetish",
"commanding",
"flushed",
"moan",
)
if any(term in text for term in medium_terms):
return 0.62
low_terms = (
"neutral",
"quiet",
"calm",
"reserved",
"relaxed",
"candid",
"closed-mouth",
"thoughtful",
"controlled",
"focused",
"steady",
"bitten-lip",
"braced",
"held breath",
"concentrated",
"aloof",
"bored",
"tired",
"unfocused",
"contented",
"fashion",
"soft",
"sleepy",
"fresh-faced",
)
if any(term in text for term in low_terms):
return 0.25
return 0.5
def _expression_entries_for_intensity(entries: list[Any], expression_intensity: float) -> list[Any]:
target = _clamped_float(expression_intensity, 0.5)
weighted: list[Any] = []
for entry in entries:
entry_intensity = _expression_intensity_hint(entry)
distance = abs(target - entry_intensity)
if distance <= 0.18:
intensity_weight = 4.0
elif distance <= 0.35:
intensity_weight = 1.4
elif distance <= 0.55:
intensity_weight = 0.35
else:
intensity_weight = 0.05
if isinstance(entry, dict):
adjusted = dict(entry)
try:
base_weight = float(adjusted.get("weight", 1.0))
except (TypeError, ValueError):
base_weight = 1.0
adjusted["weight"] = max(0.0, base_weight) * intensity_weight
weighted.append(adjusted)
else:
weighted.append({"text": _entry_text(entry), "weight": intensity_weight})
return weighted or entries
def _pose_pool(category: dict[str, Any], subcategory: dict[str, Any], item: Any, subject_type: str, poses: str) -> list[Any]: def _pose_pool(category: dict[str, Any], subcategory: dict[str, Any], item: Any, subject_type: str, poses: str) -> list[Any]:
configured = _merged_field(category, subcategory, item, "poses") configured = _merged_field(category, subcategory, item, "poses")
if configured: if configured:
@@ -1396,9 +1590,17 @@ def _pose_pool(category: dict[str, Any], subcategory: dict[str, Any], item: Any,
def _composition_pool(category: dict[str, Any], subcategory: dict[str, Any], item: Any, subject_type: str) -> list[Any]: def _composition_pool(category: dict[str, Any], subcategory: dict[str, Any], item: Any, subject_type: str) -> list[Any]:
configured = _merged_field(category, subcategory, item, "compositions") configured = _configured_pool(
category,
subcategory,
item,
"compositions",
"composition_pools",
load_composition_pool_library(),
"inherit_compositions",
)
if configured: if configured:
return _list_from(configured) return configured
if subject_type in ("group", "configured_cast"): if subject_type in ("group", "configured_cast"):
return g.GROUP_COMPOSITIONS return g.GROUP_COMPOSITIONS
if subject_type in ("layout", "scene"): if subject_type in ("layout", "scene"):
@@ -1420,6 +1622,7 @@ def _build_custom_row(
men_count: int, men_count: int,
seed: int, seed: int,
seed_config: dict[str, int], seed_config: dict[str, int],
expression_intensity: float,
) -> dict[str, Any]: ) -> dict[str, Any]:
categories = load_category_library() categories = load_category_library()
category_rng = _axis_rng(seed_config, "category", seed, row_number) category_rng = _axis_rng(seed_config, "category", seed, row_number)
@@ -1454,12 +1657,14 @@ def _build_custom_row(
pose = str(_merged_field(category, subcategory, item, "pose", "") or context.get("fallback_pose") or _choose_text( pose = str(_merged_field(category, subcategory, item, "pose", "") or context.get("fallback_pose") or _choose_text(
pose_rng, _compatible_entries(_pose_pool(category, subcategory, item, subject_type, poses), women_count, men_count) pose_rng, _compatible_entries(_pose_pool(category, subcategory, item, subject_type, poses), women_count, men_count)
)) ))
expression = _choose_text( expression_entries = _compatible_entries(
expression_rng, _expression_entries_for_intensity(_expression_pool(category, subcategory, item), expression_intensity),
_compatible_entries(_list_from(_merged_field(category, subcategory, item, "expressions", g.EXPRESSIONS)), women_count, men_count), women_count,
men_count,
) )
expression = _choose_text(expression_rng, expression_entries)
if subject_type in ("couple", "group") and ";" not in expression: if subject_type in ("couple", "group") and ";" not in expression:
expression = f"{expression}; {_choose_text(expression_rng, g.EXPRESSIONS)}" expression = f"{expression}; {_choose_text(expression_rng, expression_entries)}"
composition = _choose_text( composition = _choose_text(
composition_rng, composition_rng,
_compatible_entries(_composition_pool(category, subcategory, item, subject_type), women_count, men_count), _compatible_entries(_composition_pool(category, subcategory, item, subject_type), women_count, men_count),
@@ -1492,6 +1697,7 @@ def _build_custom_row(
"scene_slug": scene_slug, "scene_slug": scene_slug,
"pose": pose, "pose": pose,
"expression": expression, "expression": expression,
"expression_intensity": expression_intensity,
"composition": composition, "composition": composition,
"role_graph": role_graph, "role_graph": role_graph,
"positive_suffix": positive_suffix, "positive_suffix": positive_suffix,
@@ -1588,6 +1794,7 @@ def build_prompt(
women_count: int = 1, women_count: int = 1,
men_count: int = 1, men_count: int = 1,
camera_config: str | dict[str, Any] | None = None, camera_config: str | dict[str, Any] | None = None,
expression_intensity: float = 0.5,
) -> dict[str, Any]: ) -> dict[str, Any]:
apply_pool_extensions() apply_pool_extensions()
row_number = max(1, int(row_number)) row_number = max(1, int(row_number))
@@ -1599,6 +1806,7 @@ def build_prompt(
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)
pose_ratio = _ratio_or_none(standard_pose_ratio) pose_ratio = _ratio_or_none(standard_pose_ratio)
expression_intensity = _clamped_float(expression_intensity, 0.5)
parsed_seed_config = _parse_seed_config(seed_config) parsed_seed_config = _parse_seed_config(seed_config)
exact_custom_subcategory = bool(subcategory and subcategory != RANDOM_SUBCATEGORY and " / " in subcategory) exact_custom_subcategory = bool(subcategory and subcategory != RANDOM_SUBCATEGORY and " / " in subcategory)
@@ -1649,6 +1857,7 @@ def build_prompt(
int(men_count), int(men_count),
seed, seed,
parsed_seed_config, parsed_seed_config,
expression_intensity,
) )
if extra_positive.strip(): if extra_positive.strip():
@@ -1658,6 +1867,7 @@ def build_prompt(
row["prompt"] = _prepend_trigger(row["prompt"], active_trigger, bool(prepend_trigger_to_prompt)) row["prompt"] = _prepend_trigger(row["prompt"], active_trigger, bool(prepend_trigger_to_prompt))
row["negative_prompt"] = _combined_negative(row.get("negative_prompt", g.NEGATIVE_PROMPT), extra_negative) row["negative_prompt"] = _combined_negative(row.get("negative_prompt", g.NEGATIVE_PROMPT), extra_negative)
row["trigger"] = active_trigger row["trigger"] = active_trigger
row["expression_intensity"] = expression_intensity
return row return row
@@ -1700,6 +1910,8 @@ def build_insta_of_options_json(
continuity: str = "same_creator_same_room", continuity: str = "same_creator_same_room",
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",
softcore_expression_intensity: float = 0.45,
hardcore_expression_intensity: float = 0.85,
) -> str: ) -> str:
return json.dumps( return json.dumps(
{ {
@@ -1713,6 +1925,8 @@ def build_insta_of_options_json(
"continuity": continuity, "continuity": 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,
"softcore_expression_intensity": _clamped_float(softcore_expression_intensity, 0.45),
"hardcore_expression_intensity": _clamped_float(hardcore_expression_intensity, 0.85),
}, },
ensure_ascii=True, ensure_ascii=True,
sort_keys=True, sort_keys=True,
@@ -1731,6 +1945,8 @@ def _parse_insta_of_options(options_json: str | dict[str, Any] | None) -> dict[s
"continuity": "same_creator_same_room", "continuity": "same_creator_same_room",
"softcore_camera_mode": "handheld_selfie", "softcore_camera_mode": "handheld_selfie",
"hardcore_camera_mode": "same_as_softcore", "hardcore_camera_mode": "same_as_softcore",
"softcore_expression_intensity": 0.45,
"hardcore_expression_intensity": 0.85,
} }
if not options_json: if not options_json:
return defaults return defaults
@@ -1753,6 +1969,14 @@ def _parse_insta_of_options(options_json: str | dict[str, Any] | None) -> dict[s
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["softcore_expression_intensity"] = _clamped_float(
parsed.get("softcore_expression_intensity"),
defaults["softcore_expression_intensity"],
)
parsed["hardcore_expression_intensity"] = _clamped_float(
parsed.get("hardcore_expression_intensity"),
defaults["hardcore_expression_intensity"],
)
for key in ("hardcore_women_count", "hardcore_men_count"): for key in ("hardcore_women_count", "hardcore_men_count"):
try: try:
parsed[key] = max(0, min(12, int(parsed[key]))) parsed[key] = max(0, min(12, int(parsed[key])))
@@ -1845,6 +2069,7 @@ def build_insta_of_pair(
seed_config=parsed_seed_config, seed_config=parsed_seed_config,
women_count=1, women_count=1,
men_count=0, men_count=0,
expression_intensity=options["softcore_expression_intensity"],
) )
hard_row = build_prompt( hard_row = build_prompt(
category="Hardcore sexual poses", category="Hardcore sexual poses",
@@ -1868,6 +2093,7 @@ def build_insta_of_pair(
seed_config=parsed_seed_config, seed_config=parsed_seed_config,
women_count=hard_women_count, women_count=hard_women_count,
men_count=hard_men_count, men_count=hard_men_count,
expression_intensity=options["hardcore_expression_intensity"],
) )
descriptor = _insta_of_descriptor(soft_row) descriptor = _insta_of_descriptor(soft_row)