@wji.ai/cli 0.8.1 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -9
- package/skill/short-drama/SKILL.md +44 -23
- package/skill/short-drama/dashboard.html +863 -0
- package/skill/short-drama/examples/night-shift/episodes/E01.json +1 -1
- package/skill/short-drama/examples/night-shift/project.json +5 -4
- package/skill/short-drama/reference/canvas-recipes.md +1 -1
- package/skill/short-drama/reference/cinematography-low-key.md +5 -6
- package/skill/short-drama/reference/cinematography.md +1 -1
- package/skill/short-drama/reference/craft-motion.md +21 -1
- package/skill/short-drama/reference/craft-shots.md +24 -0
- package/skill/short-drama/reference/edit-tools.md +65 -0
- package/skill/short-drama/reference/mix-over-video.md +5 -1
- package/skill/short-drama/reference/operations-cli.md +3 -3
- package/skill/short-drama/reference/schema.md +18 -10
- package/skill/short-drama/reference/stage-characters.md +26 -16
- package/skill/short-drama/reference/stage-storyboard.md +34 -7
- package/skill/short-drama/reference/workspace-layout.md +1 -1
- package/skill/short-drama/scripts/selftest.mjs +440 -33
- package/skill/short-drama/scripts/short-drama.mjs +644 -107
- package/skill/short-drama/scripts/short-drama-dashboard.mjs +0 -459
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"videoResolution": "768P",
|
|
13
13
|
"references": "keyframes",
|
|
14
14
|
"style": "realistic",
|
|
15
|
-
"register": "大陆普通话"
|
|
15
|
+
"register": "大陆普通话",
|
|
16
|
+
"foregroundLevel": "high"
|
|
16
17
|
},
|
|
17
18
|
"source": {
|
|
18
19
|
"kind": "original",
|
|
@@ -278,7 +279,7 @@
|
|
|
278
279
|
"image": {
|
|
279
280
|
"prompt": "Portrait of a young East Asian woman in her mid-twenties, contemporary 2020s Chinese city, night-shift convenience store clerk, slim and upright, hair pulled into a loose low ponytail, faded navy jacket over a striped store uniform, faint shadows under alert eyes, thin lips pressed together, three-quarter view bust, plain neutral background, soft directional key light from the upper left with a cool fill, shallow depth of field",
|
|
280
281
|
"negativePrompt": "plastic skin, over-smoothed face, perfectly symmetrical face, doll-like features, dead eyes without highlights, helmet hair, flat textureless fabric, stiff mannequin pose, logo, brand name, text, watermark",
|
|
281
|
-
"sheet": "Single character
|
|
282
|
+
"sheet": "Single character sheet on ONE 16:9 landscape canvas, three panels left to right, grey-white seamless studio background, studio lighting. semi-realistic character illustration, painterly rendering with soft blended edges and visible brush texture, anatomically grounded. LEFT: one headshot, the face filling the full height of the panel, facial features large and clear. CENTER: full-body front view, the face completely covered by a flat grey box so no facial features are visible. RIGHT: full-body back view, same height and body proportions as the front view, feet on the same ground line. Subject: a young East Asian woman in her mid-twenties, contemporary Chinese night-shift convenience store clerk, faded navy jacket over a striped uniform, alert tired eyes. Same costume on both full-body views. No side view, no extra characters, no scenery, no text, no watermark.",
|
|
282
283
|
"tags": [
|
|
283
284
|
"semi-realistic",
|
|
284
285
|
"painterly",
|
|
@@ -442,7 +443,7 @@
|
|
|
442
443
|
"image": {
|
|
443
444
|
"prompt": "Portrait of an East Asian man in his early sixties, contemporary 2020s China, retired security guard, stocky build with a slight stoop, grey close-cropped crew cut, washed-out grey zip jacket, thick fingers, deep crow's feet, weathered skin with uneven tone and visible pores, three-quarter view bust, plain neutral background, soft directional key light from the upper left with a cool fill, shallow depth of field",
|
|
444
445
|
"negativePrompt": "plastic skin, over-smoothed face, perfectly symmetrical face, young face, dead eyes without highlights, helmet hair, flat textureless fabric, stiff mannequin pose, logo, brand name, text, watermark",
|
|
445
|
-
"sheet": "Single character
|
|
446
|
+
"sheet": "Single character sheet on ONE 16:9 landscape canvas, three panels left to right, grey-white seamless studio background, studio lighting. semi-realistic character illustration, painterly rendering with soft blended edges and visible brush texture, anatomically grounded. LEFT: one headshot, the face filling the full height of the panel, facial features large and clear. CENTER: full-body front view, the face completely covered by a flat grey box so no facial features are visible. RIGHT: full-body back view, same height and body proportions as the front view, feet on the same ground line. Subject: a stocky East Asian man in his early sixties, contemporary retired security guard, grey crew cut, washed-out grey zip jacket, slight stoop. Same costume on both full-body views. No side view, no extra characters, no scenery, no text, no watermark.",
|
|
446
447
|
"tags": [
|
|
447
448
|
"semi-realistic",
|
|
448
449
|
"painterly",
|
|
@@ -538,7 +539,7 @@
|
|
|
538
539
|
"image": {
|
|
539
540
|
"prompt": "Portrait of a tall thin East Asian man around forty, contemporary 2020s Chinese city at night, black hooded raincoat zipped to the chin, hood pulled low so only the jaw and unshaven stubble show, rain beading on the wet plastic, three-quarter view bust, plain neutral background, hard cool rim light from behind and a dim key from the left, shallow depth of field",
|
|
540
541
|
"negativePrompt": "plastic skin, over-smoothed face, perfectly symmetrical face, full face clearly visible, dead eyes, dry raincoat, flat textureless fabric, stiff mannequin pose, logo, brand name, text, watermark",
|
|
541
|
-
"sheet": "Single character
|
|
542
|
+
"sheet": "Single character sheet on ONE 16:9 landscape canvas, three panels left to right, grey-white seamless studio background, studio lighting. semi-realistic character illustration, painterly rendering with soft blended edges and visible brush texture, anatomically grounded. LEFT: one headshot, the face filling the full height of the panel, facial features large and clear. CENTER: full-body front view, the face completely covered by a flat grey box so no facial features are visible. RIGHT: full-body back view, same height and body proportions as the front view, feet on the same ground line. Subject: a tall thin East Asian man around forty, contemporary, black hooded raincoat with the hood low over the face, stubble. Same costume on both full-body views. No side view, no extra characters, no scenery, no text, no watermark.",
|
|
542
543
|
"tags": [
|
|
543
544
|
"semi-realistic",
|
|
544
545
|
"painterly",
|
|
@@ -185,7 +185,7 @@ SD prompt <project> score E01 > /tmp/E01-score.md # p
|
|
|
185
185
|
# Sound Designer conforms it to the edit's real cuts and writes one bed prompt per section
|
|
186
186
|
wji canvas add audio -c E1 -n "E01 score 1" -m seed-audio-1.0 --prompt-file /tmp/E01-score-1.txt --json | jq -r .id
|
|
187
187
|
wji canvas gen <node> --yes --wait # ask first; ~0.15 credits a minute
|
|
188
|
-
#
|
|
188
|
+
# lay the sections under the edit — SoundCraft, or ffmpeg if soundcraft-cli is missing (edit-tools.md)
|
|
189
189
|
wji asset upload ./E01-mix.mp4 --json | jq -r .id # → MIX
|
|
190
190
|
SD bind <project> --ep 1 wji.assets.mix MIX
|
|
191
191
|
```
|
|
@@ -78,13 +78,12 @@ the negative space is usually the dark part of the frame.
|
|
|
78
78
|
- Write the placement into `frame`: `she sits in the lower-left third, the unlit shelves filling
|
|
79
79
|
the right two-thirds of the frame`.
|
|
80
80
|
|
|
81
|
-
**前景 — a foreground layer.**
|
|
82
|
-
glass, a door frame, a hanging lamp, the shoulder of the other person (an
|
|
83
|
-
it from the scene's anchors where you can, so it also carries continuity.
|
|
84
|
-
|
|
85
|
-
of focus. Aim higher than that minimum in this look.
|
|
81
|
+
**前景 — a foreground layer.** Where the story gives a reason, shoot *past* something: the edge of a
|
|
82
|
+
shelf, rain on the glass, a door frame, a hanging lamp, the shoulder of the other person (an
|
|
83
|
+
over-the-shoulder). Take it from the scene's anchors where you can, so it also carries continuity.
|
|
84
|
+
`params.foregroundLevel` sets how often; this look suits `medium` or `high`.
|
|
86
85
|
|
|
87
|
-
**Three layers.** A defocused foreground, the person in the shadow in the mid-ground, and a
|
|
86
|
+
**Three layers, when a cut has a foreground.** A defocused foreground, the person in the shadow in the mid-ground, and a
|
|
88
87
|
practical in the background that falls into bokeh. That depth is what the wide aperture exists for.
|
|
89
88
|
|
|
90
89
|
## 4. Lens — wide open, chosen per scene
|
|
@@ -46,7 +46,7 @@ storyboard (stage 5).
|
|
|
46
46
|
| `scenes[].lighting[].prompt` | the preset's `framePhrase`, plus the sources that light the space | `scene-design` |
|
|
47
47
|
| every `cut.frame` | the `framePhrase`, the direction phrase, `light.source` verbatim, the camera phrase, the foreground | `photography` |
|
|
48
48
|
| every `cut.light` | `direction` (never one of the preset's `bannedLight` on a person), `source`, `note` | `photography` |
|
|
49
|
-
| each segment | at least `preferredShare` of the cuts with people keyed from `preferredLight`;
|
|
49
|
+
| each segment | at least `preferredShare` of the cuts with people keyed from `preferredLight`; the episode's foreground share within `params.foregroundLevel` | `photography` |
|
|
50
50
|
| every `cut.aperture` / `params.camera.aperture` | `maxStop` or faster | `photography` |
|
|
51
51
|
| video prompt, `sheets` mode | the Look line carries the preset's `videoPhrase` (for `wan`: a `画面:` line with `videoPhraseZh`). `prompt segment` writes it for you | `video-prompt` |
|
|
52
52
|
|
|
@@ -19,7 +19,27 @@ text for a machine, not prose for a reader.
|
|
|
19
19
|
what is received → one or two visible signals in a channel the size can read (gaze, breath,
|
|
20
20
|
body set, object handling) → the decision (an action taken or withheld) → the landing at the
|
|
21
21
|
end boundary. A wide cannot carry an eyelid; a close-up cannot carry a full-body retreat.
|
|
22
|
-
Keeping still is a valid performance when it is the character's strategy
|
|
22
|
+
Keeping still is a valid performance when it is the character's strategy — and even then they
|
|
23
|
+
breathe and blink (`idle: "still"`).
|
|
24
|
+
|
|
25
|
+
### Everyone on screen is alive (gate `idle-motion`, sheets mode)
|
|
26
|
+
|
|
27
|
+
A model freezes whoever the prompt gives no motion, so a sub-character stands in the background like
|
|
28
|
+
a photo. `motion` is the one action that carries the cut; everyone else who is visible gets a small
|
|
29
|
+
motion of their own in `cut.idle: { C02: "shifts her weight, picking at her sleeve" }`. The skeleton
|
|
30
|
+
writes it into the shot. The rule, per cut with two or more visible people: all but the one carrying
|
|
31
|
+
the action have one (an over-the-shoulder's shoulder does not count). Give the speaker one too when
|
|
32
|
+
they would otherwise only talk.
|
|
33
|
+
|
|
34
|
+
- **Take it from why they are there.** Waiting: shifts weight, fiddles with a strap, checks the door.
|
|
35
|
+
Working: the work carries on. Listening: a small nod, the eyes moving over the other's face. Tense:
|
|
36
|
+
a tightening jaw, a thumb rubbing a finger. Do not reuse one motion across people or cuts: two people
|
|
37
|
+
breathing in time reads as a loop.
|
|
38
|
+
- **Small enough not to steal the cut:** one motion, in place. A line, a moving mouth or a change of
|
|
39
|
+
place is a cut's action, not an idle (gated, as is a length over 90 characters).
|
|
40
|
+
- **It must not break the boundaries:** hands, held props and positions end where `end` says.
|
|
41
|
+
- **Crowds and extras** with no sheet go in `background` as a motion, not a list of people: "shoppers
|
|
42
|
+
drifting past far behind her".
|
|
23
43
|
4. **Camera** — `cameraNote`. A lock-off is written like a move: the framing, **what still moves
|
|
24
44
|
inside it**, how long it holds (`hold`). An empty camera line is read as "free to drift", and
|
|
25
45
|
the model grows a push-in nobody asked for. A move names its `motivation` (reveal / pressure
|
|
@@ -88,6 +88,30 @@ adjective:
|
|
|
88
88
|
tighter) is a statement of power — make it on purpose.
|
|
89
89
|
- **Over-the-shoulder** names whose shoulder is in the foreground (`foreground`); that decides
|
|
90
90
|
whose shot it is.
|
|
91
|
+
|
|
92
|
+
### Two people talking
|
|
93
|
+
|
|
94
|
+
A model puts into the shot everyone the shot names, and "looking at him" gives it no direction. In
|
|
95
|
+
sheets mode the gate `two-person-framing` holds every two-person segment to these, and `prompt
|
|
96
|
+
segment` writes the phrases for you:
|
|
97
|
+
|
|
98
|
+
- **Default pattern:** a two-shot once to set who stands where → **over-the-shoulder**, alternating,
|
|
99
|
+
for the exchange → a **clean single** for the punchline or the biggest reaction. A reaction lands
|
|
100
|
+
harder alone, so do not put every shot over a shoulder.
|
|
101
|
+
- **A single is single.** `characters` holds one person, the shot opens `Only @imageK in frame.`, and
|
|
102
|
+
the rest of the shot never describes the other person. Their reaction gets its own cut.
|
|
103
|
+
- **Every look has a direction.** `eyeline: { C01: "screen-left down" }` — `screen-left` or
|
|
104
|
+
`screen-right`, then `level`, `up` or `down`. The direction is toward the other person, so it follows
|
|
105
|
+
from the side they stand on. `"away screen-left"` is a deliberate look elsewhere; `"task"` keeps the
|
|
106
|
+
eyes on what the hands are doing. Height comes from the blocking: a person kneeling looks up, the
|
|
107
|
+
one standing looks down.
|
|
108
|
+
- **The sides are locked.** `axis.screenLeft` / `screenRight` become a `Sides:` line in the prompt, and
|
|
109
|
+
every shot holds them. Moving a person across needs `axisSide: cross` with a `note`.
|
|
110
|
+
- **Over-the-shoulder is its own setting.** `ots: "C01"` is the person whose shoulder is near the lens;
|
|
111
|
+
`characters` holds both people. The prompt says their face is not visible, so only one face is left
|
|
112
|
+
for the lip-sync and the eyes. It counts as a foreground for `params.foregroundLevel`, and that person
|
|
113
|
+
needs no `eyeline`. When one person is much lower, shoot up past the low shoulder or down past the
|
|
114
|
+
high one, and keep the sides.
|
|
91
115
|
- **Crossing the axis** is legal with a neutral shot, an in-shot crossing, a deliberate
|
|
92
116
|
disorientation or a re-established geography — `axisSide: cross` with a `note` that says which;
|
|
93
117
|
the new working side is then inherited by the cuts after it.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Picture edit and mix
|
|
2
|
+
|
|
3
|
+
Cut the episode before scoring it. The timeline stays in the drama folder, next to `project.json`, so a later session can reopen the cut. The mp4 uploaded to WJI is only the deliverable.
|
|
4
|
+
|
|
5
|
+
- Picture: [FilmCraft](https://getartcraft.com/apps/filmcraft), command `filmcraft-cli`. On macOS the CLI is a separate zip; the `.app` does not put it on `PATH`.
|
|
6
|
+
- Audio: [SoundCraft](https://getartcraft.com/apps/soundcraft), command `soundcraft-cli`.
|
|
7
|
+
- If `filmcraft-cli` is not installed, cut with ffmpeg. If `soundcraft-cli` is not installed, lay the mix with ffmpeg.
|
|
8
|
+
|
|
9
|
+
Look commands up. Do not invent ids.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
command -v filmcraft-cli && filmcraft-cli commands place
|
|
13
|
+
filmcraft-cli describe timeline.razor
|
|
14
|
+
command -v soundcraft-cli && soundcraft-cli commands
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Picture
|
|
18
|
+
|
|
19
|
+
Download each bound clip into `edits/E01/clips/` (segment id as the filename, story order) before cutting, so the project points at files that stay with the drama.
|
|
20
|
+
|
|
21
|
+
**FilmCraft** when `filmcraft-cli` is on `PATH`:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
filmcraft-cli --project edits/E01.fcproj --save import \
|
|
25
|
+
edits/E01/clips/E01-01.mp4 edits/E01/clips/E01-02.mp4
|
|
26
|
+
filmcraft-cli --project edits/E01.fcproj inspect sequence
|
|
27
|
+
filmcraft-cli --project edits/E01.fcproj export edits/E01-edit.mp4
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`--save` writes the timeline back to `edits/E01.fcproj`. Import puts media in the project; place and trim with a command you have `describe`d, clips in story order. `inspect sequence` is the cut list the score conforms to. Export the locked picture with no score yet. Leave `E01.fcproj` in the project after the upload. If `--project` refuses a missing file, follow `filmcraft-cli help` and still save to `edits/E01.fcproj`.
|
|
31
|
+
|
|
32
|
+
**ffmpeg** when `filmcraft-cli` is missing. Say that you fell back. There is no timeline to reopen; take cut times from the scene filter in the mix-over-video steps.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# clips.txt, one line each: file 'edits/E01/clips/E01-01.mp4'
|
|
36
|
+
ffmpeg -f concat -safe 0 -i clips.txt -c copy edits/E01-edit.mp4
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Re-encode (`-c:v libx264 -c:a aac`) only when `-c copy` fails because the clips do not share a codec or timebase.
|
|
40
|
+
|
|
41
|
+
Upload `edits/E01-edit.mp4` and bind `wji.assets.edit`. Picture lock: do not change the cut's length after the cue sheet is written. A later trim means conforming the sheet again.
|
|
42
|
+
|
|
43
|
+
## Audio
|
|
44
|
+
|
|
45
|
+
Lay the score sections under the locked picture. Dialogue already in the clips stays the anchor.
|
|
46
|
+
|
|
47
|
+
**SoundCraft** when `soundcraft-cli` is on `PATH`. The session is `edits/E01/E01.scraft`; SoundCraft keeps its media in `edits/E01/Audio Files/` beside that file. Discover import and edit ids with `soundcraft-cli commands`, then:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
soundcraft-cli run --in edits/E01/E01.scraft --cmd '<id>={...}'
|
|
51
|
+
soundcraft-cli run --in edits/E01/E01.scraft --bounce edits/E01/E01-mix.wav
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The bounce JSON reports integrated LUFS (`lufs`) and true peak (`true_peak_db`). Leave the `.scraft` and `Audio Files/` in the project.
|
|
55
|
+
|
|
56
|
+
**ffmpeg** when `soundcraft-cli` is missing: offset each stem with `adelay` and `amix`, and measure with `ffmpeg -af ebur128=peak=true`.
|
|
57
|
+
|
|
58
|
+
Mux a wav bounce onto the locked picture so WJI receives an mp4:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
ffmpeg -i edits/E01-edit.mp4 -i edits/E01/E01-mix.wav -map 0:v:0 -map 1:a:0 \
|
|
62
|
+
-c:v copy -c:a aac -shortest edits/E01-mix.mp4
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Upload `edits/E01-mix.mp4` and bind `wji.assets.mix`.
|
|
@@ -20,6 +20,8 @@ Rule: if the video already has it, don't add it again. Listen first and list wha
|
|
|
20
20
|
|
|
21
21
|
## Step 1. Analyse the video (local, free; no WJI)
|
|
22
22
|
|
|
23
|
+
If `edits/E01.fcproj` exists, take the cut list from `filmcraft-cli --project edits/E01.fcproj inspect sequence` (see `edit-tools.md`). Otherwise detect cuts from the exported picture:
|
|
24
|
+
|
|
23
25
|
```bash
|
|
24
26
|
V=input.mp4
|
|
25
27
|
ffprobe -v error -show_entries format=duration:stream=codec_type,r_frame_rate,codec_name -of json "$V"
|
|
@@ -74,6 +76,8 @@ Actions: `enter | swell | duck | drop | silence | sting | riser | whoosh | out |
|
|
|
74
76
|
|
|
75
77
|
## Step 5. Mix spec (for whoever lays it under the video)
|
|
76
78
|
|
|
79
|
+
Lay the stems with SoundCraft when `soundcraft-cli` is installed, and keep `edits/E01/E01.scraft`. Use ffmpeg only when that CLI is missing (`edit-tools.md`).
|
|
80
|
+
|
|
77
81
|
- Dialogue is the anchor: centred; everything sits under it.
|
|
78
82
|
- BGM ducks under dialogue via sidechain from the video's audio (or automation on the dialogue spans from Step 1). No standard fixes the dB; judge by ear.
|
|
79
83
|
- BGE/ambience low and continuous; crossfade across clip seams.
|
|
@@ -85,5 +89,5 @@ Actions: `enter | swell | duck | drop | silence | sting | riser | whoosh | out |
|
|
|
85
89
|
- No accidental vocals or dialogue (use spectrogram; ASR on music hallucinates, ignore nonsense text).
|
|
86
90
|
- No doubled Foley or SFX against the video.
|
|
87
91
|
- 起承轉合 arc audible; every `SPECIAL:` hit present at its timecode.
|
|
88
|
-
- Loudness
|
|
92
|
+
- Loudness: the SoundCraft bounce JSON (`lufs`, `true_peak_db`) when the mix was bounced there; otherwise `ffmpeg -af ebur128=peak=true` on the final mix.
|
|
89
93
|
FAIL → Producer before any paid regen.
|
|
@@ -15,13 +15,13 @@ node {baseDir}/scripts/short-drama.mjs <cmd> {project} …
|
|
|
15
15
|
|
|
16
16
|
| Operation | Command |
|
|
17
17
|
| --- | --- |
|
|
18
|
-
| `init` | `short-drama.mjs init {project} --title "<t>" --episodes N --minutes M --genre "<g>" --register 台湾国语 [--adapt 抽核] [--ratio 9:16] [--video h3\|seedance\|seedance2\|seedance2fast\|wan] [--resolution 480P…] [--accent "<accent>"] [--style photoreal\|realistic\|ghibli] [--references sheets\|keyframes] [--duration auto\|fixed] [--look "<lens>"] [--cinematography low-key\|sweet-romance\|period-nostalgia\|costume-idol\|costume-dark\|scifi-hard\|scifi-soft\|none] [--action <style>\|none] [--violence blood\|implied] [--grammar drama\|action\|faceslap\|comedy] [--comedy slapstick\|deadpan\|romcom\|none] [--composition standard\|art-first] [--story revenge,sweet,…] [--market mainland\|overseas] [--format short\|medium\|movie] [--lang zh]` |
|
|
18
|
+
| `init` | `short-drama.mjs init {project} --title "<t>" --episodes N --minutes M --genre "<g>" --register 台湾国语 [--adapt 抽核] [--ratio 9:16] [--video h3\|seedance\|seedance2\|seedance2fast\|wan] [--resolution 480P…] [--accent "<accent>"] [--style photoreal\|realistic\|ghibli] [--references sheets\|keyframes] [--duration auto\|fixed] [--look "<lens>"] [--cinematography low-key\|sweet-romance\|period-nostalgia\|costume-idol\|costume-dark\|scifi-hard\|scifi-soft\|none] [--action <style>\|none] [--violence blood\|implied] [--grammar drama\|action\|faceslap\|comedy] [--comedy slapstick\|deadpan\|romcom\|none] [--composition standard\|art-first] [--story revenge,sweet,…] [--market mainland\|overseas] [--format short\|medium\|movie] [--lang zh] [--image-lang en\|zh]` |
|
|
19
19
|
| `validate` | `short-drama.mjs validate {project} [--stage <stage>] [--ep N]` — exits 1 on a hard failure, 0 on advisories alone |
|
|
20
20
|
| `checkup` | `short-drama.mjs checkup {project}` — same, but prints the passing and skipped rows too |
|
|
21
21
|
| `seed` | `short-drama.mjs seed {project} script --ep N [--force]` · `… seed {project} storyboard --ep N [--force]` |
|
|
22
22
|
| `render` | `short-drama.mjs render {project} outline \| guideline \| character C01 \| scene S01 \| prop P01 \| script --ep N \| dialogue --ep N \| segment E01-01` |
|
|
23
|
-
| `dashboard` | `short-drama.mjs dashboard {project} [--output dashboard.html] [--
|
|
24
|
-
| `prompt` | `short-drama.mjs prompt {project} segment E01-01 [--json]` · `… prompt {project} audio E01-01 [--full\|--bed]` · `… prompt {project} score E01 [--fps 24] [--json]` (post soundtrack spotting sheet) · `… prompt {project} voice C01` |
|
|
23
|
+
| `dashboard` | `short-drama.mjs dashboard {project} [--output dashboard.html] [--force]` — if `dashboard.html` is missing beside `project.json`, copy the static page there; if it is already there, leave it (`--force` replaces it). The page loads `project.json` itself. Asset ids are resolved with `POST /assets/check-status` (same `url` as `GET /assets/list/:id`); images are not downloaded. Canvas and dataset ids link to `https://app.wji.ai/app/workspace/<workspaceId>` |
|
|
24
|
+
| `prompt` | `short-drama.mjs prompt {project} segment E01-01 [--json]` · `… prompt {project} audio E01-01 [--full\|--bed]` · `… prompt {project} score E01 [--fps 24] [--json]` (post soundtrack spotting sheet) · `… prompt {project} voice C01` · `… prompt {project} image C01\|S01\|P01 [--asset headshot\|sheet\|lighting:<state>\|state:<state>] [--json]` |
|
|
25
25
|
| `manifest` | `short-drama.mjs manifest {project} [--stage <stage>] [--ep N] [--json]` — exit 2 while anything is unbound |
|
|
26
26
|
| `bind` | `short-drama.mjs bind {project} [--ep N] <path> <value>` — record a WJI id in the JSON |
|
|
27
27
|
|
|
@@ -58,13 +58,15 @@ All commands accept either the directory or the `project.json` path.
|
|
|
58
58
|
| `speechAccent` | empty | e.g. `台湾口音国语` / `Taiwanese-accented Mandarin`. Any segment with dialogue must name it in its prompt (the video model speaks the lines) |
|
|
59
59
|
| `references` | `sheets` | `sheets` (**the default — no storyboard images**): the video node references the scene / character / prop sheets directly (`segmentReferences` order: environment, people, props) and the prompt describes each shot. `keyframes`: one generated image per cut, the video node references them in cut order — more images to pay for and approve, worth it when a shot has to be composed exactly. Works with every mode; H3 in sheets mode opens with an `Image k is …` legend instead of the alignment line |
|
|
60
60
|
| `humanReferences` | `true` | `false` drops character sheets from the video node's references (people are described in the prompt's Cast line instead). Any Seedance mode + `photoreal` requires it to be set explicitly: BytePlus refuses photorealistic people as reference images (`input image may contain real person`, HTTP 400) unless the account has the real-person permission enabled |
|
|
61
|
+
| `boundaryContrastCuts` | `2` | how many cuts on each side of a scene boundary the `boundary-contrast` gate compares (1–3); the last K cuts of a scene must not repeat the size + angle + people of the first K of the next scene. Mark a deliberate one with `note: "match-cut"`. The same check across episodes is the advisory `boundary-contrast-episode` |
|
|
61
62
|
| `videoDuration` | `auto` | `auto` (**the default**) passes no `-d` and lets the model's `autoDuration` match the clip to the prompt; the cut seconds are then the *planned* timing. `fixed` passes `-d` = Σ cut seconds, and is what `init`/`paramsOf` fall back to on a model with no `autoDuration` parameter (today: `minimax-h3-ref`). Asking for `auto` on such a model is a gate failure, not a silent downgrade |
|
|
62
63
|
| `camera` | ARRI ALEXA Mini LF / Zeiss Supreme Prime / T1.5 | the production's camera package: `{ body, lens, aperture }`. Every cut's `frame` names it verbatim, so the whole drama looks shot on one rig. A cut may override `focal` and `aperture` |
|
|
63
64
|
| `videoReferences` | `none` | `previous` hands each segment the clip before it as `@video1` (continuity of grade, wardrobe and camera language); `none` leaves the sheets to carry it. Opt-in, because it chains the segments: regenerating one invalidates the reference of every segment after it |
|
|
64
65
|
| `maxVideoRefs` / `maxAudioRefs` | per mode | how many clip / voice references the model takes; set them lower to tighten, never higher |
|
|
65
66
|
| `lookPhraseZh` | empty | the same look phrase in the drama's language, gated in a **Chinese** prompt (`wan`) where the English `lookPhrase` would be both out of place and extra length for the content filter to judge |
|
|
66
67
|
| `speechLeadSeconds` | 0.5 | added to a cut's dialogue seconds before the fit check — the mouth opens after the cut starts; also where a line starts in the `full` soundtrack prompt |
|
|
67
|
-
| `audioTrack` | `post` | where the soundtrack is made. **`post`** (the default): the clips carry dialogue, ambience and in-picture sfx but **no music** (`No music.` / `non_diegetic_music: N/A` / `无配乐`) and no `post: true` sfx — music and sound design cannot carry across separately generated segments, so they are made once over the **edited episode**, on its timecode (`prompt score E01`, then
|
|
68
|
+
| `audioTrack` | `post` | where the soundtrack is made. **`post`** (the default): the clips carry dialogue, ambience and in-picture sfx but **no music** (`No music.` / `non_diegetic_music: N/A` / `无配乐`) and no `post: true` sfx — music and sound design cannot carry across separately generated segments, so they are made once over the **edited episode**, on its timecode (`prompt score E01`, then `mix-over-video.md`); no per-segment audio node. `bed` (ambience + sfx + timed music, no voices) / `full` (the bed plus every line in the connected `@Audio1..3` voices — at most three speakers): a Seed Audio node per segment, music in the clip prompt. `none`: no soundtrack work |
|
|
69
|
+
| `foregroundLevel` | `medium`; `high` for 唯美 / 美感优先 (`composition: art-first`, `cinematography: sweet-romance` or `costume-idol`, a genre naming 唯美 or 美感) | how often a cut shoots past a foreground, measured across the episode: `low` up to 25 % of the cuts, `medium` 25–50 %, `high` 50 % or more (gate `photography`; `--foreground` at init). Which cuts: `stage-storyboard.md` |
|
|
68
70
|
| `scoreFps` | 24 | frame rate `prompt score` writes SMPTE `HH:MM:SS:FF` at (`--fps` overrides); set it to the edit's real fps |
|
|
69
71
|
| `subtitles` | false | `true` drops the subtitles-off sentence requirement from the prompts |
|
|
70
72
|
| `maxFrameChars` | 900 | frames longer than this are flagged as padding (advisory) |
|
|
@@ -78,6 +80,7 @@ All commands accept either the directory or the `project.json` path.
|
|
|
78
80
|
| `violence` | `blood` | `blood` lets frames and prompts show blood and wounds (never dismemberment, decapitation or organs); `implied` shows impact only through reaction and sound |
|
|
79
81
|
| `cinematography` | picked by `init` from the genre (`none` when the key is missing) | the lighting and composition preset, `CINEMATOGRAPHY_PRESETS`: `low-key` (practical sources, people in shadow, side / back / rim light, negative space), `sweet-romance` (soft bright faces, backlit halo, pastel, the couple's distance as the story), `period-nostalgia` (film look, window sun and bare bulbs, ≤ T4), `scifi-hard` (engineered light, one harsh sun, no magic or holograms), `scifi-soft` (holograms, neon, dual-tone colour), `costume-idol` (luminous, gauze, backlit silk), `costume-dark` (firelight and lattice windows, faces in shadow); both costume presets need a wide with negative space in every segment or `none`. Every preset: a named `light.source`, a foreground, ≤ T2.8 unless it says otherwise (`sweet-romance` / `costume-idol`: ≤ T2, a T1.3 package from `init`, half the frames naming their bokeh); the period and costume ones refuse sources from the wrong era. Gated in `scene-design`, `photography` and `video-prompt`; see `cinematography.md` |
|
|
80
82
|
| `lookPhrase` | empty | a lens / look phrase (e.g. `wide-aperture lens, shallow depth of field, creamy background bokeh and soft out-of-focus foreground elements`) that every segment prompt must carry verbatim |
|
|
83
|
+
| `imagePromptLang` | `en` | the language of every character / scene / prop image prompt (portrait, sheet, negative, lighting states, prop states): `en`, or `zh` when the image model takes Chinese — 简体 or 繁體, gated in that language (`init --image-lang zh`). Video prompts follow `params.video`; `identityEn`, `voice.prompt` and `videoDirection` stay English. `prompt <project> image C01 --asset headshot\|sheet\|lighting:<state>\|state:<state>` prints a node's full prompt in it |
|
|
81
84
|
| `style` | `realistic` | `photoreal` (live action), `realistic` (semi-realistic painterly) or `ghibli`; one style for the whole drama (see stage-characters) |
|
|
82
85
|
| `register` | required | the 语体 the lines are written in: `粤语` / `大陆普通话` / `台湾国语` / `港式普通话` / `古装` / `年代` / `英文美式` / `英文英式` (traditional spellings accepted). When `speechAccent` is empty it is derived from this in the prompt's language (`standard Mandarin` / `标准普通话`). A character may override it in `bible.speech.register`. The `dialogue-register` gate is **advisory** — it notes lines that break the register and never fails `validate` |
|
|
83
86
|
| `hookSeconds` | 15 | the hook image (`hookBeat`) must be on screen by this cumulative second |
|
|
@@ -120,7 +123,7 @@ written exactly like 2.5 — they differ only in the ceiling (15 s, not 30) and
|
|
|
120
123
|
| `merge[]`, `risks[]` | no | `{what, why}` / `{what, plan}` |
|
|
121
124
|
| `cutNote`, `mergeNote` | no | decision summaries shown to the user at sign-off |
|
|
122
125
|
| `brief` | yes | the 创作简报 — `{logline, audience, satisfactionEngine, mainHook, protagonistPressure, antagonistPressure, emotionalLine, promise}`, drama language, no quotes; `satisfactionEngine` names one of `打脸 / 复仇 / 强宠 / 身份揭露 / 智商碾压 / 情感释放` (and may say how it varies) |
|
|
123
|
-
| `structure[]` | yes | the three phases `{phase: "golden"|"escalation"|"climax", episodes: [from, to], task, satisfactionPlan, hookPlan}` in that order, tiling `1..episodes`; golden ends within the first ~20 % (and never before episode 3 when there are ≥ 6). Two
|
|
126
|
+
| `structure[]` | yes | the three phases `{phase: "golden"|"escalation"|"climax", episodes: [from, to], task, satisfactionPlan, hookPlan}` in that order, tiling `1..episodes`; golden ends within the first ~20 % (and never before episode 3 when there are ≥ 6). Two episodes: golden + climax only. **One episode**: golden + climax, both `[1, 1]` (retention and payoff in the same 集) |
|
|
124
127
|
| `guideline` | yes | the 创作指引 — `{tone, emotionalIntensity, dialogueStyle, visualStyle, productionLimits, forbidden[] (≥3), priorities[] (3–5)}`; `render guideline` publishes it as its own dataset and it is re-read before every stage |
|
|
125
128
|
| `beats[]` | ≥1 | `{id:"B01", type, weight:"major"|"minor", episode, setup, payoff}`; `episode` is the only place a beat's position lives |
|
|
126
129
|
| `episodes[]` | exactly `params.episodes` | `{ep, function[], task: {goal, obstacle, stake}, change, satisfaction: {type, action}, hookType, synopsis, hook, suspense, sceneIds[], characterIds[], propIds[], crowdPlan?, warnings[]}` — narrative prose, **no dialogue quotes** (`「」『』“”`). `function` ⊆ `开局钩子 / 冲突升级 / 身份揭露 / 情感推进 / 大反转 / 清算收束`; `satisfaction.type` ∈ the six 爽点 types and `action` is the visible action that does it; `hookType` ∈ `身份 / 危机 / 结果 / 情感 / 反转 / 道具`. Episode 1 includes 开局钩子; with ≥ 3 episodes, episode 2's 爽点 type is the brief's engine and episode 3 includes 冲突升级 / 身份揭露 / 大反转; the finale includes 清算收束 or 大反转 |
|
|
@@ -183,11 +186,12 @@ id or list) and `gag` (`setup` / `build` / `punchline`) with `runningGag`.
|
|
|
183
186
|
| --- | --- | --- |
|
|
184
187
|
| `tier` | — | `lead` (1–5, `arc` required) / `support` (≤10, `arc` required) / `functional` (≤10, `name` is a role label such as 急诊医生, `arc` optional) |
|
|
185
188
|
| `from[]` | — | change record: source character, merges, or `["原创"]` |
|
|
186
|
-
| `
|
|
189
|
+
| `disguise` | `lang` | optional `{ name, revealEpisode }` for a character who hides who they are (a villain under a false name, a secret identity): before `revealEpisode` every episode document — `render script` / `dialogue` / `segment`, the seed note — calls them `name` so the crew's scripts do not spoil the reveal. `name` is treated as an alias by the name gates. Gate `tiers`: `name` set and ≠ the real name, `revealEpisode` in 2–episodes |
|
|
190
|
+
| `oneLiner`, `profile.*`, `voice.timbre…emotion` | `lang` | human-readable. **No names or aliases**: in a Chinese (`wan`) prompt `timbre / pace / accent / emotion` are pasted beside the character's first line, and all six go into the voice-sample prompt — gate `voice` |
|
|
187
191
|
| `profile.evidence[]` | source | verbatim quotes, never translated |
|
|
188
|
-
| `image.prompt` |
|
|
189
|
-
| `image.negativePrompt` |
|
|
190
|
-
| `image.sheet` |
|
|
192
|
+
| `image.prompt` | **`params.imagePromptLang`** (English by default) | portrait prompt; **no names or aliases**; must state ethnicity and era |
|
|
193
|
+
| `image.negativePrompt` | `imagePromptLang` | style-dependent, see stage-characters |
|
|
194
|
+
| `image.sheet` | `imagePromptLang` | 16:9 three-panel sheet (headshot filling the height, front full body with the face covered by a grey box, back full body), grey-white studio background, studio lighting; no names; contains the style's render phrase (`STYLE_PRESETS[style].render`, or `.renderZh` in Chinese) |
|
|
191
195
|
| `voice.prompt` | English | ≤400 characters, a static voice identity, **no quoted lines** |
|
|
192
196
|
| `voice.videoDirection` | **English** | ≤160 characters, one clause: how they speak (pace, where the voice sits, what it does at the end of a phrase, accent). It is pasted beside **every line this character says** in a video prompt, and gated there — compress `voice.prompt`, do not repeat it |
|
|
193
197
|
| `identityEn` | **English** | ≤80 characters, the name-free way a prompt refers to this person (`the woman in her mid thirties in headphones`). Distinct per character; no names. Used to attribute dialogue in `keyframes` mode and anywhere a `@imageK` label is not available |
|
|
@@ -209,8 +213,8 @@ id or list) and `gag` (`setup` / `build` / `punchline`) with `runningGag`.
|
|
|
209
213
|
|
|
210
214
|
Outline stage: `id`, `name`, `primary`, `reusePlan` (required for a scene used in
|
|
211
215
|
only one episode). Art stage: `summary` (design intent), `anchors` (3–5,
|
|
212
|
-
paintable and checkable), `lighting` (≥1,
|
|
213
|
-
episodes it appears in), `image.*` (
|
|
216
|
+
paintable and checkable), `lighting` (≥1, prompts in `imagePromptLang`, derived from the
|
|
217
|
+
episodes it appears in), `image.*` (`imagePromptLang`, **empty scene — negative prompt
|
|
214
218
|
must ban people**), `variantOf` + `changes` for variants of a parent scene.
|
|
215
219
|
`wji.nodes.lighting` / `wji.assets.lighting` are keyed by lighting `state`.
|
|
216
220
|
|
|
@@ -283,14 +287,14 @@ prompt bans hands**). At most `thresholds.maxProps` (8).
|
|
|
283
287
|
| Field | Rules |
|
|
284
288
|
| --- | --- |
|
|
285
289
|
| `targetSeconds` | seeded from `params`; the episode estimate must be within `± tolerance` |
|
|
286
|
-
| `hook` / `cliff` | descriptions of the opening hook and closing suspense, not lines |
|
|
290
|
+
| `hook` / `cliff` | descriptions of the opening hook and closing suspense, not lines. `seed script` leaves `hook` empty (the writer describes the opening image) and fills `cliff` from the outline episode's `hook` |
|
|
287
291
|
| `beatsClaimed[]` | outline beat ids this episode realises; must equal the beats whose `episode` is this one |
|
|
288
292
|
| `hookBeat` | `[sceneIndex, beatIndex]` (1-based) where the hook's concrete image lands — within the first `hookWindow` beats of the episode and by `hookSeconds` of cumulative time |
|
|
289
293
|
| `function` / `task` / `change` / `hookType` | seeded from the outline episode and gated equal to it (`script-design`) |
|
|
290
294
|
| `satisfactionBeat` | `[sceneIndex, beatIndex]` of the 爽点 — an action beat, or a line with an action beat beside it; after the hook, before the last beat |
|
|
291
295
|
| `flow[].turn` | `true` on a beat that brings new information, an obstacle, a counter or a choice; the `rhythm` gate measures hook → turns → 爽点 → cliff against `turnSeconds` |
|
|
292
296
|
| `scenes[].sceneId` / `lighting` / `characters` / `props` | must exist in `project.json`; `lighting` must be a state of that scene |
|
|
293
|
-
| `flow[]` | alternating **action beats** `{action}` (narrative, one thing per beat, **common actions only**, no quotes) and **line beats** `{speaker, line, delivery?}`; `speaker` ∈ scene `characters` or `VO
|
|
297
|
+
| `flow[]` | alternating **action beats** `{action}` (narrative, one thing per beat, **common actions only**, no quotes) and **line beats** `{speaker, line, delivery?}`; `speaker` ∈ scene `characters` or `VO` (an off-screen voice: it gets no `@audio` reference, the prompt calls it off-screen and keeps every visible mouth closed while it speaks); `line` ≤ `maxLineChars` non-whitespace characters; `delivery` names nobody when the video prompt carries it (a Chinese `wan` prompt, or an English delivery) — gate `beat-flow` |
|
|
294
298
|
|
|
295
299
|
Duration: `Σ lines (chars ÷ charsPerSecond) + Σ actions × actionSeconds`.
|
|
296
300
|
|
|
@@ -321,8 +325,12 @@ Duration: `Σ lines (chars ÷ charsPerSecond) + Σ actions × actionSeconds`.
|
|
|
321
325
|
| `prompt` | the video prompt, shape by `params.video` (below); gate `prompt-hygiene` (no `…`, no ids / paths / notes, subtitles off unless `params.subtitles`, H3 lips-closed clause, sheets-mode `@imageK` per shot) |
|
|
322
326
|
| `segments[].axis` | `{ between: [C01, C02], workingSide, screenLeft, screenRight }`, required when two or more people appear in the segment |
|
|
323
327
|
| `segments[].grammar` / `exceptions` | `grammar` (a string or an array) says how the segment is cut — `drama` · `action` · `faceslap` · `comedy`; missing, it is `params.grammar`; the old `action: true` means `action`. Each grammar's rules apply (gates `grammar`, `action`, `faceslap`, `comedy`; advisory `action-advice`, `grammar-advice`) and the prompt carries each grammar's line. `exceptions: [{ rule, reason }]` waives one rule for this segment when the user asks — rules `open-wide` `one-move` `impact-reaction` `no-graphic` `reveal-insert` `reaction-chain` `punchline-reaction` `setup-first` (the old `actionExceptions` still works) |
|
|
328
|
+
| `cuts[].idle` | sheets mode: `{ <id>: "shifts her weight" }`, a small motion in place for a person other than the one carrying the action, or `"still"` (breathing and blinking only). In a cut with two or more visible people, all but one need one (gate `idle-motion`) |
|
|
329
|
+
| `cuts[].eyeline` | sheets mode, two-person segments, **required**: `{ <id>: "screen-left\|screen-right level\|up\|down" }` for everyone on screen, or `"away screen-left"`, or `"task"` (gate `two-person-framing`) |
|
|
330
|
+
| `cuts[].ots` | the person whose shoulder is near the lens in an over-the-shoulder; `characters` holds both people; counts as a foreground; that person needs no `eyeline` |
|
|
324
331
|
| `segments[].sound` | `{ ambience, sfx: [{ cut, event, post? }], music }` — ambience identical across a scene's segments; `music` is `N/A`, an intent string, or `{ intent, cues: [{ at: <cut number \| "10.5s">, do: enter \| swell \| duck \| drop \| silence \| out, note }] }` — gate `sound`. An sfx with `post: true` is **sound design outside the story world** (stinger, riser, whoosh, impact hit, reverse cymbal) — never in the clip prompt, made over the edit. In `audioTrack: post` the `music` is the **spotting intent** for the episode score: it never enters the clip, and `prompt score E01` lays it, the post sfx, the lines and the scene beds on the episode timeline. In `bed` / `full`, `prompt audio E01-01` derives the segment's Seed Audio soundtrack prompt from it (timecoded `[start s:end s]` ambience / sfx / music, plus the lines with `--full`) — gate `audio-track` |
|
|
325
332
|
| `wji.assets.edit` / `wji.assets.mix` (episode) | `audioTrack: post` only — the locked edit of the episode (clips cut together, no music) and the final mixed episode (edit + score + sound design). `manifest` asks for the edit once every clip is bound, then for the mix |
|
|
333
|
+
| `segments[].refOverride[]` | optional — the Director pins this segment's **picture** references instead of the derived set (environment, people, props): `[{ entity, asset?: "sheet" \| "headshot" \| "lighting:<state>", faceOf?, why?, use?, zh? }]` in the order they will be connected (`@image1…`). `faceOf: "C01"` says this picture supplies that character's face, so their lines bind to it even when a body sheet of theirs comes later. Voices and the previous clip are still appended after the pictures. An empty array means "derived" |
|
|
326
334
|
| `storyboard.visualPlans[]` | `{ sceneIndex, turn, alignment, pressure, progression, rhythm, landing, sound }` for the 爽点 scene and the cliff scene (seeded) — gate `visual-plan` |
|
|
327
335
|
| `props[].textPolicy` | optional `{ mode: readable \| symbolic \| blank, text }` — gate `text-policy` |
|
|
328
336
|
|
|
@@ -61,23 +61,32 @@ the `seedNote` and `render dialogue` puts them beside each line for the polish p
|
|
|
61
61
|
**must** ban `photorealistic`. Both gated. Always add `logo, brand name, text` — a "store
|
|
62
62
|
uniform" comes back wearing a real chain's badge otherwise (seen in testing), and a real brand
|
|
63
63
|
on a character is a problem in every shot after it.
|
|
64
|
-
- `image.sheet`: the
|
|
64
|
+
- `image.sheet`: the three-panel sheet, written out in full every time. Left to right on
|
|
65
|
+
one 16:9 canvas, grey-white seamless studio background, studio lighting:
|
|
65
66
|
|
|
66
67
|
```
|
|
67
|
-
|
|
68
|
-
│
|
|
69
|
-
│
|
|
70
|
-
│
|
|
71
|
-
│
|
|
72
|
-
|
|
73
|
-
RIGHT-BOTTOM: 4–5 small detail studies;
|
|
74
|
-
the detail studies give way, not the figures
|
|
68
|
+
┌────────────┬────────────┬────────────┐
|
|
69
|
+
│ headshot │ front │ back │ ONE 16:9 canvas
|
|
70
|
+
│ face fills│ full body │ full body │ grey-white studio background
|
|
71
|
+
│ the height│ grey box │ │ studio lighting
|
|
72
|
+
│ │ over face │ │
|
|
73
|
+
└────────────┴────────────┴────────────┘
|
|
75
74
|
```
|
|
76
75
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
```
|
|
77
|
+
Single character sheet on ONE 16:9 landscape canvas, three panels left to right,
|
|
78
|
+
grey-white seamless studio background, studio lighting. <style render phrase>.
|
|
79
|
+
LEFT: one headshot, the face filling the full height of the panel, facial features
|
|
80
|
+
large and clear. CENTER: full-body front view, the face completely covered by a flat
|
|
81
|
+
grey box so no facial features are visible. RIGHT: full-body back view, same height
|
|
82
|
+
and body proportions as the front view, feet on the same ground line. Subject:
|
|
83
|
+
<ethnicity, age, era, identity, hair, outfit>. Same costume on both full-body views.
|
|
84
|
+
No side view, no extra characters, no scenery, no text, no watermark.
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The face lives only in the left headshot. The center panel is the costume and the body.
|
|
88
|
+
Copy the style render phrase verbatim (`STYLE_PRESETS[style].render` — `node scripts/short-drama.mjs`
|
|
89
|
+
exports it). Gated: `16:9`, render phrase, no names.
|
|
81
90
|
|
|
82
91
|
## Voice
|
|
83
92
|
|
|
@@ -167,9 +176,10 @@ invents a voice that fits it, which is why the headshot must be approved first.
|
|
|
167
176
|
one reference, so pass only the headshot node. About 3.6 credits a sample.
|
|
168
177
|
|
|
169
178
|
Generate headshots first (a set of headshots is a batch — quote the total), **look at them**
|
|
170
|
-
(download and open), then sheets and voices from the approved headshots. Check a sheet for:
|
|
171
|
-
face
|
|
172
|
-
|
|
179
|
+
(download and open), then sheets and voices from the approved headshots. Check a sheet for: the
|
|
180
|
+
headshot face fills the panel height, the front full body has its face covered by a flat grey
|
|
181
|
+
box, the back full body matches that height, grey-white studio background. Regenerate rather
|
|
182
|
+
than accept a sheet with a visible face on the front body — every shot inherits it. Listen to the voice
|
|
173
183
|
before you attach it: the age and the weight of the voice should match the face.
|
|
174
184
|
|
|
175
185
|
Attach and bind:
|
|
@@ -163,11 +163,37 @@ tables are in `cinematography.md` and the preset's guide.
|
|
|
163
163
|
|
|
164
164
|
### Foreground
|
|
165
165
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
`foreground` names what sits between the lens and the subject — a console edge, a curtain of rain
|
|
167
|
+
off an awning, a blurred shoulder, a doorframe — and the frame prompt says it is thrown out of focus.
|
|
168
|
+
`""` is a deliberately clean frame.
|
|
169
|
+
|
|
170
|
+
**How often** is `params.foregroundLevel`, gated across the episode so scenes can differ:
|
|
171
|
+
|
|
172
|
+
| Level | Cuts with a foreground | Use |
|
|
173
|
+
|---|---|---|
|
|
174
|
+
| `low` | up to 25 % | clean, direct, face-led; only where the story asks for one |
|
|
175
|
+
| `medium` (default) | 25–50 % | where the story or the shot type gives a reason |
|
|
176
|
+
| `high` (default for 唯美 / 美感优先) | 50 % or more | a layered, voyeuristic or crowded look; soft, composed beauty |
|
|
177
|
+
|
|
178
|
+
唯美 / 美感优先 means `composition: art-first`, `cinematography: sweet-romance` or `costume-idol`, or a genre
|
|
179
|
+
that says 唯美 or 美感. Naming `foregroundLevel` overrides this.
|
|
180
|
+
|
|
181
|
+
**Which cuts** — story first, then shot type, then shot size. Remove it in your head: if the audience
|
|
182
|
+
loses no meaning, shoot the cut clean.
|
|
183
|
+
|
|
184
|
+
1. **Story decides whether.** Threat (the barrel in front of him), watching or hiding (a doorframe, a
|
|
185
|
+
curtain), trapped or outnumbered (bodies closing in), distance between two people, power (the
|
|
186
|
+
powerful one's shoulder or weapon), closeness (her hair, soft), a clue that matters later.
|
|
187
|
+
2. **Shot type decides what.** Over-the-shoulder always has one (the listener's shoulder); POV has what
|
|
188
|
+
they look past; an establishing shot has a layer of the place. Usually clean: a reaction close-up, an
|
|
189
|
+
insert, the 打脸 proof or reveal, an action impact, a moment of isolation.
|
|
190
|
+
3. **Shot size decides how much.** A wide can take a large shape on one edge; a medium one edge; a close
|
|
191
|
+
a soft sliver, never near the eyes or mouth; an extreme close-up almost never.
|
|
192
|
+
|
|
193
|
+
Take it from the scene: it must already be there and on the camera's side of the action. Keep it off
|
|
194
|
+
the face, the mouth, the key prop and the action. Prefer simple still shapes (edges, frames, shoulders)
|
|
195
|
+
over foreground faces and hands, which can read as an extra person. Do not repeat one object across
|
|
196
|
+
cuts unless it is a story motif. At `high`, keep the same reasons and simply use them more.
|
|
171
197
|
|
|
172
198
|
Directing feel (not gated): reverse angles for dialogue (asker's close-up, answerer's close-up);
|
|
173
199
|
the first cut of an episode moves; a key action gets its own 2 s insert; a reaction shot after a
|
|
@@ -412,8 +438,9 @@ score. So by default (`params.audioTrack: post`) **the clips carry no music**: t
|
|
|
412
438
|
says `No music.` (H3 `non_diegetic_music: N/A`, Wan `无配乐`), keeps the dialogue, the ambience and
|
|
413
439
|
the in-picture sfx, and drops every sfx marked `post: true` (stingers, risers, whooshes, impact
|
|
414
440
|
hits — sound design outside the story world). The storyboard still writes `sound.music` (intent and
|
|
415
|
-
cues) and the `post: true` hits: they are the **spotting notes**. When every clip is in, the
|
|
416
|
-
|
|
441
|
+
cues) and the `post: true` hits: they are the **spotting notes**. When every clip is in, cut the
|
|
442
|
+
episode as in `edit-tools.md` (FilmCraft, or ffmpeg if `filmcraft-cli` is missing; the `.fcproj`
|
|
443
|
+
stays in the project), and score that locked picture as one piece on its timecode:
|
|
417
444
|
|
|
418
445
|
```bash
|
|
419
446
|
$SD prompt <project> score E01 # planned spotting sheet: SMPTE rows + ≤120 s Seed Audio sections
|
|
@@ -73,7 +73,7 @@ the headshot + sheet as image references and the voice as audio.
|
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
- headshot: `canvas add image -n "C01 headshot" -m <image model> -r 1:1 -p "<image.prompt>"`
|
|
76
|
-
- sheet: `canvas add image -n "C01 sheet" --from <headshotNode> -m <image model> -r 16:9 -p "@image1 is the face to match. <image.sheet>"`
|
|
76
|
+
- sheet: `canvas add image -n "C01 sheet" --from <headshotNode> -m <image model> -r 16:9 -p "@image1 is the face to match exactly. <image.sheet>"`
|
|
77
77
|
- voice: `canvas add audio -n "C01 voice" -m <audio/tts model> -p "<voice.prompt>"` (optional; skip when no voice model is enabled)
|
|
78
78
|
- title: `canvas add title -t "C01 林小夏" --at <x>,<y>` — one per row, rows 700 px apart
|
|
79
79
|
|