Expand H3 action roles and pin Action node height

This commit is contained in:
GenorTG
2026-11-09 14:55:17 +01:00
parent 9006dd068e
commit 979427ed8a
7 changed files with 181 additions and 56 deletions
+1 -1
View File
@@ -140,7 +140,7 @@ Makes a long MiniMax H3 video part by part. One node does the generating (**Prom
|---|---|
| **H3 Modes** | What writes the prompt (`prompt_source`: my idea or action preset; the other node is ignored) and what kind of video: **T2V** (text only, empty latent), **I2V** (first frame encoded and anchored at frame 0), **L2V** (last frame anchored at the end), **FL2V** (both), **REF2V** (pictures, videos and sounds encoded as references). Also one clip or several parts, extend an existing video, second stage on/off. Its overview lists the files chosen in the Load nodes with a tick for each; rows the mode does not use are dimmed. Each mode also picks its prompt template (T2VA, I2VA, L2VA, FL2VA, Ref2VA). |
| **H3 Idea (my text)** | Your own text with the saved-ideas bar (Save / Save as / Delete, arrows to flip through; stored in `user/promptforge/ideas.json`), negative prompt and dialogue with its language. |
| **H3 Action (adult presets)** | A solo act or a position (28), a camera angle (13) and who is who: up to 3 subjects, each from a reference picture (by the number of its Load slot), the first frame, or described in words, with a `look` text. The roles of the chosen action are shown under it (for cowgirl: subject 1 on top, subject 2 on the back). Notes carry setting and mood. The prompt writer writes the whole video prompt (NSFW template) as one continuous shot with the act already in progress, for one clip or spread over the parts: lead-in, main action, peak. Works with every mode; where a picture is not available the person is described in words. |
| **H3 Action (adult presets)** | A solo act or a position (57, each with an explicit who-is-where definition and a recommended 9:16 or 16:9 frame), a camera angle (13) and who is who: every role has its own picture picker (Woman, Man, Partner) for a reference picture by its input slot, the first frame, or none and only a description (`look` text). The roles of the chosen action are shown under it (for cowgirl: subject 1 on top, subject 2 on the back). Notes carry setting and mood. The prompt writer writes the whole video prompt (NSFW template) as one continuous shot with the act already in progress, for one clip or spread over the parts: lead-in, main action, peak. Works with every mode; where a picture is not available the person is described in words. |
| **H3 Prompt Writer Settings** | How the writer writes, for the Idea and the Action node alike: use the writer or not, template (SFW and NSFW alike; a template you pick wins over the style), style, temperature, top_p, top_k, repetition penalty, max tokens, retries, approval. |
| **H3 Parts** (optional) | A box for each part of a long video (+ / -); used when Modes says several parts. A box can start with `(10s)` for its own length or `raw:` for a finished prompt. Without it the generator makes one clip. |
| **H3 References** | The first and last frame (I2V, L2V, FL2V), up to 9 reference pictures, 3 reference videos (with soundtracks) and 3 reference sounds, each with a description (wire a Load Image / Load Audio DESCRIPTION, or type it). The prompt writer is told what `<Picture N>`, `<Video N>` and `<Audio N>` are, so the Story can name them; the descriptions stay put while you rewrite the Story. |
+119 -38
View File
@@ -17,36 +17,75 @@ RULES = """# Coherence rules for this video (mandatory, they override the templa
11. Write plain present tense with concrete visible facts: no statements about quality, no lines about what is absent, no stage directions."""
# label -> (roles of the people in the order A, B; the arrangement)
ACTIONS = {
"Solo - self touching, lying down": (('the person',), "{A} lies on a bed or sofa, fully naked, and touches their own body: palms over the breasts and stomach, then fingers on the vulva, stroking the labia and circling the clitoris, one or two fingers sliding inside, hips rocking into the hand. For a male, the hand wraps around the erect penis instead."),
"Solo - self touching, standing": (('the person',), "{A} stands, fully naked, runs their hands over the breasts, stomach, hips and thighs, then between the legs, rubbing the vulva and clitoris with the fingers, one hand braced on a wall or furniture. For a male, the hand strokes the erect penis instead."),
"Solo - masturbating with a hand": (('the person',), "{A} masturbates with a hand: fingers rubbing the clitoris and sliding into the vagina, starting slowly and building the pace. For a male, the fist strokes the erect penis from base to glans."),
"Solo - masturbating with a toy": (('the person',), "{A} pushes a sex toy into the vagina and thrusts it, the other hand working the clitoris, starting slowly and building the pace."),
"Solo - male, jerking off": (('the man',), "{A} strokes his erect penis with one hand, the fist moving from base to glans, hips pushing into the stroke, the other hand resting on his thigh or balls. He starts slowly and builds the pace."),
"Solo - male, jerking off with a toy": (('the man',), "{A} pushes his erect penis into a masturbation sleeve or toy and thrusts into it, one hand holding the toy, starting slowly and building the pace."),
"Solo - striptease": (('the person',), "{A} starts dressed and slowly undresses for the camera: lifts the top over the head and drops it, pushes the bottoms down the legs and steps out of them, until fully naked; then poses and touches their own body, hands over the breasts, stomach, hips and between the legs. She starts clothed and is naked only after the last garment comes off."),
"Oral - blowjob, kneeling": (('kneels and gives oral sex', 'stands and receives'), "{A} kneels in front of {B}, who stands with an erect penis; {A} holds it at the base, takes the glans and shaft into the mouth and sucks, the head moving along it."),
"Oral - blowjob, lying": (('lies between the legs, gives oral sex', 'lies on the back, receives'), "{B} lies on their back with an erect penis; {A} lies or kneels between {B}'s legs, takes the penis into the mouth and sucks, the head bobbing along the shaft, one hand at the base."),
"Oral - cunnilingus": (('kneels between the legs, gives oral sex', 'lies back, receives'), "{B} lies back with the legs apart; {A} kneels between {B}'s legs, presses the mouth to the genitals, licks and sucks the clitoris and labia, hands holding the thighs open. If {B} is male, the tongue runs along the erect shaft instead."),
"Oral - 69": (('partner', 'partner'), "{A} and {B} lie head to toe, each with the other's genitals at the mouth, giving oral sex at the same time: {A} sucks {B}'s erect penis while {B} licks {A}'s vulva."),
"Hands - handjob": (('gives the handjob', 'receives'), "{A} sits or kneels beside or in front of {B} and strokes {B}'s erect penis with one hand, the thumb sliding over the glans, building the pace."),
"Hands - titfuck": (('gives, with the breasts', 'receives'), "{B} sits above {A}'s chest with an erect penis; {A} presses her breasts around the shaft and moves her chest up and down while {B} thrusts between them."),
"Hands - mutual masturbation": (('partner', 'partner'), "{A} and {B} sit or lie facing each other, each reaching between the other's legs: {A} strokes {B}'s erect penis, {B} fingers {A}'s vulva and clitoris."),
"Position - missionary": (('lies on the back', 'on top, between the legs'), "{A} lies on their back, legs open and around {B}; {B} lies on top between {A}'s legs, face to face, erect penis in {A}'s vagina, thrusting with a steady rhythm."),
"Position - cowgirl": (('on top, rides', 'lies on the back'), "{B} lies on their back with an erect penis; {A} straddles {B} facing them, already seated with the penis inside her, riding up and down."),
"Position - reverse cowgirl": (('on top, facing away', 'lies on the back'), "{B} lies on their back with an erect penis; {A} straddles {B} facing away from them, already seated with the penis inside her, riding up and down, back and rear toward {B}."),
"Position - doggy style": (('on hands and knees', 'behind'), "{A} is on hands and knees; {B} kneels or stands behind, holds {A}'s hips and thrusts into {A} from behind."),
"Position - prone bone": (('lies face down', 'on top from behind'), "{A} lies face down, flat on the stomach; {B} lies on top of {A} from behind, penis in {A}, thrusting."),
"Position - spooning": (('in front', 'behind'), "{A} and {B} lie on their sides, {B} pressed against {A}'s back, penis in {A} from behind, thrusting slowly."),
"Position - standing, face to face": (('held up, arms around', 'holds and lifts'), "{A} and {B} stand facing each other; {B} holds {A} up by the thighs and ass, {A}'s arms around {B}'s neck and legs around {B}'s waist, erect penis in {A}'s vagina (not her mouth), {B} thrusting."),
"Position - standing, from behind": (('bent forward', 'stands behind'), "{A} stands bent forward with hands on a wall or furniture; {B} stands behind, holds {A}'s hips and thrusts into {A} from behind."),
"Position - one leg up": (('one leg raised', 'holds the leg up'), "{A} and {B} stand facing each other; {B} holds one of {A}'s legs up by the thigh and thrusts into {A} standing."),
"Position - lap, face to face": (('on the lap, facing', 'sits'), "{B} sits; {A} sits on {B}'s lap facing them, arms around each other, {A} moving up and down on {B}'s erect penis."),
"Position - legs on shoulders": (('on the back, legs raised', 'kneels in front'), "{A} lies on their back with the legs raised and the calves resting on {B}'s shoulders; {B} kneels in front, leans forward and thrusts into {A}."),
"Position - edge of the bed": (('lies at the edge', 'stands at the foot of the bed'), "{A} lies on their back at the edge of the bed with the legs hanging open; {B} stands at the foot of the bed between {A}'s legs and thrusts into {A}."),
"Position - against the wall": (('pinned against the wall', 'stands and holds'), "{A} is pinned between {B} and a wall, {B}'s hands under {A}'s thighs holding her up, {A}'s legs around {B}'s waist and arms around {B}'s neck, erect penis in {A}, {B} thrusting."),
"Position - scissoring": (('partner', 'partner'), "{A} and {B} lie facing each other with the legs interlocked, hips pressed together, grinding their genitals together; if one is male, his erect penis slides between the other's thighs and vulva."),
# solo
"Solo - self touching, lying down": ((("female", "the woman"),), "{A} lies on a bed or sofa, fully naked, and touches her own body: palms over the breasts and stomach, then fingers on the vulva, stroking the labia and circling the clitoris, one or two fingers sliding inside, hips rocking into the hand. For a male, the hand wraps around the erect penis instead.", "16:9"),
"Solo - self touching, standing": ((("female", "the woman"),), "{A} stands, fully naked, runs her hands over the breasts, stomach, hips and thighs, then between the legs, rubbing the vulva and clitoris with the fingers, one hand braced on a wall or furniture. For a male, the hand strokes the erect penis instead.", "9:16"),
"Solo - self touching, sitting": ((("female", "the woman"),), "{A} sits with the back against a headboard or wall, the legs open toward the camera, fully naked, one hand on a breast and the other between the legs, fingers rubbing the clitoris and sliding into the vagina, the hips lifting into the hand.", "9:16"),
"Solo - masturbating with a hand": ((("female", "the woman"),), "{A} masturbates with a hand: fingers rubbing the clitoris and sliding into the vagina, starting slowly and building the pace, the other hand on a breast or gripping the bedding. For a male, the fist strokes the erect penis from base to glans.", "16:9"),
"Solo - masturbating with a toy": ((("female", "the woman"),), "{A} pushes a sex toy into the vagina and thrusts it, the other hand working the clitoris, the hips meeting every push, starting slowly and building the pace.", "16:9"),
"Solo - toy, standing against a wall": ((("female", "the woman"),), "{A} stands with one shoulder against a wall, one leg lifted and braced on a chair, fully naked, and works a toy in and out of the vagina with one hand while the other rubs the clitoris.", "9:16"),
"Solo - in the shower": ((("female", "the woman"),), "{A} stands under running water in a shower, fully naked, one hand braced on the wet tiles, the other between the legs working the vulva and clitoris, water running over the breasts and back, the hips rolling into the hand.", "9:16"),
"Solo - in front of a mirror": ((("female", "the woman"),), "{A} stands in front of a mirror, fully naked, facing the reflection, one hand on a breast and the other between the legs, watching herself, the hips pushing into the fingers.", "9:16"),
"Solo - male, jerking off": ((("male", "the man"),), "{A} strokes his erect penis with one hand, the fist moving from base to glans, hips pushing into the stroke, the other hand resting on his thigh or balls. He starts slowly and builds the pace.", "16:9"),
"Solo - male, jerking off with a toy": ((("male", "the man"),), "{A} pushes his erect penis into a masturbation sleeve or toy and thrusts into it, one hand holding the toy, starting slowly and building the pace, the hips driving forward.", "16:9"),
"Solo - striptease": ((("female", "the woman"),), "{A} starts dressed and slowly undresses for the camera: lifts the top over the head and drops it, pushes the bottoms down the legs and steps out of them, until fully naked; then poses and touches her own body, hands over the breasts, stomach, hips and between the legs. She starts clothed and is naked only after the last garment comes off.", "9:16"),
# oral
"Oral - blowjob, kneeling": ((("female", "the woman kneels, gives oral"), ("male", "the man stands, receives")), "{A} kneels in front of {B}, who stands with an erect penis; {A} holds it at the base, takes the glans and shaft into the mouth and sucks, the head moving along it. {B}'s hands rest on her head or hips.", "9:16"),
"Oral - blowjob, lying between the legs": ((("female", "the woman lies down, gives oral"), ("male", "the man lies on his back, receives")), "{B} lies on his back with an erect penis; {A} lies between {B}'s legs on her stomach, takes the penis into the mouth and sucks, the head bobbing along the shaft, one hand at the base.", "16:9"),
"Oral - blowjob, from the side": ((("female", "the woman kneels, gives oral"), ("male", "the man stands, receives")), "{B} stands; {A} kneels beside him, turned so her profile is to the camera, and takes the erect penis into the mouth, one hand at the base, the head sliding up and down the shaft while {B} looks down at her.", "16:9"),
"Oral - face sitting": ((("female", "the woman sits on top, receives oral"), ("male", "the man lies below, gives oral")), "{B} lies on his back; {A} straddles his face, facing the camera, her vulva at his mouth, {B}'s hands on her thighs holding her down while his tongue works the clitoris and labia. Her hands rest on the headboard or his stomach.", "9:16"),
"Oral - cunnilingus": ((("female", "the woman lies back, receives oral"), ("male", "the man kneels between her legs, gives oral")), "{A} lies back with the legs apart; {B} kneels between {A}'s legs, presses the mouth to the vulva, licks and sucks the clitoris and labia, hands holding the thighs open. If {A} is male, the tongue runs along the erect shaft instead.", "16:9"),
"Oral - 69": ((("female", "the woman on the bottom"), ("male", "the man on top")), "{A} and {B} lie head to toe, each with the other's genitals at the mouth, giving oral sex at the same time: {B} sucks {A}'s vulva while {A} takes {B}'s erect penis into her mouth.", "16:9"),
"Oral - deepthroat, kneeling": ((("female", "the woman kneels, gives oral"), ("male", "the man stands, receives")), "{A} kneels in front of {B} and takes the erect penis deep into the mouth and throat, nose against his body, one hand at the base, then pulls back to the glans and pushes in again in a steady rhythm, saliva on the shaft.", "9:16"),
# hands
"Hands - handjob": ((("female", "the woman gives"), ("male", "the man receives")), "{A} sits or kneels beside {B} and strokes {B}'s erect penis with one hand, the thumb sliding over the glans, building the pace, the other hand on his thigh.", "16:9"),
"Hands - handjob from behind": ((("female", "the woman reaches around, gives"), ("male", "the man stands in front, receives")), "{B} stands; {A} stands behind him, pressed to his back, reaches around his hips and strokes the erect penis, the fist moving base to glans, her chin at his shoulder.", "16:9"),
"Hands - fingering from behind": ((("female", "the woman in front, receives"), ("male", "the man behind, gives")), "{A} stands or kneels in front of {B}; {B} reaches around from behind, one hand on her breast and the other between her legs, fingers rubbing the clitoris and sliding into the vagina while she leans back into him.", "16:9"),
"Hands - titfuck, lying": ((("female", "the woman below, gives with the breasts"), ("male", "the man above, receives")), "{A} lies on her back; {B} straddles her chest with an erect penis; {A} presses her breasts around the shaft and moves her chest up and down while {B} thrusts between them, his hands braced beside her head.", "9:16"),
"Hands - titfuck, sitting": ((("female", "the woman sits, gives with the breasts"), ("male", "the man kneels above, receives")), "{A} sits leaned back on a sofa; {B} kneels over her chest with an erect penis; {A} presses her breasts around the shaft and moves her chest while {B} thrusts between them, looking down at her.", "9:16"),
"Hands - mutual masturbation": ((("female", "the woman"), ("male", "the man")), "{A} and {B} sit or lie facing each other, each reaching between the other's legs: {A} strokes {B}'s erect penis, {B} fingers {A}'s vulva and clitoris, both hands moving at the same pace.", "16:9"),
# intercourse, lying
"Position - missionary": ((("female", "the woman on her back"), ("male", "the man on top, between her legs")), "{A} lies on her back, legs open and around {B}; {B} lies on top between {A}'s legs, face to face, erect penis in {A}'s vagina, thrusting with a steady rhythm, his forearms beside her shoulders.", "16:9"),
"Position - missionary, legs on shoulders": ((("female", "the woman on her back, legs raised"), ("male", "the man kneels in front")), "{A} lies on her back with the legs raised and the calves resting on {B}'s shoulders; {B} kneels in front, leans forward and thrusts into {A} at a deep, steady pace, her hips lifted off the bed.", "16:9"),
"Position - missionary, pillow under the hips": ((("female", "the woman on her back, hips raised"), ("male", "the man above")), "{A} lies on her back with a pillow under the hips and the legs wide and high; {B} lies above her, face to face, erect penis in {A}'s vagina, thrusting down at an angle that presses the clitoris, her hands on his back.", "16:9"),
"Position - prone bone": ((("female", "the woman face down"), ("male", "the man on top from behind")), "{A} lies face down, flat on the stomach, the legs together; {B} lies on top of {A} from behind, penis in {A}, thrusting in short, firm strokes, his hands beside her shoulders.", "16:9"),
"Position - spooning": ((("female", "the woman in front"), ("male", "the man behind")), "{A} and {B} lie on their sides, {B} pressed against {A}'s back, penis in {A} from behind, one hand on her breast, thrusting slowly, both bodies curved together.", "16:9"),
"Position - scissoring": ((("female", "the first woman"), ("female", "the second woman")), "{A} and {B} lie facing each other with the legs interlocked, hips pressed together, grinding their vulvas together in slow circles, hands holding each other's thighs or hips. If one is male, his erect penis slides between the other's thighs and vulva.", "16:9"),
# intercourse, seated / on top
"Position - cowgirl": ((("female", "the woman on top, rides"), ("male", "the man lies on his back")), "{B} lies on his back with an erect penis; {A} straddles {B} facing him, already seated with the penis inside her, riding up and down, her hands on his chest, her breasts moving with the bounce.", "9:16"),
"Position - cowgirl, leaning back": ((("female", "the woman on top, leans back"), ("male", "the man lies on his back")), "{B} lies on his back; {A} sits on him facing away-leaning back with her hands planted on his thighs behind her, the penis inside her, rolling and grinding her hips in slow circles, her back arched.", "9:16"),
"Position - reverse cowgirl": ((("female", "the woman on top, facing away"), ("male", "the man lies on his back")), "{B} lies on his back with an erect penis; {A} straddles {B} facing away from him, already seated with the penis inside her, riding up and down, her back and rear toward {B}, his hands on her hips.", "9:16"),
"Position - reverse cowgirl, leaning forward": ((("female", "the woman on top, facing away, leans forward"), ("male", "the man lies on his back")), "{B} lies on his back; {A} sits on him facing away and leans forward with her hands on his shins, the penis inside her, bouncing her hips back onto him in quick strokes, her rear high and toward the camera.", "9:16"),
"Position - lotus": ((("female", "the woman in his lap, facing"), ("male", "the man sits")), "{B} sits cross-legged or on a chair; {A} sits on his lap facing him, arms around his neck and legs wrapped around his waist, erect penis in her vagina, grinding her hips against him. Their faces are close together.", "9:16"),
"Position - seated reverse": ((("female", "the woman in his lap, facing away"), ("male", "the man sits")), "{B} sits on a chair or the floor; {A} sits on his lap facing away, the penis inside her, her back against his chest, his hands on her breasts, both moving her hips up and down.", "9:16"),
"Position - seated on the edge of the bed": ((("female", "the woman sits at the edge"), ("male", "the man kneels in front")), "{A} sits on the edge of the bed, leaning back on her hands, legs open; {B} kneels on the floor in front of her and thrusts into {A}, his hands under her thighs holding them up.", "16:9"),
"Position - edge of the bed": ((("female", "the woman lies at the edge"), ("male", "the man stands at the foot of the bed")), "{A} lies on her back at the edge of the bed with the legs hanging open; {B} stands at the foot of the bed between {A}'s legs and thrusts into {A}, holding her hips.", "16:9"),
# intercourse, standing
"Position - standing, face to face": ((("female", "the woman held up, arms around him"), ("male", "the man holds and lifts her")), "{A} and {B} stand facing each other; {B} holds {A} up by the thighs and ass, {A}'s arms around {B}'s neck and legs around {B}'s waist, erect penis in {A}'s vagina (not her mouth), {B} thrusting up into her.", "9:16"),
"Position - standing, from behind": ((("female", "the woman bent forward"), ("male", "the man stands behind")), "{A} stands bent forward with hands on a wall or furniture, the legs apart; {B} stands behind, holds {A}'s hips and thrusts into {A} from behind, his body against her rear.", "9:16"),
"Position - standing, one leg up": ((("female", "the woman with one leg raised"), ("male", "the man holds the leg up")), "{A} and {B} stand facing each other; {B} holds one of {A}'s legs up by the thigh and thrusts into {A} standing, her other hand on his shoulder for balance.", "9:16"),
"Position - against the wall": ((("female", "the woman pinned against the wall"), ("male", "the man holds her up")), "{A} is pinned between {B} and a wall, {B}'s hands under {A}'s thighs holding her up, {A}'s legs around {B}'s waist and arms around {B}'s neck, erect penis in {A}, {B} thrusting up into her.", "9:16"),
"Position - standing 69": ((("female", "the woman hangs upside down, receives oral"), ("male", "the man stands, holds her, gives oral")), "{B} stands and holds {A} upside down against his body, her legs over his shoulders and her vulva at his mouth; {B} licks and sucks her clitoris while {A} takes his erect penis into her mouth, both giving oral at once.", "9:16"),
# intercourse, kneeling / from behind
"Position - doggy style": ((("female", "the woman on hands and knees"), ("male", "the man kneels behind")), "{A} is on hands and knees, the back arched and the rear toward {B}; {B} kneels behind, holds {A}'s hips and thrusts into {A} from behind in long strokes.", "16:9"),
"Position - doggy style, facing a mirror": ((("female", "the woman on hands and knees"), ("male", "the man kneels behind")), "{A} is on hands and knees facing a mirror, the rear toward {B}; {B} kneels behind, holds her hips and thrusts into her from behind while both watch the reflection and {A} watches {B}.", "16:9"),
"Position - kneeling, pulled up by the hips": ((("female", "the woman kneels, pulled up"), ("male", "the man kneels behind")), "{B} kneels behind {A}; {A} is on her knees, the upper body pulled up against {B}'s chest, his arm across her ribs and the other hand on her breast, the penis inside her, short upward thrusts while she leans her head back on his shoulder.", "9:16"),
"Position - leapfrog": ((("female", "the woman bent over, hands on the floor"), ("male", "the man stands behind")), "{A} stands bent fully forward with her hands on the floor, the legs straight and apart; {B} stands behind her and thrusts down into {A}, his hands on her hips, her breasts hanging.", "16:9"),
"Position - wheelbarrow": ((("female", "the woman on her hands, legs held"), ("male", "the man behind, holds her legs")), "{A} is on her hands on the floor, {B} holds her legs at his hips like a wheelbarrow and thrusts into her from behind, her upper body low and her arms braced.", "16:9"),
# anal
"Position - anal, doggy style": ((("female", "the woman on hands and knees"), ("male", "the man kneels behind")), "{A} is on hands and knees, the back arched, the rear toward {B}; {B} kneels behind, guides the erect penis into her anus and thrusts in short, steady strokes, one thumb pressing her lower back.", "16:9"),
"Position - anal, missionary": ((("female", "the woman on her back, hips raised"), ("male", "the man above, between her legs")), "{A} lies on her back with a pillow under the hips and the legs up; {B} lies above her and pushes the erect penis into her anus, slow deep strokes, her hands gripping his arms.", "16:9"),
"Position - anal, spooning": ((("female", "the woman on her side"), ("male", "the man behind")), "{A} lies on her side with the top leg drawn up; {B} lies behind her, penis in her anus, one hand on her hip, thrusting slowly, both bodies close together.", "16:9"),
"Position - anal, cowgirl": ((("female", "the woman on top, facing away"), ("male", "the man lies on his back")), "{B} lies on his back with an erect penis; {A} straddles him facing away and lowers herself onto the penis anally, then rides up and down in small, controlled strokes, her hands on his thighs.", "9:16"),
# other
"Position - intercrural, between the thighs": ((("female", "the woman in front"), ("male", "the man behind")), "{A} stands bent slightly forward or kneels with the legs together; {B} pushes the erect penis between {A}'s closed thighs from behind and thrusts, the shaft sliding under her vulva, his hands on her hips.", "16:9"),
"Position - footjob": ((("female", "the woman sits, gives with the feet"), ("male", "the man sits or lies, receives")), "{B} sits or lies back with an erect penis; {A} sits facing him, braces her feet around the shaft and strokes it with the soles, the toes curling over the glans.", "16:9"),
"Position - piledriver": ((("female", "the woman upside down on her shoulders"), ("male", "the man above")), "{A} lies on her back with the hips raised and the legs folded back over her body, shoulders and head on the bed; {B} stands or kneels above and thrusts down into {A}'s vagina, her ankles near his shoulders.", "9:16"),
"Position - butterfly": ((("female", "the woman on her back, hips raised"), ("male", "the man kneels, holds her hips")), "{A} lies on her back with a pillow under the hips, the legs bent and open; {B} kneels close, lifts her hips with both hands and thrusts into {A} at an upward angle, her feet off the bed.", "16:9"),
"Position - V": ((("female", "the woman on her back, legs wide"), ("male", "the man kneels in front")), "{A} lies on her back with both legs lifted and spread wide in a V; {B} kneels in front holding her ankles and thrusts into {A}, the camera facing the point where the bodies meet.", "16:9"),
}
CAMERAS = {
"POV (the viewer is B)": "Point of view of {B}: the camera sees {A} and the action from {B}'s eyes, {B}'s hands and body entering the frame.",
"POV (the viewer is A)": "Point of view of {A}: the camera sees {B} and the action from {A}'s eyes, {A}'s hands and body entering the frame.",
@@ -69,7 +108,33 @@ ANY_CAMERA = "(writer's choice)"
def roles(label):
return ACTIONS[label][0]
"""The role texts of an action, in subject order."""
return [role for _gender, role in ACTIONS[label][0]]
def _role_text(role):
"""The role without the repeated gender prefix, for display next to the gender label."""
for prefix in ("the first woman", "the second woman", "the woman", "the man", "the person"):
if role == prefix:
return "solo"
if role.startswith(prefix + " "):
return role[len(prefix) + 1:]
return role
def role_info(label):
"""The role texts with the gender that decides the subject pickers."""
return [{"gender": gender, "role": _role_text(role)} for gender, role in ACTIONS[label][0]]
def stages_for(label):
"""How many bodies the action needs: 1 (solo) or 2."""
return len(ACTIONS[label][0])
def aspect(label):
"""Recommended frame shape for this arrangement."""
return ACTIONS[label][2]
def stage(index, total):
@@ -83,35 +148,51 @@ def stage(index, total):
return f"This is part {index + 1} of {total} of the action: the main action, changing the pace and the camera framing from the previous part."
def _role_name(genders, index):
"""A readable name for a subject role, numbered when both roles share a gender."""
gender = genders[index]
if gender == "male":
return "the man"
if gender == "female":
same = [i for i, g in enumerate(genders) if g == "female"]
return f"the woman {same.index(index) + 1}" if len(same) > 1 else "the woman"
return "the person"
def default_camera(label):
"""The framing used when the camera is left to the writer: both bodies and faces in one still frame."""
return "front view" if len(ACTIONS[label][0]) == 1 else "side view, full bodies"
return "front view" if stages_for(label) == 1 else "side view, full bodies"
def brief(label, camera, subjects, notes=""):
"""The text the prompt writer gets for one chosen action: who is who, the arrangement and the camera. `subjects` are three descriptions (the third may be empty)."""
count = len(ACTIONS[label][0])
counts = stages_for(label)
genders = [gender for gender, _role in ACTIONS[label][0]]
names = [_role_name(genders, index) for index in range(counts)]
a, b = subjects[0], subjects[1]
ratio = aspect(label)
shape = "portrait" if ratio == "9:16" else "landscape"
lines = [
f"Action preset: {label}.",
f"Action: {ACTIONS[label][1].format(A=a, B=b)}",
"This action is exactly what happens, from the first second to the last; the notes only give the setting, the looks, the nudity and the mood.",
"The act is already in progress in the first sentence: the genitals, hands or mouths are already in contact and moving; no setup.",
f"Recommended aspect ratio: {ratio} ({shape}). Frame every body and face fully inside it and keep that framing for the whole clip.",
"Write 550-700 words in the detailed description, one continuous shot covering the whole clip beat by beat.",
]
for number, (role, who) in enumerate(zip(ACTIONS[label][0], subjects), start=1):
lines.append(f"Subject {number} ({role}): {who}")
lines.append("Only Subject 1 and Subject 2 appear in the video." if count == 2 else "Only Subject 1 appears in the video.")
if count == 1:
for index, (role, who) in enumerate(zip(ACTIONS[label][0], subjects), start=1):
lines.append(f"Subject {index} is {names[index - 1]} ({_role_text(role[1])}): {who}")
lines.append("Only Subject 1 and Subject 2 appear in the video." if counts == 2 else "Only Subject 1 appears in the video.")
if counts == 1:
if subjects[1] or subjects[2]:
extra = [f"Subject {number} ({who})" for number, who in ((2, subjects[1]), (3, subjects[2])) if who]
lines.append("Attached references only, not in the video: " + ", ".join(extra) + ". Use them for anatomy, scale and detail.")
lines.append("Subject 1 is the only body in the scene; only Subject 1's genitals are described. The extra reference pictures are anatomy examples, never a second body.")
if "male" in label.lower():
if genders[0] == "male":
lines.append("Subject 1 is male: use his penis and balls, never a vulva. If the reference picture shows a woman, describe her with a penis.")
else:
lines.append("Subject 1 is female in the reference: use her vulva, labia, clitoris and vagina, never a penis.")
elif count == 2 and subjects[2]:
elif counts == 2 and subjects[2]:
lines.append(f"Subject 3 is also in the video, not part of the action: {subjects[2]}")
lines.append("Use the pictures for identity and anatomy only: ignore their clothing, pose, lighting and background.")
if "nudity" in notes.lower() or "naked" in notes.lower():
@@ -120,7 +201,7 @@ def brief(label, camera, subjects, notes=""):
else:
lines.append("Both subjects are fully naked from the first frame; no clothing from the pictures remains.")
camera = default_camera(label) if camera == ANY_CAMERA else camera
if camera in CAMERAS and not (count == 1 and camera in SOLO_CAMERAS):
if camera in CAMERAS and not (counts == 1 and camera in SOLO_CAMERAS):
lines.append(f"Camera: {CAMERAS[camera].format(A=a, B=b)}")
if notes.strip():
lines.append(f"Notes: {notes.strip()}")
+3 -1
View File
@@ -365,13 +365,15 @@ class PROMPTFORGE_H3Generate:
for position, (name, frame) in enumerate(((name, frame) for name, frame in (("first frame", first if kind in ("I2V", "FL2V") else None), ("last frame", last if kind in ("L2V", "FL2V") else None)) if frame is not None), start=1):
tags[name] = f"<Picture {position}>"
genders = [gender for gender, _role in h3_actions.ACTIONS[action_name][0]] + ["any"]
def subject(number):
source, look = story[f"subject_{number}_from"], story[f"subject_{number}_look"].strip()
if source != h3_settings.SUBJECT_SOURCES[0] and source not in tags:
print(f"[PromptForge] subject {number}: the {source} is not used in {kind} mode (or its Load node is off); the person is described in words.")
if source in tags:
return f"{tags[source]} ({look})" if look else tags[source]
return look or ("the first person", "the second person", "")[number - 1]
return look or {"female": "a woman", "male": "a man"}.get(genders[number - 1], "a person")
block = h3_actions.brief(action_name, story["camera_angle"], [subject(number) for number in (1, 2, 3)], story["notes"])
if several:
+2 -1
View File
@@ -57,4 +57,5 @@ async def _delete(request):
@PromptServer.instance.routes.get("/promptforge/actions")
async def _actions(request):
return web.json_response({label: {"roles": list(roles)} for label, (roles, _) in h3_actions.ACTIONS.items()})
return web.json_response({label: {"roles": h3_actions.role_info(label), "aspect": h3_actions.aspect(label)}
for label in h3_actions.ACTIONS})
+7 -6
View File
@@ -38,7 +38,7 @@ PROMPT_STYLES = ("standard", "NSFW")
MODES = ("T2V - text to video", "I2V - first frame to video", "L2V - last frame to video", "FL2V - first and last frame to video", "REF2V - references (pictures, videos, sounds)")
GENERATIONS = ("one clip", "several parts (endless video)")
PROMPT_SOURCES = ("my idea (Idea node)", "action preset (Action node)")
SUBJECT_SOURCES = ("describe in words", "first frame") + tuple(f"reference picture {number}" for number in range(1, 10))
SUBJECT_SOURCES = ("none (describe in words)", "first frame") + tuple(f"reference picture {number}" for number in range(1, 10))
AUTOMATIC_TEMPLATE = "automatic (by mode and style)"
DIALOGUE_AUTO = "auto (from the prompts)"
DIALOGUE_LANGUAGES = (DIALOGUE_AUTO, "Arabic", "Chinese", "English", "French", "German", "Italian", "Japanese", "Korean", "Portuguese", "Russian", "Spanish", "other (type below)")
@@ -112,16 +112,17 @@ class PROMPTFORGE_H3Action(_Settings):
KEY = "action"
RETURN_TYPES = ("PROMPTFORGE_H3_ACTION",)
RETURN_NAMES = ("action",)
DESCRIPTION = ("Adult content from a preset instead of your own text: a solo act or a position, who is who, and a camera angle. The prompt writer writes the whole video prompt (NSFW template), "
"for one clip or spread over the parts. Say who the subjects are: a reference picture (the number of its Load Image slot) or a description in words. "
"Works with every mode; without reference pictures the people are described in words. The Modes node chooses between this node and the Idea node.")
DESCRIPTION = ("Adult content from a preset instead of your own text: a solo act or a position with clearly defined roles, who is who, and a camera angle. Every role has its own picture picker, "
"so the woman and the man each get their own reference picture (Picture 1, 2, 3 ...), the first frame, or none and only a description. The preset also recommends the frame shape: "
"9:16 for standing, sitting, on-top and solo action, 16:9 for lying and side arrangements. The prompt writer writes the whole video prompt (NSFW template), for one clip or spread over the parts. "
"Works with every mode. The Modes node chooses between this node and the Idea node.")
@classmethod
def INPUT_TYPES(cls):
subjects = {}
for number in range(1, 4):
subjects[f"subject_{number}_from"] = (list(SUBJECT_SOURCES), {"default": SUBJECT_SOURCES[0], "tooltip": "Which picture shows this subject: the first frame, or a reference picture by the number of its slot in the References node. Or describe the person in words."})
subjects[f"subject_{number}_look"] = ("STRING", {"default": "", "multiline": True, "tooltip": "The person in words: gender, age, hair, body, clothes. With a picture it adds to the picture, without one it is all the writer knows."})
subjects[f"subject_{number}_from"] = (list(SUBJECT_SOURCES), {"default": SUBJECT_SOURCES[0], "tooltip": "Which picture shows this subject: the first frame, a reference picture by the number of its References slot, or none and only the description. The label above says which role it is (woman, man, partner)."})
subjects[f"subject_{number}_look"] = ("STRING", {"default": "", "multiline": True, "tooltip": "The person in words: hair, body, face, age. Used when no picture is chosen for this role, and added to the picture when one is."})
return {
"required": {
"action": (list(h3_actions.ACTIONS), {"default": next(iter(h3_actions.ACTIONS)), "tooltip": "The act or position. The roles of the subjects are shown below it."}),
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "ComfyUI-PromptForge"
description = "ComfyUI prompt enhancement suite: model/config node with GPU-aware auto settings, prompt enhancer with a preset catalog, and a standalone config finder."
version = "1.28.6"
version = "1.28.7"
license = { file = "LICENSE" }
dependencies = [
"transformers",
+48 -8
View File
@@ -308,7 +308,8 @@ app.registerExtension({
}
if (nodeData.name === ACTION) {
// the roles of the chosen action and only the subject slots it uses
// the role of every subject slot (woman / man / partner), its picture picker and the recommended frame
const SAVED = "h3_action_size";
const onCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function () {
onCreated?.apply(this, arguments);
@@ -321,27 +322,66 @@ app.registerExtension({
const action = find("action");
node.widgets.splice(node.widgets.indexOf(roles), 1);
node.widgets.splice(node.widgets.indexOf(action) + 1, 0, roles);
const nameFor = (gender, index, list = []) => {
if (gender === "female") {
const women = list.map((item, at) => item.gender === "female" ? at : -1).filter((at) => at >= 0);
return women.length > 1 ? `Woman ${women.indexOf(index) + 1}` : "Woman";
}
return gender === "male" ? "Man" : `Partner ${index + 1}`;
};
let known = null;
const refresh = async () => {
known = known || await (await api.fetchApi("/promptforge/actions")).json();
const list = known[String(action.value)]?.roles || [];
label.innerHTML = list.map((role, index) => `<b>Subject ${index + 1}</b>: ${role}`).join("<br>") + `<br><b>Subject 3</b>: an extra person who is in the video but not in the act (optional)`;
const entry = known[String(action.value)] || { roles: [] };
const list = entry.roles || [];
const shape = entry.aspect === "9:16" ? "portrait" : "landscape";
label.innerHTML = list.map((item, index) => `<b>${nameFor(item.gender, index, list)}</b>: ${item.role}`).join("<br>")
+ (entry.aspect ? `<br><b>Frame</b>: ${entry.aspect} (${shape})` : "")
+ "<br><b>Extra</b>: a person in the video but not in the act (optional)";
for (let number = 1; number <= 3; number++) {
const used = number <= list.length || number === 3;
setShown(find(`subject_${number}_from`), used);
setShown(find(`subject_${number}_look`), used);
const gender = list[number - 1]?.gender;
const name = number === 3 && !gender ? "Extra" : nameFor(gender, number - 1, list);
const from = find(`subject_${number}_from`);
const look = find(`subject_${number}_look`);
if (from) {
from.label = `${name} — picture`;
from.options = { ...(from.options || {}), label: from.label };
from.tooltip = `Which picture is the ${name.toLowerCase()}? None = describe only.`;
}
if (look) {
look.label = `${name} — description`;
look.options = { ...(look.options || {}), label: look.label };
}
setShown(from, used);
setShown(look, used);
}
node.setSize([node.size[0], node.computeSize()[1]]);
node.setDirtyCanvas(true, true);
};
node.promptforgeRestoreActionSize = () => {
const saved = node.properties?.[SAVED];
const minimum = node.computeSize();
node.setSize([Math.max(saved?.[0] || node.size[0], 620), Math.max(saved?.[1] || node.size[1], minimum[1])]);
};
const callback = action.callback;
action.callback = (...args) => { callback?.apply(action, args); refresh(); };
node.promptforgeRefreshAction = refresh;
refresh();
node.setSize([Math.max(node.size[0], 560), Math.max(node.size[1], node.computeSize()[1])]);
node.promptforgeRestoreActionSize();
};
const onResize = nodeType.prototype.onResize;
nodeType.prototype.onResize = function (size) {
onResize?.apply(this, arguments);
this.properties = this.properties || {};
this.properties[SAVED] = [Math.round(size[0]), Math.round(size[1])];
};
const onConfigure = nodeType.prototype.onConfigure;
nodeType.prototype.onConfigure = function () { const result = onConfigure?.apply(this, arguments); this.promptforgeRefreshAction?.(); return result; };
nodeType.prototype.onConfigure = function () {
const result = onConfigure?.apply(this, arguments);
this.promptforgeRefreshAction?.();
setTimeout(() => this.promptforgeRestoreActionSize?.(), 0);
return result;
};
return;
}