save changes

This commit is contained in:
2025-09-15 07:39:50 +02:00
parent 0521b28626
commit b153826a0d
21 changed files with 4098 additions and 5 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

@ -0,0 +1,830 @@
{
"song": {
"title": "Crimson Shadows",
"artist": "Nocturna",
"genre": "Gothic Metal",
"mood": "Dark, powerful, dramatic"
},
"character": {
"bodyType": "slim yet strong presence",
"hairStyle": "long black hair with red streaks, slightly messy"
},
"scenes": [
{
"sceneId": 1,
"time": "Night",
"location": "Ballroom with broken chandeliers and candles",
"outfit": "white gothic ball gown with lace sleeves",
"cuts": [
{
"cutId": 1,
"pose": "standing tall in the center of the ballroom",
"action": "raising arms dramatically",
"camera": [
"full body shot with chandeliers above",
"low angle from cracked floor",
"overhead view of gown spreading",
"face zoom in on serious look",
"back shot showing gown trail"
]
},
{
"cutId": 2,
"pose": "leaning back scream",
"action": "shouting with wide-open mouth",
"camera": [
"face zoom in during scream",
"low angle emphasizing mouth open",
"side shot with candlelight shadows",
"zoom in to part of body trembling hand",
"overhead shot capturing scream posture"
]
},
{
"cutId": 3,
"pose": "walking slowly",
"action": "dragging fingers on curtain",
"camera": [
"back shot trailing gown",
"side shot of hand on velvet",
"full body shot from distance",
"face zoom in on haunted gaze",
"low angle with curtain shadows"
]
},
{
"cutId": 4,
"pose": "kneeling on broken tiles",
"action": "holding necklace tightly",
"camera": [
"overhead shot of kneeling pose",
"face zoom in with tears",
"low angle from shattered floor",
"side shot framing necklace",
"back shot with gown spread"
]
},
{
"cutId": 5,
"pose": "spinning mid-gown flare",
"action": "screaming with wide-open mouth mid-spin",
"camera": [
"face zoom in with hair flying",
"low angle capturing spin",
"overhead highlighting gown swirl",
"zoom in to mouth open",
"side shot with candle flames shaking"
]
}
]
},
{
"sceneId": 2,
"time": "Night",
"location": "Dark graveyard with blood moon",
"outfit": "red gothic dress with corset and veil",
"cuts": [
{
"cutId": 1,
"pose": "standing among gravestones",
"action": "lifting veil slowly",
"camera": [
"full body shot with gravestones",
"face zoom in through veil",
"overhead shot with moonlight",
"side shot with crosses behind",
"back shot with veil flowing"
]
},
{
"cutId": 2,
"pose": "bending forward toward the sky",
"action": "shouting with wide-open mouth at the blood moon",
"camera": [
"face zoom in from below",
"low angle with moon glow",
"overhead capturing scream to sky",
"side shot emphasizing mouth open",
"zoom in to part of body trembling hands"
]
},
{
"cutId": 3,
"pose": "kneeling by gravestone",
"action": "touching carved letters",
"camera": [
"back shot with gravestone silhouette",
"low angle with fog rising",
"face zoom in teary",
"overhead with candle accents",
"side shot of hand on stone"
]
},
{
"cutId": 4,
"pose": "walking past tombs",
"action": "dragging gown through dirt",
"camera": [
"full body wide shot",
"back shot with gown trail",
"overhead with drifting fog",
"side shot framing gravestones",
"zoom in to part of body—bleeding palm"
]
},
{
"cutId": 5,
"pose": "clutching chest in anguish",
"action": "screaming again with wide-open mouth",
"camera": [
"face zoom in intense",
"low angle shaky frame",
"overhead gown spread",
"side shot with blood moon glow",
"zoom in to part of body—trembling mouth"
]
}
]
},
{
"sceneId": 3,
"time": "Night",
"location": "Castle corridor with torches",
"outfit": "black velvet gothic gown with silver accents",
"cuts": [
{
"cutId": 1,
"pose": "walking slowly through corridor",
"action": "dragging hand across stone wall",
"camera": [
"back shot trailing behind",
"side shot hand scraping the wall",
"face zoom in haunted eyes",
"overhead torches flickering",
"full body wide frame"
]
},
{
"cutId": 2,
"pose": "standing between torches, head tilted back",
"action": "shouting with wide-open mouth",
"camera": [
"low angle torch flames",
"face zoom in mouth open",
"overhead capturing hall symmetry",
"side shot framing torches",
"zoom in to part of body—clenched fists"
]
},
{
"cutId": 3,
"pose": "kneeling near cracked stone",
"action": "holding broken goblet",
"camera": [
"overhead spilled goblet and shards",
"side shot with long shadow",
"face zoom in tragic expression",
"back shot of corridor depth",
"low angle torch flicker"
]
},
{
"cutId": 4,
"pose": "standing with hand raised",
"action": "reaching toward ceiling arches",
"camera": [
"full body shot angled upward",
"low angle showing height of arches",
"face zoom in determination",
"back shot framed by arch",
"overhead with soft torch glow"
]
},
{
"cutId": 5,
"pose": "arched back against wall",
"action": "screaming again with wide-open mouth",
"camera": [
"side shot hair flowing",
"face zoom in mouth open",
"low angle wall cracks",
"back shot dramatic shadow",
"overhead dim torch light"
]
}
]
},
{
"sceneId": 4,
"time": "Night",
"location": "Castle rooftop under stars and aurora",
"outfit": "red flowing gothic gown with crimson sash",
"cuts": [
{
"cutId": 1,
"pose": "standing on rooftop edge",
"action": "arms spread to the sky",
"camera": [
"full body shot with starscape",
"overhead aurora curtains",
"face zoom in serene gaze",
"back shot with gown streaming",
"low angle with night sky dome"
]
},
{
"cutId": 2,
"pose": "kneeling on cold stone",
"action": "shouting with wide-open mouth into the wind",
"camera": [
"face zoom in under stars",
"overhead aurora swirl around",
"side shot mouth open profile",
"low angle wind tugging fabric",
"zoom in to part of body—trembling hand on stone"
]
},
{
"cutId": 3,
"pose": "walking slowly along the ledge",
"action": "dragging gown across stone",
"camera": [
"back shot trailing fabric",
"side shot with aurora behind",
"face zoom in sorrow",
"overhead glinting stars",
"full body wide rooftop span"
]
},
{
"cutId": 4,
"pose": "sitting on the ledge",
"action": "staring at the blood moon",
"camera": [
"face zoom in pale moonlight",
"side shot moon edge glow",
"back shot open horizon",
"overhead framing gown spread",
"low angle subtle lens flare"
]
},
{
"cutId": 5,
"pose": "arched back with hair whipping",
"action": "screaming again with wide-open mouth",
"camera": [
"face zoom in intensity",
"side shot mouth open",
"low angle aurora painted sky",
"overhead moon halo",
"back shot wind-rushed hair"
]
}
]
},
{
"sceneId": 5,
"time": "Night",
"location": "Cathedral interior with neon red lamps",
"outfit": "white gothic lace dress with spiked choker",
"cuts": [
{
"cutId": 1,
"pose": "standing at altar",
"action": "raising arms slowly",
"camera": [
"full body altar frame",
"low angle stained glass glow",
"overhead neon lamps",
"face zoom in cold eyes",
"side shot candle rows"
]
},
{
"cutId": 2,
"pose": "in front of the altar",
"action": "shouting with wide-open mouth",
"camera": [
"face zoom in wide scream",
"side shot neon wash",
"low angle altar cross",
"overhead long shadows",
"zoom in to part of body—tensed throat"
]
},
{
"cutId": 3,
"pose": "kneeling in prayer",
"action": "clutching rosary tightly",
"camera": [
"back shot neon cross silhouette",
"face zoom in closed eyes",
"overhead subtle aura",
"side shot trembling fingers",
"low angle pew shadows"
]
},
{
"cutId": 4,
"pose": "walking through pews",
"action": "gown brushing wood",
"camera": [
"full body wide aisle",
"back shot trailing fabric",
"overhead shadow mosaic",
"face zoom in haunted look",
"side shot cracked pew ends"
]
},
{
"cutId": 5,
"pose": "kneeling backward",
"action": "screaming again with wide-open mouth",
"camera": [
"face zoom in strained",
"overhead neon cross glow",
"side shot mouth open profile",
"low angle altar frame",
"zoom in to part of body—open mouth"
]
}
]
},
{
"sceneId": 6,
"time": "Night",
"location": "Dungeon with chains and damp stone",
"outfit": "black gothic leather gown with lace gloves",
"cuts": [
{
"cutId": 1,
"pose": "standing near dangling chains",
"action": "touching cold iron",
"camera": [
"face zoom in fogged breath",
"side shot hand on chain",
"back shot against damp wall",
"low angle shadows of chains",
"overhead dim torch circle"
]
},
{
"cutId": 2,
"pose": "head thrown back",
"action": "shouting with wide-open mouth while gripping chains",
"camera": [
"face zoom in raw scream",
"side shot taut chains",
"low angle wall fissures",
"overhead claustrophobic frame",
"zoom in to part of body—mouth close"
]
},
{
"cutId": 3,
"pose": "kneeling, wrists lifted",
"action": "pulling against shackles",
"camera": [
"face zoom in wet lashes",
"side shot trembling wrists",
"back shot chain stretch",
"overhead square of light",
"low angle torch sputter"
]
},
{
"cutId": 4,
"pose": "standing and straining",
"action": "yanking chains from ring",
"camera": [
"full body struggle shot",
"side shot metal tension",
"back shot elongated shadow",
"face zoom in fury",
"overhead dust falling"
]
},
{
"cutId": 5,
"pose": "arched back, wrists high",
"action": "screaming again with wide-open mouth",
"camera": [
"face zoom in contorted scream",
"side shot mouth open",
"overhead chains rattling",
"low angle shadow clawing up wall",
"zoom in to part of body—throat close"
]
}
]
},
{
"sceneId": 7,
"time": "Morning",
"location": "Girly bedroom with dark lace curtains",
"outfit": "black pleated mini skirt, oversized white shirt, ripped stockings",
"cuts": [
{
"cutId": 1,
"pose": "sitting on bed cross-legged",
"action": "playing with necklace charm",
"camera": [
"full body shot on bed",
"side shot necklace in hand",
"face zoom in subtle smile",
"overhead quilt folds",
"back shot window light through lace"
]
},
{
"cutId": 2,
"pose": "leaning back over pillows",
"action": "shouting with wide-open mouth",
"camera": [
"face zoom in open mouth",
"side shot messy hair",
"low angle bed frame edge",
"overhead scream posture",
"zoom in to part of body—trembling hand"
]
},
{
"cutId": 3,
"pose": "lying sideways",
"action": "looking softly at camera",
"camera": [
"face zoom in gentle eyes",
"side shot pillow texture",
"overhead relaxed pose",
"back shot lace curtain glow",
"full body bed perspective"
]
},
{
"cutId": 4,
"pose": "standing by window",
"action": "touching glass",
"camera": [
"face zoom in window reflection",
"side shot pale fingers on glass",
"overhead curtain shadow pattern",
"back shot from doorway",
"low angle diffused light"
]
},
{
"cutId": 5,
"pose": "kneeling on bed",
"action": "screaming again with wide-open mouth",
"camera": [
"face zoom in scream",
"side shot mouth open profile",
"low angle trembling posture",
"overhead tangled sheets",
"zoom in to part of body—throat close"
]
}
]
},
{
"sceneId": 8,
"time": "Night",
"location": "Urban street with neon lights and wet pavement",
"outfit": "red tartan skirt, black crop top, fishnet stockings",
"cuts": [
{
"cutId": 1,
"pose": "walking on crosswalk",
"action": "flicking hair",
"camera": [
"full body wide crosswalk",
"side shot neon reflections",
"face zoom in smirk",
"back shot traffic lights",
"overhead city glow grid"
]
},
{
"cutId": 2,
"pose": "standing under neon sign",
"action": "shouting with wide-open mouth",
"camera": [
"face zoom in neon bloom",
"side shot mouth open",
"low angle billboard glow",
"overhead zebra stripes",
"zoom in to part of body—clenched fists"
]
},
{
"cutId": 3,
"pose": "leaning on graffiti wall",
"action": "crossing arms with attitude",
"camera": [
"side shot tag texture",
"face zoom in cool gaze",
"back shot city night bokeh",
"overhead shadow frame",
"full body casual stance"
]
},
{
"cutId": 4,
"pose": "kneeling on sidewalk",
"action": "tracing chalk graffiti",
"camera": [
"overhead chalk lines",
"side shot hand drawing",
"face zoom in focused",
"back shot wet pavement shimmer",
"low angle neon bleed"
]
},
{
"cutId": 5,
"pose": "jumping mid-beat",
"action": "screaming again with wide-open mouth in mid-air",
"camera": [
"face zoom in scream",
"side shot mouth open",
"overhead body frozen mid-jump",
"back shot neon flare",
"zoom in to part of body—throat close"
]
}
]
},
{
"sceneId": 9,
"time": "Afternoon",
"location": "Park bench under autumn trees",
"outfit": "black floral dress with lace stockings and boots",
"cuts": [
{
"cutId": 1,
"pose": "sitting on bench",
"action": "looking down thoughtfully",
"camera": [
"full body bench shot",
"side shot fallen leaves",
"face zoom in pensive gaze",
"overhead bench frame",
"back shot winding path"
]
},
{
"cutId": 2,
"pose": "standing in leaf fall",
"action": "shouting with wide-open mouth to the sky",
"camera": [
"face zoom in scream",
"side shot mouth open",
"low angle tree branches",
"overhead spinning leaves",
"zoom in to part of body—trembling hands"
]
},
{
"cutId": 3,
"pose": "lying on grass",
"action": "picking petals from a flower",
"camera": [
"overhead flower detail",
"face zoom in soft smile",
"side shot fingers on petals",
"back shot grassy field",
"full body relaxed frame"
]
},
{
"cutId": 4,
"pose": "walking along path",
"action": "hands in pockets",
"camera": [
"back shot autumn walkway",
"side shot casual stride",
"face zoom in subtle grin",
"overhead tree shadows",
"full body natural stance"
]
},
{
"cutId": 5,
"pose": "kneeling by the bench",
"action": "screaming again with wide-open mouth",
"camera": [
"face zoom in intensity",
"side shot mouth open profile",
"low angle roots and soil",
"overhead leaf carpet",
"zoom in to part of body—throat close"
]
}
]
},
{
"sceneId": 10,
"time": "Afternoon",
"location": "Trendy café corner with plants and soft lamps",
"outfit": "gothic casual: black skirt, lace top, choker",
"cuts": [
{
"cutId": 1,
"pose": "sitting at table",
"action": "smiling softly while stirring drink",
"camera": [
"full body cozy table shot",
"side shot spoon and cup",
"face zoom in gentle eyes",
"overhead latte foam swirl",
"back shot plant backdrop"
]
},
{
"cutId": 2,
"pose": "standing beside the table",
"action": "shouting with wide-open mouth",
"camera": [
"face zoom in bright lamps",
"side shot mouth open",
"low angle lamp glow",
"overhead table layout",
"zoom in to part of body—tensed throat"
]
},
{
"cutId": 3,
"pose": "writing in notebook",
"action": "biting lip thoughtfully",
"camera": [
"overhead page and pen",
"face zoom in focused eyes",
"side shot lace sleeve",
"back shot framed by chairs",
"full body seated posture"
]
},
{
"cutId": 4,
"pose": "sipping from cup",
"action": "looking away shyly",
"camera": [
"face zoom in eyes over rim",
"side shot cup touch",
"overhead saucer on table",
"back shot warm ambience",
"low angle lamp string bulbs"
]
},
{
"cutId": 5,
"pose": "kneeling on chair",
"action": "screaming again with wide-open mouth",
"camera": [
"face zoom in scream",
"side shot mouth open",
"low angle chair legs",
"overhead tabletop pattern",
"zoom in to part of body—throat close"
]
}
]
},
{
"sceneId": 11,
"time": "Evening",
"location": "City rooftop with skyline lights",
"outfit": "casual gothic: leather jacket, mini skirt, torn tights",
"cuts": [
{
"cutId": 1,
"pose": "standing on edge",
"action": "arms open to the wind",
"camera": [
"full body skyline silhouette",
"back shot city bokeh",
"side shot hair lifted",
"overhead roof lines",
"face zoom in calm power"
]
},
{
"cutId": 2,
"pose": "kneeling near the parapet",
"action": "shouting with wide-open mouth",
"camera": [
"face zoom in stormy scream",
"side shot mouth open",
"low angle city glow",
"overhead concrete texture",
"zoom in to part of body—clenched fists"
]
},
{
"cutId": 3,
"pose": "sitting on ledge",
"action": "looking down at traffic",
"camera": [
"back shot roads below",
"side shot dangling feet",
"face zoom in reflective",
"overhead ledge line",
"full body relaxed sit"
]
},
{
"cutId": 4,
"pose": "walking across roof",
"action": "wind blowing hair and jacket",
"camera": [
"full body wide stride",
"side shot jacket flutter",
"back shot trailing steps",
"overhead roof grid",
"face zoom in focused"
]
},
{
"cutId": 5,
"pose": "small jump on roof seam",
"action": "screaming again with wide-open mouth",
"camera": [
"face zoom in scream",
"side shot mouth open",
"overhead mid-air frame",
"low angle skyline towers",
"zoom in to part of body—throat close"
]
}
]
},
{
"sceneId": 12,
"time": "Night",
"location": "Underground subway platform with graffiti",
"outfit": "gothic streetwear: ripped jeans, black hoodie, silver chains",
"cuts": [
{
"cutId": 1,
"pose": "standing on platform",
"action": "hands in pockets, head tilted",
"camera": [
"full body trackside shot",
"side shot hoodie folds",
"face zoom in under tube light",
"overhead platform tiles",
"back shot tunnel perspective"
]
},
{
"cutId": 2,
"pose": "leaning toward the tunnel",
"action": "shouting with wide-open mouth",
"camera": [
"face zoom in echoing mouth",
"side shot mouth open",
"low angle rail shine",
"overhead yellow line",
"zoom in to part of body—tensed throat"
]
},
{
"cutId": 3,
"pose": "sitting on bench",
"action": "head tilted down over chains",
"camera": [
"back shot empty platform",
"side shot chain glint",
"face zoom in shadowed eyes",
"overhead bench geometry",
"full body moody sit"
]
},
{
"cutId": 4,
"pose": "walking along platform edge",
"action": "swinging arms slowly",
"camera": [
"full body long leading lines",
"side shot sneaker scuff",
"face zoom in composed",
"overhead fluorescent bands",
"back shot receding tunnel"
]
},
{
"cutId": 5,
"pose": "arched back near the pillar",
"action": "screaming again with wide-open mouth",
"camera": [
"face zoom in scream",
"side shot mouth open",
"low angle pillar grit",
"overhead tiled grid",
"zoom in to part of body—throat close"
]
}
]
}
]
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

@ -0,0 +1,272 @@
{
"character": {
"bodyType": "slim and youthful",
"hairStyle": "long wavy hair with pastel blue and purple colors"
},
"scenes": [
{
"sceneId": 1,
"time": "Morning",
"location": "Cozy bedroom with pastel bedding and sunlight through curtains",
"outfit": "white oversized pajama shirt with thigh-high stockings",
"cuts": [
{
"cutId": 1,
"pose": "lying on bed with head resting on hands",
"action": "smiling softly at the camera",
"camera": [
"overhead shot from above capturing sunlight on her hair",
"close-up of her face with shallow depth of field",
"side angle showing body stretched on bed",
"slow zoom-in from doorway",
"handheld camera wobble for intimate feeling"
]
},
{
"cutId": 2,
"pose": "stretching arms above head while sitting on bed",
"action": "yawning cutely with eyes half closed",
"camera": [
"medium shot from foot of bed",
"low angle from floor emphasizing legs",
"wide shot with window light flaring",
"tracking shot circling around her stretch",
"soft focus tilt-shift framing"
]
},
{
"cutId": 3,
"pose": "kneeling on bed with playful look",
"action": "blowing a kiss to the camera",
"camera": [
"front close-up catching kiss in slow motion",
"wide shot with pastel background",
"camera tilt from below lips to eyes",
"360° pan around her kneeling pose",
"handheld push-in as she blows kiss"
]
}
]
},
{
"sceneId": 2,
"time": "Morning",
"location": "Bedroom near window with plants and sunlight",
"outfit": "light pastel camisole with short pleated skirt and stockings",
"cuts": [
{
"cutId": 1,
"pose": "sitting on window sill, legs slightly crossed",
"action": "looking outside then turning to smile",
"camera": [
"silhouette against sunlight",
"side profile with plants blurred in foreground",
"close-up of smile turning to camera",
"dolly-in from outside window glass",
"over-shoulder shot capturing view outside"
]
},
{
"cutId": 2,
"pose": "leaning against the wall, hands behind back",
"action": "giggling with shy expression",
"camera": [
"eye-level shot with wall texture visible",
"slight high angle to emphasize cuteness",
"medium close-up on giggle with tilt",
"rack focus between wall décor and her face",
"soft handheld sway left to right"
]
},
{
"cutId": 3,
"pose": "lying on floor with legs up against the wall",
"action": "kicking feet playfully while laughing",
"camera": [
"top-down view from ceiling",
"low angle from foot level",
"side shot capturing playful kicks",
"slow pan across her body",
"handheld camera zoom-in to laughter"
]
}
]
},
{
"sceneId": 3,
"time": "Afternoon",
"location": "Sunny street with pastel shops and flowers",
"outfit": "short pastel pink dress with white stockings and sneakers",
"cuts": [
{
"cutId": 1,
"pose": "standing with one hand on hip",
"action": "spinning gently in place",
"camera": [
"full body wide shot with pastel shops",
"low angle capturing dress swirl",
"tracking shot circling spin",
"handheld slow zoom-in to face",
"rear shot revealing spin from behind"
]
},
{
"cutId": 2,
"pose": "walking with small steps",
"action": "waving happily at the camera",
"camera": [
"tracking dolly shot in front",
"overhead drone shot of street",
"side follow shot at hip level",
"handheld jitter to mimic vlog",
"close-up on waving hand with face blurred"
]
},
{
"cutId": 3,
"pose": "leaning forward playfully",
"action": "making a heart shape with hands",
"camera": [
"tight close-up on hands forming heart",
"fish-eye wide close-up",
"side shot at 45°",
"POV shot as if receiving heart",
"zoom burst effect from wide to close"
]
}
]
},
{
"sceneId": 4,
"time": "Afternoon",
"location": "Trendy café with bright modern interior",
"outfit": "white blouse tucked into pleated skirt with stockings",
"cuts": [
{
"cutId": 1,
"pose": "sitting at a table with chin on hands",
"action": "smiling softly while tilting head",
"camera": [
"front eye-level close-up",
"soft focus on eyes, blurred background",
"over-shoulder shot of coffee cup",
"panning shot across table to her",
"low angle from table surface"
]
},
{
"cutId": 2,
"pose": "standing by window with crossed legs",
"action": "writing playfully in a small notebook",
"camera": [
"profile with sunlight flare",
"close-up of pen on paper then tilt to face",
"tracking shot from feet to head",
"reflection in window glass",
"medium shot with bokeh lights behind"
]
},
{
"cutId": 3,
"pose": "sitting sideways on chair with one leg up",
"action": "taking a sip from a cup with cute expression",
"camera": [
"tight focus on lips touching cup",
"medium shot framed by chair back",
"slight dutch angle for energy",
"slow dolly-in on playful sip",
"wide establishing shot of café"
]
}
]
},
{
"sceneId": 5,
"time": "Night",
"location": "Elegant ballroom with chandeliers",
"outfit": "sparkly silver mini dress with black stockings and heels",
"cuts": [
{
"cutId": 1,
"pose": "standing tall with one hand on waist",
"action": "turning slowly while smiling confidently",
"camera": [
"wide shot capturing chandelier",
"low angle emphasizing elegance",
"tracking dolly rotation",
"close-up of smile during turn",
"rear tracking shot revealing gown shimmer"
]
},
{
"cutId": 2,
"pose": "sitting gracefully on a velvet chair",
"action": "crossing legs elegantly",
"camera": [
"medium close-up from side",
"top-down angle showing chair texture",
"front focus on crossed legs",
"soft rack focus from chair to her face",
"panning shot circling chair"
]
},
{
"cutId": 3,
"pose": "leaning on railing with dreamy look",
"action": "gazing at the chandelier lights",
"camera": [
"over-shoulder shot of chandelier view",
"profile silhouette with golden backlight",
"wide shot capturing ballroom depth",
"handheld tilt up from railing to face",
"slow dolly-out revealing emptiness of hall"
]
}
]
},
{
"sceneId": 6,
"time": "Night",
"location": "Luxurious bedroom with soft golden lighting",
"outfit": "black lace camisole with mini skirt and stockings",
"cuts": [
{
"cutId": 1,
"pose": "lying sideways on bed with legs slightly bent",
"action": "looking at the camera with sultry eyes",
"camera": [
"close-up on eyes with blurred background",
"tracking shot along legs up to face",
"overhead soft focus",
"low angle from bed surface",
"handheld intimate pan across body"
]
},
{
"cutId": 2,
"pose": "sitting at vanity table",
"action": "putting on earrings slowly",
"camera": [
"mirror reflection focus",
"close-up on hands adjusting earrings",
"profile side shot with warm glow",
"over-shoulder shot including vanity lights",
"slow push-in from doorway"
]
},
{
"cutId": 3,
"pose": "kneeling on bed with arched back",
"action": "running hand through hair with sensual expression",
"camera": [
"rear shot with back arch emphasized",
"medium close-up focusing on hair movement",
"low angle capturing curves",
"soft focus candlelight bokeh",
"circling dolly shot for dramatic effect"
]
}
]
}
]
}

Binary file not shown.

View File

@ -0,0 +1,240 @@
import dotenv from 'dotenv';
import path from 'path';
import fs from 'fs/promises';
import { logger } from '../lib/logger';
import { callOpenAI } from '../lib/openai';
import { generateImage as generateFaceImage } from '../lib/image-generator-face';
dotenv.config();
type Size = { width: number; height: number };
interface MusicSpotCharacter {
bodyType: string;
hairStyle: string;
}
interface MusicSpotCut {
cutId: number;
pose: string;
action: string;
camera?: string[]; // list of camera variants per cut
}
interface MusicSpotScene {
sceneId: number;
time: string;
location: string;
outfit: string;
cuts: MusicSpotCut[];
}
interface MusicSpotConfig {
character: MusicSpotCharacter;
scenes: MusicSpotScene[];
}
interface Server {
baseUrl?: string;
outputDir?: string;
inputDir?: string;
name: string;
}
const DEFAULT_SIZE: Size = { width: 720, height: 1280 };
const FOLDER = process.argv[2] || process.env.MUSICSPOT_FOLDER || 'oputstise';
const FOLDER_SAFE = FOLDER.replace(/[/\\?%*:|"<>]/g, '_');
const FACE_SRC = path.resolve(`src/musicspot_generator/${FOLDER}/face.png`);
const GENERATED_DIR = path.resolve('generated');
function loadServers(): Server[] {
const servers: Server[] = [
{
name: 'SERVER1',
baseUrl: process.env.SERVER1_COMFY_BASE_URL,
outputDir: process.env.SERVER1_COMFY_OUTPUT_DIR,
},
/*
{
name: 'SERVER2',
baseUrl: process.env.SERVER2_COMFY_BASE_URL,
outputDir: process.env.SERVER2_COMFY_OUTPUT_DIR,
}, */
]
.filter((s) => !!s.baseUrl && !!s.outputDir)
.map((s) => ({
...s,
inputDir: s.outputDir!.replace(/output/i, 'input'),
}));
if (servers.length === 0) {
logger.warn('No servers configured. Please set SERVER{N}_COMFY_BASE_URL and SERVER{N}_COMFY_OUTPUT_DIR in .env');
} else {
for (const s of servers) {
logger.info(`Configured ${s.name}: baseUrl=${s.baseUrl}, outputDir=${s.outputDir}, inputDir=${s.inputDir}`);
}
}
return servers;
}
async function ensureDirs() {
await fs.mkdir(GENERATED_DIR, { recursive: true });
}
async function copyFaceToServers(servers: Server[]): Promise<string> {
const faceFileName = 'face.png';
// Validate face source
try {
await fs.access(FACE_SRC);
} catch {
throw new Error(`Face image not found at ${FACE_SRC}`);
}
for (const srv of servers) {
if (!srv.inputDir) continue;
const dest = path.join(srv.inputDir, faceFileName);
try {
await fs.mkdir(srv.inputDir, { recursive: true });
await fs.copyFile(FACE_SRC, dest);
logger.info(`Copied face image to ${srv.name} input: ${dest}`);
} catch (err) {
logger.warn(`Failed to copy face image to ${srv.name}: ${err}`);
}
}
return faceFileName; // Comfy expects file name present in input dir
}
function buildImagePromptRequest(
character: MusicSpotCharacter,
scene: MusicSpotScene,
cut: MusicSpotCut,
cameraIntent: string
): string {
return `
Return exactly one JSON object, nothing else: { "imagePrompt": "Cinematic realistic photo, (camera framing),(character),(pose),(time),(location),(outfit),(action),(lighting)" }.
Write "imagePrompt" in around 110140 words to generate a still portrait image (720x1280 vertical).
Keep a consistent character identity using the provided face image (identity preservation), but do not mention any camera brand/model.
Describe clearly and concretely:
- Character: ${character.bodyType}; hair: ${character.hairStyle}
- Camera framing/composition intention: ${cameraIntent}
- Time: ${scene.time}
- Location: ${scene.location}
- Outfit: ${scene.outfit}
- Pose: ${cut.pose}
- Action/Expression: ${cut.action}
- Lighting: please be creative and make beautiful lighting, I like something like luminous, colorful
Only respond with JSON.
`.trim();
}
async function getImagePromptFromOpenAI(req: string): Promise<string> {
const res = await callOpenAI(req);
const prompt = res?.imagePrompt || res?.image_prompt || res?.prompt;
if (!prompt || typeof prompt !== 'string') {
throw new Error('OpenAI failed to return imagePrompt JSON.');
}
return prompt.trim();
}
function pickServer(servers: Server[], idx: number): Server {
if (servers.length === 0) {
throw new Error('No servers configured.');
}
return servers[idx % servers.length];
}
async function main() {
try {
await ensureDirs();
// Load scenes.json
const configRaw = await fs.readFile(path.resolve(`src/musicspot_generator/${FOLDER}/scenes.json`), 'utf-8');
const cfg: MusicSpotConfig = JSON.parse(configRaw);
const servers = loadServers();
if (servers.length === 0) {
return;
}
// Ensure face.png in each server's input
const faceFileName = await copyFaceToServers(servers);
// Generate images only (no video here). Intended to be run first.
let imageTaskIndex = 0;
for (const scene of cfg.scenes) {
logger.info(`=== Scene ${scene.sceneId}: Image generation start ===`);
for (const cut of scene.cuts) {
const cameraVariants =
Array.isArray(cut.camera) && cut.camera.length > 0
? cut.camera
: ['eye-level medium shot', 'slight left 30°', 'slight right 30°', 'slight high angle', 'slight low angle'];
for (let camIdx = 0; camIdx < cameraVariants.length; camIdx++) {
const cameraIntent = cameraVariants[camIdx];
const variantIndex = camIdx + 1;
const imgFileName = `${FOLDER_SAFE}_musicspot_s${scene.sceneId}_c${cut.cutId}_v${variantIndex}.png`;
const outputPath = path.join(GENERATED_DIR, imgFileName);
// Skip generation if target file already exists
try {
await fs.access(outputPath);
logger.info(`Skipping generation, file already exists: ${outputPath}`);
continue;
} catch {
// File does not exist; proceed with generation
}
// 1) Generate image prompt for this camera
logger.info(`Scene ${scene.sceneId} - Cut ${cut.cutId} - Cam${variantIndex}: generating image prompt...`);
const imgPromptReq = buildImagePromptRequest(cfg.character, scene, cut, cameraIntent);
let imagePrompt: string;
try {
imagePrompt = await getImagePromptFromOpenAI(imgPromptReq);
} catch (err) {
logger.error(
`OpenAI image prompt failed for scene ${scene.sceneId} cut ${cut.cutId} cam ${variantIndex}: ${err}`
);
continue;
}
// 2) Generate one image using face conditioning for this specific camera
const serverForImage = pickServer(servers, imageTaskIndex++);
logger.info(`Generating image (${imgFileName}) on ${serverForImage.name}...`);
try {
// Use only the face file name for the workflow image input (Comfy expects it in its input dir)
const finalImagePath = await generateFaceImage(
`Realistic photo, ultra detailed, high contrast, ${imagePrompt}`,
faceFileName,
imgFileName,
serverForImage.baseUrl!,
serverForImage.outputDir!,
DEFAULT_SIZE
);
logger.info(`Image generated: ${finalImagePath}`);
} catch (err) {
logger.error(`Image generation failed (${imgFileName}) on ${serverForImage.name}: ${err}`);
continue;
}
}
}
logger.info(`=== Scene ${scene.sceneId}: Image generation complete ===`);
}
logger.info('Image generation for all scenes completed.');
} catch (err) {
logger.error('Fatal error in music spot image generator:', err);
}
}
main().catch((err) => {
logger.error('Unhandled error:', err);
});

View File

@ -0,0 +1,366 @@
import dotenv from 'dotenv';
import path from 'path';
import fs from 'fs/promises';
import { logger } from '../lib/logger';
import { callOpenAI } from '../lib/openai';
import { generateImage as generateFaceImage } from '../lib/image-generator-face';
import { generateVideo } from '../lib/video-generator';
dotenv.config();
type Size = { width: number; height: number };
interface MusicSpotCharacter {
bodyType: string;
hairStyle: string;
}
interface MusicSpotCut {
cutId: number;
pose: string;
action: string;
camera?: string[]; // list of 5 camera variants per cut
}
interface MusicSpotScene {
sceneId: number;
time: string;
location: string;
outfit: string;
cuts: MusicSpotCut[];
}
interface MusicSpotConfig {
character: MusicSpotCharacter;
scenes: MusicSpotScene[];
}
interface Server {
baseUrl?: string;
outputDir?: string;
inputDir?: string;
name: string;
}
const DEFAULT_SIZE: Size = { width: 720, height: 1280 };
const FACE_SRC = path.resolve('src/musicspot_generator/face.png');
const GENERATED_DIR = path.resolve('generated');
function loadServers(): Server[] {
const servers: Server[] = [
{
name: 'SERVER1',
baseUrl: process.env.SERVER1_COMFY_BASE_URL,
outputDir: process.env.SERVER1_COMFY_OUTPUT_DIR,
}
/*{
name: 'SERVER2',
baseUrl: process.env.SERVER2_COMFY_BASE_URL,
outputDir: process.env.SERVER2_COMFY_OUTPUT_DIR,
}*/,
]
.filter(s => !!s.baseUrl && !!s.outputDir)
.map(s => ({
...s,
// Convert output dir to input dir by convention
inputDir: s.outputDir!.replace(/output/i, 'input'),
}));
if (servers.length === 0) {
logger.warn('No servers configured. Please set SERVER{N}_COMFY_BASE_URL and SERVER{N}_COMFY_OUTPUT_DIR in .env');
} else {
for (const s of servers) {
logger.info(`Configured ${s.name}: baseUrl=${s.baseUrl}, outputDir=${s.outputDir}, inputDir=${s.inputDir}`);
}
}
return servers;
}
async function ensureDirs() {
await fs.mkdir(GENERATED_DIR, { recursive: true });
}
async function copyFaceToServers(servers: Server[]): Promise<{ fileName: string; absPerServer: Record<string, string> }> {
const faceFileName = 'face.png';
const absPerServer: Record<string, string> = {};
// Validate face source
try {
await fs.access(FACE_SRC);
} catch {
throw new Error(`Face image not found at ${FACE_SRC}`);
}
for (const srv of servers) {
if (!srv.inputDir) continue;
const dest = path.join(srv.inputDir, faceFileName);
try {
await fs.mkdir(srv.inputDir, { recursive: true });
await fs.copyFile(FACE_SRC, dest);
absPerServer[srv.name] = path.resolve(dest);
logger.info(`Copied face image to ${srv.name} input: ${dest}`);
} catch (err) {
logger.warn(`Failed to copy face image to ${srv.name}: ${err}`);
}
}
return { fileName: faceFileName, absPerServer };
}
function buildImagePromptRequest(
character: MusicSpotCharacter,
scene: MusicSpotScene,
cut: MusicSpotCut,
cameraIntent: string
): string {
// Ask OpenAI to return JSON: { "imagePrompt": "..." }
return `
Return exactly one JSON object, nothing else: { "imagePrompt": "..." }.
Write "imagePrompt" in around 110140 words to generate a still portrait image (720x1280 vertical).
Keep a consistent character identity using the provided face image (identity preservation), but do not mention any camera brand/model.
Describe clearly and concretely:
- Character: ${character.bodyType}; hair: ${character.hairStyle}
- Time: ${scene.time}
- Location: ${scene.location}
- Outfit: ${scene.outfit}
- Pose: ${cut.pose}
- Action/Expression: ${cut.action}
- Camera framing/composition intention: ${cameraIntent}
- Lighting/mood/style: cohesive and realistic, natural skin tones, soft depth of field.
Avoid: brand names, copyrighted characters, extreme or explicit content, text overlays, watermarks, multiple people. Focus on a single subject medium-full portrait, tasteful and aesthetic. Use simple sentences.
Only respond with JSON.
`.trim();
}
function buildVideoPromptRequest(
character: MusicSpotCharacter,
scene: MusicSpotScene,
cut: MusicSpotCut,
cameraIntent: string
): string {
// Ask OpenAI to return JSON: { "videoPrompt": "..." }
// Strong constraints to avoid "cut/zoom" etc. Keep a single continuous 8s shot.
return `
Return exactly one JSON object and nothing else: { "videoPrompt": "..." }.
Write "videoPrompt" in 100140 words. Present tense. Concrete, simple sentences.
HARD RULES:
- One continuous 8-second shot (oner). No edits.
- Fixed location and general vantage; maintain spatial continuity.
- No zooms, no rack zoom, no smash/push-in, no cuts, no transitions, no "meanwhile".
- Camera motion: at most a slight pan/tilt or subtle dolly within 1 meter.
- Keep framing consistent (vertical 720x1280). Avoid technical brand names or lens jargon.
Incorporate the following camera intention: "${cameraIntent}".
If it conflicts with HARD RULES (e.g., zoom, push-in, extreme moves), reinterpret it into a subtle, compliant motion (e.g., gentle glide, slight pan/tilt) while preserving the creative intent.
Describe:
1) Main action: ${cut.action}
2) Pose/composition: ${cut.pose}
3) Scene/time/location/outfit: ${scene.time}; ${scene.location}; outfit: ${scene.outfit}
4) Lighting/mood/style coherent with the character: ${character.bodyType}; hair: ${character.hairStyle}
Prohibited (case-insensitive): cut, cuts, cutting, quick cut, insert, close-up, extreme close-up, zoom, zooming, push-in, pull-out, whip, switch angle, change angle, montage, cross-cut, smash cut, transition, meanwhile, later.
Only respond with JSON.
`.trim();
}
async function getImagePromptFromOpenAI(req: string): Promise<string> {
const res = await callOpenAI(req);
const prompt = res?.imagePrompt || res?.image_prompt || res?.prompt;
if (!prompt || typeof prompt !== 'string') {
throw new Error('OpenAI failed to return imagePrompt JSON.');
}
return prompt.trim();
}
async function getVideoPromptFromOpenAI(req: string): Promise<string> {
const res = await callOpenAI(req);
const prompt = res?.videoPrompt || res?.video_prompt || res?.prompt;
if (!prompt || typeof prompt !== 'string') {
throw new Error('OpenAI failed to return videoPrompt JSON.');
}
return prompt.trim();
}
function pickServer(servers: Server[], idx: number): Server {
if (servers.length === 0) {
throw new Error('No servers configured.');
}
return servers[idx % servers.length];
}
async function copyImageToAllServerInputs(servers: Server[], localGeneratedImagePath: string): Promise<string> {
const fileName = path.basename(localGeneratedImagePath);
const copies: string[] = [];
for (const s of servers) {
if (!s.inputDir) continue;
const dest = path.join(s.inputDir, fileName);
try {
await fs.copyFile(localGeneratedImagePath, dest);
copies.push(dest);
logger.debug(`Copied ${fileName} to ${s.name} input: ${dest}`);
} catch (err) {
logger.warn(`Failed to copy ${fileName} to ${s.name} input: ${err}`);
}
}
return fileName; // return the name used for Comfy workflows
}
async function main() {
try {
await ensureDirs();
// Load scenes.json
const configRaw = await fs.readFile(path.resolve('src/musicspot_generator/scenes.json'), 'utf-8');
const cfg: MusicSpotConfig = JSON.parse(configRaw);
const servers = loadServers();
if (servers.length === 0) {
return;
}
// Ensure face.png in each server's input
const { fileName: faceFileName } = await copyFaceToServers(servers);
// Two-phase per scene:
// Phase 1: Generate ALL images for the scene (to keep image model warm)
// Phase 2: Generate ALL videos for the images of that scene (to keep video model warm)
let imageTaskIndex = 0;
let videoTaskIndex = 0;
for (const scene of cfg.scenes) {
logger.info(`=== Scene ${scene.sceneId}: Phase 1 - Image generation start ===`);
type SceneImageItem = {
sceneId: number;
cutId: number;
cameraIntent: string;
imgFileName: string;
imgPath: string;
};
const sceneImageItems: SceneImageItem[] = [];
// Phase 1: images
for (const cut of scene.cuts) {
const cameraVariants = Array.isArray(cut.camera) && cut.camera.length > 0
? cut.camera
: ['eye-level medium shot', 'slight left 30°', 'slight right 30°', 'slight high angle', 'slight low angle'];
for (let camIdx = 0; camIdx < cameraVariants.length; camIdx++) {
const cameraIntent = cameraVariants[camIdx];
const variantIndex = camIdx + 1;
// 1) Generate image prompt for this camera
logger.info(`Scene ${scene.sceneId} - Cut ${cut.cutId} - Cam${variantIndex}: generating image prompt...`);
const imgPromptReq = buildImagePromptRequest(cfg.character, scene, cut, cameraIntent);
let imagePrompt: string;
try {
imagePrompt = await getImagePromptFromOpenAI(imgPromptReq);
} catch (err) {
logger.error(`OpenAI image prompt failed for scene ${scene.sceneId} cut ${cut.cutId} cam ${variantIndex}: ${err}`);
continue;
}
// 2) Generate one image using face conditioning for this specific camera
const serverForImage = pickServer(servers, imageTaskIndex++);
const imgFileName = `musicspot_s${scene.sceneId}_c${cut.cutId}_v${variantIndex}.png`;
logger.info(`Generating image (${imgFileName}) on ${serverForImage.name}...`);
try {
// Use only the face file name for the workflow image input (Comfy expects it in its input dir)
const finalImagePath = await generateFaceImage(
imagePrompt,
faceFileName,
imgFileName,
serverForImage.baseUrl!,
serverForImage.outputDir!,
DEFAULT_SIZE
);
logger.info(`Image generated: ${finalImagePath}`);
sceneImageItems.push({
sceneId: scene.sceneId,
cutId: cut.cutId,
cameraIntent,
imgFileName,
imgPath: finalImagePath,
});
} catch (err) {
logger.error(`Image generation failed (${imgFileName}) on ${serverForImage.name}: ${err}`);
continue;
}
}
}
logger.info(`=== Scene ${scene.sceneId}: Phase 1 complete. Generated ${sceneImageItems.length} image(s). ===`);
logger.info(`=== Scene ${scene.sceneId}: Phase 2 - Video generation start ===`);
// Phase 2: videos for all images generated in this scene
for (const item of sceneImageItems) {
// Find original cut info for prompts
const cut = scene.cuts.find(c => c.cutId === item.cutId);
if (!cut) {
logger.warn(`Cut ${item.cutId} not found for scene ${scene.sceneId}, skipping video.`);
continue;
}
// 3) Generate video prompt for this camera
logger.info(`Generating video prompt for ${item.imgFileName} (scene ${scene.sceneId}, cut ${item.cutId})...`);
const vidPromptReq = buildVideoPromptRequest(cfg.character, scene, cut, item.cameraIntent);
let videoPrompt: string;
try {
videoPrompt = await getVideoPromptFromOpenAI(vidPromptReq);
} catch (err) {
logger.error(`OpenAI video prompt failed for ${item.imgFileName}: ${err}`);
continue;
}
// 4) Copy the base image to every server's input folder
const imageFileNameForComfy = await copyImageToAllServerInputs(servers, item.imgPath);
// 5) Generate video on a chosen server (round-robin)
const serverForVideo = pickServer(servers, videoTaskIndex++);
const videoFileName = item.imgFileName.replace(/\.png$/i, '.mp4');
logger.info(`Generating video (${videoFileName}) on ${serverForVideo.name} using ${imageFileNameForComfy}...`);
try {
const videoPath = await generateVideo(
videoPrompt,
imageFileNameForComfy,
videoFileName,
serverForVideo.baseUrl!,
serverForVideo.outputDir!,
DEFAULT_SIZE
);
logger.info(`Video generated: ${videoPath}`);
} catch (err) {
logger.error(`Video generation failed (${videoFileName}) on ${serverForVideo.name}: ${err}`);
}
}
logger.info(`=== Scene ${scene.sceneId}: Phase 2 complete. ===`);
}
logger.info('Music spot generation completed.');
} catch (err) {
logger.error('Fatal error in music spot generator:', err);
} finally {
// Optional: do not exit to allow long-running processes; but align with other scripts:
// process.exit();
}
}
main().catch(err => {
logger.error('Unhandled error:', err);
// process.exit(1);
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

@ -0,0 +1,827 @@
{
"character": {
"bodyType": "slim and youthful",
"hairStyle": "long straight blonde hair, white skin, blue eyes, light makeup"
},
"scenes": [
{
"sceneId": 1,
"time": "Evening",
"location": "Recording studio with warm lighting, microphone, pop filter, and soundproof walls",
"outfit": "cute pastel dress with stockings and studio-style headphones",
"cuts": [
{
"cutId": 1,
"pose": "standing close to microphone with both hands on headphones",
"action": "singing passionately with eyes closed",
"camera": [
"front medium shot centered on microphone and pop filter",
"close-up on her lips behind pop filter",
"side profile with blurred studio lights",
"low angle capturing hands holding headphones",
"over-shoulder shot showing recording booth perspective"
]
},
{
"cutId": 2,
"pose": "leaning slightly forward into microphone",
"action": "singing softly with focused expression",
"camera": [
"tight close-up through pop filter mesh",
"side close-up emphasizing focus in her eyes",
"rear shot with blurred soundboard in background",
"medium shot with studio monitors visible",
"handheld dolly-in for intimate effect"
]
},
{
"cutId": 3,
"pose": "one hand holding microphone stand, other hand raised slightly",
"action": "singing energetically with emotion",
"camera": [
"front low angle emphasizing raised hand",
"wide shot showing booth environment",
"profile shot with dramatic lighting",
"close-up on hand gripping mic stand",
"rear shot with blurred pop filter frame"
]
},
{
"cutId": 4,
"pose": "adjusting headphones with one hand while singing",
"action": "smiling softly between lyrics",
"camera": [
"front close-up on headphones adjustment",
"side profile smile with blurred mic",
"tight close-up on eyes sparkling",
"rear dolly shot focusing on headphones",
"medium shot with warm studio lights behind"
]
},
{
"cutId": 5,
"pose": "sitting on studio stool in front of mic",
"action": "closing eyes and holding lyrics sheet while singing",
"camera": [
"front shot framing mic and lyric sheet",
"close-up on lyric sheet in hand",
"side shot capturing relaxed pose",
"rear shot with blurred mixing desk",
"wide establishing shot of whole studio booth"
]
}
]
},
{
"sceneId": 2,
"time": "Day",
"location": "Minimalistic modern recording studio, all white walls and furniture, microphone with pop filter and sound panels",
"outfit": "elegant pure white dress with sheer stockings and studio headphones",
"cuts": [
{
"cutId": 1,
"pose": "standing gracefully in front of white microphone setup",
"action": "singing with serene expression, both hands gently on headphones",
"camera": [
"front medium shot with pure white background",
"close-up on serene face framed by white headphones",
"side profile with white mic and pop filter",
"low angle emphasizing elegance of white dress",
"rear over-shoulder shot into white booth"
]
},
{
"cutId": 2,
"pose": "leaning slightly toward microphone stand",
"action": "singing softly with eyes closed",
"camera": [
"tight close-up through white pop filter mesh",
"profile capturing closed eyes and focus",
"rear shot showing clean white acoustic panels",
"medium shot with glowing white lighting",
"slow dolly-in on her face from side"
]
},
{
"cutId": 3,
"pose": "raising one hand gracefully in the air while singing",
"action": "expressing emotion with body movement",
"camera": [
"front low angle emphasizing raised hand",
"wide shot of white studio environment",
"side profile with flowing dress detail",
"close-up on extended hand in soft light",
"rear shot capturing silhouette in white glow"
]
},
{
"cutId": 4,
"pose": "adjusting headphones gently with one hand",
"action": "smiling subtly between lyrics",
"camera": [
"front close-up with headphones in frame",
"side profile smile against white wall",
"tight close-up on sparkling eyes",
"medium shot with microphone blurred in foreground",
"over-shoulder capturing white minimalist interior"
]
},
{
"cutId": 5,
"pose": "sitting elegantly on modern white stool",
"action": "holding lyric sheet in lap while singing softly",
"camera": [
"front shot framing mic and lyric sheet",
"close-up on hands holding paper",
"side shot with white studio lights glowing",
"rear shot showing full white booth setup",
"wide establishing shot of minimal white studio"
]
}
]
},
{
"sceneId": 3,
"time": "Afternoon",
"location": "Spacious dance studio with mirrored walls, wooden floor, and bright ceiling lights",
"outfit": "sporty outfit: cropped pastel hoodie with black track pants and white sneakers",
"appearance": "hair wet and clinging to face from sweat, skin glowing with perspiration",
"cuts": [
{
"cutId": 1,
"pose": "standing in front of mirror with hands on knees",
"action": "breathing heavily, sweat face, wet hair",
"camera": [
"front medium shot showing sweat on face",
"rear shot capturing reflection in mirror",
"low angle emphasizing strong posture",
"side close-up on sweat dripping",
"handheld shaky cam for intensity"
]
},
{
"cutId": 2,
"pose": "leaning back slightly with arms raised",
"action": "stretching arms after intense dancing",
"camera": [
"front wide shot with arms extended",
"side profile with sweat-soaked hair",
"low angle highlighting arms and torso",
"rear shot with mirrored reflection",
"close-up on exhausted but determined face"
]
},
{
"cutId": 3,
"pose": "mid-dance move, sliding one foot forward",
"action": "serious expression, body low to ground",
"camera": [
"front wide shot capturing dynamic motion",
"low angle emphasizing movement power",
"side tracking following slide motion",
"rear shot reflecting in mirrors",
"close-up on concentrated eyes"
]
},
{
"cutId": 4,
"pose": "sitting on floor with legs stretched out",
"action": "wiping sweat from forehead with towel",
"camera": [
"close-up on towel wiping sweat",
"side profile with hair clinging to cheek",
"rear shot showing reflection in mirror",
"wide establishing studio shot",
"handheld zoom-in on tired smile"
]
},
{
"cutId": 5,
"pose": "crouching low with one hand on floor",
"action": "finishing dance move with powerful stance",
"camera": [
"front low angle emphasizing strength",
"side profile showing sweat flying off",
"rear shot with doubled mirror reflection",
"medium shot focusing on determined eyes",
"handheld circular shot for energy"
]
}
]
},
{
"sceneId": 4,
"time": "Morning",
"location": "Park with trees and soft mist",
"outfit": "pastel knit sweater with checkered mini skirt and black stockings",
"cuts": [
{
"cutId": 1,
"pose": "walking slowly on misty path",
"action": "touching leaves as she passes",
"camera": [
"wide shot with mist and sunrays",
"rear tracking shot through trees",
"close-up of hand brushing leaves",
"side profile with foggy background",
"drone rising above misty path"
]
},
{
"cutId": 2,
"pose": "standing still among trees",
"action": "stretching arms out to feel the morning air",
"camera": [
"front wide shot with rays filtering",
"over-shoulder capturing mist depth",
"low angle emphasizing trees and sky",
"medium shot with fog swirling around",
"handheld pan upward following arms"
]
},
{
"cutId": 3,
"pose": "sitting on wooden bench",
"action": "exhaling softly and smiling peacefully",
"camera": [
"side profile with mist behind",
"close-up on serene smile",
"rear shot framing bench and fog",
"slow dolly-in through mist",
"wide landscape shot with her small figure"
]
},
{
"cutId": 4,
"pose": "kneeling on grass with hands in dew",
"action": "looking at water drops sparkling",
"camera": [
"macro close-up of dew on grass",
"low angle framing her hands",
"profile shot with blurred trees",
"overhead soft focus on her and ground",
"slow tilt from grass to her eyes"
]
},
{
"cutId": 5,
"pose": "walking across a small wooden bridge",
"action": "pausing to lean on railing and smile",
"camera": [
"wide shot of bridge in mist",
"rear tracking her walk",
"medium shot of leaning pose",
"close-up on gentle smile",
"drone reveal showing forest below"
]
}
]
},
{
"sceneId": 5,
"time": "Afternoon",
"location": "Flower field under bright blue sky",
"outfit": "pastel pink one-piece dress with white lace stockings",
"cuts": [
{
"cutId": 1,
"pose": "standing with arms open wide",
"action": "spinning slowly among flowers",
"camera": [
"wide drone shot circling her spin",
"low angle capturing flowers and sky",
"tracking shot through flowers toward her",
"rear silhouette against sunlit sky",
"close-up on smiling face with flowers blurred"
]
},
{
"cutId": 2,
"pose": "kneeling among flowers",
"action": "gently touching petals with fingers",
"camera": [
"macro close-up of fingers on petals",
"profile shot with blurred blooms",
"overhead capturing flower pattern",
"low angle with flowers framing face",
"slow dolly-in through blossoms"
]
},
{
"cutId": 3,
"pose": "lying on back in field",
"action": "looking up at clouds and laughing",
"camera": [
"overhead birds-eye view",
"close-up on laughing face with flowers",
"side shot across grass level",
"rear shot with blue sky backdrop",
"handheld zoom-in with shaky intimacy"
]
},
{
"cutId": 4,
"pose": "walking barefoot through flowers",
"action": "lifting skirt slightly while smiling",
"camera": [
"low angle on feet in grass",
"wide tracking shot from side",
"rear dolly-in following footsteps",
"medium shot with skirt lift motion",
"close-up on glowing smile"
]
},
{
"cutId": 5,
"pose": "sitting cross-legged in flowers",
"action": "making a small flower crown",
"camera": [
"front close-up on crown weaving",
"side profile with blurred blooms",
"top-down focusing on hands",
"rack focus from flowers to her eyes",
"wide establishing shot of field"
]
}
]
},
{
"sceneId": 6,
"time": "Afternoon",
"location": "Sunny street with pastel shops and flowers",
"outfit": "pastel yellow mini dress with white knee-high socks and sneakers",
"cuts": [
{
"cutId": 1,
"pose": "standing in middle of street",
"action": "spinning playfully with dress flaring",
"camera": [
"wide establishing shot of street",
"low angle focusing on dress spin",
"tracking dolly circling her",
"rear shot capturing spin from behind",
"slow zoom-in on smiling face"
]
},
{
"cutId": 2,
"pose": "walking along pastel shop windows",
"action": "running fingers along glass",
"camera": [
"side tracking shot at hip level",
"close-up on hand brushing glass",
"reflection shot from window",
"rear follow dolly with blurred people",
"wide shot capturing full storefront"
]
},
{
"cutId": 3,
"pose": "leaning against pastel wall",
"action": "making a heart shape with hands",
"camera": [
"tight close-up on hands forming heart",
"fish-eye wide close-up",
"side shot at 45°",
"POV as if receiving heart",
"zoom burst effect from wide to close"
]
},
{
"cutId": 4,
"pose": "sitting on curb",
"action": "tying shoelaces while smiling up",
"camera": [
"low angle close-up on hands and laces",
"rear over-shoulder capturing face",
"front medium shot with pastel background",
"wide shot with street perspective",
"handheld dolly-in to her smile"
]
},
{
"cutId": 5,
"pose": "holding ice cream",
"action": "taking playful bite and laughing",
"camera": [
"close-up on lips with ice cream",
"profile with blurred shops",
"rear shot with dripping ice cream",
"slow-motion front focus on laugh",
"wide shot with passing bicycles"
]
}
]
},
{
"sceneId": 7,
"time": "Afternoon",
"location": "Trendy café with modern pastel interior",
"outfit": "white blouse tucked into pleated skirt with sheer white stockings",
"cuts": [
{
"cutId": 1,
"pose": "sitting at table with chin on hands",
"action": "smiling softly while tilting head",
"camera": [
"front eye-level close-up",
"over-shoulder with coffee cup foreground",
"soft focus on eyes with blurred café",
"panning shot across table",
"low angle from tabletop upward"
]
},
{
"cutId": 2,
"pose": "standing by window",
"action": "writing playfully in notebook",
"camera": [
"profile with sunlight flare",
"close-up of pen moving on page",
"reflection in window glass",
"tracking dolly up from feet",
"medium shot with soft bokeh"
]
},
{
"cutId": 3,
"pose": "sitting sideways on chair",
"action": "taking sip of coffee",
"camera": [
"tight focus on lips with cup",
"medium framed by chair back",
"dutch angle for playful energy",
"slow dolly-in on sip",
"wide establishing café interior"
]
},
{
"cutId": 4,
"pose": "leaning on counter",
"action": "resting chin on hands dreamily",
"camera": [
"profile with pastel counter lights",
"close-up on dreamy eyes",
"rear shot capturing café depth",
"handheld tilt upward from arms",
"medium shot with blurred background"
]
},
{
"cutId": 5,
"pose": "standing at café entrance",
"action": "waving playfully toward camera",
"camera": [
"wide shot framing doorway",
"rear dolly out into street",
"tight close-up on waving hand",
"low angle capturing door sign",
"tracking shot circling wave"
]
}
]
},
{
"sceneId": 8,
"time": "Afternoon",
"location": "Hilltop path with light mist and mountain view",
"outfit": "lavender cardigan with flared pastel skirt and gray tights",
"cuts": [
{
"cutId": 1,
"pose": "walking up misty hill path",
"action": "holding skirt slightly while climbing",
"camera": [
"rear tracking shot on path",
"low angle capturing feet and mist",
"wide shot with valley below",
"side profile with wind blowing hair",
"drone reveal rising above hill"
]
},
{
"cutId": 2,
"pose": "standing at lookout",
"action": "stretching arms wide into mist",
"camera": [
"front wide shot with horizon",
"rear silhouette against misty valley",
"low angle with sky backdrop",
"close-up on hands spreading",
"slow dolly-in from side"
]
},
{
"cutId": 3,
"pose": "sitting on rock",
"action": "tying hair with calm expression",
"camera": [
"medium shot side profile",
"close-up on hands tying hair",
"rear shot framing valley mist",
"handheld zoom to her smile",
"wide establishing shot with mountains"
]
},
{
"cutId": 4,
"pose": "kneeling on grass",
"action": "picking wildflowers gently",
"camera": [
"macro on hand picking flowers",
"low angle with valley in blur",
"profile with mist behind",
"top-down on her hands",
"slow pan across field to her face"
]
},
{
"cutId": 5,
"pose": "walking down hill path",
"action": "looking back over shoulder smiling",
"camera": [
"rear tracking dolly with path",
"close-up on over-shoulder smile",
"wide shot with valley mist",
"side tracking with flowing skirt",
"drone pull-back revealing whole scene"
]
}
]
},
{
"sceneId": 9,
"time": "Night",
"location": "Balcony overlooking city lights",
"outfit": "elegant black mini dress with sheer black stockings and heels",
"cuts": [
{
"cutId": 1,
"pose": "standing at balcony railing",
"action": "gazing out over city lights",
"camera": [
"rear wide shot with city skyline",
"profile silhouette against glowing lights",
"close-up on hands gripping railing",
"low angle capturing her and the skyline",
"slow dolly-in from doorway"
]
},
{
"cutId": 2,
"pose": "leaning on railing with chin resting",
"action": "smiling softly at the horizon",
"camera": [
"side profile with blurred city bokeh",
"tight close-up on soft smile",
"rear tracking shot along railing",
"medium shot with glowing skyline",
"handheld tilt capturing candid mood"
]
},
{
"cutId": 3,
"pose": "sitting on balcony chair",
"action": "crossing legs elegantly while holding a glass",
"camera": [
"front eye-level medium shot",
"low angle focusing on legs and glass",
"profile silhouette against city",
"close-up on glass near lips",
"wide establishing balcony view"
]
},
{
"cutId": 4,
"pose": "standing near glass door",
"action": "touching window glass gently",
"camera": [
"rear shot with reflection in glass",
"side profile with neon reflection",
"close-up of hand on glass surface",
"soft focus tracking along her arm",
"wide shot framing city lights outside"
]
},
{
"cutId": 5,
"pose": "walking back inside",
"action": "glancing over shoulder toward city",
"camera": [
"rear tracking shot into room",
"close-up on over-shoulder look",
"wide establishing shot of balcony",
"low angle capturing dress movement",
"slow dolly-in on her eyes"
]
}
]
},
{
"sceneId": 10,
"time": "Night",
"location": "Garden with lanterns and candlelight",
"outfit": "flowy white long dress with lace stockings",
"cuts": [
{
"cutId": 1,
"pose": "walking along lantern-lit path",
"action": "holding a lantern gently in hand",
"camera": [
"wide shot with glowing lanterns",
"rear tracking with path perspective",
"close-up on lantern light in hand",
"profile with candle bokeh",
"drone shot rising above path"
]
},
{
"cutId": 2,
"pose": "kneeling by candle arrangement",
"action": "lighting one candle softly",
"camera": [
"macro on candle flame igniting",
"low angle capturing her face glow",
"rear shot with many candles blurred",
"side profile with flickering shadows",
"handheld close-up on gentle smile"
]
},
{
"cutId": 3,
"pose": "sitting at small garden table",
"action": "resting chin on hands dreamily",
"camera": [
"front close-up with lantern foreground",
"side profile framed by soft lights",
"wide shot showing whole garden",
"over-shoulder focusing on glowing eyes",
"slow dolly-in to serene smile"
]
},
{
"cutId": 4,
"pose": "standing beneath lantern tree",
"action": "raising arms as if embracing lights",
"camera": [
"low angle capturing lantern canopy",
"rear silhouette with glowing tree",
"profile with bokeh lights behind",
"wide establishing garden lights",
"tracking circle shot around her pose"
]
},
{
"cutId": 5,
"pose": "walking barefoot on grass",
"action": "spinning lightly with dress flowing",
"camera": [
"rear dolly capturing flowing fabric",
"low angle focusing on feet in grass",
"wide shot with lanterns in background",
"close-up on hair moving in spin",
"handheld pan following her spin"
]
}
]
},
{
"sceneId": 11,
"time": "Night",
"location": "Grand medieval-style ballroom lit by hundreds of candles with golden chandeliers and gothic arches",
"outfit": "elegant layered gown with multiple flowing fabric layers in soft pastel tones, paired with lace stockings and delicate shoes",
"appearance": "hair styled in loose curls with a jeweled tiara, candlelight reflecting on the layered dress",
"cuts": [
{
"cutId": 1,
"pose": "standing gracefully in center of hall with arms slightly lifted",
"action": "beginning a slow dance step, gazing downward softly",
"camera": [
"wide establishing shot of candlelit hall",
"low angle emphasizing layered gown",
"side profile with candles blurred in background",
"rear shot capturing the dress flowing behind",
"close-up on soft serene expression"
]
},
{
"cutId": 2,
"pose": "spinning lightly with gown flaring out",
"action": "smiling gracefully as layers swirl",
"camera": [
"front wide shot capturing gown layers in motion",
"low angle showing candles and fabric layers",
"rear shot with chandeliers glowing above",
"close-up on swirling gown fabric",
"handheld circular dolly around spin"
]
},
{
"cutId": 3,
"pose": "holding skirt edges delicately with both hands",
"action": "stepping forward slowly with elegance",
"camera": [
"medium shot focusing on skirt layers",
"profile capturing her careful step",
"low angle with candlelight flicker",
"rear shot with her shadow on marble floor",
"close-up on hands lifting fabric layers"
]
},
{
"cutId": 4,
"pose": "pausing mid-dance with one hand extended outward",
"action": "gazing toward candlelit balcony dreamily",
"camera": [
"front shot framing extended hand",
"side profile with glowing candles",
"rear wide shot capturing full hall depth",
"close-up on face with candlelight reflections",
"tracking dolly moving slowly toward her"
]
},
{
"cutId": 5,
"pose": "ending dance with a graceful curtsy",
"action": "bowing slightly with layered gown cascading",
"camera": [
"wide shot showing curtsy against hall arches",
"low angle emphasizing fabric folds",
"close-up on gown layers folding gracefully",
"rear shot with chandeliers above her",
"handheld tilt from gown to her smile"
]
}
]
},
{
"sceneId": 12,
"time": "Night",
"location": "Asian summer festival with hundreds of glowing lanterns floating into the night sky",
"outfit": "cute pink summer dress with light fabric and white sandals",
"appearance": "hair gently tied back with loose strands, warm glow of lantern light reflecting on her skin",
"cuts": [
{
"cutId": 1,
"pose": "standing among festival crowd holding a lantern",
"action": "smiling softly as she prepares to release it",
"camera": [
"front medium shot with lantern glowing in hands",
"close-up on her smile illuminated by warm light",
"side profile with lantern crowd blurred",
"rear shot with lanterns flying above",
"wide establishing shot of festival atmosphere"
]
},
{
"cutId": 2,
"pose": "lifting lantern upward with both hands",
"action": "watching it float away with hopeful expression",
"camera": [
"low angle capturing lantern rising above her",
"front wide shot with multiple lanterns",
"close-up on her face lit by lantern glow",
"rear silhouette shot against lantern sky",
"handheld tilt following lantern upward"
]
},
{
"cutId": 3,
"pose": "walking slowly through festival path",
"action": "gazing up at lantern-filled sky",
"camera": [
"wide shot with festival stalls glowing",
"rear tracking shot with lanterns overhead",
"profile with lantern light on her cheek",
"close-up on eyes reflecting lanterns",
"medium shot with soft bokeh background"
]
},
{
"cutId": 4,
"pose": "kneeling to help a child with lantern",
"action": "smiling warmly while adjusting the lantern",
"camera": [
"front medium shot of interaction",
"close-up on hands fixing lantern",
"side profile with glowing lanterns above",
"rear shot with childs lantern rising",
"wide establishing shot with crowd and lights"
]
},
{
"cutId": 5,
"pose": "standing still gazing upward",
"action": "holding hands together in small prayer as lanterns rise",
"camera": [
"low angle with sky full of lanterns",
"front shot focusing on gentle prayer",
"rear silhouette against glowing night sky",
"side profile capturing emotional look",
"drone pull-out showing her among crowd"
]
}
]
}
]
}

Binary file not shown.

View File

@ -0,0 +1,272 @@
{
"character": {
"bodyType": "slim and youthful",
"hairStyle": "long wavy hair with pastel blue and purple colors"
},
"scenes": [
{
"sceneId": 1,
"time": "Morning",
"location": "Cozy bedroom with pastel bedding and sunlight through curtains",
"outfit": "white oversized pajama shirt with thigh-high stockings",
"cuts": [
{
"cutId": 1,
"pose": "lying on bed with head resting on hands",
"action": "smiling softly at the camera",
"camera": [
"overhead shot from above capturing sunlight on her hair",
"close-up of her face with shallow depth of field",
"side angle showing body stretched on bed",
"slow zoom-in from doorway",
"handheld camera wobble for intimate feeling"
]
},
{
"cutId": 2,
"pose": "stretching arms above head while sitting on bed",
"action": "yawning cutely with eyes half closed",
"camera": [
"medium shot from foot of bed",
"low angle from floor emphasizing legs",
"wide shot with window light flaring",
"tracking shot circling around her stretch",
"soft focus tilt-shift framing"
]
},
{
"cutId": 3,
"pose": "kneeling on bed with playful look",
"action": "blowing a kiss to the camera",
"camera": [
"front close-up catching kiss in slow motion",
"wide shot with pastel background",
"camera tilt from below lips to eyes",
"360° pan around her kneeling pose",
"handheld push-in as she blows kiss"
]
}
]
},
{
"sceneId": 2,
"time": "Morning",
"location": "Bedroom near window with plants and sunlight",
"outfit": "light pastel camisole with short pleated skirt and stockings",
"cuts": [
{
"cutId": 1,
"pose": "sitting on window sill, legs slightly crossed",
"action": "looking outside then turning to smile",
"camera": [
"silhouette against sunlight",
"side profile with plants blurred in foreground",
"close-up of smile turning to camera",
"dolly-in from outside window glass",
"over-shoulder shot capturing view outside"
]
},
{
"cutId": 2,
"pose": "leaning against the wall, hands behind back",
"action": "giggling with shy expression",
"camera": [
"eye-level shot with wall texture visible",
"slight high angle to emphasize cuteness",
"medium close-up on giggle with tilt",
"rack focus between wall décor and her face",
"soft handheld sway left to right"
]
},
{
"cutId": 3,
"pose": "lying on floor with legs up against the wall",
"action": "kicking feet playfully while laughing",
"camera": [
"top-down view from ceiling",
"low angle from foot level",
"side shot capturing playful kicks",
"slow pan across her body",
"handheld camera zoom-in to laughter"
]
}
]
},
{
"sceneId": 3,
"time": "Afternoon",
"location": "Sunny street with pastel shops and flowers",
"outfit": "short pastel pink dress with white stockings and sneakers",
"cuts": [
{
"cutId": 1,
"pose": "standing with one hand on hip",
"action": "spinning gently in place",
"camera": [
"full body wide shot with pastel shops",
"low angle capturing dress swirl",
"tracking shot circling spin",
"handheld slow zoom-in to face",
"rear shot revealing spin from behind"
]
},
{
"cutId": 2,
"pose": "walking with small steps",
"action": "waving happily at the camera",
"camera": [
"tracking dolly shot in front",
"overhead drone shot of street",
"side follow shot at hip level",
"handheld jitter to mimic vlog",
"close-up on waving hand with face blurred"
]
},
{
"cutId": 3,
"pose": "leaning forward playfully",
"action": "making a heart shape with hands",
"camera": [
"tight close-up on hands forming heart",
"fish-eye wide close-up",
"side shot at 45°",
"POV shot as if receiving heart",
"zoom burst effect from wide to close"
]
}
]
},
{
"sceneId": 4,
"time": "Afternoon",
"location": "Trendy café with bright modern interior",
"outfit": "white blouse tucked into pleated skirt with stockings",
"cuts": [
{
"cutId": 1,
"pose": "sitting at a table with chin on hands",
"action": "smiling softly while tilting head",
"camera": [
"front eye-level close-up",
"soft focus on eyes, blurred background",
"over-shoulder shot of coffee cup",
"panning shot across table to her",
"low angle from table surface"
]
},
{
"cutId": 2,
"pose": "standing by window with crossed legs",
"action": "writing playfully in a small notebook",
"camera": [
"profile with sunlight flare",
"close-up of pen on paper then tilt to face",
"tracking shot from feet to head",
"reflection in window glass",
"medium shot with bokeh lights behind"
]
},
{
"cutId": 3,
"pose": "sitting sideways on chair with one leg up",
"action": "taking a sip from a cup with cute expression",
"camera": [
"tight focus on lips touching cup",
"medium shot framed by chair back",
"slight dutch angle for energy",
"slow dolly-in on playful sip",
"wide establishing shot of café"
]
}
]
},
{
"sceneId": 5,
"time": "Night",
"location": "Elegant ballroom with chandeliers",
"outfit": "sparkly silver mini dress with black stockings and heels",
"cuts": [
{
"cutId": 1,
"pose": "standing tall with one hand on waist",
"action": "turning slowly while smiling confidently",
"camera": [
"wide shot capturing chandelier",
"low angle emphasizing elegance",
"tracking dolly rotation",
"close-up of smile during turn",
"rear tracking shot revealing gown shimmer"
]
},
{
"cutId": 2,
"pose": "sitting gracefully on a velvet chair",
"action": "crossing legs elegantly",
"camera": [
"medium close-up from side",
"top-down angle showing chair texture",
"front focus on crossed legs",
"soft rack focus from chair to her face",
"panning shot circling chair"
]
},
{
"cutId": 3,
"pose": "leaning on railing with dreamy look",
"action": "gazing at the chandelier lights",
"camera": [
"over-shoulder shot of chandelier view",
"profile silhouette with golden backlight",
"wide shot capturing ballroom depth",
"handheld tilt up from railing to face",
"slow dolly-out revealing emptiness of hall"
]
}
]
},
{
"sceneId": 6,
"time": "Night",
"location": "Luxurious bedroom with soft golden lighting",
"outfit": "black lace camisole with mini skirt and stockings",
"cuts": [
{
"cutId": 1,
"pose": "lying sideways on bed with legs slightly bent",
"action": "looking at the camera with sultry eyes",
"camera": [
"close-up on eyes with blurred background",
"tracking shot along legs up to face",
"overhead soft focus",
"low angle from bed surface",
"handheld intimate pan across body"
]
},
{
"cutId": 2,
"pose": "sitting at vanity table",
"action": "putting on earrings slowly",
"camera": [
"mirror reflection focus",
"close-up on hands adjusting earrings",
"profile side shot with warm glow",
"over-shoulder shot including vanity lights",
"slow push-in from doorway"
]
},
{
"cutId": 3,
"pose": "kneeling on bed with arched back",
"action": "running hand through hair with sensual expression",
"camera": [
"rear shot with back arch emphasized",
"medium close-up focusing on hair movement",
"low angle capturing curves",
"soft focus candlelight bokeh",
"circling dolly shot for dramatic effect"
]
}
]
}
]
}

View File

@ -0,0 +1,259 @@
import dotenv from 'dotenv';
import path from 'path';
import fs from 'fs/promises';
import { logger } from '../lib/logger';
import { callOpenAI } from '../lib/openai';
import { generateVideo } from '../lib/video-generator';
dotenv.config();
type Size = { width: number; height: number };
interface MusicSpotCharacter {
bodyType: string;
hairStyle: string;
}
interface MusicSpotCut {
cutId: number;
pose: string;
action: string;
camera?: string[]; // list of camera variants per cut
}
interface MusicSpotScene {
sceneId: number;
time: string;
location: string;
outfit: string;
cuts: MusicSpotCut[];
}
interface MusicSpotConfig {
character: MusicSpotCharacter;
scenes: MusicSpotScene[];
}
interface Server {
baseUrl?: string;
outputDir?: string;
inputDir?: string;
name: string;
}
const DEFAULT_SIZE: Size = { width: 720, height: 1280 };
const FOLDER = process.argv[2] || process.env.MUSICSPOT_FOLDER || 'oputstise';
const FOLDER_SAFE = FOLDER.replace(/[/\\?%*:|"<>]/g, '_');
const GENERATED_DIR = path.resolve('generated');
function loadServers(): Server[] {
const servers: Server[] = [
{
name: 'SERVER1',
baseUrl: process.env.SERVER1_COMFY_BASE_URL,
outputDir: process.env.SERVER1_COMFY_OUTPUT_DIR,
},
/*
{
name: 'SERVER2',
baseUrl: process.env.SERVER2_COMFY_BASE_URL,
outputDir: process.env.SERVER2_COMFY_OUTPUT_DIR,
},*/
]
.filter((s) => !!s.baseUrl && !!s.outputDir)
.map((s) => ({
...s,
inputDir: s.outputDir!.replace(/output/i, 'input'),
}));
if (servers.length === 0) {
logger.warn('No servers configured. Please set SERVER{N}_COMFY_BASE_URL and SERVER{N}_COMFY_OUTPUT_DIR in .env');
} else {
for (const s of servers) {
logger.info(`Configured ${s.name}: baseUrl=${s.baseUrl}, outputDir=${s.outputDir}, inputDir=${s.inputDir}`);
}
}
return servers;
}
async function ensureDirs() {
await fs.mkdir(GENERATED_DIR, { recursive: true });
}
function buildVideoPromptRequest(
character: MusicSpotCharacter,
scene: MusicSpotScene,
cut: MusicSpotCut,
cameraIntent: string
): string {
return `
Return exactly one JSON object and nothing else: { "videoPrompt": "..." }.
Write "videoPrompt" in 100140 words. Present tense. Concrete, simple sentences.
HARD RULES:
- One continuous 8-second shot (oner). No edits.
- Fixed location and general vantage; maintain spatial continuity.
- No zooms, no rack zoom, no smash/push-in, no cuts, no transitions, no "meanwhile".
- Camera motion: at most a slight pan/tilt or subtle dolly within 1 meter.
- Keep framing consistent (vertical 720x1280). Avoid technical brand names or lens jargon.
Incorporate the following camera intention: "${cameraIntent}".
If it conflicts with HARD RULES (e.g., zoom, push-in, extreme moves), reinterpret it into a subtle, compliant motion (e.g., gentle glide, slight pan/tilt) while preserving the creative intent.
Describe:
1) Main action: ${cut.action}
2) Pose/composition: ${cut.pose}
3) Scene/time/location/outfit: ${scene.time}; ${scene.location}; outfit: ${scene.outfit}
4) Lighting/mood/style coherent with the character: ${character.bodyType}; hair: ${character.hairStyle}
Prohibited (case-insensitive): cut, cuts, cutting, quick cut, insert, close-up, extreme close-up, zoom, zooming, push-in, pull-out, whip, switch angle, change angle, montage, cross-cut, smash cut, transition, meanwhile, later.
Only respond with JSON.
`.trim();
}
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
async function getVideoPromptFromOpenAI(req: string): Promise<string> {
const res = await callOpenAI(req);
const prompt = res?.videoPrompt || res?.video_prompt || res?.prompt;
if (!prompt || typeof prompt !== 'string') {
throw new Error('OpenAI failed to return videoPrompt JSON.');
}
return prompt.trim();
}
function pickServer(servers: Server[], idx: number): Server {
if (servers.length === 0) {
throw new Error('No servers configured.');
}
return servers[idx % servers.length];
}
async function copyImageToAllServerInputs(servers: Server[], localGeneratedImagePath: string): Promise<string> {
const fileName = path.basename(localGeneratedImagePath);
for (const s of servers) {
if (!s.inputDir) continue;
const dest = path.join(s.inputDir, fileName);
try {
await fs.copyFile(localGeneratedImagePath, dest);
logger.debug(`Copied ${fileName} to ${s.name} input: ${dest}`);
} catch (err) {
logger.warn(`Failed to copy ${fileName} to ${s.name} input: ${err}`);
}
}
return fileName; // return the name used for Comfy workflows
}
async function fileExists(p: string): Promise<boolean> {
try {
await fs.access(p);
return true;
} catch {
return false;
}
}
async function main() {
try {
await ensureDirs();
// Load scenes.json
const configRaw = await fs.readFile(path.resolve(`src/musicspot_generator/${FOLDER}/scenes.json`), 'utf-8');
const cfg: MusicSpotConfig = JSON.parse(configRaw);
const servers = loadServers();
if (servers.length === 0) {
return;
}
// Generate videos only, based on images already present in ./generated
let videoTaskIndex = 0;
for (const scene of cfg.scenes) {
logger.info(`=== Scene ${scene.sceneId}: Video generation start ===`);
for (const cut of scene.cuts) {
const cameraVariants =
Array.isArray(cut.camera) && cut.camera.length > 0
? cut.camera
: ['eye-level medium shot', 'slight left 30°', 'slight right 30°', 'slight high angle', 'slight low angle'];
for (let camIdx = 0; camIdx < cameraVariants.length; camIdx++) {
const cameraIntent = cameraVariants[camIdx];
const variantIndex = camIdx + 1;
const imgFileName = `${FOLDER_SAFE}_musicspot_s${scene.sceneId}_c${cut.cutId}_v${variantIndex}.png`;
const imgPath = path.join(GENERATED_DIR, imgFileName);
// Only proceed if image exists
const hasImage = await fileExists(imgPath);
if (!hasImage) {
logger.warn(`Skipping video: source image not found: ${imgPath}`);
continue;
}
const videoFileName = imgFileName.replace(/\.png$/i, '.mp4');
const videoOutPath = path.join(GENERATED_DIR, videoFileName);
// Skip if video already
const hasVideo = await fileExists(videoOutPath);
if (hasVideo) {
logger.info(`Video already exists, skipping: ${videoOutPath}`);
continue;
}
// 1) Generate video prompt for this camera
logger.info(
`Scene ${scene.sceneId} - Cut ${cut.cutId} - Cam${variantIndex}: generating video prompt from image ${imgFileName}...`
);
const vidPromptReq = buildVideoPromptRequest(cfg.character, scene, cut, cameraIntent);
let videoPrompt: string;
try {
videoPrompt = await getVideoPromptFromOpenAI(vidPromptReq);
} catch (err) {
logger.error(`OpenAI video prompt failed for ${imgFileName}: ${err}`);
continue;
}
// 2) Copy the base image to every server's input folder
const imageFileNameForComfy = await copyImageToAllServerInputs(servers, imgPath);
// 3) Generate video on a chosen server (round-robin)
const serverForVideo = pickServer(servers, videoTaskIndex++);
logger.info(`Generating video (${videoFileName}) on ${serverForVideo.name} using ${imageFileNameForComfy}...`);
try {
const videoPath = await generateVideo(
videoPrompt,
imageFileNameForComfy,
videoFileName,
serverForVideo.baseUrl!,
serverForVideo.outputDir!,
DEFAULT_SIZE,
true,
true
);
await sleep(10000); // wait a bit for file system to settle
logger.info(`Video generated: ${videoPath}`);
} catch (err) {
logger.error(`Video generation failed (${videoFileName}) on ${serverForVideo.name}: ${err}`);
}
}
}
logger.info(`=== Scene ${scene.sceneId}: Video generation complete ===`);
}
logger.info('Video generation for all scenes completed.');
} catch (err) {
logger.error('Fatal error in music spot video generator:', err);
}
}
main().catch((err) => {
logger.error('Unhandled error:', err);
});