new h3 nodes

This commit is contained in:
Dag Thomas Olsen
2026-08-19 11:40:04 +02:00
parent 1ce0bee617
commit aba45b5b60
33 changed files with 15815 additions and 19 deletions
+88 -1
View File
@@ -600,7 +600,7 @@ Writes the base format — `integrated_multimodal_description`, `overall_soundsc
| `task_type` | `T2VA` (text only), `I2VA` (first frame), `FL2VA` (first + last), `L2VA` (last frame). Non-T2VA emits the exact reference-alignment instruction line |
| `duration_seconds` | Drives cut times and the `S.SS` value in the alignment line |
| `shot_plan` | Auto, or force 1–4 shots |
| `visual_style` | Auto, or one of the guide's styles (`Cinematic`, `live-action`, `2D-animated`, `3D CG`, `claymation`, `watercolor`, `vintage film`) |
| `visual_style` | Auto, one of the guide's styles (`Cinematic`, `live-action`, `2D-animated`, `3D CG`, `claymation`, `watercolor`, `vintage film`), or anything from the APNext Cinematic vocabulary (film stock/format, colour grading, the aesthetics list). Pick **Custom** and type your own in `custom_visual_style` - a filled-in custom box always wins over the dropdown |
| `wildness` | **0 = literal, 100 = fully unhinged.** See below |
| `camera_motion` / `camera_amplitude` / `camera_speed` | The guide's full camera vocabulary. Medium amplitude and normal speed are omitted from the output, as the spec requires |
| `include_dialogue` | Off ⇒ no `(Sx)` IDs and no `<d>` blocks at all |
@@ -691,6 +691,93 @@ Any sizes mix freely; numbering follows connection order per kind. With referenc
└──session_id──────────────────────────────────────────────resume_session_id┘
```
#### APNext context sockets (all writer nodes)
Every H3 writer — `APNext H3 Prompt Writer`, `APNext H3 Reference Prompt Writer`, all the Claude Code H3 nodes (Writer, Reference Writer, Refiner, Continue Writer, Crossover Writer, Scenes Writer) and the generic `APNext Claude Code` node — has `context_1..context_8` sockets (they grow as you connect them). Wire any other APNext node into them: **Time, Scene, Poses, Plots, Feelings, Cinematic, Photography, Science, Geography, Architecture, Fashion, People, Interaction, Stuff, Vehicle, Typography, Brands, Art/Artist, Keywords, Video Game, Human, Character**, or `H3 Characters` (`cast`) and another writer's `h3_prompt`.
The node looks up which node type feeds each socket (via ComfyUI's hidden graph inputs), labels the socket in the UI (`context: time`), and hands the model a block like:
```
CONTEXT FROM CONNECTED APNEXT NODES
1. [time] from APNext Time (fields: eras=Victorian, time (Random))
Input: Victorian era, dusk,
Use it as: Period, era, decade or time of day. Make it visible: period-correct wardrobe, props, ... State the time of day in every shot's opening line ...
2. [feelings] from APNext Feelings (fields: creepy (Multiple Random))
Input: a creeping dread, flickering candles,
Use it as: Emotional tone. Express it through performance, delivery, body language, lighting ... not by naming the emotion.
```
The per-kind instructions live in `utils/apnext_context.py` (`CATEGORY_GUIDANCE`) — edit them to change how a category steers the scene. Unknown sources are passed through as generic context. Example workflows: `examples/h3/h3_context_claude_code.json` and `h3_context_ollama_qwen.json` (same sockets on the plain writer with `model_override = ollama:qwen3:8b`).
#### APNext H3 Characters
**Display Name:** `APNext H3 Characters`
A lookup node for the character reference set. It reads `data/h3/characters.tsv` (`Relative File Path`, `Character / Subject Name`, `Real Actor / Actress`, `Franchise / Show`) and outputs the pieces as separate strings plus a ready-made cast line.
| Input | Description |
|-------|-------------|
| `character` | Dropdown of every unique `Character — Actor (Franchise)` entry, or `🎲 random` |
| `franchise_filter` | Only used with random: restrict the pool to one franchise/show |
| `seed` | Drives the random pick, so it stays stable per queue |
| `cast_in` *(optional)* | Cast lines from an upstream Characters node; this node's line is appended, so several chain into one cast list |
| Output | Description |
|--------|-------------|
| `character` / `actor` / `franchise` | The three columns (franchise verbatim, including any appended look description) |
| `file_path` | The reference clip's relative path |
| `cast` | `Character (played by Actor) from Show` — the `subject_definitions:` form, with anything chained through `cast_in` above it |
Edit the TSV to add or remove entries; duplicates (same character, actor and franchise) are collapsed automatically.
#### APNext H3 Crossover Writer
**Display Name:** `APNext H3 Crossover Writer`
Takes a cast (from `H3 Characters` nodes, or typed by hand) and your steer, and has the local Claude Code CLI write **1–10 crossover scenes** — characters from different shows sharing one story — each a complete four-section T2VA prompt (`subject_definitions:` / `integrated_multimodal_description:` / `overall_soundscape:` / `non_diegetic_music:`). The rules come from `data/h3/guide_crossover_en.md`, distilled from rendered crossover productions: actor pinning, `<Subject 1>` speaker binding, silence mandates, `not in frame` isolation, positioned two-shots, no dead air, grounded entrances, hand-offs between scenes. No title cards are written unless you ask.
| Input | Description |
|-------|-------------|
| `direction` | Your creative brief: premise, tone, where it happens, what must happen, who should clash |
| `extra_cast` | Extra characters typed by hand, one per line |
| `cast_1..cast_4` *(optional)* | `cast` outputs from Characters nodes (chain several through `cast_in`, or use several sockets) |
| `scene_count` | 1–10 scenes |
| `duration_mode` / `scene_duration` | Fixed length per scene, or let Claude pace each scene 5–15 s (`scene_duration` is the fallback) |
| `continuity_mode` | **Independent clips** (each scene its own T2V clip, hard cuts, `already speaking` openers) or **Continuous chain** — scenes written for C2V / motion-context chaining per `data/h3/guide_chain_en.md`: scene N+1 opens on scene N's last frame, one continuous take with no `the shot cuts to`, a 2 s silent hand-off before a *new* speaker's first line, outgoing person kept as a tagged subject, rotating moving closers, one lighting string |
| `shots_per_scene`, `visual_style`, `dialogue_language`, `wildness` | Same meaning as on the other writers |
| `wardrobe` *(optional)* | Wardrobe lock, one line per character (`Sheldon: brown corduroy jacket over a green Flash T-shirt, khakis`), copied word-for-word into every scene. Empty = Claude fixes one outfit per character in the synopsis's `Wardrobe:` lines and repeats it in every scene. The Scenes Writer has the same input |
| `model`, `research`, `director`, `use_subscription`, `timeout_seconds`, `seed` | The Claude Code block; `director` loads the `h3-crossover` skill with verified gold examples |
| Output | Description |
|--------|-------------|
| `scenes` **(list)** | One prompt per scene — a downstream video node runs once per element |
| `durations` **(list)** | The matching seconds per scene; wire into your frame-count math |
| `scenes_text` | All scenes with `=== SCENE NN | duration: S.S ===` envelopes, for preview/saving |
| `synopsis`, `cast`, `scene_count`, `session_id`, `info` | Story summary, the merged cast, how many scenes parsed, and the Claude Code session for the refiner |
#### APNext H3 Claude Code Scenes Writer
**Display Name:** `APNext H3 Claude Code Scenes Writer`
The Claude Code prompt writer, but for a run of scenes: one idea in, **1–10 consecutive T2VA prompts** out in the base three-field format, each with its own duration, forming one continuous story. Same director skills, camera vocabulary, dialogue toggles and wildness bands as `APNext H3 Claude Code Writer`. Optional `image` and `subject_/scenery_/object_` sockets are described into every scene for consistency (nothing becomes `<Picture N>`). Has the same `continuity_mode` switch (independent clips vs continuous chain). Outputs mirror the crossover writer: `scenes` and `durations` are lists.
#### APNext H3 Scene Pick
**Display Name:** `APNext H3 Scene Pick`
Collapses a `scenes` list to one element by `index` (0-based, clamped) and returns that scene, its `duration`, the resolved `index` and the list `count`. Fix the writer's seed, then step the index to render scenes one at a time.
```
[H3 Characters] ─cast─► [H3 Characters] ─cast─► [H3 Crossover Writer] ─scenes (list)──► [MiniMax H3 video] (renders every scene)
└─durations (list)─► [duration → frames]
└─scenes_text──────► [H3 Prompt Preview]
[H3 Crossover Writer] ─scenes/durations─► [H3 Scene Pick index=2] ─scene/duration─► [MiniMax H3 video] (renders one)
```
#### APNext H3 Scenes → Contex Loop Plan
**Display Name:** `APNext H3 Scenes → Contex Loop Plan`
For **continuity across scenes** (the last frames and audio of scene N carried into scene N+1). Converts the `scenes` / `durations` lists into the plan JSON that [ComfyUI-MiniMaxH3-Contex-Loop](https://github.com/ethanfel/ComfyUI-MiniMaxH3-Contex-Loop)'s `MiniMax H3 Contex Loop Plan` node accepts on `plan_json_input` (`shots[]` with `id`, `prompt`, `duration_seconds`, `seed`, plus optional `prompt_prefix` and `defaults.steps`). That pack's loop body then renders every scene in order with the previous tail as motion/audio context, checkpoints and final assembly. Alternatives for one-scene-per-run chains: core `MiniMaxH3AddGuide`, `ComfyUI-H3-Motion-Context`, or this repo's `APNext H3 Claude Code Continue Writer` (last frame → I2VA first frame).
Example workflows in `examples/h3/`: `h3_crossover_batch.json` (render every scene in one queue), `h3_crossover_pick_one.json` (Scene Pick + incrementing index, one scene per run), `h3_scenes_batch.json`, `h3_scenes_pick_one.json`, and `h3_crossover_contex_chain.json` — the Contex-Loop *T2V – Normal* example with the crossover writer in continuous-chain mode feeding `Scenes → Contex Loop Plan` into `plan_json_input`, so the whole run renders scene after scene with the previous tail carried forward.
---
#### Dialogue, `<d>` tags and language
+42
View File
@@ -427,6 +427,13 @@ try:
except Exception:
pass
try:
from .nodes.h3.claude_code_continue_writer import H3ClaudeCodeContinueWriter
NEW_MAPPINGS["H3ClaudeCodeContinueWriter"] = H3ClaudeCodeContinueWriter
NEW_DISPLAY_MAPPINGS["H3ClaudeCodeContinueWriter"] = "APNext H3 Claude Code Continue Writer"
except Exception:
pass
try:
from .nodes.h3.ref_prompt_writer import H3RefPromptWriter
NEW_MAPPINGS["H3RefPromptWriter"] = H3RefPromptWriter
@@ -441,6 +448,41 @@ try:
except Exception:
pass
try:
from .nodes.h3.characters import H3Characters
NEW_MAPPINGS["H3Characters"] = H3Characters
NEW_DISPLAY_MAPPINGS["H3Characters"] = "APNext H3 Characters"
except Exception:
pass
try:
from .nodes.h3.claude_code_crossover_writer import H3ClaudeCodeCrossoverWriter
NEW_MAPPINGS["H3ClaudeCodeCrossoverWriter"] = H3ClaudeCodeCrossoverWriter
NEW_DISPLAY_MAPPINGS["H3ClaudeCodeCrossoverWriter"] = "APNext H3 Crossover Writer"
except Exception:
pass
try:
from .nodes.h3.claude_code_scenes_writer import H3ClaudeCodeScenesWriter
NEW_MAPPINGS["H3ClaudeCodeScenesWriter"] = H3ClaudeCodeScenesWriter
NEW_DISPLAY_MAPPINGS["H3ClaudeCodeScenesWriter"] = "APNext H3 Claude Code Scenes Writer"
except Exception:
pass
try:
from .nodes.h3.scene_pick import H3ScenePick
NEW_MAPPINGS["H3ScenePick"] = H3ScenePick
NEW_DISPLAY_MAPPINGS["H3ScenePick"] = "APNext H3 Scene Pick"
except Exception:
pass
try:
from .nodes.h3.scenes_to_chain_plan import H3ScenesToChainPlan
NEW_MAPPINGS["H3ScenesToChainPlan"] = H3ScenesToChainPlan
NEW_DISPLAY_MAPPINGS["H3ScenesToChainPlan"] = "APNext H3 Scenes → Contex Loop Plan"
except Exception:
pass
# Combine mappings (modular nodes + dynamic nodes)
NODE_CLASS_MAPPINGS = {**NEW_MAPPINGS, **DYNAMIC_MAPPINGS}
NODE_DISPLAY_NAME_MAPPINGS = {**NEW_DISPLAY_MAPPINGS, **DYNAMIC_DISPLAY_MAPPINGS}
+345
View File
@@ -0,0 +1,345 @@
Relative File Path Character / Subject Name Real Actor / Actress Franchise / Show
good/MiniMax_H3_00609_.mp4 Caitlyn Katie Leung Arcane
good/MiniMax_H3_00610_.mp4 Jinx Ella Purnell Arcane
good/MiniMax_H3_00611_.mp4 Silco Jason Spisak Arcane
good/MiniMax_H3_00612_.mp4 Vi Hailee Steinfeld Arcane
good/MiniMax_H3_00613_.mp4 Black Widow Scarlett Johansson Marvel's Avengers
good/MiniMax_H3_00614_.mp4 Bruce Banner Mark Ruffalo Marvel's Avengers
good/MiniMax_H3_00615_.mp4 Nick Fury Samuel L. Jackson Marvel's Avengers
good/MiniMax_H3_00616_.mp4 Scarlet Witch Elizabeth Olsen Marvel's Avengers
good/MiniMax_H3_00617_.mp4 Steve Rogers Chris Evans Marvel's Avengers
good/MiniMax_H3_00618_.mp4 Thor Chris Hemsworth Marvel's Avengers
good/MiniMax_H3_00619_.mp4 Tony Stark Robert Downey Jr. Marvel's Avengers
good/MiniMax_H3_00620_.mp4 Howard Wolowitz Simon Helberg The Big Bang Theory
good/MiniMax_H3_00621_.mp4 Leonard Hofstadter Johnny Galecki The Big Bang Theory
good/MiniMax_H3_00622_.mp4 Penny Kaley Cuoco The Big Bang Theory
good/MiniMax_H3_00623_.mp4 Raj Koothrappali Kunal Nayyar The Big Bang Theory
good/MiniMax_H3_00624_.mp4 Sheldon Cooper Jim Parsons The Big Bang Theory
good/MiniMax_H3_00625_.mp4 Jesse Pinkman Aaron Paul Breaking Bad
good/MiniMax_H3_00626_.mp4 Saul Goodman Bob Odenkirk Breaking Bad
good/MiniMax_H3_00627_.mp4 Walter White Bryan Cranston Breaking Bad
good/MiniMax_H3_00628_.mp4 Larry David Larry David Curb Your Enthusiasm
good/MiniMax_H3_00636_.mp4 Malcolm Reynolds Nathan Fillion Firefly
good/MiniMax_H3_00639_.mp4 Chandler Bing Matthew Perry Friends
good/MiniMax_H3_00640_.mp4 Joey Tribbiani Matt LeBlanc Friends
good/MiniMax_H3_00642_.mp4 Phoebe Buffay Lisa Kudrow Friends
good/MiniMax_H3_00643_.mp4 Rachel Green Jennifer Aniston Friends
good/MiniMax_H3_00644_.mp4 Ross Geller David Schwimmer Friends
good/MiniMax_H3_00645_.mp4 Daenerys Targaryen Emilia Clarke Game of Thrones
good/MiniMax_H3_00646_.mp4 Jon Snow Kit Harington Game of Thrones
good/MiniMax_H3_00648_.mp4 Tyrion Lannister Peter Dinklage Game of Thrones
good/MiniMax_H3_00649_.mp4 Dr. Gregory House Hugh Laurie House M.D.
good/MiniMax_H3_00653_.mp4 Frank Underwood Kevin Spacey House of Cards
good/MiniMax_H3_00654_.mp4 Barney Stinson Neil Patrick Harris How I Met Your Mother
good/MiniMax_H3_00655_.mp4 Ted Mosby Josh Radnor How I Met Your Mother
good/MiniMax_H3_00656_.mp4 John Oliver John Oliver Last Week Tonight
good/MiniMax_H3_00657_.mp4 Chloe Decker Lauren German Lucifer
good/MiniMax_H3_00658_.mp4 Lucifer Morningstar Tom Ellis Lucifer
good/MiniMax_H3_00661_.mp4 Adrian Monk Tony Shalhoub Monk
good/MiniMax_H3_00666_.mp4 Mr. Bean Rowan Atkinson Mr. Bean
good/MiniMax_H3_00667_.mp4 Darlene Alderson Carly Chaikin Mr. Robot
good/MiniMax_H3_00668_.mp4 Elliot Alderson Rami Malek Mr. Robot
good/MiniMax_H3_00670_.mp4 Beth Smith Sarah Chalke Rick and Morty
good/MiniMax_H3_00672_.mp4 Morty Smith Justin Roiland Rick and Morty
good/MiniMax_H3_00673_.mp4 Rick Sanchez Justin Roiland Rick and Morty
good/MiniMax_H3_00674_.mp4 Summer Smith Spencer Grammer Rick and Morty
good/MiniMax_H3_00675_.mp4 RoboCop Peter Weller RoboCop
good/MiniMax_H3_00676_.mp4 Dr. John Watson Martin Freeman Sherlock (BBC)
good/MiniMax_H3_00677_.mp4 Sherlock Holmes Benedict Cumberbatch Sherlock (BBC)
good/MiniMax_H3_00678_.mp4 Dr. Hannibal Lecter Anthony Hopkins The Silence of the Lambs
good/MiniMax_H3_00679_.mp4 Data Brent Spiner Star Trek: The Next Generation
good/MiniMax_H3_00682_.mp4 Geordi La Forge LeVar Burton Star Trek: The Next Generation
good/MiniMax_H3_00683_.mp4 Jean-Luc Picard Patrick Stewart Star Trek: The Next Generation
good/MiniMax_H3_00684_.mp4 William Riker Jonathan Frakes Star Trek: The Next Generation
good/MiniMax_H3_00685_.mp4 Worf Michael Dorn Star Trek: The Next Generation
good/MiniMax_H3_00686_.mp4 Castiel Misha Collins Supernatural
good/MiniMax_H3_00687_.mp4 Dean Winchester Jensen Ackles Supernatural
good/MiniMax_H3_00688_.mp4 Sam Winchester Jared Padalecki Supernatural
good/MiniMax_H3_00689_.mp4 The Terminator T-800 Arnold Schwarzenegger The Terminator
good/MiniMax_H3_00691_.mp4 Dwight Schrute Rainn Wilson The Office
good/MiniMax_H3_00692_.mp4 Jim Halpert John Krasinski The Office
good/MiniMax_H3_00693_.mp4 Michael Scott Steve Carell The Office
good/MiniMax_H3_00694_.mp4 Pam Beesly Jenna Fischer The Office
good/MiniMax_H3_00697_.mp4 Tony Soprano James Gandolfini The Sopranos
good/MiniMax_H3_00698_.mp4 Dana Scully Gillian Anderson The X-Files
good/MiniMax_H3_00699_.mp4 Fox Mulder David Duchovny The X-Files
good/MiniMax_H3_00700_.mp4 Amy Farrah Fowler Mayim Bialik The Big Bang Theory
good/MiniMax_H3_00701_.mp4 Bernadette Rostenkowski Melissa Rauch The Big Bang Theory
good/MiniMax_H3_00704_.mp4 Bobby Singer Jim Beaver Supernatural
good/MiniMax_H3_00707_.mp4 Loki Tom Hiddleston Marvel's Avengers
good/MiniMax_H3_00708_.mp4 Doctor Strange Benedict Cumberbatch Marvel's Avengers
good/MiniMax_H3_00709_.mp4 Deadpool Ryan Reynolds Marvel's Avengers
good/MiniMax_H3_00710_.mp4 Wolverine Hugh Jackman Marvel's Avengers
good/MiniMax_H3_00716_.mp4 Andy Bernard Ed Helms The Office
good/MiniMax_H3_00724_.mp4 Jerry Seinfeld Jerry Seinfeld Seinfeld
good/MiniMax_H3_00725_.mp4 George Costanza Jason Alexander Seinfeld
good/MiniMax_H3_00726_.mp4 Elaine Benes Julia Louis-Dreyfus Seinfeld
good/MiniMax_H3_00728_.mp4 Carrie Bradshaw Sarah Jessica Parker Sex and the City
good/MiniMax_H3_00732_.mp4 Dr. Temperance Brennan Emily Deschanel Bones
good/MiniMax_H3_00733_.mp4 Seeley Booth David Boreanaz Bones
good/MiniMax_H3_00734_.mp4 John Rambo Sylvester Stallone Rambo
good/MiniMax_H3_00735_.mp4 John McClane Bruce Willis Die Hard
good/MiniMax_H3_00739_.mp4 Jake Peralta Andy Samberg Brooklyn Nine-Nine
good/MiniMax_H3_00740_.mp4 Captain Raymond Holt Andre Braugher Brooklyn Nine-Nine
good/MiniMax_H3_00747_.mp4 Jack Shephard Matthew Fox Lost
good/MiniMax_H3_00748_.mp4 John Locke Terry O'Quinn Lost
good/MiniMax_H3_00750_.mp4 Buffy Summers Sarah Michelle Gellar Buffy the Vampire Slayer
good/MiniMax_H3_00751_.mp4 The Tenth Doctor David Tennant Doctor Who
good/MiniMax_H3_00754_.mp4 Jack Bauer Kiefer Sutherland 24
good/MiniMax_H3_00755_.mp4 Don Draper Jon Hamm Mad Men
good/MiniMax_H3_00756_.mp4 Indiana Jones Harrison Ford Indiana Jones
good/MiniMax_H3_00757_.mp4 Captain Jack Sparrow Johnny Depp Pirates of the Caribbean
good/MiniMax_H3_00758_.mp4 Maximus Decimus Meridius Russell Crowe Gladiator
good/MiniMax_H3_00759_.mp4 Clark Kent Tom Welling Smallville
good/MiniMax_H3_00762_.mp4 Liz Lemon Tina Fey 30 Rock
good/MiniMax_H3_00763_.mp4 Jack Donaghy Alec Baldwin 30 Rock
good/MiniMax_H3_00765_.mp4 Will Smith Will Smith The Fresh Prince of Bel-Air
good/MiniMax_H3_00773_.mp4 James Bond (Daniel Craig) Daniel Craig James Bond
good/MiniMax_H3_00774_.mp4 Gandalf Ian McKellen Lord of the Rings
good/MiniMax_H3_00775_.mp4 Star-Lord Chris Pratt Marvel's Avengers
good/MiniMax_H3_00787_.mp4 Wednesday Addams Jenna Ortega Wednesday
good/MiniMax_H3_00788_.mp4 Angel David Boreanaz Buffy the Vampire Slayer
good/MiniMax_H3_00871_.mp4 Rick Grimes Andrew Lincoln The Walking Dead
good/MiniMax_H3_00872_.mp4 Negan Jeffrey Dean Morgan The Walking Dead
good/MiniMax_H3_00873_.mp4 Daryl Dixon Norman Reedus The Walking Dead
good/MiniMax_H3_00874_.mp4 Michonne Danai Gurira The Walking Dead
good/MiniMax_H3_00875_.mp4 Glenn Rhee Steven Yeun The Walking Dead
good/MiniMax_H3_00877_.mp4 President Josiah "Jed" Bartlet Martin Sheen The West Wing
good/MiniMax_H3_00878_.mp4 Josh Lyman Bradley Whitford The West Wing
good/MiniMax_H3_00883_.mp4 Darth Vader (voiced by James Earl Jones) from Star Wars Darth Vader (voiced by James Earl Jones) from Star Wars Real Person / Celebrity
good/MiniMax_H3_00884_.mp4 Thanos Josh Brolin Marvel's Avengers
good/MiniMax_H3_00888_.mp4 Homelander Antony Starr The Boys
good/MiniMax_H3_00890_.mp4 Tony Montana Al Pacino Scarface
good/MiniMax_H3_00891_.mp4 Patrick Bateman Christian Bale American Psycho
good/MiniMax_H3_00894_.mp4 Miranda Priestly Meryl Streep The Devil Wears Prada
good/MiniMax_H3_00900_.mp4 Maleficent Angelina Jolie Maleficent
good/MiniMax_H3_00901_.mp4 Neo Keanu Reeves The Matrix
good/MiniMax_H3_00902_.mp4 John Wick Keanu Reeves John Wick
good/MiniMax_H3_00903_.mp4 Ethan Hunt Tom Cruise Mission: Impossible
good/MiniMax_H3_00905_.mp4 Han Solo Harrison Ford Star Wars
good/MiniMax_H3_00906_.mp4 Michael Corleone Al Pacino The Godfather
good/MiniMax_H3_00907_.mp4 Travis Bickle Robert De Niro Taxi Driver
good/MiniMax_H3_00908_.mp4 Marty McFly Michael J. Fox Back to the Future
good/MiniMax_H3_00910_.mp4 Forrest Gump Tom Hanks Forrest Gump
good/MiniMax_H3_00911_.mp4 Bruce Wayne / Batman Christian Bale The Dark Knight
good/MiniMax_H3_00912_.mp4 Harry Potter Daniel Radcliffe Harry Potter
good/MiniMax_H3_00913_.mp4 Tony Stark Robert Downey Jr. Marvel's Avengers
good/MiniMax_H3_00914_.mp4 The Terminator T-800 Arnold Schwarzenegger The Terminator
good/MiniMax_H3_00915_.mp4 Captain Jack Sparrow Johnny Depp Pirates of the Caribbean
good/MiniMax_H3_00916_.mp4 Hermione Granger Emma Watson Harry Potter
good/MiniMax_H3_00917_.mp4 Ron Weasley Rupert Grint Harry Potter
good/MiniMax_H3_00918_.mp4 Rubeus Hagrid Robbie Coltrane Harry Potter
good/MiniMax_H3_00920_.mp4 Catwoman / Selina Kyle Michelle Pfeiffer Batman Returns
good/MiniMax_H3_00921_.mp4 Elizabeth Swann Keira Knightley Pirates of the Caribbean
good/MiniMax_H3_00924_.mp4 Billy Butcher Karl Urban The Boys
good/MiniMax_H3_00955_.mp4 Eric Cartman Trey Parker South Park
good/MiniMax_H3_00956_.mp4 Stan Marsh Trey Parker South Park
good/MiniMax_H3_00957_.mp4 Kyle Broflovski Matt Stone South Park
good/MiniMax_H3_00958_.mp4 Kenny McCormick Matt Stone South Park
good/MiniMax_H3_00959_.mp4 Randy Marsh Trey Parker South Park
good/MiniMax_H3_00960_.mp4 Charlie Harper Charlie Sheen Two and a Half Men
good/MiniMax_H3_00961_.mp4 Alan Harper Jon Cryer Two and a Half Men
good/MiniMax_H3_00965_.mp4 Spock Leonard Nimoy Star Trek: The Original Series
good/MiniMax_H3_00968_.mp4 Paul Atreides Timothée Chalamet Dune
good/MiniMax_H3_00971_.mp4 Dominic Toretto Vin Diesel Fast & Furious
good/MiniMax_H3_00972_.mp4 Willow Rosenberg Alyson Hannigan Buffy the Vampire Slayer
good/MiniMax_H3_00974_.mp4 Max Black Kat Dennings 2 Broke Girls
good/MiniMax_H3_00975_.mp4 Caroline Channing Beth Behrs 2 Broke Girls
good/MiniMax_H3_00976_.mp4 Lt. Colonel John Sheppard Joe Flanigan Stargate Atlantis
good/MiniMax_H3_00977_.mp4 Dr. Rodney McKay David Hewlett Stargate Atlantis
good/MiniMax_H3_00978_.mp4 Clara Oswald Jenna Coleman Doctor Who
good/MiniMax_H3_00979_.mp4 The Twelfth Doctor Peter Capaldi Doctor Who
good/MiniMax_H3_00981_.mp4 The Eleventh Doctor Matt Smith Doctor Who
good/MiniMax_H3_00987_.mp4 Richard B. Riddick Vin Diesel The Chronicles of Riddick
good/MiniMax_H3_00988_.mp4 Martin Riggs Mel Gibson Lethal Weapon
good/MiniMax_H3_00991_.mp4 Axel Foley Eddie Murphy Beverly Hills Cop
good/MiniMax_H3_00994_.mp4 Dr. Doug Ross George Clooney ER
good/MiniMax_H3_01004_.mp4 Harry Callahan Clint Eastwood Dirty Harry
good/MiniMax_H3_01008_.mp4 Patrick Jane Simon Baker The Mentalist
good/MiniMax_H3_01009_.mp4 John Reese Jim Caviezel Person of Interest
good/MiniMax_H3_01010_.mp4 Harold Finch Michael Emerson Person of Interest
good/MiniMax_H3_01013_.mp4 Special Agent Leroy Jethro Gibbs Mark Harmon NCIS
good/MiniMax_H3_01014_.mp4 Anthony DiNozzo Michael Weatherly NCIS
good/MiniMax_H3_01015_.mp4 Ziva David Cote de Pablo NCIS
good/MiniMax_H3_01016_.mp4 Abby Sciuto Pauley Perrette NCIS
good/MiniMax_H3_01019_.mp4 John Nolan Nathan Fillion The Rookie
good/MiniMax_H3_01024_.mp4 Richard Castle Nathan Fillion Castle
good/MiniMax_H3_01025_.mp4 Kate Beckett Stana Katic Castle
good/MiniMax_H3_01026_.mp4 Jax Teller Charlie Hunnam Sons of Anarchy
good/MiniMax_H3_01028_.mp4 Carrie Mathison Claire Danes Homeland
good/MiniMax_H3_01029_.mp4 Nicholas Brody Damian Lewis Homeland
good/MiniMax_H3_01031_.mp4 Raymond Reddington James Spader The Blacklist
good/MiniMax_H3_01033_.mp4 Javier Peña Pedro Pascal Narcos
good/MiniMax_H3_01039_.mp4 Sergeant Terry Jeffords Terry Crews Brooklyn Nine-Nine, a massive muscular bodybuilder with a shaved bald head, wearing a tight dress shirt and suspenders over his huge chest, holding a plastic cup of yogurt with a tiny spoon.
good/MiniMax_H3_01040_.mp4 Detective Rosa Diaz Stephanie Beatriz Brooklyn Nine-Nine, a tough intense detective with wild dark curly hair framing her face, wearing a dark leather motorcycle jacket and gold badge on her belt.
good/MiniMax_H3_01049_.mp4 Spike James Marsters Buffy the Vampire Slayer, a rebellious vampire with bleached-blonde slicked hair, cheekbones, wearing a dark leather duster coat over a black shirt.
good/MiniMax_H3_01052_.mp4 Saul Berenson Mandy Patinkin Homeland, a senior CIA official with a thick grey beard and silver hair, dark glasses, wearing a dark suit and sweater.
good/MiniMax_H3_01054_.mp4 Jason Bourne Matt Damon The Bourne Identity, a lethal operative with short dark hair, piercing hyper-focused eyes, wearing a dark grey jacket over a black t-shirt.
good/MiniMax_H3_01057_.mp4 Ragnar Lothbrok Travis Fimmel Vikings, a Viking king with piercing ice-blue eyes, braided hair, scruffy beard, wearing leather armor and holding an iron hand axe.
good/MiniMax_H3_01060_.mp4 Bjorn Ironside Alexander Ludwig Vikings, a massive Viking warrior with braided blonde hair, beard, tattoos, wearing fur-trimmed leather armor, holding a heavy broadsword.
good/MiniMax_H3_01063_.mp4 Tommy Shelby Cillian Murphy Peaky Blinders, a gangster with piercing icy blue eyes, undercut hair, tweed flat cap, heavy wool coat over a three-piece suit, smoking a cigarette.
good/MiniMax_H3_01065_.mp4 Michael Scofield Wentworth Miller Prison Break, a man with buzzcut hair, pale blue eyes, wearing a grey long-sleeve shirt with intricate blueprint tattoos visible on his arms.
good/MiniMax_H3_01069_.mp4 Frank Martin Jason Statham The Transporter
good/MiniMax_H3_01072_.mp4 Max Rockatansky Mel Gibson Mad Max
good/MiniMax_H3_01073_.mp4 Bryan Mills Liam Neeson Taken
good/MiniMax_H3_01076_.mp4 Phil Wenneck Bradley Cooper The Hangover
good/MiniMax_H3_01079_.mp4 Hank Moody David Duchovny Californication
good/MiniMax_H3_01080_.mp4 Ace Ventura Jim Carrey Ace Ventura
good/MiniMax_H3_01095_.mp4 Edward Cullen Robert Pattinson Twilight
good/MiniMax_H3_01096_.mp4 Bella Swan Kristen Stewart Twilight
good/MiniMax_H3_01098_.mp4 Mathayus Dwayne Johnson The Scorpion King
good/MiniMax_H3_01139_.mp4 Diana Prince / Wonder Woman Gal Gadot Wonder Woman
good/MiniMax_H3_01142_.mp4 Jane Smith Angelina Jolie Mr. & Mrs. Smith
good/MiniMax_H3_01181_.mp4 Celeste Wright Nicole Kidman Big Little Lies
good/MiniMax_H3_01182_.mp4 Madeline Martha Mackenzie Reese Witherspoon Big Little Lies
good/MiniMax_H3_01198_.mp4 Eve Polastri Sandra Oh Killing Eve
good/MiniMax_H3_01204_.mp4 Annalise Keating Viola Davis How to Get Away with Murder
good/MiniMax_H3_01221_.mp4 Annette Hargrove Reese Witherspoon Cruel Intentions
good/MiniMax_H3_01228_.mp4 Natalie Cook Cameron Diaz Charlie's Angels
good/MiniMax_H3_01229_.mp4 Dylan Sanders Drew Barrymore Charlie's Angels
good/MiniMax_H3_01230_.mp4 Alex Munday Lucy Liu Charlie's Angels
good/MiniMax_H3_01267_.mp4 Nicolas Cage Nicolas Cage Real Person / Celebrity
good/MiniMax_H3_01268_.mp4 Castor Troy Nicolas Cage Face/Off
good/MiniMax_H3_01269_.mp4 Johnny Blaze / Ghost Rider Nicolas Cage Ghost Rider
good/MiniMax_H3_01270_.mp4 Jack Black Jack Black Real Person / Celebrity
good/MiniMax_H3_01273_.mp4 Taylor Swift Taylor Swift Real Person / Celebrity
good/MiniMax_H3_01274_.mp4 John Travolta John Travolta Real Person / Celebrity
good/MiniMax_H3_01275_.mp4 Sean Archer John Travolta Face/Off
good/MiniMax_H3_01279_.mp4 Julia Roberts Julia Roberts Real Person / Celebrity
good/MiniMax_H3_01281_.mp4 Morgan Freeman Morgan Freeman Real Person / Celebrity
good/MiniMax_H3_01282_.mp4 Ellis Boyd 'Red' Redding Morgan Freeman The Shawshank Redemption
good/MiniMax_H3_01283_.mp4 Ben Affleck Ben Affleck Real Person / Celebrity
good/MiniMax_H3_01284_.mp4 Matt Damon Matt Damon Bourne Franchise
good/MiniMax_H3_01285_.mp4 Robin Williams Robin Williams Real Person / Celebrity
good/MiniMax_H3_01286_.mp4 Angelina Jolie Angelina Jolie Lara Croft: Tomb Raider
good/MiniMax_H3_01288_.mp4 Sandra Bullock Sandra Bullock Real Person / Celebrity
good/MiniMax_H3_01289_.mp4 Tom Hanks Tom Hanks Forrest Gump
good/MiniMax_H3_01290_.mp4 Samuel L. Jackson Samuel L. Jackson Marvel's Avengers
good/MiniMax_H3_01291_.mp4 John Travolta John Travolta Real Person / Celebrity
good/MiniMax_H3_01293_.mp4 Jennifer Love Hewitt Jennifer Love Hewitt Real Person / Celebrity
good/MiniMax_H3_01295_.mp4 Anthony Hopkins Anthony Hopkins The Silence of the Lambs
good/MiniMax_H3_01297_.mp4 Brad Pitt Brad Pitt Real Person / Celebrity
good/MiniMax_H3_01298_.mp4 Robert De Niro Robert De Niro Taxi Driver
good/MiniMax_H3_01299_.mp4 Kevin Costner Kevin Costner Real Person / Celebrity
good/MiniMax_H3_01300_.mp4 Tom Cruise Tom Cruise Mission: Impossible
good/MiniMax_H3_01301_.mp4 Michael Douglas Michael Douglas Real Person / Celebrity
good/MiniMax_H3_01302_.mp4 Denzel Washington Denzel Washington Real Person / Celebrity
good/MiniMax_H3_01303_.mp4 Jim Carrey Jim Carrey Ace Ventura
good/MiniMax_H3_01304_.mp4 Jack Nicholson Jack Nicholson Real Person / Celebrity
good/MiniMax_H3_01306_.mp4 Tom Selleck Tom Selleck Magnum, P.I.
good/MiniMax_H3_01307_.mp4 Jackie Chan Jackie Chan Real Person / Celebrity
good/MiniMax_H3_01309_.mp4 Paul Rudd Paul Rudd Real Person / Celebrity
good/MiniMax_H3_01310_.mp4 Will Ferrell Will Ferrell Real Person / Celebrity
good/MiniMax_H3_01311_.mp4 Ben Stiller Ben Stiller Real Person / Celebrity
good/MiniMax_H3_01312_.mp4 Owen Wilson Owen Wilson Real Person / Celebrity
good/MiniMax_H3_01313_.mp4 Vince Vaughn Vince Vaughn Real Person / Celebrity
good/MiniMax_H3_01314_.mp4 Harrison Ford Harrison Ford Indiana Jones
good/MiniMax_H3_01316_.mp4 Adam Sandler Adam Sandler Real Person / Celebrity
good/MiniMax_H3_01317_.mp4 Ashton Kutcher Ashton Kutcher Real Person / Celebrity
good/MiniMax_H3_01318_.mp4 Grant Gustin Grant Gustin Real Person / Celebrity
good/MiniMax_H3_01319_.mp4 Tom Hiddleston Tom Hiddleston Marvel's Avengers
good/MiniMax_H3_01320_.mp4 Natalie Portman Natalie Portman Real Person / Celebrity
good/MiniMax_H3_01321_.mp4 Tim Allen Tim Allen Real Person / Celebrity
good/MiniMax_H3_01325_.mp4 Bruce Wayne / Batman Ben Affleck Batman v Superman
good/MiniMax_H3_01326_.mp4 Jason Bourne Matt Damon Bourne Franchise
good/MiniMax_H3_01327_.mp4 Daniel Hillard / Mrs. Doubtfire Robin Williams Mrs. Doubtfire
good/MiniMax_H3_01328_.mp4 Lara Croft Angelina Jolie Lara Croft: Tomb Raider
good/MiniMax_H3_01330_.mp4 Gracie Hart Sandra Bullock Miss Congeniality
good/MiniMax_H3_01331_.mp4 Forrest Gump Tom Hanks Forrest Gump
good/MiniMax_H3_01332_.mp4 Jules Winnfield Samuel L. Jackson Pulp Fiction
good/MiniMax_H3_01333_.mp4 Vincent Vega John Travolta Pulp Fiction
good/MiniMax_H3_01335_.mp4 Melinda Gordon Jennifer Love Hewitt Ghost Whisperer
good/MiniMax_H3_01336_.mp4 Dylan Sanders Drew Barrymore Charlie's Angels
good/MiniMax_H3_01337_.mp4 Dr. Hannibal Lecter Anthony Hopkins The Silence of the Lambs
good/MiniMax_H3_01338_.mp4 The Narrator Edward Norton Fight Club
good/MiniMax_H3_01339_.mp4 Tyler Durden Brad Pitt Fight Club
good/MiniMax_H3_01341_.mp4 John Dutton Kevin Costner Yellowstone
good/MiniMax_H3_01342_.mp4 Maverick / Pete Mitchell Tom Cruise Top Gun
good/MiniMax_H3_01343_.mp4 Gordon Gekko Michael Douglas Wall Street
good/MiniMax_H3_01344_.mp4 Alonzo Harris Denzel Washington Training Day
good/MiniMax_H3_01345_.mp4 Ace Ventura Jim Carrey Ace Ventura
good/MiniMax_H3_01347_.mp4 Alvy Singer Woody Allen Annie Hall
good/MiniMax_H3_01349_.mp4 Inspector Lee Jackie Chan Rush Hour
good/MiniMax_H3_01351_.mp4 Scott Lang / Ant-Man Paul Rudd Marvel's Avengers
good/MiniMax_H3_01352_.mp4 Ron Burgundy Will Ferrell Anchorman
good/MiniMax_H3_01353_.mp4 Derek Zoolander Ben Stiller Zoolander
good/MiniMax_H3_01354_.mp4 Hansel Owen Wilson Zoolander
good/MiniMax_H3_01355_.mp4 Jeremy Grey Vince Vaughn Wedding Crashers
good/MiniMax_H3_01356_.mp4 Indiana Jones Harrison Ford Indiana Jones
good/MiniMax_H3_01357_.mp4 Seth Jonah Hill Superbad
good/MiniMax_H3_01358_.mp4 Happy Gilmore Adam Sandler Happy Gilmore
good/MiniMax_H3_01359_.mp4 Michael Kelso Ashton Kutcher That '70s Show
good/MiniMax_H3_01360_.mp4 Barry Allen / The Flash Grant Gustin The Flash
good/MiniMax_H3_01361_.mp4 Loki Tom Hiddleston Marvel's Avengers
good/MiniMax_H3_01363_.mp4 Tim "The Tool Man" Taylor Tim Allen Home Improvement
good/MiniMax_H3_01364_.mp4 Rick O'Connell Brendan Fraser The Mummy
good/MiniMax_H3_01369_.mp4 William Munny Clint Eastwood Unforgiven
good/MiniMax_H3_01370_.mp4 Dr. Robert Ford Anthony Hopkins Westworld
good/MiniMax_H3_01376_.mp4 Dr. Robert Ford Anthony Hopkins Westworld
good/MiniMax_H3_01401_.mp4 Sylvester Stallone Sylvester Stallone Rambo
good/MiniMax_H3_01402_.mp4 Rocky Balboa Sylvester Stallone Rocky
good/MiniMax_H3_01407_.mp4 Idris Elba Idris Elba Real Person / Celebrity
good/MiniMax_H3_01408_.mp4 John Luther Idris Elba Luther
good/MiniMax_H3_01421_.mp4 Adam Driver Adam Driver Star Wars
good/MiniMax_H3_01422_.mp4 Kylo Ren Adam Driver Star Wars
good/MiniMax_H3_01427_.mp4 Daniel Craig Daniel Craig James Bond
good/MiniMax_H3_01428_.mp4 James Bond Daniel Craig Casino Royale
good/MiniMax_H3_01435_.mp4 Gordon Ramsay Gordon Ramsay Hell's Kitchen
good/MiniMax_H3_01436_.mp4 Chef Gordon Ramsay Gordon Ramsay Hell's Kitchen
good/MiniMax_H3_01440_.mp4 Geralt of Rivia Henry Cavill The Witcher
good/MiniMax_H3_01441_.mp4 Hugh Jackman Hugh Jackman Real Person / Actor
good/MiniMax_H3_01442_.mp4 Wolverine Hugh Jackman X-Men
good/MiniMax_H3_01443_.mp4 Jake Gyllenhaal Jake Gyllenhaal Nightcrawler
good/MiniMax_H3_01444_.mp4 Lou Bloom Jake Gyllenhaal Nightcrawler
good/MiniMax_H3_01445_.mp4 Jamie Foxx Jamie Foxx Django Unchained
good/MiniMax_H3_01446_.mp4 Django Jamie Foxx Django Unchained
good/MiniMax_H3_01451_.mp4 Joe Rogan Joe Rogan Real Person / Actor
good/MiniMax_H3_01452_.mp4 Joe Rogan Joe Rogan The Joe Rogan Experience
good/MiniMax_H3_01453_.mp4 John Cena John Cena Peacemaker
good/MiniMax_H3_01454_.mp4 Peacemaker John Cena Peacemaker
good/MiniMax_H3_01456_.mp4 Dr. Walter Bishop John Noble Fringe
good/MiniMax_H3_01459_.mp4 Kanye West Kanye West Real Person / Actor
good/MiniMax_H3_01460_.mp4 Ye (played by Kanye West) live in concert Ye (played by Kanye West) live in concert Real Person / Actor
good/MiniMax_H3_01462_.mp4 Billy Butcher Karl Urban The Boys
good/MiniMax_H3_01463_.mp4 Leonardo DiCaprio Leonardo DiCaprio The Wolf of Wall Street
good/MiniMax_H3_01464_.mp4 Jordan Belfort Leonardo DiCaprio The Wolf of Wall Street
good/MiniMax_H3_01465_.mp4 Mads Mikkelsen Mads Mikkelsen Hannibal
good/MiniMax_H3_01466_.mp4 Dr. Hannibal Lecter Mads Mikkelsen Hannibal
good/MiniMax_H3_01467_.mp4 Matthew McConaughey Matthew McConaughey Real Person / Actor
good/MiniMax_H3_01468_.mp4 Rust Cohle Matthew McConaughey True Detective
good/MiniMax_H3_01469_.mp4 Neil deGrasse Tyson Neil deGrasse Tyson Cosmos
good/MiniMax_H3_01470_.mp4 Host Neil deGrasse Tyson Neil deGrasse Tyson Cosmos
good/MiniMax_H3_01471_.mp4 Michael Fassbender Michael Fassbender X-Men
good/MiniMax_H3_01473_.mp4 Woody Harrelson Woody Harrelson Zombieland
good/MiniMax_H3_01474_.mp4 Tallahassee Woody Harrelson Zombieland
good/MiniMax_H3_01475_.mp4 Norman Reedus Norman Reedus Real Person / Actor
good/MiniMax_H3_01476_.mp4 Daryl Dixon Norman Reedus The Walking Dead
good/MiniMax_H3_01478_.mp4 Alluri Sitarama Raju Ram Charan RRR
good/MiniMax_H3_01481_.mp4 Robert Pattinson Robert Pattinson Real Person / Actor
good/MiniMax_H3_01482_.mp4 Edward Cullen Robert Pattinson Twilight
good/MiniMax_H3_01483_.mp4 Ryan Gosling Ryan Gosling Drive
good/MiniMax_H3_01484_.mp4 Driver Ryan Gosling Drive
good/MiniMax_H3_01485_.mp4 Ryan Reynolds Ryan Reynolds Real Person / Actor
good/MiniMax_H3_01486_.mp4 Deadpool Ryan Reynolds Deadpool
good/MiniMax_H3_01489_.mp4 Snoop Dogg Snoop Dogg Real Person / Actor
good/MiniMax_H3_01490_.mp4 Snoop Dogg (played by Snoop Dogg) live in concert performance Snoop Dogg (played by Snoop Dogg) live in concert performance Real Person / Actor
good/MiniMax_H3_01497_.mp4 Willem Dafoe Willem Dafoe Spider-Man
good/MiniMax_H3_01498_.mp4 Norman Osborn Willem Dafoe Spider-Man
good/MiniMax_H3_01499_.mp4 Michael Jackson Michael Jackson Real Person / Actor
good/MiniMax_H3_01503_.mp4 Kobe Bryant Kobe Bryant Real Person / Actor
good/MiniMax_H3_01504_.mp4 Kobe Bryant Kobe Bryant Lakers court
good/MiniMax_H3_01508_.mp4 Komaram Bheem N. T. Rama Rao Jr. RRR
good/MiniMax_H3_01509_.mp4 Dave Chappelle Dave Chappelle Real Person / Actor
good/MiniMax_H3_01510_.mp4 Dave Chappelle Dave Chappelle Chappelle's Show
good/MiniMax_H3_01511_.mp4 David Letterman David Letterman Real Person / Actor
good/MiniMax_H3_01512_.mp4 David Letterman David Letterman Late Show
good/MiniMax_H3_01513_.mp4 Conan O'Brien Conan O'Brien Real Person / Actor
good/MiniMax_H3_01514_.mp4 Conan O'Brien Conan O'Brien Late Night
good/MiniMax_H3_01532_.mp4 Willow Rosenberg Alyson Hannigan Buffy the Vampire Slayer
good/MiniMax_H3_01547_.mp4 Barack Obama Barack Obama the Oval Office
good/MiniMax_H3_01548_.mp4 President Barack Obama Barack Obama the Oval Office
good/MiniMax_H3_01549_.mp4 Donald Trump Donald Trump Real Person / Actor
good/MiniMax_H3_01550_.mp4 Donald Trump Donald Trump The Apprentice
good/MiniMax_H3_01568_.mp4 Agent Olivia Dunham Anna Torv Fringe
good/MiniMax_H3_01571_.mp4 Anne Hathaway Anne Hathaway The Devil Wears Prada
good/MiniMax_H3_01572_.mp4 Andy Sachs Anne Hathaway The Devil Wears Prada
Can't render this file because it contains an unexpected character in line 107 and column 45.
+81
View File
@@ -0,0 +1,81 @@
# MiniMax-H3 Continuous Chain Rules (C2V / motion-context continuation)
Use these when the scenes will be rendered as a **continuous chain**: the last frames (and
audio) of scene N are pinned to the head of scene N+1 (ComfyUI `MiniMaxH3AddGuide`, H3
Motion Context, or the Contex Loop pack). The model starts scene N+1 *on the previous
ending* and trims that head off the delivered file. Independent-clip habits break here;
these rules replace them for every scene after the first.
## 1. Scene 01 is a normal opener; every later scene inherits its start
- Scene 01 follows the ordinary T2V rules (state the style, light and setting; open on the
speaker `already speaking, with no silent establishing beat`).
- Scene 02 onward: `[Shot 1]` **opens on the last frame of the previous scene**. Its first
sentence names that: `Opens on the last frame of the previous scene: <Subject 2> Name (S2)
still in close-up at screen-right, glass at her lips, warm amber lamplight.` Then describe
**what changes** - who enters, who speaks, a small camera move. Do not re-describe a fresh
establishing shot of the location; do not paste the same wide "establishing boilerplate"
at the start and end of every scene (that is what produces the "sandwich" loop).
- Every scene still repeats the style words, the lighting lock and the wardrobe anchors in
its own text - the chain inherits pixels, not the prompt.
## 2. One continuous take, never a hard cut
- A pinned head fights editorial cuts: the model wanders after `the shot cuts to` and then
snaps back to the pinned ending for the last third. So inside a chained scene write **one
unbroken camera move** from the inherited frame to a new closer.
- `[Shot 2]` / `[Shot 3]` may still mark timing beats, but say `without cutting` /
`the camera keeps moving` / `in the same continuous take`. Never `the shot cuts to`.
- The last beat is a **moving closer**, not a hold: a sip in progress, a turn, a truck, a
step out of frame, a hand lowering a prop. Do not end on a frozen tableau or a still stare.
## 3. Rotate the closer - it is the next scene's opening frame
- The final framing of scene N **is** `[Shot 1]` of scene N+1. Do not close on the same
framing you opened with, and do not close on the same class of shot two scenes running.
- Rotate closer classes: face-into-lens close-up, profile, hands + prop filling the frame,
looking down, eyes only, a walk-away over the shoulder, a two-shot only once in a while
and never as the default. A held two-shot of two people looking at each other is illegal
as a default closer.
## 4. Speaker hand-off: never open a subject change on dialogue
Before writing a chained scene, decide: *is the person who speaks first already the person on
screen at the end of the previous scene?*
- **Same speaker on screen** - continuation opener; the line can start at once.
- **Different person speaks first** - mandatory three-part hand-off:
1. **Inherited silent beat, 00:00.000-00:02.000.** Name the previous scene's on-screen
subject by `<Subject N>` tag, state what they are finishing, and mark them silent:
`mouth closed, completely silent, speaking no dialogue. No dialogue, narration or voice
of any kind is heard during this opening beat, and no lips move.`
2. **A named transition out of them, without cutting** - a truck, whip pan, arc or rack
focus that leaves them out of frame and settles on the new speaker.
3. **The new speaker's first `<d>` at 00:02.000 or later, said out loud**: `At 00:02.000,
now that the camera has settled on <Subject 1> Name (S1) alone in frame and only now,
he says: <d>...</d>`.
- The outgoing person is on screen during that beat, so they **must be declared** in
`subject_definitions:` (next free number after the scene's real cast) and referred to by
tag, never bare name. After the transition add `<Subject N> is not in frame.` to every
later beat.
- Budget the hand-off: a subject-change scene needs ~2 s of head before its first line, so
such a scene carries less dialogue than a same-speaker scene of equal length. Group
same-speaker scenes deliberately - two scenes on one person cost no hand-off.
## 5. Latent identity persistence
- If a person other than the speaker was visible at the end of the previous scene (a
panel neighbour, someone at the table), they are still in the inherited pixels. Keep them
in `subject_definitions:` (speaker stays `<Subject 1>`, the neighbour `<Subject 2>`) and
describe their passive presence with a silence mandate. Dropping the tag turns them into
a hallucinated stranger.
## 6. Positions, lighting, lines
- State `screen-left` / `screen-center` / `screen-right` for everyone in every multi-person
beat and keep the seats unless someone clearly moves.
- One lighting string for the whole chain (`bright daylight on skin`, `warm amber
lamplight`); never drift morning -> sunset -> night unless the story jumps time.
- New dialogue in every scene; never recycle a line already spoken in the chain.
- One new person or one new action per scene is plenty; the plot must advance (a new
person, a new action, a new closer), not remix the previous two-shot.
+131
View File
@@ -0,0 +1,131 @@
# MiniMax-H3 Crossover Scene Rules
Production-tested rules for writing multi-character *crossover* scenes (characters from
different shows and films sharing one story) as independent MiniMax-H3 T2VA clips. These
sit on top of the official base guide: the official guide owns field names and grammar;
this document owns the four-section crossover layout and the anti-artifact rules learned
from rendering hundreds of these scenes.
## 1. The four-section scene layout
Every scene is one plain-text prompt in exactly this order, sections separated by one blank
line, no markdown, no bold, no bullets:
```
subject_definitions:
<Subject 1> [Character] (played by [Actor]) from [Show]
<Subject 2> [Character] (played by [Actor]) from [Show]
integrated_multimodal_description:
[Shot 1] [Visual style, lighting, time of day, setting]. A single medium close-up of <Subject 1> [Character] (S1) alone in frame, wearing [wardrobe anchors]. <Subject 2> is not in frame. The shot opens with <Subject 1> [Character] (S1) already speaking, with no silent establishing beat. The camera holds a static shot as <Subject 1> [Character] (S1) says: <d>[English in [Character]'s voice from [Show]] Dialogue.</d>
[Shot 2] At 00:05.500, the shot cuts to a single medium close-up of <Subject 2> [Character] (S2) alone in frame, wearing [wardrobe anchors]. <Subject 1> is not in frame. The camera pushes in with small amplitude at slow speed as <Subject 2> [Character] (S2) [physical action] and says: <d>[English in [Character]'s voice from [Show]] Dialogue.</d>
overall_soundscape:
[Room tone, physical sounds, ambient environment, non-verbal human sounds. Never voices.]
non_diegetic_music:
N/A
```
- `subject_definitions:` lists every character who appears on screen in this scene, one per
line, in the exact form `<Subject N> Character (played by Actor) from Show`. Use the
character, actor and show strings you were given verbatim - never rename, never
substitute another actor. If the show string carries extra look notes after a comma,
keep them as wardrobe/physical anchors in the shots but put only the show name after
`from`.
- Never bold section headers. `subject_definitions:` not `**subject_definitions:**`.
- `[Shot 1]` never carries a timecode. `[Shot 2]` and later always open with
`At MM:SS.mmm, the shot cuts to ...`.
- Scenes with no people (never the case here unless asked) write `subject_definitions:` then
`N/A` on its own line.
## 2. Speaker binding - the rules that stop face and voice blending
1. **The primary speaker of the scene is always `<Subject 1>`.** H3 binds dialogue audio
to `<Subject 1>`. Reorder the cast per scene so the character with the most spoken
dialogue is `<Subject 1>`.
2. **Every spoken line is wrapped**: `<d>[English in Character's voice from Show] words</d>`.
Speaker phrase, ID `(S1)`, action and delivery stay outside the tag; only the language
tag and the words go inside.
3. **Stable IDs** `(S1)`, `(S2)`, `(S3)` across every shot of the scene. `(S1,S2)` only for a
single word or short phrase spoken in unison.
4. **Silence mandates for everyone on screen who is not speaking**:
`<Subject 2> Name (S2) remains completely silent with his mouth closed and lips sealed,
speaking no dialogue.` Add it whenever a non-speaker is in frame, and after a speaker
finishes early in a shot: `then falls silent with her mouth closed and lips sealed,
speaking no further dialogue for the remainder of the shot.`
5. **Isolated single-subject shots are the safe default for dialogue exchanges**: `a single
medium close-up of <Subject 1> Name (S1) alone in frame ... <Subject 2> is not in
frame.` Say who is *not* in frame explicitly.
6. **Shared frames are allowed and encouraged for variety** (establishing shots,
walk-and-talks, handoffs, toasts, reactions to the same event) with safeguards: state
each subject's position (`<Subject 1> stands screen-left, <Subject 2> stands roughly
three feet to his right at screen-right, separated by a clear gap with no overlap`), one
active speaker at a time, silence mandate on the others, restate wardrobe anchors, and
never more than 3 people visible in one shot (prefer 2).
7. **After an action beat, cut to the victor alone** before they deliver a line, with an
off-camera eye-line (`looking down at the fallen guard off-camera`).
8. **Off-screen voiceover**: `says in an off-screen voiceover: <d>...</d> while his lips
remain completely closed.`
9. **Never name a voice or dialogue in `overall_soundscape:`** (`and Saul's fast-talking
voice` is a known double-voice bug). Non-verbal sounds (a cough, a laugh) are fine.
Never put musical score in the soundscape; it belongs in `non_diegetic_music:`.
## 3. Identity anchoring
- Restate key wardrobe / physical anchors for every subject in every shot they appear in
(`brown leather jacket over a dark flannel shirt, silver pendant necklace`; `bald head,
facial scar over his right eye`). Generic terms (`the agent`, `in a shirt`) drift.
- Anchor era/age for long-career actors when it matters:
`John McClane (played by 35-year-old Bruce Willis, 1988 Die Hard era) from Die Hard`, and
restate the age-defining traits in the shots.
- Keep costume, hair, props and demographics identical across every scene of the story.
- Visible on-screen text goes in English double quotes: `a red neon sign reading "QUARANTINE"`.
## 4. Pacing, duration and dead air
- Each scene lands between 5 and 20 seconds; prefer 12-15 s for dialogue scenes, 5-8 s for
transitions, punches and arrivals. Never exceed 20 s.
- **Fill the whole duration** with dialogue and physical action. No multi-second silent
stares, no frozen smiles at the end. If dialogue ends 1-2 s early, end on concrete
movement: pocketing a prop, turning and walking away, stepping out of frame, closing a
case, sliding into a car.
- Proven structures for a 15 s scene:
- A: S1 speaks (00:00-00:07) -> S2 speaks and acts to the end (00:07-00:15)
- B: S1 speaks (00:00-00:06) -> S2 speaks (00:06-00:11) -> S2 pockets prop, turns, walks (00:11-00:15)
- C: S1 (00:00-00:05) -> S2 (00:05-00:10) -> S1 quick closer while stepping forward (00:10-00:15)
- Spread cut timecodes across the actual scene duration (`00:05.500`, `00:10.500` for 15 s;
`00:04.000`, `00:08.000` for 12 s). Vary the shot pattern between scenes.
- Open T2V clips on the speaker already talking: `The shot opens with <Subject 1> Name (S1)
already speaking, with no silent establishing beat.`
## 5. Staging, story and flow across scenes
- **Show, don't tell.** Characters physically do things: uncork a flask, unlatch a case,
tap a glowing interface, unhinge a trunk, aim a device, lean on a hood, push through a
door. Never a static monologue into the lens.
- **Cause and effect on screen**: light from a portal reflects on wardrobe; a thrown knife
hits, the target groans and drops the case - impact and reaction in the same shot.
- **Precise action mechanics**: `executes a razor-sharp tactical drop, ducking under the
gunfire in one fluid motion and sliding into a low crouch behind the concrete pillar on
the left of frame` beats `he dodges`.
- **Grounded entrances**: every character has an on-screen reason to be there (a badge, a
job, a line, an invitation, a delivery). Stagger arrivals; do not dump the whole cast into
scene 1.
- **Story blocks**: keep the same 2-3 characters for 2-4 consecutive scenes resolving a
beat before moving to another group. Adjacent scenes share connective tissue: end on a
look off-screen, a handoff, a walk toward a spot, a question - and start the next scene
from that beat. Keep key props alive across scenes.
- **Location variety**: 2-3 scenes in one spot, then move (lobby -> corridor -> office ->
parking lot). Every shot states style, lighting temperature and time of day, and these
stay identical inside a sequence unless the story jumps time.
- **Exits and entrances**: anchor the camera position and the movement vector
(`looking back at the glass entrance from outside ... they walk out through the doors,
moving away from the building behind them`).
- Camera vocabulary: `[Motion] + [with small/large amplitude] + [at slow/fast speed]` -
push in, pull out, pan, truck, tilt, pedestal, arc, tracking, static, POV, roll, shake.
- Play every character *in character*: their vocabulary, rhythm, humour and moral compass
from their own show, colliding with the others'. That collision is the comedy or drama.
- Skip fiddly held instruments (a guitar in hands smears); a cup, a file, a phone, a
badge, a weapon at rest are safe.
+68
View File
@@ -0,0 +1,68 @@
---
name: h3-crossover
description: The four-section MiniMax H3 crossover-scene contract the APNext H3 Crossover Writer node emits - subject_definitions / integrated_multimodal_description / overall_soundscape / non_diegetic_music per scene, the scene envelope the node parses, how the cast list maps to <Subject N>, speaker binding, silence mandates and shared-frame safeguards, and how a run of scenes hangs together as one story. Load with h3-prompt-director for crossover work.
---
# H3 crossover scenes
This is what the `APNext H3 Crossover Writer` node expects back: a short synopsis, then
one envelope per scene, each envelope holding a complete four-section T2VA prompt. The node
splits on the envelope markers, so they must be exact and nothing else may sit outside them.
```
=== SYNOPSIS ===
Title: <a title for this crossover>
Logline: <one or two sentences>
Cast: <who is in it and why each is there, one line per character>
=== END SYNOPSIS ===
=== SCENE 01 | duration: 15.0 ===
subject_definitions:
<Subject 1> Character (played by Actor) from Show
<Subject 2> Character (played by Actor) from Show
integrated_multimodal_description:
[Shot 1] ...
[Shot 2] At 00:05.500, the shot cuts to ...
overall_soundscape:
...
non_diegetic_music:
N/A
=== END SCENE 01 ===
```
- Scene numbers are two digits and count up from 01. `duration:` is the length in seconds
the node will render that scene at; respect the duration the node asks for, or if it lets
you vary, keep every scene between 5 and 20 s and prefer 12-15 s for dialogue.
- Inside an envelope: exactly the four section labels, plain text, one blank line between
sections and between shots. No markdown fences, no bold, no commentary.
- No title cards, credit cards or logo cards unless the node explicitly asks for one. The
scenes are the story itself.
## Cast
The node hands you a cast list, one `Character (played by Actor) from Show` per line, and
sometimes a free-text steer from the user. Use every listed character at least once
across the run unless the steer says otherwise; do not invent extra named characters (an
unnamed off-camera guard or waiter is fine). Per scene, order `subject_definitions:` so
the character with the most dialogue is `<Subject 1>` - H3 binds the audio to that slot.
Copy character, actor and show strings verbatim.
## What makes a crossover work
- Each character behaves exactly as they do in their own show - vocabulary, rhythm,
attitude - and the story is the collision of those worlds. Sheldon Cooper explaining
the rules to Jack Sparrow *is* the scene.
- Everyone has an on-screen reason to be there, revealed by a line, a badge, an action.
- Something physical happens in every scene. Props travel. Doors open. Someone leaves.
- Consecutive scenes hand off to each other (a look off-screen, a question, an object)
and the location moves every 2-3 scenes.
## Reference library
| Read this | When |
|---|---|
| `17_CROSSOVER_GOLD_EXAMPLES.md` | Every run - verified rendered scenes to match for density, opener, isolation, silence mandates and closers. |
@@ -0,0 +1,186 @@
# Crossover gold examples
Rendered and verified crossover scenes (T2VA, 10-15 s). Match their density: style + light + setting first, wardrobe anchors on every subject in every shot, `already speaking` opener, explicit `not in frame`, silence mandates, physical closers, no voices in the soundscape.
## Example 1 - Two-character shared frame, prop-driven comedy, purple mist cause-and-effect (15 s)
```
subject_definitions:
<Subject 1> Malcolm Reynolds from Firefly
<Subject 2> Dr. Gregory House from House M.D.
integrated_multimodal_description:
[Shot 1] Live-action, 35mm film aesthetic, cool medical blue lighting, Princeton-Plainsboro Diagnostics glass office. <Subject 1> Malcolm Reynolds (S1), in his brown trench coat and leather holster, sets a heavy metallic canister onto the glass desk. <Subject 2> Dr. Gregory House (S2), in a dark blazer leaning on his wooden cane, looks at it skeptically. <Subject 1> Malcolm Reynolds (S1) says with a southern drawl: <d>[English in Malcolm Reynolds's voice from Firefly] Just deliver the cargo, they said. No questions asked.</d>
[Shot 2] At 00:06.000, the shot cuts to a close-up of <Subject 2> Dr. Gregory House (S2). House pokes the canister valve with the rubber tip of his walking cane. The seal pops, and a glowing purple mist immediately streams out into the air. <Subject 2> Dr. Gregory House (S2) deadpans: <d>[English in Dr. Gregory House's voice from House M.D.] Well, that's definitely not saline.</d>
[Shot 3] At 00:11.000, the shot cuts to a medium two-shot as <Subject 1> Malcolm Reynolds (S1) coughs into his sleeve, waving his hand at the spreading mist while House stares with narrowed eyes.
overall_soundscape:
Hum of fluorescent lights, heavy metallic clonk of canister, hiss of leaking pressurized gas, and Malcolm coughing.
non_diegetic_music:
N/A
```
## Example 2 - Isolated single-subject dialogue exchange with 'not in frame' declarations, silence mandates, wardrobe anchors, established location boilerplate (15 s)
```
subject_definitions:
<Subject 1> Lucifer Morningstar (played by Tom Ellis) from Lucifer
<Subject 2> Pam Beesly (played by Jenna Fischer) from The Office
integrated_multimodal_description:
[Shot 1] Live-action, 2000s documentary comedy aesthetic, bright overcast afternoon sunlight. Scranton Business Park asphalt parking lot, painted white parking stall lines, Dunder Mifflin brick building exterior in the background, industrial dark metal dumpster near the curb. A glowing cyan dimensional rift hangs in the air above the dumpster. A single medium close-up of <Subject 1> Lucifer Morningstar (S1) alone in frame, wearing an impeccably tailored three-piece black suit, crisp white shirt, red pocket square, dark slicked hair. <Subject 2> is not in frame. The shot opens with <Subject 1> Lucifer Morningstar (S1) already speaking, with no silent establishing beat. The camera pushes in with small amplitude at slow speed as <Subject 1> Lucifer Morningstar (S1) leans in with a charming smile and says: <d>[English in Lucifer Morningstar's voice from Lucifer] Tell me, my dear receptionist... what is it you truly desire from this cosmic circus?</d> <Subject 1> Lucifer Morningstar (S1) then falls silent with his mouth closed and lips sealed, speaking no further dialogue for the remainder of the shot.
[Shot 2] At 00:06.500, the shot cuts to a single medium close-up of <Subject 2> Pam Beesly (S2) alone in frame, wearing a light pink cardigan sweater over a floral white blouse, knee-length grey skirt, brown hair in a half-up style. <Subject 1> is not in frame. The camera holds a static shot as <Subject 2> Pam Beesly (S2) sighs and says: <d>[English in Pam Beesly's voice from The Office] Honestly? A quiet afternoon without alien lawyers or demon lords.</d> <Subject 2> Pam Beesly (S2) then falls silent with her mouth closed and lips sealed, speaking no further dialogue for the remainder of the shot.
[Shot 3] At 00:10.900, the shot cuts to a single medium close-up of <Subject 1> Lucifer Morningstar (S1) alone in frame, wearing an impeccably tailored three-piece black suit, crisp white shirt, red pocket square, dark slicked hair. <Subject 2> is not in frame. <Subject 1> Lucifer Morningstar (S1) remains completely silent with his mouth closed and lips sealed, speaking no dialogue. The camera pulls out with small amplitude at slow speed as <Subject 1> Lucifer Morningstar (S1) tilts his head with a delighted smile at the answer.
overall_soundscape:
Soft breeze and distant chatter. Constant parking lot room tone of distant highway traffic and wind moving across open asphalt. A low electrical hum carries from the dimensional rift.
non_diegetic_music:
N/A
```
## Example 3 - Prop-driven exchange (alien ledger + tape recorder), tilt-down closer on the prop (15 s)
```
subject_definitions:
<Subject 1> John Locke (played by Terry O'Quinn) from Lost
<Subject 2> Fox Mulder (played by David Duchovny) from The X-Files
integrated_multimodal_description:
[Shot 1] Live-action, 2000s documentary comedy aesthetic, bright overcast afternoon sunlight. Scranton Business Park asphalt parking lot, painted white parking stall lines, Dunder Mifflin brick building exterior in the background, industrial dark metal dumpster near the curb. A glowing cyan dimensional rift hangs in the air above the dumpster. The rectangular Extradition Ledger tablet of brushed obsidian-grey alien alloy, covered in glowing cyan holographic glyphs and a gold orbital seal, rests on the asphalt. A single medium close-up of <Subject 1> John Locke (S1) alone in frame, wearing a tan tactical vest over an olive khaki shirt, bald head, facial scar over his right eye. <Subject 2> is not in frame. The shot opens with <Subject 1> John Locke (S1) already speaking, with no silent establishing beat. The camera pushes in with small amplitude at slow speed as <Subject 1> John Locke (S1) traces a glowing glyph with his thumb and says: <d>[English in John Locke's voice from Lost] These glyphs match the hieroglyphs underneath the Swan Station hatch.</d> <Subject 1> John Locke (S1) then falls silent with his mouth closed and lips sealed, speaking no further dialogue for the remainder of the shot.
[Shot 2] At 00:04.500, the shot cuts to a single medium close-up of <Subject 2> Fox Mulder (S2) alone in frame, wearing a dark blue FBI suit jacket, striped tie, gold FBI ID badge pinned to his breast pocket, dark hair. <Subject 1> is not in frame. The camera holds a static shot as <Subject 2> Fox Mulder (S2) clicks open a tape recorder and says: <d>[English in Fox Mulder's voice from The X-Files] Recording entry. Subject claims island connection to alien Extradition Ledger.</d> <Subject 2> Fox Mulder (S2) then falls silent with his mouth closed and lips sealed, speaking no further dialogue for the remainder of the shot.
[Shot 3] At 00:08.900, the shot cuts to a single medium close-up of <Subject 1> John Locke (S1) alone in frame, wearing a tan tactical vest over an olive khaki shirt, bald head, facial scar over his right eye. <Subject 2> is not in frame. <Subject 1> John Locke (S1) remains completely silent with his mouth closed and lips sealed, speaking no dialogue. The camera tilts down with small amplitude at slow speed as <Subject 1> John Locke (S1) keeps his thumb on the glyph as the cyan light pulses under his skin.
overall_soundscape:
Tablet energy hum and tape recorder click click. Constant parking lot room tone of distant highway traffic and wind moving across open asphalt. A low electrical hum carries from the dimensional rift.
non_diegetic_music:
N/A
```
## Example 4 - Two isolated close-ups then a positioned two-shot with a one-word unison line (15 s)
```
subject_definitions:
<Subject 1> Dean Winchester (played by Jensen Ackles) from Supernatural
<Subject 2> Lucifer Morningstar (played by Tom Ellis) from Lucifer
integrated_multimodal_description:
[Shot 1] Live-action, 2000s documentary comedy aesthetic, bright overcast afternoon sunlight. Scranton Business Park asphalt parking lot, painted white parking stall lines, Dunder Mifflin brick building exterior in the background, industrial dark metal dumpster near the curb. The sky above the dumpster is clear and unbroken where the rift has sealed. The black 1967 Chevy Impala is parked alongside the stall lines. A single medium close-up of <Subject 1> Dean Winchester (S1) alone in frame, wearing a brown leather jacket over a dark flannel shirt and dark t-shirt, silver pendant necklace. <Subject 2> is not in frame. The shot opens with <Subject 1> Dean Winchester (S1) already speaking, with no silent establishing beat. The camera tilts down with small amplitude at slow speed as <Subject 1> Dean Winchester (S1) pops a beer bottle cap off with a silver lighter and says: <d>[English in Dean Winchester's voice from Supernatural] Next time an intergalactic rift opens, buy me a beer first, Morningstar.</d> <Subject 1> Dean Winchester (S1) then falls silent with his mouth closed and lips sealed, speaking no further dialogue for the remainder of the shot.
[Shot 2] At 00:06.200, the shot cuts to a single medium close-up of <Subject 2> Lucifer Morningstar (S2) alone in frame, wearing an impeccably tailored three-piece black suit, crisp white shirt, red pocket square, dark slicked hair. <Subject 1> is not in frame. The camera pushes in with small amplitude at slow speed as <Subject 2> Lucifer Morningstar (S2) raises his crystal glass in a toast and says: <d>[English in Lucifer Morningstar's voice from Lucifer] Deal, Winchester! Top shelf bourbon on my tab!</d> <Subject 2> Lucifer Morningstar (S2) then falls silent with his mouth closed and lips sealed, speaking no further dialogue for the remainder of the shot.
[Shot 3] At 00:10.800, the shot cuts to a two-shot of <Subject 1> Dean Winchester and <Subject 2> Lucifer Morningstar (S1,S2) side by side in frame, separated by a clear gap with no overlap between them. <Subject 1> Dean Winchester (S1) stands frame left wearing a brown leather jacket over a dark flannel shirt and dark t-shirt, silver pendant necklace. <Subject 2> Lucifer Morningstar (S2) stands frame right wearing an impeccably tailored three-piece black suit, crisp white shirt, red pocket square, dark slicked hair. The camera pulls out with small amplitude at slow speed as both men clink the beer bottle against the crystal glass and say one single word in unison: <d>[English in Dean Winchester's voice from Supernatural and Lucifer Morningstar's voice from Lucifer] Cheers.</d> Both men then fall silent with their mouths closed and lips sealed, speaking no further dialogue for the remainder of the shot.
overall_soundscape:
Beer bottle cap pop sizzle and glass toast clink. Constant parking lot room tone of distant highway traffic and wind moving across open asphalt.
non_diegetic_music:
N/A
```
## Example 5 - Single character talking to an off-camera stranger, entrance with a visible reason (lanyard), exit on movement (10 s)
```
subject_definitions:
<Subject 1> John McClane (played by Bruce Willis) from Die Hard
integrated_multimodal_description:
[Shot 1] Live-action, high-gloss corporate holiday aesthetic, polished black marble floor reflecting warm string-light garlands wrapped around white marble columns, a manned reception desk with a brass "SABRE" logo sign, glass turnstile badge-check gates, floor-to-ceiling windows showing the city skyline at night. A medium shot of <Subject 1> John McClane (S1) alone in frame, wearing a rumpled black tuxedo jacket over an untucked white dress shirt, black bow tie hanging loose and undone around his collar, leaning on the reception desk toward an unnamed security guard just off-camera. The shot opens with <Subject 1> John McClane (S1) already speaking, with no silent establishing beat: <d>[English in John McClane's voice from Die Hard] I'm not on your list. I'm looking for my daughter. Wednesday Addams, works down in the mailroom.</d>
[Shot 2] At 00:05.500, the shot cuts to a closer medium close-up of <Subject 1> John McClane (S1) alone in frame, accepting a visitor lanyard from the off-camera guard and clipping it on, giving a tired, wry half-smile. He says: <d>[English in John McClane's voice from Die Hard] Yeah, I know how that sounds. Long story.</d> He pushes through the turnstile and walks toward the elevator bank.
overall_soundscape:
Constant lobby room tone of distant holiday music filtering down from upstairs and the soft echo of marble floors. A visitor lanyard clip snapping shut.
non_diegetic_music:
N/A
```
## Example 6 - Single character on a radio - reason to be there stated on screen, prop action closer (10 s)
```
subject_definitions:
<Subject 1> Seeley Booth (played by David Boreanaz) from Bones
integrated_multimodal_description:
[Shot 1] Live-action, warm ballroom string-light glow near a side corridor just off the main floor, muted from the party noise. A medium close-up of <Subject 1> Seeley Booth (S1) alone in frame, wearing a dark two-button suit, white shirt, gold FBI badge clipped to his belt, a discreet earpiece, speaking into his radio mic clipped near his collar. The shot opens with <Subject 1> Seeley Booth (S1) already speaking, with no silent establishing beat: <d>[English in Seeley Booth's voice from Bones] Copy that. Two men on the freight elevator, two on the main stairwell. Nobody gets waved through on a smile tonight.</d>
[Shot 2] At 00:05.500, the shot cuts to a closer angle as <Subject 1> Seeley Booth (S1) clips his radio back onto his belt and rolls his shoulders, glancing once more toward the glittering ballroom floor. He says under his breath: <d>[English in Seeley Booth's voice from Bones] Rich people and open bars. What could possibly go wrong.</d>
overall_soundscape:
Muted ballroom room tone bleeding faintly from around the corner. Radio clip snapping shut and a low static crackle.
non_diegetic_music:
N/A
```
## Example 7 - Action driving scene, urgent dialogue, physical cause and effect (yanks wheel, tyres screech) (10 s)
```
subject_definitions:
<Subject 1> Jack Bauer (played by Kiefer Sutherland) from 24
integrated_multimodal_description:
[Shot 1] Live-action, dark government sedan interior at night, dashboard instrument lights glowing faintly green, city street lights streaking past the window. A medium close-up of <Subject 1> Jack Bauer (S1) alone in frame, wearing a dark tactical jacket over a black shirt, shoulder holster, a discreet earpiece, gripping the steering wheel as a phone clipped to the dash lights up with an alert. The shot opens with <Subject 1> Jack Bauer (S1) already speaking, with no silent establishing beat, listening intently before responding sharply: <d>[English in Jack Bauer's voice from 24] Say that again. Sabre Tower's internal security grid just went dark building-wide?</d>
[Shot 2] At 00:05.000, the shot cuts to a closer angle as <Subject 1> Jack Bauer (S1) yanks the wheel hard, cutting across two lanes with a screech of tires, jaw set. He says, already in motion: <d>[English in Jack Bauer's voice from 24] I'm four minutes out. Get me eyes on every camera feed in that building before I get there.</d>
overall_soundscape:
Constant road noise and a tire screech as the car cuts across lanes. A sharp phone alert chime.
non_diegetic_music:
N/A
```
## Example 8 - Battle staging: shield wall, spear impacts, shouted dialogue with precise mechanics (10 s)
```
subject_definitions:
<Subject 1> Maximus Decimus Meridius (played by Russell Crowe) from Gladiator
integrated_multimodal_description:
[Shot 1] Live-action, outer stone pass wall, green mist blowing between ancient pillars. A medium shot of <Subject 1> Maximus Decimus Meridius (S1) alone in frame, pressing his shoulder firmly into the rear of his legionaries' overlapping red scutum shield wall as dark shadow spears crash against the wood and steel. The shot opens with <Subject 1> Maximus Decimus Meridius (S1) already speaking, with no silent establishing beat, shouting above the clatter: <d>[English in Maximus Decimus Meridius's voice from Gladiator] Brace! Lock shoulders! No shadow breaks this wall!</d>
[Shot 2] At 00:05.000, the shot cuts to a tighter angle as <Subject 1> Maximus Decimus Meridius (S1) thrusts his gladius over the shield rim, slaying an attacking phantom soldier. He yells with intense resolve: <d>[English in Maximus Decimus Meridius's voice from Gladiator] Hold!</d>
overall_soundscape:
Heavy shield crashes, splintering wood impacts, metal clanking, and disciplined legion shouts.
non_diegetic_music:
N/A
```
## Example 9 - Villain beat: fury, off-camera eye-line toward another character, threat line (10 s)
```
subject_definitions:
<Subject 1> Maleficent (played by Angelina Jolie) from Maleficent
integrated_multimodal_description:
[Shot 1] Live-action, upper stone dais terrace, smoke and embers drifting. A medium shot of <Subject 1> Maleficent (S1) alone in frame, black feather-trimmed robe, clutching her gnarled wooden staff as she watches her thorn barrier dissolve into glowing ash. Her face twists with cold fury. The shot opens with <Subject 1> Maleficent (S1) already speaking, with no silent establishing beat, snarling through gritted teeth: <d>[English in Maleficent's voice from Maleficent] Impossible! My dark magic will not be denied by a beast!</d>
[Shot 2] At 00:05.000, the shot cuts to a tighter angle as <Subject 1> Maleficent (S1) turns her dark glare down toward the lower colonnade where Jack Sparrow stands holding the key. She hiss-threatens: <d>[English in Maleficent's voice from Maleficent] Bring me that key, pirate, or I shall flay your soul!</d>
overall_soundscape:
Hissing green fire embers, wind howling through ruins, and staff hum.
non_diegetic_music:
N/A
```
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+14 -3
View File
@@ -9,6 +9,13 @@
# text into their `idea` or `extra_instructions`. The H3 writers can also target
# Claude Code directly by picking a `claudecode:` entry in their model dropdown.
from ...utils.apnext_context import (
build_context,
context_hidden_inputs,
context_inputs,
context_summary,
with_context,
)
from ...utils.claude_code import (
CLAUDE_CODE_MODELS,
RESEARCH_TOOLS,
@@ -101,7 +108,9 @@ class ClaudeCodeNode:
"throwaway scratch folder, which is the safe default."
),
}),
**context_inputs(),
},
"hidden": context_hidden_inputs(),
}
RETURN_TYPES = ("STRING", "STRING", "STRING")
@@ -130,9 +139,11 @@ class ClaudeCodeNode:
system_prompt=DEFAULT_SYSTEM_PROMPT,
resume_session_id="",
working_dir="",
**context_slots,
):
context_text, context_entries = build_context(context_slots, target="the answer")
try:
if not prompt.strip() and image is None:
if not prompt.strip() and image is None and not context_entries:
raise ValueError("Provide a prompt, an image, or both.")
if not find_cli():
@@ -146,11 +157,11 @@ class ClaudeCodeNode:
print(
f"🤖 Claude Code | {model} | {len(images) if images else 0} image(s) | "
f"research {'on' if enable_research else 'off'}"
f"research {'on' if enable_research else 'off'} | context: {context_summary(context_entries)}"
)
result = run_claude_code(
prompt.strip(),
with_context(prompt.strip(), context_text),
system_prompt=system_prompt.strip() or None,
images=images,
model=model,
+15
View File
@@ -7,6 +7,11 @@ from .claude_code_ref_writer import H3ClaudeCodeRefWriter
from .claude_code_refiner import H3ClaudeCodeRefiner
from .ref_prompt_writer import H3RefPromptWriter
from .prompt_preview import H3PromptPreview
from .characters import H3Characters
from .claude_code_crossover_writer import H3ClaudeCodeCrossoverWriter
from .claude_code_scenes_writer import H3ClaudeCodeScenesWriter
from .scene_pick import H3ScenePick
from .scenes_to_chain_plan import H3ScenesToChainPlan
NODE_CLASS_MAPPINGS = {
"H3BasePromptWriter": H3BasePromptWriter,
@@ -16,6 +21,11 @@ NODE_CLASS_MAPPINGS = {
"H3ClaudeCodeRefiner": H3ClaudeCodeRefiner,
"H3ClaudeCodeContinueWriter": H3ClaudeCodeContinueWriter,
"H3PromptPreview": H3PromptPreview,
"H3Characters": H3Characters,
"H3ClaudeCodeCrossoverWriter": H3ClaudeCodeCrossoverWriter,
"H3ClaudeCodeScenesWriter": H3ClaudeCodeScenesWriter,
"H3ScenePick": H3ScenePick,
"H3ScenesToChainPlan": H3ScenesToChainPlan,
}
NODE_DISPLAY_NAME_MAPPINGS = {
@@ -26,4 +36,9 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"H3ClaudeCodeRefiner": "APNext H3 Claude Code Refiner",
"H3ClaudeCodeContinueWriter": "APNext H3 Claude Code Continue Writer",
"H3PromptPreview": "APNext H3 Prompt Preview",
"H3Characters": "APNext H3 Characters",
"H3ClaudeCodeCrossoverWriter": "APNext H3 Crossover Writer",
"H3ClaudeCodeScenesWriter": "APNext H3 Claude Code Scenes Writer",
"H3ScenePick": "APNext H3 Scene Pick",
"H3ScenesToChainPlan": "APNext H3 Scenes → Contex Loop Plan",
}
+29 -2
View File
@@ -6,6 +6,13 @@
import random
from ...utils.apnext_context import (
build_context,
context_hidden_inputs,
context_inputs,
context_summary,
with_context,
)
from ...utils.constants import CUSTOM_CATEGORY
from ...utils.image_utils import tensor2pil
from ...utils.llm_router import AUTO_DETECT, call_llm, is_interrupt, list_all_models
@@ -17,6 +24,7 @@ from .common import (
DIALOGUE_LANGUAGES,
SHOT_PLANS,
VISUAL_STYLES,
resolve_visual_style,
camera_directive,
resolve_dialogue_language,
extract_section,
@@ -89,7 +97,10 @@ class H3BasePromptWriter:
"shot_plan": (SHOT_PLANS, {"default": AUTO}),
"visual_style": (VISUAL_STYLES, {
"default": AUTO,
"tooltip": "Style stated at the start of [Shot 1]. Auto derives it from the idea or the attached image.",
"tooltip": (
"Style stated at the start of [Shot 1]. Auto derives it from the idea or the attached image. "
"The list is the guide's styles plus the APNext Cinematic vocabulary (film stock, grading, aesthetics); pick Custom and fill in custom_visual_style to write your own."
),
}),
"wildness": ("INT", {
"default": 25, "min": 0, "max": 100, "step": 1,
@@ -156,6 +167,13 @@ class H3BasePromptWriter:
"dialect)' or 'Latin'. Overrides the dropdown when filled in."
),
}),
"custom_visual_style": ("STRING", {
"default": "",
"tooltip": (
"Any visual style not in the dropdown, e.g. 'hand-painted cel animation' or "
"'Kodak Vision3 500T, anamorphic'. Overrides the dropdown when filled in."
),
}),
"model_override": ("STRING", {
"default": "",
"tooltip": (
@@ -172,7 +190,9 @@ class H3BasePromptWriter:
"local 8000. Ignored by the cloud providers."
),
}),
**context_inputs(),
},
"hidden": context_hidden_inputs(),
}
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING", "IMAGE", "IMAGE")
@@ -477,20 +497,25 @@ class H3BasePromptWriter:
image=None,
extra_instructions="",
custom_dialogue_language="",
custom_visual_style="",
model_override="",
local_base_url="",
**context_slots,
):
# Frame 0 and the final frame of the batch go back out, so the H3 video
# node's first_frame / last_frame can be wired straight from this node.
frames = (image[0:1], image[-1:]) if image is not None else (None, None)
context_text, context_entries = build_context(context_slots)
try:
if not idea.strip() and image is None:
if not idea.strip() and image is None and not context_entries:
raise ValueError("Provide an idea, an image, or both.")
dialogue_language = resolve_dialogue_language(
dialogue_language, custom_dialogue_language
)
visual_style = resolve_visual_style(visual_style, custom_visual_style)
current_seed = seed if seed != -1 else random.randint(0, 0xffffffffffffffff)
rng = random.Random(current_seed)
@@ -517,10 +542,12 @@ class H3BasePromptWriter:
len(images) if images else 0,
rng,
)
user_prompt = with_context(user_prompt, context_text)
warn_if_images_unused(task_type, len(images) if images else 0)
print(
f"🎬 H3 Prompt Writer | {task_type} | {duration_seconds:.2f}s | "
f"context: {context_summary(context_entries)} | "
f"wildness {wildness} ({wild_label}) | seed {current_seed}"
)
+137
View File
@@ -0,0 +1,137 @@
# APNext H3 Characters
#
# Picks a character from data/h3/characters.tsv (Relative File Path,
# Character / Subject Name, Real Actor / Actress, Franchise / Show) and exposes
# the character name, actor and franchise as separate STRING outputs, plus the
# reference clip path. Supports a fixed dropdown pick or a seeded random pick,
# optionally filtered by franchise.
import csv
import os
import random
from ...utils.constants import CUSTOM_CATEGORY
_DATA_PATH = os.path.join(
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
"data",
"h3",
"characters.tsv",
)
_RANDOM = "🎲 random"
_ALL_FRANCHISES = "(all)"
def _load_rows():
"""Read the TSV and return de-duplicated rows as dicts, in file order."""
rows = []
seen = set()
if not os.path.isfile(_DATA_PATH):
return rows
with open(_DATA_PATH, "r", encoding="utf-8", newline="") as f:
reader = csv.reader(f, delimiter="\t")
header = next(reader, None)
for raw in reader:
if len(raw) < 4:
continue
path, character, actor, franchise = (c.strip() for c in raw[:4])
if not character:
continue
key = (character, actor, franchise)
if key in seen:
continue
seen.add(key)
rows.append(
{
"path": path,
"character": character,
"actor": actor,
"franchise": franchise,
"label": _make_label(character, actor, franchise),
}
)
return rows
def _make_label(character, actor, franchise):
short_franchise = franchise.split(",")[0].strip()
if actor and actor != character:
return f"{character} — {actor} ({short_franchise})"
return f"{character} ({short_franchise})"
def cast_line_for(row):
"""`Character (played by Actor) from Show` - the subject_definitions form."""
show = row["franchise"].split(",")[0].strip()
actor = row["actor"]
if actor and actor != row["character"]:
return f"{row['character']} (played by {actor}) from {show}"
return f"{row['character']} from {show}"
_ROWS = _load_rows()
_BY_LABEL = {r["label"]: r for r in _ROWS}
_FRANCHISES = sorted({r["franchise"].split(",")[0].strip() for r in _ROWS}, key=str.lower)
class H3Characters:
@classmethod
def INPUT_TYPES(cls):
labels = [_RANDOM] + [r["label"] for r in _ROWS]
return {
"required": {
"character": (labels, {"default": labels[1] if len(labels) > 1 else _RANDOM}),
"franchise_filter": (
[_ALL_FRANCHISES] + _FRANCHISES,
{
"default": _ALL_FRANCHISES,
"tooltip": (
"Only used when character is random: limits the draw to one show. "
"Picking a named character snaps this to their show, so switching "
"to random afterwards draws a castmate."
),
},
),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xFFFFFFFFFFFFFFFF}),
},
"optional": {
"cast_in": ("STRING", {
"forceInput": True,
"tooltip": (
"Cast lines from an upstream H3 Characters node. This node's cast line "
"is appended, so several can be chained into one cast list."
),
}),
},
}
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING")
RETURN_NAMES = ("character", "actor", "franchise", "file_path", "cast")
FUNCTION = "pick"
CATEGORY = f"{CUSTOM_CATEGORY}/H3"
DESCRIPTION = (
"Character / actor / franchise lookup for MiniMax-H3 prompts. Pick a "
"character from the list or choose random (seeded, optionally filtered "
"by franchise). Outputs the character name, the real actor/actress, the "
"franchise/show, the reference clip path, and a ready-made cast line "
"(`Character (played by Actor) from Show`) that chains through cast_in "
"into the H3 Crossover Writer."
)
def pick(self, character, franchise_filter, seed, cast_in=""):
if not _ROWS:
return ("", "", "", "", cast_in or "")
if character == _RANDOM:
pool = _ROWS
if franchise_filter != _ALL_FRANCHISES:
pool = [r for r in _ROWS if r["franchise"].split(",")[0].strip() == franchise_filter] or _ROWS
row = random.Random(seed).choice(pool)
else:
row = _BY_LABEL.get(character) or _ROWS[0]
cast_line = cast_line_for(row)
cast = (cast_in or "").strip()
cast = f"{cast}\n{cast_line}" if cast else cast_line
return (row["character"], row["actor"], row["franchise"], row["path"], cast)
+27 -2
View File
@@ -8,6 +8,13 @@
import random
from ...utils.claude_code import is_interrupt
from ...utils.apnext_context import (
build_context,
context_hidden_inputs,
context_inputs,
context_summary,
with_context,
)
from ...utils.constants import CUSTOM_CATEGORY
from ...utils.image_utils import tensor2pil
from .base_prompt_writer import TASK_TYPES, H3BasePromptWriter, _FIELDS, warn_if_images_unused
@@ -25,6 +32,7 @@ from .common import (
DIALOGUE_LANGUAGES,
SHOT_PLANS,
VISUAL_STYLES,
resolve_visual_style,
collect_typed_references,
extract_section,
resolve_dialogue_language,
@@ -61,7 +69,10 @@ class H3ClaudeCodeBaseWriter(H3BasePromptWriter):
"shot_plan": (SHOT_PLANS, {"default": AUTO}),
"visual_style": (VISUAL_STYLES, {
"default": AUTO,
"tooltip": "Style stated at the start of [Shot 1]. Auto derives it from the idea or the attached image.",
"tooltip": (
"Style stated at the start of [Shot 1]. Auto derives it from the idea or the attached image. "
"The list is the guide's styles plus the APNext Cinematic vocabulary (film stock, grading, aesthetics); pick Custom and fill in custom_visual_style to write your own."
),
}),
"wildness": ("INT", {
"default": 25, "min": 0, "max": 100, "step": 1,
@@ -109,13 +120,21 @@ class H3ClaudeCodeBaseWriter(H3BasePromptWriter):
"dialect)' or 'Latin'. Overrides the dropdown when filled in."
),
}),
"custom_visual_style": ("STRING", {
"default": "",
"tooltip": (
"Any visual style not in the dropdown, e.g. 'hand-painted cel animation' or "
"'Kodak Vision3 500T, anamorphic'. Overrides the dropdown when filled in."
),
}),
}
optional.update(claude_code_optional_inputs())
# Typed references: pictures the writer describes in words. The video
# model never sees them, so any size goes and none becomes <Picture N>.
optional.update(typed_reference_inputs())
optional.update(context_inputs())
return {"required": required, "optional": optional}
return {"required": required, "optional": optional, "hidden": context_hidden_inputs()}
RETURN_TYPES = ("STRING",) * 6 + ("IMAGE", "IMAGE")
RETURN_NAMES = (
@@ -164,6 +183,7 @@ class H3ClaudeCodeBaseWriter(H3BasePromptWriter):
image=None,
extra_instructions="",
custom_dialogue_language="",
custom_visual_style="",
resume_session_id="",
working_dir="",
**reference_slots,
@@ -172,6 +192,7 @@ class H3ClaudeCodeBaseWriter(H3BasePromptWriter):
# video node's first_frame / last_frame can be wired straight from here.
# Typed references never go to the video node, only to the writer.
frames = (image[0:1], image[-1:]) if image is not None else (None, None)
context_text, context_entries = build_context(reference_slots)
references = collect_typed_references(reference_slots, tensor2pil)
try:
if not idea.strip() and image is None and not references:
@@ -181,6 +202,8 @@ class H3ClaudeCodeBaseWriter(H3BasePromptWriter):
dialogue_language, custom_dialogue_language
)
visual_style = resolve_visual_style(visual_style, custom_visual_style)
current_seed = seed if seed != -1 else random.randint(0, 0xffffffffffffffff)
rng = random.Random(current_seed)
@@ -207,11 +230,13 @@ class H3ClaudeCodeBaseWriter(H3BasePromptWriter):
rng,
references=[label for label, _ in references],
)
user_prompt = with_context(user_prompt, context_text)
warn_if_images_unused(task_type, len(keyframes), len(references))
print(
f"🎬 H3 Claude Code Writer | {task_type} | {len(keyframes)} keyframe image(s) | "
f"refs: {', '.join(label for label, _ in references) or 'none'} | "
f"context: {context_summary(context_entries)} | "
f"{duration_seconds:.2f}s | "
f"wildness {wildness} ({wild_label}) | research "
f"{'on' if research else 'off'} | director "
+28 -2
View File
@@ -18,6 +18,13 @@
import random
from ...utils.claude_code import is_interrupt
from ...utils.apnext_context import (
build_context,
context_hidden_inputs,
context_inputs,
context_summary,
with_context,
)
from ...utils.constants import CUSTOM_CATEGORY
from ...utils.image_utils import tensor2pil
from .base_prompt_writer import H3BasePromptWriter, _FIELDS
@@ -35,6 +42,7 @@ from .common import (
DIALOGUE_LANGUAGES,
SHOT_PLANS,
VISUAL_STYLES,
resolve_visual_style,
camera_directive,
extract_section,
resolve_dialogue_language,
@@ -125,7 +133,10 @@ class H3ClaudeCodeContinueWriter(H3BasePromptWriter):
"shot_plan": (SHOT_PLANS, {"default": AUTO}),
"visual_style": (VISUAL_STYLES, {
"default": AUTO,
"tooltip": "Auto keeps the style of the previous clip, which is almost always what you want.",
"tooltip": (
"Auto keeps the style of the previous clip, which is almost always what you want. "
"The list is the guide's styles plus the APNext Cinematic vocabulary (film stock, grading, aesthetics); pick Custom and fill in custom_visual_style to write your own."
),
}),
"wildness": ("INT", {
"default": 25, "min": 0, "max": 100, "step": 1,
@@ -174,6 +185,13 @@ class H3ClaudeCodeContinueWriter(H3BasePromptWriter):
"dialect)' or 'Latin'. Overrides the dropdown when filled in."
),
}),
"custom_visual_style": ("STRING", {
"default": "",
"tooltip": (
"Any visual style not in the dropdown, e.g. 'hand-painted cel animation' or "
"'Kodak Vision3 500T, anamorphic'. Overrides the dropdown when filled in."
),
}),
"resume_session_id": ("STRING", {
"default": "",
"tooltip": (
@@ -191,7 +209,9 @@ class H3ClaudeCodeContinueWriter(H3BasePromptWriter):
}),
}
return {"required": required, "optional": optional}
optional.update(context_inputs())
return {"required": required, "optional": optional, "hidden": context_hidden_inputs()}
RETURN_TYPES = ("STRING",) * 6 + ("IMAGE", "IMAGE")
RETURN_NAMES = (
@@ -383,9 +403,12 @@ class H3ClaudeCodeContinueWriter(H3BasePromptWriter):
previous_prompt="",
extra_instructions="",
custom_dialogue_language="",
custom_visual_style="",
resume_session_id="",
working_dir="",
**context_slots,
):
context_text, context_entries = build_context(context_slots, target="the next clip")
indices = select_last_frames(frames, frame_count, frame_stride)
context = frames[indices]
first_frame = frames[-1:]
@@ -393,6 +416,7 @@ class H3ClaudeCodeContinueWriter(H3BasePromptWriter):
dialogue_language = resolve_dialogue_language(
dialogue_language, custom_dialogue_language
)
visual_style = resolve_visual_style(visual_style, custom_visual_style)
resuming = bool(resume_session_id.strip())
current_seed = seed if seed != -1 else random.randint(0, 0xffffffffffffffff)
@@ -422,6 +446,8 @@ class H3ClaudeCodeContinueWriter(H3BasePromptWriter):
rng,
)
user_prompt = with_context(user_prompt, context_text)
print(
f"🎬 H3 Claude Code Continue Writer | {continuation_mode.split(' ')[0]} | "
f"frames {indices} of {int(frames.shape[0])} | {duration_seconds:.2f}s | "
+438
View File
@@ -0,0 +1,438 @@
# APNext H3 Crossover Writer
#
# Takes a cast of characters (from H3 Characters nodes or typed by hand) plus
# the user's steer, and has the local Claude Code CLI write 1-10 crossover
# scenes in the four-section MiniMax-H3 T2VA layout (subject_definitions /
# integrated_multimodal_description / overall_soundscape / non_diegetic_music).
#
# The scenes come back as a ComfyUI list, one prompt per element, with a
# matching list of durations, so a video node downstream renders every scene in
# one queue. H3ScenePick pulls a single scene out of that list.
import random
import re
from ...utils.claude_code import is_interrupt
from ...utils.apnext_context import (
build_context,
context_hidden_inputs,
context_inputs,
context_summary,
with_context,
)
from ...utils.constants import CUSTOM_CATEGORY
from .claude_code_support import (
CORE_SKILL,
claude_code_inputs,
claude_code_optional_inputs,
directions_with_research,
run_h3_claude_code,
)
from .common import (
AUTO,
VISUAL_STYLES,
resolve_visual_style,
DIALOGUE_LANGUAGES,
load_guide,
resolve_dialogue_language,
wildness_directive,
)
from .scenes_support import (
WARDROBE_TOOLTIP,
wardrobe_directive,
CONTINUITY_MODES,
DURATION_MODES,
MAX_SCENES,
chain_system_block,
continuity_directive,
duration_directive,
envelope_contract,
parse_scenes,
scenes_to_text,
)
CROSSOVER_SKILLS = (CORE_SKILL, "h3-crossover", "h3-style-craft")
MAX_CAST_SOCKETS = 4
CAST_SOCKETS = tuple(f"cast_{i}" for i in range(1, MAX_CAST_SOCKETS + 1))
SHOTS_PER_SCENE = [AUTO, "1", "2", "3", "4"]
_SCENE_FIELDS = (
"subject_definitions",
"integrated_multimodal_description",
"overall_soundscape",
"non_diegetic_music",
)
def parse_cast(*blocks):
"""Merge cast blocks into unique, non-empty lines in first-seen order."""
seen = set()
cast = []
for block in blocks:
for line in (block or "").splitlines():
line = line.strip().lstrip("-*• ").strip()
if not line:
continue
# Tolerate lines already carrying a <Subject N> tag.
line = re.sub(r"^<Subject\s+\d+>\s*", "", line, flags=re.IGNORECASE)
key = line.lower()
if key in seen:
continue
seen.add(key)
cast.append(line)
return cast
class H3ClaudeCodeCrossoverWriter:
"""
APNext H3 Crossover Writer
Writes 1-10 crossover scenes for a given cast with the local Claude Code CLI,
following the production rules distilled from rendered crossover movies.
"""
@classmethod
def INPUT_TYPES(cls):
required = {
"direction": ("STRING", {
"multiline": True,
"default": "",
"tooltip": (
"Your steer for the AI: premise, tone, genre, where it happens, what "
"must happen, running gags, who should clash. Free text - this is the "
"creative brief the scenes are built from."
),
}),
"extra_cast": ("STRING", {
"multiline": True,
"default": "",
"tooltip": (
"Extra cast typed by hand, one per line, ideally as "
"`Character (played by Actor) from Show`. Merged with the cast sockets."
),
}),
"scene_count": ("INT", {
"default": 3, "min": 1, "max": MAX_SCENES,
"tooltip": "How many scenes to write. Each becomes one element of the scenes list.",
}),
"duration_mode": (DURATION_MODES, {"default": DURATION_MODES[0]}),
"continuity_mode": (CONTINUITY_MODES, {
"default": CONTINUITY_MODES[0],
"tooltip": (
"Independent clips: each scene is its own T2V clip with hard cuts. "
"Continuous chain: scenes are written for C2V / motion-context chaining "
"(Contex Loop, Add Guide, Motion Context) - scene N+1 opens on scene N's "
"last frame, one continuous take, speaker hand-off beats, rotating closers."
),
}),
"scene_duration": ("FLOAT", {
"default": 15.0, "min": 5.0, "max": 20.0, "step": 0.5,
"tooltip": (
"Seconds per scene in Fixed mode. Also the fallback if Claude omits a "
"duration in Vary mode. Cut timecodes are spread across this length."
),
}),
"shots_per_scene": (SHOTS_PER_SCENE, {
"default": AUTO,
"tooltip": "Shots per scene. Auto lets Claude choose 2-3 to fit the duration.",
}),
"visual_style": (VISUAL_STYLES, {
"default": "Live-action, 35mm cinematic film aesthetic",
"tooltip": (
"Opens every [Shot 1]; kept identical across the run for continuity. The "
"list is the guide's styles plus the APNext Cinematic vocabulary (film "
"stock, grading, aesthetics); Custom uses custom_visual_style, Auto lets "
"Claude pick one that suits the cast."
),
}),
"dialogue_language": (DIALOGUE_LANGUAGES, {"default": "English"}),
"wildness": ("INT", {
"default": 35, "min": 0, "max": 100, "step": 1,
"tooltip": "0 = grounded and faithful to each show, 100 = fully unhinged. Above 40 seeds surreal events.",
}),
}
required.update(claude_code_inputs())
required["seed"] = ("INT", {
"default": -1, "min": -1, "max": 0xffffffffffffffff,
"tooltip": "Seeds the surreal picks and controls caching. -1 re-runs every queue.",
})
optional = {
name: ("STRING", {
"forceInput": True,
"tooltip": (
"A cast line or block from an H3 Characters node (its `cast` output). "
"Chain several Characters nodes or use several sockets."
),
})
for name in CAST_SOCKETS
}
optional["custom_dialogue_language"] = ("STRING", {"default": ""})
optional["custom_visual_style"] = ("STRING", {
"default": "",
"tooltip": (
"Any visual style not in the dropdown, e.g. 'hand-painted cel animation' or "
"'Kodak Vision3 500T, anamorphic'. Overrides the dropdown when filled in."
),
})
optional["wardrobe"] = ("STRING", {"multiline": True, "default": "", "tooltip": WARDROBE_TOOLTIP})
optional["extra_instructions"] = ("STRING", {"multiline": True, "default": ""})
optional.update(claude_code_optional_inputs())
optional.update(context_inputs())
return {"required": required, "optional": optional, "hidden": context_hidden_inputs()}
RETURN_TYPES = ("STRING", "FLOAT", "STRING", "STRING", "STRING", "INT", "STRING", "STRING")
RETURN_NAMES = (
"scenes",
"durations",
"scenes_text",
"synopsis",
"cast",
"scene_count",
"session_id",
"info",
)
OUTPUT_IS_LIST = (True, True, False, False, False, False, False, False)
FUNCTION = "write_scenes"
CATEGORY = f"{CUSTOM_CATEGORY}/H3"
DESCRIPTION = (
"Writes 1-10 MiniMax-H3 crossover scenes for a cast of characters with the local "
"Claude Code CLI. `scenes` and `durations` are lists (one element per scene) so a "
"video node downstream renders them all; use H3 Scene Pick to grab one."
)
@classmethod
def IS_CHANGED(cls, seed=-1, **kwargs):
return float("nan") if seed == -1 else seed
# ------------------------------------------------------------------
# Prompt construction
# ------------------------------------------------------------------
def _build_system_prompt(self, continuity_mode=CONTINUITY_MODES[0]):
base_guide = load_guide("guide_base_en.md")
crossover_guide = load_guide("guide_crossover_en.md")
return (
"You are a MiniMax-H3 crossover screenwriter and prompt engineer. You are "
"given a cast of characters from different shows and films and a creative brief, "
"and you write a short run of self-contained scenes in which those characters "
"meet, each scene as a complete production-ready H3 T2VA prompt.\n\n"
"Two documents follow. The official guide is authoritative for grammar, camera "
"vocabulary, timestamps, speaker IDs and <d> blocks. The crossover rules are "
"authoritative for the four-section scene layout, subject binding, silence "
"mandates, pacing and story flow across scenes.\n\n"
"=== BEGIN MINIMAX-H3 VIDEO PROMPT WRITING GUIDE ===\n"
f"{base_guide}\n"
"=== END MINIMAX-H3 VIDEO PROMPT WRITING GUIDE ===\n\n"
"=== BEGIN MINIMAX-H3 CROSSOVER SCENE RULES ===\n"
f"{crossover_guide}\n"
"=== END MINIMAX-H3 CROSSOVER SCENE RULES ==="
+ chain_system_block(continuity_mode) + "\n\n"
+ envelope_contract(_SCENE_FIELDS) + "\n"
"- The synopsis block also carries `Cast:` with one line per character and why "
"they are there.\n"
"- Use the character, actor and show strings exactly as given.\n"
"- Write everything in English except dialogue, which uses the requested language "
"inside the <d>[...] tag."
)
def _build_user_prompt(
self,
cast,
direction,
scene_count,
duration_mode,
continuity_mode,
scene_duration,
shots_per_scene,
visual_style,
dialogue_language,
wildness,
extra_instructions,
rng,
wardrobe="",
):
lines = ["CAST (use these strings verbatim in subject_definitions):"]
lines += [f"- {c}" for c in cast]
lines.append("")
lines.append("CREATIVE BRIEF FROM THE USER:")
lines.append(direction.strip() or "(none - invent a premise that fits this cast)")
lines.append("")
lines.append("DIRECTIVES:")
lines.append(
f"- Write exactly {scene_count} scene{'s' if scene_count != 1 else ''}, "
f"numbered 01 to {scene_count:02d}, forming one continuous story with "
"hand-offs between adjacent scenes."
)
lines.append(f"- {duration_directive(duration_mode, scene_duration)}")
lines.append(f"- {continuity_directive(continuity_mode)}")
if shots_per_scene == AUTO:
lines.append("- Use 2-3 shots per scene, varying the pattern between scenes.")
else:
lines.append(f"- Use exactly {shots_per_scene} shot(s) per scene.")
if visual_style == AUTO:
lines.append(
"- Visual style: choose one concrete style that suits this cast (e.g. "
"`Live-action, 35mm cinematic film aesthetic`), open every [Shot 1] with it "
"followed by lighting, time of day and setting, and keep it identical across "
"the run."
)
else:
lines.append(
f"- Visual style: open every [Shot 1] with `{visual_style.strip()}` followed by "
"lighting, time of day and setting; keep it identical across the run."
)
if dialogue_language:
lines.append(
f"- Dialogue language: {dialogue_language}. Every <d> tag reads "
f"`<d>[{dialogue_language} in Character's voice from Show] ...</d>`."
)
else:
lines.append(
"- Dialogue language: pick one that fits the setting and use it consistently "
"in every <d> tag."
)
lines.append(f"- {wardrobe_directive(wardrobe)}")
lines.append(
"- Every listed character appears at least once across the run (unless the brief "
"says otherwise); introduce them with an on-screen reason to be there and stagger "
"arrivals rather than crowding scene 01. Max 3 people visible in one shot."
)
lines.append(
"- The character with the most dialogue in a scene is <Subject 1> in that scene."
)
wild_lines, wild_label = wildness_directive(wildness, rng)
lines += [f"- {w}" for w in wild_lines]
extra = (extra_instructions or "").strip()
if extra:
lines.append("")
lines.append("EXTRA INSTRUCTIONS:")
lines.append(extra)
lines.append("")
lines.append(
"Now write the synopsis block and the scene envelopes, and nothing else."
)
return "\n".join(lines), wild_label
# ------------------------------------------------------------------
# Execution
# ------------------------------------------------------------------
def write_scenes(
self,
direction,
extra_cast,
scene_count,
duration_mode,
continuity_mode,
scene_duration,
shots_per_scene,
visual_style,
dialogue_language,
wildness,
model,
research,
director,
use_subscription,
timeout_seconds,
seed,
custom_dialogue_language="",
custom_visual_style="",
wardrobe="",
extra_instructions="",
resume_session_id="",
working_dir="",
**cast_slots,
):
context_text, context_entries = build_context(cast_slots, target="the scenes")
cast = parse_cast(*(cast_slots.get(name) for name in CAST_SOCKETS), extra_cast)
cast_text = "\n".join(cast)
try:
if not cast:
raise ValueError(
"No cast. Wire an H3 Characters `cast` output into cast_1 or type "
"characters into extra_cast."
)
dialogue_language = resolve_dialogue_language(
dialogue_language, custom_dialogue_language
)
visual_style = resolve_visual_style(visual_style, custom_visual_style)
current_seed = seed if seed != -1 else random.randint(0, 0xffffffffffffffff)
rng = random.Random(current_seed)
user_prompt, wild_label = self._build_user_prompt(
cast,
direction,
scene_count,
duration_mode,
continuity_mode,
scene_duration,
shots_per_scene,
visual_style,
dialogue_language,
wildness,
directions_with_research(extra_instructions, research),
rng,
wardrobe=wardrobe,
)
user_prompt = with_context(user_prompt, context_text)
print(
f"🎬 H3 Crossover Writer | {len(cast)} cast | {scene_count} scene(s) | "
f"context: {context_summary(context_entries)} | "
f"{duration_mode.split(' ')[0].lower()} {scene_duration:.1f}s | "
f"{'chain' if continuity_mode == CONTINUITY_MODES[1] else 'independent'} | "
f"wildness {wildness} ({wild_label}) | research {'on' if research else 'off'} | "
f"director {'on' if director else 'off'} | seed {current_seed}"
)
text, session_id, info = run_h3_claude_code(
self._build_system_prompt(continuity_mode),
user_prompt,
None,
model,
research,
use_subscription,
timeout_seconds,
resume_session_id,
working_dir,
director,
skills=CROSSOVER_SKILLS,
)
synopsis, parsed = parse_scenes(text, scene_duration)
if not parsed:
raise ValueError("Claude Code returned no scenes.")
if len(parsed) != scene_count:
print(
f"⚠️ H3 Crossover Writer: asked for {scene_count} scene(s), parsed {len(parsed)}."
)
scenes = [p for _, _, p in parsed]
durations = [d for _, d, _ in parsed]
scenes_text = scenes_to_text(synopsis, parsed)
return (
scenes,
durations,
scenes_text,
synopsis,
cast_text,
len(scenes),
session_id,
info,
)
except Exception as exc:
if is_interrupt(exc):
raise
print(f"❌ H3 Crossover Writer error: {exc}")
import traceback
print(traceback.format_exc())
message = f"Error occurred while writing crossover scenes: {exc}"
return ([message], [float(scene_duration)], message, "", cast_text, 0, "", "error")
+27 -2
View File
@@ -7,6 +7,13 @@
import random
from ...utils.claude_code import is_interrupt
from ...utils.apnext_context import (
build_context,
context_hidden_inputs,
context_inputs,
context_summary,
with_context,
)
from ...utils.constants import CUSTOM_CATEGORY
from ...utils.image_utils import tensor2pil
from .claude_code_support import (
@@ -24,6 +31,7 @@ from .common import (
DIALOGUE_LANGUAGES,
SHOT_PLANS,
VISUAL_STYLES,
resolve_visual_style,
collect_reference_images,
extract_section,
reference_image_inputs,
@@ -67,7 +75,10 @@ class H3ClaudeCodeRefWriter(H3RefPromptWriter):
"shot_plan": (SHOT_PLANS, {"default": AUTO}),
"visual_style": (VISUAL_STYLES, {
"default": AUTO,
"tooltip": "In full-reference mode the style is stated in one or two sentences BEFORE [Shot 1].",
"tooltip": (
"In full-reference mode the style is stated in one or two sentences BEFORE [Shot 1]. "
"The list is the guide's styles plus the APNext Cinematic vocabulary (film stock, grading, aesthetics); pick Custom and fill in custom_visual_style to write your own."
),
}),
"wildness": ("INT", {
"default": 25, "min": 0, "max": 100, "step": 1,
@@ -116,12 +127,20 @@ class H3ClaudeCodeRefWriter(H3RefPromptWriter):
"dialect)' or 'Latin'. Overrides the dropdown when filled in."
),
}),
"custom_visual_style": ("STRING", {
"default": "",
"tooltip": (
"Any visual style not in the dropdown, e.g. 'hand-painted cel animation' or "
"'Kodak Vision3 500T, anamorphic'. Overrides the dropdown when filled in."
),
}),
}
optional.update(claude_code_optional_inputs())
# Image sockets last, so the front-end can grow and trim them at the tail.
optional.update(context_inputs())
optional.update(reference_image_inputs())
return {"required": required, "optional": optional}
return {"required": required, "optional": optional, "hidden": context_hidden_inputs()}
_IMAGE_OUTPUT_TYPES, _IMAGE_OUTPUT_NAMES = reference_image_outputs()
RETURN_TYPES = ("STRING",) * 9 + _IMAGE_OUTPUT_TYPES
@@ -174,17 +193,20 @@ class H3ClaudeCodeRefWriter(H3RefPromptWriter):
reference_notes="",
extra_instructions="",
custom_dialogue_language="",
custom_visual_style="",
resume_session_id="",
working_dir="",
**image_slots,
):
# The same tensors go back out on image_1..image_9 so the video node
# can be wired from this node and numbering can never drift.
context_text, context_entries = build_context(image_slots)
passthrough = tuple(image_slots.get(name) for name in self._IMAGE_OUTPUT_NAMES)
try:
dialogue_language = resolve_dialogue_language(
dialogue_language, custom_dialogue_language
)
visual_style = resolve_visual_style(visual_style, custom_visual_style)
images = [pil for _, pil in collect_reference_images(passthrough, tensor2pil)]
@@ -217,8 +239,11 @@ class H3ClaudeCodeRefWriter(H3RefPromptWriter):
rng,
)
user_prompt = with_context(user_prompt, context_text)
print(
f"🎬 H3 Claude Code Reference Writer | {len(images)} image(s) | "
f"context: {context_summary(context_entries)} | "
f"{duration_seconds:.2f}s | wildness {wildness} ({wild_label}) | "
f"research {'on' if research else 'off'} | director "
f"{'on' if director else 'off'} | seed {current_seed}"
+14 -2
View File
@@ -11,6 +11,13 @@
import random
from ...utils.claude_code import is_interrupt
from ...utils.apnext_context import (
build_context,
context_hidden_inputs,
context_inputs,
context_summary,
with_context,
)
from ...utils.constants import CUSTOM_CATEGORY
from ...utils.image_utils import tensor2pil
from .base_prompt_writer import H3BasePromptWriter
@@ -82,8 +89,9 @@ class H3ClaudeCodeRefiner:
}),
"working_dir": ("STRING", {"default": ""}),
}
optional.update(context_inputs())
return {"required": required, "optional": optional}
return {"required": required, "optional": optional, "hidden": context_hidden_inputs()}
RETURN_TYPES = ("STRING",) * 10
RETURN_NAMES = (
@@ -130,7 +138,9 @@ class H3ClaudeCodeRefiner:
session_id="",
image=None,
working_dir="",
**context_slots,
):
context_text, context_entries = build_context(context_slots, target="the revised prompt")
try:
if not instruction.strip():
raise ValueError("Describe what to change in `instruction`.")
@@ -164,8 +174,10 @@ class H3ClaudeCodeRefiner:
"request implies and leave the rest intact. Output only the prompt."
)
user_prompt = with_context(user_prompt, context_text)
print(
f"🎬 H3 Claude Code Refiner | "
f"🎬 H3 Claude Code Refiner | context: {context_summary(context_entries)} | "
f"{'resuming ' + session_id.strip()[:8] if resuming else 'fresh session'} | "
f"research {'on' if research else 'off'}"
)
+432
View File
@@ -0,0 +1,432 @@
# APNext H3 Claude Code Scenes Writer
#
# The base-format H3 writer, but for a run of scenes: one idea in, 1-10
# complete T2VA prompts out as a ComfyUI list, each in the three-field base
# format (integrated_multimodal_description / overall_soundscape /
# non_diegetic_music) and each with its own duration. Same director skills,
# camera vocabulary and wildness bands as the single-prompt writer; the scenes
# form one continuous story with hand-offs between them.
import random
from ...utils.claude_code import is_interrupt
from ...utils.apnext_context import (
build_context,
context_hidden_inputs,
context_inputs,
context_summary,
with_context,
)
from ...utils.constants import CUSTOM_CATEGORY
from ...utils.image_utils import tensor2pil
from .base_prompt_writer import _FIELDS
from .claude_code_support import (
BASE_SKILLS,
claude_code_inputs,
claude_code_optional_inputs,
directions_with_research,
run_h3_claude_code,
)
from .common import (
AUTO,
CAMERA_AMPLITUDES,
CAMERA_MOTIONS,
CAMERA_SPEEDS,
DIALOGUE_LANGUAGES,
SHOT_PLANS,
VISUAL_STYLES,
resolve_visual_style,
camera_directive,
collect_typed_references,
load_guide,
resolve_dialogue_language,
toggle_directives,
typed_reference_inputs,
wildness_directive,
)
from .scenes_support import (
WARDROBE_TOOLTIP,
wardrobe_directive,
CONTINUITY_MODES,
DURATION_MODES,
MAX_SCENES,
chain_system_block,
continuity_directive,
duration_directive,
envelope_contract,
parse_scenes,
scenes_to_text,
)
_SHOT_COUNTS = {"Single shot": 1, "Two shots": 2, "Three shots": 3, "Four shots": 4}
class H3ClaudeCodeScenesWriter:
"""
APNext H3 Claude Code Scenes Writer
Expands one idea into 1-10 consecutive MiniMax-H3 T2VA scenes with the local
Claude Code CLI. Outputs the scenes and their durations as lists.
"""
@classmethod
def INPUT_TYPES(cls):
required = {
"idea": ("STRING", {
"multiline": True,
"default": "",
"tooltip": (
"The story, sequence or idea to expand into several scenes. Say what "
"happens; the node handles the H3 grammar."
),
}),
"scene_count": ("INT", {
"default": 3, "min": 1, "max": MAX_SCENES,
"tooltip": "How many scenes to write. Each becomes one element of the scenes list.",
}),
"duration_mode": (DURATION_MODES, {"default": DURATION_MODES[0]}),
"continuity_mode": (CONTINUITY_MODES, {
"default": CONTINUITY_MODES[0],
"tooltip": (
"Independent clips: each scene is its own T2V clip with hard cuts. "
"Continuous chain: scenes are written for C2V / motion-context chaining "
"(Contex Loop, Add Guide, Motion Context) - scene N+1 opens on scene N's "
"last frame, one continuous take, speaker hand-off beats, rotating closers."
),
}),
"scene_duration": ("FLOAT", {
"default": 10.0, "min": 1.0, "max": 20.0, "step": 0.5,
"tooltip": (
"Seconds per scene in Fixed mode, and the fallback when Claude omits a "
"duration in Vary mode."
),
}),
"shot_plan": (SHOT_PLANS, {"default": AUTO, "tooltip": "Shots per scene."}),
"visual_style": (VISUAL_STYLES, {
"default": AUTO,
"tooltip": (
"Style stated at the start of every [Shot 1]; kept identical across the run. "
"The list is the guide's styles plus the APNext Cinematic vocabulary (film stock, grading, aesthetics); pick Custom and fill in custom_visual_style to write your own."
),
}),
"wildness": ("INT", {
"default": 25, "min": 0, "max": 100, "step": 1,
"tooltip": "0 = literal and conservative, 100 = fully unhinged.",
}),
"camera_motion": (CAMERA_MOTIONS, {"default": AUTO}),
"camera_amplitude": (CAMERA_AMPLITUDES, {"default": AUTO}),
"camera_speed": (CAMERA_SPEEDS, {"default": AUTO}),
"include_dialogue": ("BOOLEAN", {"default": True}),
"dialogue_language": (DIALOGUE_LANGUAGES, {"default": "English"}),
"include_on_screen_text": ("BOOLEAN", {"default": False}),
"include_soundscape": ("BOOLEAN", {"default": True}),
"include_non_diegetic_music": ("BOOLEAN", {"default": True}),
}
required.update(claude_code_inputs())
required["seed"] = ("INT", {
"default": -1, "min": -1, "max": 0xffffffffffffffff,
"tooltip": "Seeds the surreal picks and controls caching. -1 re-runs every queue.",
})
optional = {
"image": ("IMAGE", {
"tooltip": (
"Context only: pictures to describe from (a look, a place, a person). "
"The scenes are T2VA, so nothing becomes <Picture N>."
),
}),
"extra_instructions": ("STRING", {"multiline": True, "default": ""}),
"custom_dialogue_language": ("STRING", {"default": ""}),
"custom_visual_style": ("STRING", {
"default": "",
"tooltip": (
"Any visual style not in the dropdown, e.g. 'hand-painted cel animation' or "
"'Kodak Vision3 500T, anamorphic'. Overrides the dropdown when filled in."
),
}),
"wardrobe": ("STRING", {"multiline": True, "default": "", "tooltip": WARDROBE_TOOLTIP}),
}
optional.update(claude_code_optional_inputs())
optional.update(typed_reference_inputs())
optional.update(context_inputs())
return {"required": required, "optional": optional, "hidden": context_hidden_inputs()}
RETURN_TYPES = ("STRING", "FLOAT", "STRING", "STRING", "INT", "STRING", "STRING")
RETURN_NAMES = (
"scenes",
"durations",
"scenes_text",
"synopsis",
"scene_count",
"session_id",
"info",
)
OUTPUT_IS_LIST = (True, True, False, False, False, False, False)
FUNCTION = "write_scenes"
CATEGORY = f"{CUSTOM_CATEGORY}/H3"
DESCRIPTION = (
"Expands one idea into 1-10 consecutive MiniMax-H3 T2VA scenes (base format) with "
"the local Claude Code CLI. `scenes` and `durations` are lists so a video node "
"downstream renders them all; use H3 Scene Pick to grab one."
)
@classmethod
def IS_CHANGED(cls, seed=-1, **kwargs):
return float("nan") if seed == -1 else seed
# ------------------------------------------------------------------
def _build_system_prompt(self, continuity_mode=CONTINUITY_MODES[0]):
guide = load_guide("guide_base_en.md")
return (
"You are a MiniMax-H3 video prompt engineer and sequence director. You expand a "
"user's idea into a run of consecutive scenes, each a complete, production-ready "
"H3 T2VA prompt in the base format, that together tell one continuous story.\n\n"
"The authoritative specification follows. Obey it exactly - field names, shot "
"labels, timestamp formats, camera vocabulary, speaker IDs and <d> blocks all "
"follow this document.\n\n"
"=== BEGIN MINIMAX-H3 VIDEO PROMPT WRITING GUIDE ===\n"
f"{guide}\n"
"=== END MINIMAX-H3 VIDEO PROMPT WRITING GUIDE ==="
+ chain_system_block(continuity_mode) + "\n\n"
+ envelope_contract(_FIELDS) + "\n"
"- Each scene is a T2VA prompt: no alignment lines and no <Picture N> labels. "
"Continuity comes from restating the same style, lighting, wardrobe and setting "
"words in every scene, from ending one scene on a beat the next one picks up, and "
"- in continuous-chain mode - from opening each scene on the previous ending as "
"the chain rules describe.\n"
"- Keep every recurring subject visually anchored the same way in every scene "
"(hair, wardrobe, colours, distinguishing marks); give each vocal source a stable "
"(S1)/(S2) ID across the run.\n"
"- Fill each scene's whole duration with action; no dead air, no static stares "
"at the end. If dialogue ends early, close on concrete movement.\n"
"- Write everything in English except dialogue, lyrics and on-screen text, which "
"keep their own language."
)
def _build_user_prompt(
self,
idea,
scene_count,
duration_mode,
continuity_mode,
scene_duration,
shot_plan,
visual_style,
wildness,
camera_motion,
camera_amplitude,
camera_speed,
include_dialogue,
dialogue_language,
include_on_screen_text,
include_soundscape,
include_non_diegetic_music,
extra_instructions,
image_count,
references,
rng,
wardrobe="",
):
directives = [
f"Write exactly {scene_count} scene{'s' if scene_count != 1 else ''}, numbered "
f"01 to {scene_count:02d}, forming one continuous story with a hand-off between "
"adjacent scenes (a look, a movement, an object, a question the next scene answers).",
duration_directive(duration_mode, scene_duration),
continuity_directive(continuity_mode),
]
if shot_plan == AUTO:
directives.append(
"Choose the shot count per scene that fits the action (usually 1-3), varying "
"the pattern between scenes. [Shot 1] carries no timestamp; every later shot "
"opens with a strictly increasing `[Shot N] At MM:SS.mmm, ...` cut time inside "
"that scene's duration."
)
else:
count = _SHOT_COUNTS.get(shot_plan, 1)
directives.append(
f"Use exactly {count} shot{'s' if count > 1 else ''} per scene. [Shot 1] "
"carries no timestamp; every later shot opens with a strictly increasing "
"`[Shot N] At MM:SS.mmm, ...` cut time inside that scene's duration."
)
if visual_style == AUTO:
hint = " and the attached image(s)" if image_count else ""
directives.append(
f"Visual style: choose one that fits the idea{hint}, state it at the start of "
"every [Shot 1], and keep it identical across all scenes."
)
else:
directives.append(
f"Visual style: open every [Shot 1] with `{visual_style}` as the stated style."
)
directives.append(wardrobe_directive(wardrobe))
directives.append(camera_directive(camera_motion, camera_amplitude, camera_speed))
directives.extend(
toggle_directives(
include_dialogue,
include_on_screen_text,
include_soundscape,
include_non_diegetic_music,
dialogue_language,
)
)
wild_lines, wild_label = wildness_directive(wildness, rng)
directives.extend(wild_lines)
if extra_instructions.strip():
directives.append(f"Additional direction from the user: {extra_instructions.strip()}")
numbered = "\n".join(f"{i}. {line}" for i, line in enumerate(directives, 1))
source_parts = []
if image_count:
source_parts.append(
f"The first {image_count} attached image(s) are visual context: derive look, "
"subjects, wardrobe, colours and setting from what you actually see and keep "
"them consistent across every scene. They are not keyframes."
)
if references:
source_parts.append(
"The remaining attached images are typed references (" + ", ".join(references) +
"): describe each in words so the video model can reproduce it from text - a "
"subject carries over its identity and wardrobe, a scenery its place and light, "
"an object its shape and material - and use them consistently in every scene."
)
source_parts.append(f"The user's idea:\n{idea.strip() or '(none - use the images)'}")
source = "\n\n".join(source_parts)
return (
f"{source}\n\n"
f"Write the scenes under these constraints:\n{numbered}\n\n"
"Return only the synopsis block and the scene envelopes."
), wild_label
# ------------------------------------------------------------------
def write_scenes(
self,
idea,
scene_count,
duration_mode,
continuity_mode,
scene_duration,
shot_plan,
visual_style,
wildness,
camera_motion,
camera_amplitude,
camera_speed,
include_dialogue,
dialogue_language,
include_on_screen_text,
include_soundscape,
include_non_diegetic_music,
model,
research,
director,
use_subscription,
timeout_seconds,
seed,
image=None,
extra_instructions="",
custom_dialogue_language="",
custom_visual_style="",
wardrobe="",
resume_session_id="",
working_dir="",
**reference_slots,
):
context_text, context_entries = build_context(reference_slots, target="the scenes")
references = collect_typed_references(reference_slots, tensor2pil)
try:
if not idea.strip() and image is None and not references and not context_entries:
raise ValueError("Provide an idea, an image, a reference, or some of each.")
dialogue_language = resolve_dialogue_language(
dialogue_language, custom_dialogue_language
)
visual_style = resolve_visual_style(visual_style, custom_visual_style)
current_seed = seed if seed != -1 else random.randint(0, 0xffffffffffffffff)
rng = random.Random(current_seed)
context = [tensor2pil(frame) for frame in image] if image is not None else []
images = (context + [pil for _, pil in references]) or None
user_prompt, wild_label = self._build_user_prompt(
idea,
scene_count,
duration_mode,
continuity_mode,
scene_duration,
shot_plan,
visual_style,
wildness,
camera_motion,
camera_amplitude,
camera_speed,
include_dialogue,
dialogue_language,
include_on_screen_text,
include_soundscape,
include_non_diegetic_music,
directions_with_research(extra_instructions, research),
len(context),
[label for label, _ in references],
rng,
wardrobe=wardrobe,
)
user_prompt = with_context(user_prompt, context_text)
print(
f"🎬 H3 Scenes Writer | {scene_count} scene(s) | "
f"context: {context_summary(context_entries)} | "
f"{duration_mode.split(' ')[0].lower()} {scene_duration:.1f}s | "
f"{'chain' if continuity_mode == CONTINUITY_MODES[1] else 'independent'} | "
f"{len(context)} context image(s) | wildness {wildness} ({wild_label}) | "
f"research {'on' if research else 'off'} | director {'on' if director else 'off'} | "
f"seed {current_seed}"
)
text, session_id, info = run_h3_claude_code(
self._build_system_prompt(continuity_mode),
user_prompt,
images,
model,
research,
use_subscription,
timeout_seconds,
resume_session_id,
working_dir,
director,
skills=BASE_SKILLS,
)
synopsis, parsed = parse_scenes(text, scene_duration)
if not parsed:
raise ValueError("Claude Code returned no scenes.")
if len(parsed) != scene_count:
print(f"⚠️ H3 Scenes Writer: asked for {scene_count} scene(s), parsed {len(parsed)}.")
scenes = [p for _, _, p in parsed]
durations = [d for _, d, _ in parsed]
return (
scenes,
durations,
scenes_to_text(synopsis, parsed),
synopsis,
len(scenes),
session_id,
info,
)
except Exception as exc:
if is_interrupt(exc):
raise
print(f"❌ H3 Scenes Writer error: {exc}")
import traceback
print(traceback.format_exc())
message = f"Error occurred while writing the H3 scenes: {exc}"
return ([message], [float(scene_duration)], message, "", 0, "", "error")
+57 -2
View File
@@ -7,6 +7,7 @@
#
# Source: https://huggingface.co/MiniMaxAI/MiniMax-H3/tree/main/docs
import json
import os
import re
@@ -82,10 +83,13 @@ CAMERA_MOTIONS = [
CAMERA_AMPLITUDES = [AUTO, "with small amplitude", "medium (omit)", "with large amplitude"]
CAMERA_SPEEDS = [AUTO, "at slow speed", "normal (omit)", "at fast speed"]
VISUAL_STYLES = [
AUTO,
VISUAL_STYLE_CUSTOM = "Custom (use custom_visual_style)"
# The guide's own examples first ...
_GUIDE_VISUAL_STYLES = [
"Cinematic",
"live-action",
"Live-action, 35mm cinematic film aesthetic",
"2D-animated",
"3D CG",
"claymation",
@@ -93,6 +97,57 @@ VISUAL_STYLES = [
"vintage film",
]
# ... then the APNext Cinematic vocabulary (data/next/cinematic/*.json), so the
# same looks the APNext Cinematic node emits can be picked straight from the
# dropdown. Film stock / format, grading and the big aesthetics list are the
# ones that read as a *style*; genre, directors, movies and shot types steer
# better through the context sockets.
_APNEXT_CINEMATIC_DIR = os.path.join(os.path.dirname(_DATA_DIR), "next", "cinematic")
_APNEXT_VISUAL_STYLE_FILES = ("film_type", "color_grading", "aesthetics")
def _apnext_cinematic_items(name):
path = os.path.join(_APNEXT_CINEMATIC_DIR, f"{name}.json")
try:
with open(path, "r", encoding="utf-8") as fh:
items = json.load(fh).get("items", [])
except (OSError, ValueError):
return []
return [str(item).strip() for item in items if str(item).strip()]
def _build_visual_styles():
styles = [AUTO, VISUAL_STYLE_CUSTOM]
seen = {s.lower() for s in styles}
for source in [_GUIDE_VISUAL_STYLES] + [
_apnext_cinematic_items(name) for name in _APNEXT_VISUAL_STYLE_FILES
]:
for item in source:
if item.lower() not in seen:
seen.add(item.lower())
styles.append(item)
return styles
VISUAL_STYLES = _build_visual_styles()
def resolve_visual_style(choice, custom=""):
"""
Turn the visual_style dropdown (plus its free-text companion) into the
style string the prompt builders expect.
A non-empty custom value always wins, so anything can be typed in. "Custom"
with an empty text box, like an empty choice, falls back to AUTO so the
model derives the style itself.
"""
custom = (custom or "").strip()
if custom:
return custom
if not choice or choice == VISUAL_STYLE_CUSTOM:
return AUTO
return choice
SHOT_PLANS = [AUTO, "Single shot", "Two shots", "Three shots", "Four shots"]
_SHOT_COUNTS = {
+28 -2
View File
@@ -5,6 +5,13 @@
import random
from ...utils.apnext_context import (
build_context,
context_hidden_inputs,
context_inputs,
context_summary,
with_context,
)
from ...utils.constants import CUSTOM_CATEGORY
from ...utils.image_utils import tensor2pil
from ...utils.llm_router import AUTO_DETECT, call_llm, is_interrupt, list_all_models
@@ -16,6 +23,7 @@ from .common import (
DIALOGUE_LANGUAGES,
SHOT_PLANS,
VISUAL_STYLES,
resolve_visual_style,
camera_directive,
collect_reference_images,
resolve_dialogue_language,
@@ -91,7 +99,10 @@ class H3RefPromptWriter:
"shot_plan": (SHOT_PLANS, {"default": AUTO}),
"visual_style": (VISUAL_STYLES, {
"default": AUTO,
"tooltip": "In full-reference mode the style is stated in one or two sentences BEFORE [Shot 1].",
"tooltip": (
"In full-reference mode the style is stated in one or two sentences BEFORE [Shot 1]. "
"The list is the guide's styles plus the APNext Cinematic vocabulary (film stock, grading, aesthetics); pick Custom and fill in custom_visual_style to write your own."
),
}),
"wildness": ("INT", {
"default": 25, "min": 0, "max": 100, "step": 1,
@@ -143,6 +154,13 @@ class H3RefPromptWriter:
"dialect)' or 'Latin'. Overrides the dropdown when filled in."
),
}),
"custom_visual_style": ("STRING", {
"default": "",
"tooltip": (
"Any visual style not in the dropdown, e.g. 'hand-painted cel animation' or "
"'Kodak Vision3 500T, anamorphic'. Overrides the dropdown when filled in."
),
}),
"model_override": ("STRING", {
"default": "",
"tooltip": (
@@ -159,9 +177,11 @@ class H3RefPromptWriter:
"local 8000. Ignored by the cloud providers."
),
}),
**context_inputs(),
# Image sockets last, so the front-end can grow and trim them at the tail.
**reference_image_inputs(),
},
"hidden": context_hidden_inputs(),
}
_IMAGE_OUTPUT_TYPES, _IMAGE_OUTPUT_NAMES = reference_image_outputs()
@@ -372,21 +392,24 @@ class H3RefPromptWriter:
reference_notes="",
extra_instructions="",
custom_dialogue_language="",
custom_visual_style="",
model_override="",
local_base_url="",
**image_slots,
):
# The same tensors go back out on image_1..image_9 so the video node
# can be wired from this node and numbering can never drift.
context_text, context_entries = build_context(image_slots)
passthrough = tuple(image_slots.get(name) for name in self._IMAGE_OUTPUT_NAMES)
try:
dialogue_language = resolve_dialogue_language(
dialogue_language, custom_dialogue_language
)
visual_style = resolve_visual_style(visual_style, custom_visual_style)
images = [pil for _, pil in collect_reference_images(passthrough, tensor2pil)]
if not idea.strip() and not images and not reference_notes.strip():
if not idea.strip() and not images and not reference_notes.strip() and not context_entries:
raise ValueError("Provide an idea, at least one reference image, or reference notes.")
current_seed = seed if seed != -1 else random.randint(0, 0xffffffffffffffff)
@@ -415,8 +438,11 @@ class H3RefPromptWriter:
rng,
)
user_prompt = with_context(user_prompt, context_text)
print(
f"🎬 H3 Reference Prompt Writer | {len(images)} image(s) | "
f"context: {context_summary(context_entries)} | "
f"{duration_seconds:.2f}s | wildness {wildness} ({wild_label}) | seed {current_seed}"
)
+55
View File
@@ -0,0 +1,55 @@
# APNext H3 Scene Pick
#
# Pulls one scene out of the list the H3 Crossover Writer emits. ComfyUI would
# otherwise run every downstream node once per element; this collapses the list
# to a single prompt (and its duration) chosen by index.
from ...utils.constants import CUSTOM_CATEGORY
class H3ScenePick:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"scenes": ("STRING", {
"forceInput": True,
"tooltip": "The `scenes` list from H3 Crossover Writer (or any STRING list).",
}),
"index": ("INT", {
"default": 0, "min": 0, "max": 999,
"tooltip": "0-based scene index. Values past the end are clamped to the last scene.",
}),
},
"optional": {
"durations": ("FLOAT", {
"forceInput": True,
"tooltip": "The matching `durations` list; the picked scene's duration comes out.",
}),
},
}
INPUT_IS_LIST = True
RETURN_TYPES = ("STRING", "FLOAT", "INT", "INT")
RETURN_NAMES = ("scene", "duration", "index", "count")
FUNCTION = "pick"
CATEGORY = f"{CUSTOM_CATEGORY}/H3"
DESCRIPTION = (
"Selects one scene (and its duration) from the list produced by the H3 Crossover "
"Writer, so a single scene can be rendered or refined on its own."
)
def pick(self, scenes, index, durations=None):
scenes = list(scenes or [])
if not scenes:
return ("", 0.0, 0, 0)
idx = int(index[0]) if isinstance(index, (list, tuple)) else int(index)
idx = max(0, min(idx, len(scenes) - 1))
duration = 0.0
if durations:
durations = list(durations)
duration = float(durations[min(idx, len(durations) - 1)])
return (scenes[idx], duration, idx, len(scenes))
+189
View File
@@ -0,0 +1,189 @@
# Shared pieces for the H3 nodes that write several scenes in one call.
#
# Both multi-scene writers (the crossover writer and the plain scenes writer)
# ask Claude Code for the same envelope layout, split it the same way, and hand
# the result out as a ComfyUI list. This keeps the contract in one place.
import re
from .common import strip_code_fence
MAX_SCENES = 10
DURATION_MODES = [
"Fixed (every scene = scene_duration)",
"Vary 5-15s (let Claude pace each scene)",
]
_SYNOPSIS_RE = re.compile(
r"===\s*SYNOPSIS\s*===\s*(.*?)\s*===\s*END SYNOPSIS\s*===", re.DOTALL | re.IGNORECASE
)
_SCENE_RE = re.compile(
r"===\s*SCENE\s+(\d+)\s*(?:\|\s*duration:\s*([0-9.]+)\s*(?:s|sec|seconds)?\s*)?===\s*"
r"(.*?)\s*===\s*END SCENE\s*\1\s*===",
re.DOTALL | re.IGNORECASE,
)
def envelope_contract(section_labels):
"""The output-contract paragraph both writers append to their system prompt."""
labels = ", ".join(f"`{label}:`" for label in section_labels)
first = section_labels[0]
return (
"Output contract - the node parses this mechanically, so it must be exact:\n"
"- First a synopsis block:\n"
" === SYNOPSIS ===\n"
" Title: ...\n Logline: one or two sentences\n"
" Wardrobe: one line per recurring character - `Name: anchor, anchor, anchor`\n"
" === END SYNOPSIS ===\n"
"- Then one envelope per scene, numbered from 01:\n"
" === SCENE 01 | duration: 15.0 ===\n"
f" {first}: ...\n (remaining sections)\n"
" === END SCENE 01 ===\n"
f"- Inside an envelope: exactly the section labels {labels} in that order, plain "
"text, one blank line between sections and between shots. No markdown, no bold, no "
"fences, no bullets, no commentary anywhere in the output.\n"
"- `duration:` is the length in seconds that scene will be rendered at; write it as a "
"plain number with one decimal.\n"
"- Never write a title card, credit card, logo card or any shot without people unless "
"explicitly asked. Every scene is story."
)
WARDROBE_TOOLTIP = (
"Wardrobe lock, one line per character, e.g. `Sheldon: brown corduroy jacket over a "
"green Flash T-shirt, khakis`. Used word-for-word in every scene. Empty = Claude fixes "
"one outfit per character itself (in the synopsis) and repeats it in every scene."
)
def wardrobe_directive(wardrobe=""):
"""
Wardrobe drifts between scenes unless each outfit is fixed once and copied.
With user text: that text is the lock. Without: Claude writes the lock into
the synopsis first, then reuses it.
"""
wardrobe = (wardrobe or "").strip()
if wardrobe:
lines = [line.strip() for line in wardrobe.splitlines() if line.strip()]
return (
"Wardrobe lock (from the user) - copy these anchors word-for-word into the "
"synopsis `Wardrobe:` lines, into every scene's subject_definitions and into "
"the first shot each character appears in; never add, remove or recolour a "
"garment between scenes:\n" + "\n".join(f" {line}" for line in lines)
)
return (
"Wardrobe lock: before scene 01, fix ONE outfit per character - 2-4 concrete "
"anchors (garment, colour, material or pattern, one distinctive item) - write "
"them as the synopsis `Wardrobe:` lines, then reuse that exact wording in every "
"scene's subject_definitions and in the first shot each character appears in. "
"Clothes, hair and props never change between scenes unless the brief explicitly "
"calls for a costume change, and then the change is stated on screen."
)
def duration_directive(duration_mode, scene_duration):
if duration_mode == DURATION_MODES[0]:
return (
f"Every scene is exactly {scene_duration:.1f} seconds; write "
f"`duration: {scene_duration:.1f}` in every envelope and spread the cut "
"timecodes across that length. Fill the whole duration with action (and dialogue "
"where allowed); no dead air at the end."
)
return (
"Choose each scene's duration yourself between 5.0 and 15.0 seconds (prefer 12-15 "
"for dialogue or a developing action, 5-8 for a transition or punch) and write it as "
"`duration: S.S` in that scene's envelope. Fill the whole duration."
)
def parse_scenes(text, fallback_duration):
"""
Split Claude's answer into (synopsis, [(number, duration, prompt), ...]).
Falls back to treating the whole text as one scene if no envelopes are
found, so a slightly off-format answer still yields something usable.
"""
synopsis_match = _SYNOPSIS_RE.search(text)
synopsis = synopsis_match.group(1).strip() if synopsis_match else ""
scenes = []
for match in _SCENE_RE.finditer(text):
number = int(match.group(1))
duration = float(match.group(2)) if match.group(2) else float(fallback_duration)
prompt = strip_code_fence(match.group(3))
prompt = re.sub(r"\*\*(\w+):\*\*", r"\1:", prompt) # de-bold stray headers
scenes.append((number, duration, prompt))
if not scenes:
body = text
if synopsis_match:
body = text[: synopsis_match.start()] + text[synopsis_match.end():]
body = strip_code_fence(body)
if body.strip():
scenes.append((1, float(fallback_duration), body.strip()))
scenes.sort(key=lambda s: s[0])
return synopsis, scenes
def scenes_to_text(synopsis, parsed):
"""Re-serialise parsed scenes into one readable, re-parseable string."""
text = "\n\n".join(
f"=== SCENE {n:02d} | duration: {d:.1f} ===\n{p}\n=== END SCENE {n:02d} ==="
for n, d, p in parsed
)
if synopsis:
text = f"=== SYNOPSIS ===\n{synopsis}\n=== END SYNOPSIS ===\n\n{text}"
return text
# ----------------------------------------------------------------------
# Continuity: independent clips vs a continuous C2V chain
# ----------------------------------------------------------------------
CONTINUITY_MODES = [
"Independent clips (hard cuts, T2V openers)",
"Continuous chain (each scene continues the last frames)",
]
def is_chain_mode(continuity_mode):
return continuity_mode == CONTINUITY_MODES[1]
def chain_system_block(continuity_mode):
"""Chain rules for the system prompt; "" when writing independent clips."""
if not is_chain_mode(continuity_mode):
return ""
from .common import load_guide
return (
"\n\n=== BEGIN MINIMAX-H3 CONTINUOUS CHAIN RULES ===\n"
f"{load_guide('guide_chain_en.md')}\n"
"=== END MINIMAX-H3 CONTINUOUS CHAIN RULES ===\n\n"
"These scenes will be rendered as a continuous chain: the last frames and audio of "
"each scene are pinned to the head of the next. The chain rules override the "
"independent-clip habits (hard cuts, re-establishing shots, 'already speaking' "
"openers on a new speaker) for every scene after the first."
)
def continuity_directive(continuity_mode):
"""One user-prompt directive line describing the continuity contract."""
if not is_chain_mode(continuity_mode):
return (
"Continuity mode: independent clips. Each scene is a self-contained T2V clip "
"with its own establishing [Shot 1] and hard cuts between shots; adjacent scenes "
"hand off through story (a look, a prop, a question), not through pixels."
)
return (
"Continuity mode: CONTINUOUS CHAIN. Scene 01 is a normal opener. From scene 02 on, "
"[Shot 1] opens on the last frame of the previous scene (name who is where and the "
"framing), the whole scene is one continuous take with no `the shot cuts to`, the "
"closer is a moving shot of a different class than the scene's opening framing, and "
"when the first speaker is not the person on screen at the previous ending, use the "
"three-part hand-off (silent inherited beat to 00:02.000, named transition without "
"cutting, first <d> at 00:02.000 or later, outgoing person declared as a subject). "
"Keep the same lighting string, screen positions and wardrobe anchors in every scene."
)
+102
View File
@@ -0,0 +1,102 @@
# APNext H3 Scenes → Contex Loop Plan
#
# Turns the `scenes` / `durations` lists from the H3 Crossover Writer or the H3
# Scenes Writer into the plan JSON that ComfyUI-MiniMaxH3-Contex-Loop's
# `MiniMax H3 Contex Loop Plan` node accepts on its `plan_json_input` socket:
#
# {"prompt_prefix": "...", "defaults": {...}, "shots": [{"id", "prompt",
# "duration_seconds", "seed"}, ...]}
#
# That pack then renders every scene in sequence, carrying the last frames
# (and optionally audio) of each scene into the next one.
import json
from ...utils.constants import CUSTOM_CATEGORY
class H3ScenesToChainPlan:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"scenes": ("STRING", {
"forceInput": True,
"tooltip": "The `scenes` list from an H3 Crossover / Scenes Writer.",
}),
"id_prefix": ("STRING", {
"default": "scene",
"tooltip": "Shot ids become `<id_prefix>_01`, `_02`, ... (checkpoint names).",
}),
"base_seed": ("INT", {
"default": 0, "min": 0, "max": 0xffffffffffffffff,
"tooltip": "Seed for shot 1; each later shot uses base_seed + index. -1 in seed_mode omits seeds.",
}),
"seed_mode": (["base_seed + index", "omit (plan node derives seeds)"], {
"default": "base_seed + index",
}),
"steps": ("INT", {
"default": 0, "min": 0, "max": 10000,
"tooltip": "Sampler steps written into defaults. 0 = leave to the Plan node.",
}),
},
"optional": {
"durations": ("FLOAT", {
"forceInput": True,
"tooltip": "The matching `durations` list; written as duration_seconds per shot.",
}),
"prompt_prefix": ("STRING", {
"multiline": True, "default": "",
"tooltip": "Global continuity text prepended to every scene by the Plan node.",
}),
},
}
INPUT_IS_LIST = True
RETURN_TYPES = ("STRING", "INT")
RETURN_NAMES = ("plan_json", "shot_count")
FUNCTION = "build"
CATEGORY = f"{CUSTOM_CATEGORY}/H3"
DESCRIPTION = (
"Converts the scenes/durations lists into MiniMax H3 Contex Loop plan JSON "
"(wire into MiniMax H3 Contex Loop Plan → plan_json_input) so the whole run "
"renders scene after scene with motion/audio carried across each join."
)
@staticmethod
def _first(value, default):
if isinstance(value, (list, tuple)):
return value[0] if value else default
return value if value is not None else default
def build(self, scenes, id_prefix, base_seed, seed_mode, steps, durations=None, prompt_prefix=None):
scenes = [s for s in (scenes or []) if isinstance(s, str) and s.strip()]
durations = list(durations or [])
id_prefix = (self._first(id_prefix, "scene") or "scene").strip() or "scene"
base_seed = int(self._first(base_seed, 0))
seed_mode = self._first(seed_mode, "base_seed + index")
steps = int(self._first(steps, 0))
prompt_prefix = (self._first(prompt_prefix, "") or "").strip()
shots = []
for i, prompt in enumerate(scenes):
shot = {"id": f"{id_prefix}_{i + 1:02d}", "prompt": prompt.splitlines()}
if i < len(durations):
shot["duration_seconds"] = float(durations[i])
if seed_mode.startswith("base_seed"):
shot["seed"] = (base_seed + i) & 0xffffffffffffffff
shots.append(shot)
plan = {}
if prompt_prefix:
plan["prompt_prefix"] = prompt_prefix
defaults = {}
if steps > 0:
defaults["steps"] = steps
if durations:
defaults["duration_seconds"] = float(durations[0])
if defaults:
plan["defaults"] = defaults
plan["shots"] = shots
return (json.dumps(plan, indent=2, ensure_ascii=False), len(shots))
+243
View File
@@ -0,0 +1,243 @@
# APNext context sockets for the Claude Code nodes.
#
# The APNext generator nodes (APNext Time, Scene, Poses, Plots, Feelings,
# Cinematic, Science, Geography, Architecture, ...) all emit a plain STRING.
# Wired straight into an `idea` field they are just words; the writer cannot
# tell a time period from a pose. These helpers give every Claude Code node a
# row of `context_N` sockets and, using ComfyUI's hidden PROMPT / UNIQUE_ID
# inputs, look up *which* node feeds each socket. The category then selects a
# short instruction on how that kind of input should steer the scene, and the
# whole thing is handed to Claude as a labelled context block.
#
# Everything degrades gracefully: with no graph access the text is still used,
# labelled as generic context.
import re
MAX_CONTEXT = 8
CONTEXT_SOCKETS = tuple(f"context_{i}" for i in range(1, MAX_CONTEXT + 1))
HIDDEN_GRAPH = "apnext_graph"
HIDDEN_NODE_ID = "apnext_node_id"
# Widgets on the APNext generator nodes that are not category fields.
_NON_FIELD_INPUTS = {"prompt", "separator", "string", "seed", "attributes"}
_INACTIVE_VALUES = {"None", "", None}
# How each APNext category should steer a video prompt. Keys are the data/next
# folder names (== the node's `_subcategory`); a few extra keys cover our own
# nodes and the fallback.
CATEGORY_GUIDANCE = {
"time": (
"Period, era, decade or time of day. Make it visible: period-correct wardrobe, "
"props, vehicles, technology, signage and architecture; the light and colour of "
"that hour. State the time of day in every shot's opening line and keep it "
"consistent across shots."
),
"scene": (
"Setting and environment: location type, weather, textures, plants, modifiers. "
"Use it as the place the action happens - describe it concretely in [Shot 1] "
"and keep its light, weather and materials consistent."
),
"poses": (
"Physical staging of the subject(s): body position, gesture, portrait pose. Turn "
"it into what the character is physically doing at the start of the shot and how "
"they move out of it; never leave a pose as a static hold."
),
"plots": (
"Story beats and genre. Dramatise them as on-screen events - cause and effect, "
"an arc across the shots (and across scenes when several are written). Show, "
"don't narrate."
),
"feelings": (
"Emotional tone. Express it through performance, delivery, body language, "
"lighting temperature, colour, pacing and sound - not by naming the emotion."
),
"cinematic": (
"Camera and film language: shot type, genre, effects, colour grading, film stock, "
"director/movie references. Translate into the stated visual style, framing, "
"camera moves and grade; a director or movie reference means their observable "
"craft (lens, palette, blocking, rhythm), never a name-drop."
),
"photography": (
"Photographic look: camera, lens, film, lighting setup, grade. Fold into the "
"visual style and lighting description as observable qualities (depth of field, "
"grain, key/fill, colour)."
),
"science": (
"Scientific subject matter (astronomy, elements, mathematics, medical). Use it as "
"accurate, concrete visual content - real phenomena, instruments, environments - "
"and get the details right."
),
"geography": (
"Country, nationality, territory. Set the place and its real look: landscape, "
"architecture, climate, signage language, people and dress typical of it."
),
"architecture": (
"Buildings, interiors, materials, architectural styles. Use as the built "
"environment - describe structure, materials, scale and light behaviour "
"concretely; keep it consistent across shots."
),
"art": "Artistic medium, style, palette, technique. Drives the stated visual style and how surfaces and light are rendered.",
"artist": "Named artists / illustrators as style anchors. Translate into their observable visual traits (palette, line, light, composition); do not just name them.",
"brands": "Brand names. Show them as products, logos on props or wardrobe, storefronts and signage (in double quotes when text is readable), only where the idea allows.",
"character": "Character archetypes / fictional figures. Cast them as the on-screen subject(s) with a concrete physical description, wardrobe and behaviour.",
"fashion": "Wardrobe, accessories, hairstyles, designers. Dress the subjects accordingly and restate the wardrobe anchors in every shot.",
"human": "Occupations, hobbies, groups, festivities. Use to define who the people are, what they are doing and the social setting.",
"interaction": "How people interact (couples, groups, crowds, individuals). Stage the physical interaction beat by beat, with clear positions.",
"keywords": "Loose modifiers / genre / trending keywords. Treat as flavour: fold the useful ones into style, mood and detail; ignore anything that contradicts the idea.",
"people": "Archetypes, body types, expressions, eye colour. Use as concrete physical description of the subject(s), restated per shot.",
"stuff": "Objects and props (everyday, seasonal, sci-fi, gadgets). Put them in the scene as physical props the characters handle or that dress the set.",
"typography": "Fonts, word art, text styles. Only for readable on-screen text (titles, signs) - write the text in double quotes and describe the lettering style.",
"vehicle": "Vehicles. Put them in the scene as physical objects (make, era, colour, condition) that are driven, ridden or parked, with correct scale.",
"video_game": "Games, engines, designers, actions. Use as visual/gameplay reference: the look, the world, the kind of action - not UI overlays unless asked.",
# Our own nodes
"cast": "Cast lines `Character (played by Actor) from Show`. These are the characters to write for; use the strings verbatim in subject_definitions and play them in character.",
"h3_prompt": "An existing H3 prompt. Treat it as reference material for style, subjects and continuity - do not copy it back unless asked to revise it.",
"context": "Free-form context. Use whatever in it helps make the scene concrete and consistent; the idea/direction still leads.",
}
_KNOWN_CLASSES = {
"H3Characters": "cast",
"H3ClaudeCodeBaseWriter": "h3_prompt",
"H3ClaudeCodeRefWriter": "h3_prompt",
"H3ClaudeCodeRefiner": "h3_prompt",
"H3ClaudeCodeContinueWriter": "h3_prompt",
"H3BasePromptWriter": "h3_prompt",
"H3RefPromptWriter": "h3_prompt",
"H3ScenePick": "h3_prompt",
}
def context_inputs():
"""The optional context sockets, to merge into a node's `optional` inputs."""
return {
name: ("STRING", {
"forceInput": True,
"tooltip": (
"Steering input from another APNext node (Time, Scene, Poses, Plots, "
"Feelings, Cinematic, Science, Geography, Architecture, Fashion, ...). "
"The node detects which kind it is and tells Claude how to use it. "
"Sockets grow as you connect them."
),
})
for name in CONTEXT_SOCKETS
}
def context_hidden_inputs():
"""Hidden inputs that let the node inspect the graph it runs in."""
return {HIDDEN_GRAPH: "PROMPT", HIDDEN_NODE_ID: "UNIQUE_ID"}
def pop_context(kwargs):
"""
Remove context sockets and hidden graph inputs from a **kwargs dict, so
other consumers of that dict (typed references, image slots) stay clean.
Returns (context_values_by_socket, graph, node_id).
"""
values = {name: kwargs.pop(name, None) for name in CONTEXT_SOCKETS}
graph = kwargs.pop(HIDDEN_GRAPH, None)
node_id = kwargs.pop(HIDDEN_NODE_ID, None)
return values, graph, node_id
def _category_for(class_type):
if not class_type:
return "context", ""
if class_type in _KNOWN_CLASSES:
return _KNOWN_CLASSES[class_type], class_type
if class_type.endswith("PromptNode"):
stem = class_type[: -len("PromptNode")]
category = re.sub(r"(?<!^)(?=[A-Z])", "_", stem).lower()
return (category if category in CATEGORY_GUIDANCE else "context"), f"APNext {stem}"
return "context", class_type
def _active_fields(inputs):
fields = []
for key, value in (inputs or {}).items():
if key in _NON_FIELD_INPUTS or isinstance(value, list):
continue
if value in _INACTIVE_VALUES:
continue
fields.append(f"{key}={value}" if value not in ("Random", "Multiple Random") else f"{key} ({value})")
return fields
def resolve_context(values, graph, node_id):
"""
Turn the raw socket values into labelled entries:
[{"socket", "category", "source", "fields", "text", "guidance"}, ...]
"""
entries = []
my_inputs = {}
if isinstance(graph, dict) and node_id is not None:
me = graph.get(str(node_id)) or graph.get(node_id) or {}
my_inputs = me.get("inputs", {}) if isinstance(me, dict) else {}
for socket in CONTEXT_SOCKETS:
text = values.get(socket)
if not isinstance(text, str) or not text.strip():
continue
category, source, fields = "context", "", []
link = my_inputs.get(socket)
if isinstance(link, list) and len(link) >= 1 and isinstance(graph, dict):
src = graph.get(str(link[0])) or {}
category, source = _category_for(src.get("class_type"))
if source.startswith("APNext "):
fields = _active_fields(src.get("inputs"))
entries.append({
"socket": socket,
"category": category,
"source": source,
"fields": fields,
"text": text.strip(),
"guidance": CATEGORY_GUIDANCE.get(category, CATEGORY_GUIDANCE["context"]),
})
return entries
def context_block(entries, target="the scene"):
"""The labelled block appended to a Claude Code user prompt; "" if nothing is wired."""
if not entries:
return ""
lines = [
"CONTEXT FROM CONNECTED APNEXT NODES",
f"Each item below is a steering input of a specific kind. Reconcile them into one "
f"coherent version of {target}; the user's own idea/direction still leads. Where an "
"input lists several options, pick what fits and make it concrete and visible on "
"screen. Never paste an input's wording back verbatim as a list of tags - translate "
"it into filmed detail.",
]
for i, e in enumerate(entries, 1):
header = f"{i}. [{e['category']}]"
if e["source"]:
header += f" from {e['source']}"
if e["fields"]:
header += f" (fields: {', '.join(e['fields'])})"
lines.append(header)
lines.append(f" Input: {e['text']}")
lines.append(f" Use it as: {e['guidance']}")
return "\n".join(lines)
def build_context(kwargs, target="the scene"):
"""
One-call helper for node functions: pops the sockets from **kwargs, resolves
them against the graph and returns (block_text, entries).
"""
values, graph, node_id = pop_context(kwargs)
entries = resolve_context(values, graph, node_id)
return context_block(entries, target), entries
def with_context(user_prompt, block):
"""Append the context block to a finished user prompt (before nothing else)."""
if not block:
return user_prompt
return f"{user_prompt.rstrip()}\n\n{block}\n"
def context_summary(entries):
"""Short log line: 'time, scene, poses' or 'none'."""
return ", ".join(e["category"] for e in entries) or "none"
+135
View File
@@ -0,0 +1,135 @@
// APNext context socket autogrow + labelling
//
// The Claude Code / H3 writer nodes declare eight optional STRING sockets
// (context_1..context_8) for steering input from other APNext nodes (Time,
// Scene, Poses, Plots, Feelings, Cinematic, ...). Eight empty sockets are
// noise, so only the connected ones plus one spare are shown. When a socket is
// connected, its label shows what it is fed by ("context: time"), mirroring
// what the back end works out from the graph at run time.
//
// Purely cosmetic: every socket is a declared optional input, so a workflow
// keeps working with this file removed.
import { app } from "../../../scripts/app.js";
const NODE_CLASSES = new Set([
"ClaudeCodeNode",
"H3BasePromptWriter",
"H3RefPromptWriter",
"H3ClaudeCodeBaseWriter",
"H3ClaudeCodeRefWriter",
"H3ClaudeCodeRefiner",
"H3ClaudeCodeContinueWriter",
"H3ClaudeCodeCrossoverWriter",
"H3ClaudeCodeScenesWriter",
]);
const MAX_CONTEXT = 8;
const slotName = (i) => `context_${i}`;
const SLOT_RE = /^context_(\d+)$/;
const KNOWN = {
H3Characters: "cast",
H3ClaudeCodeBaseWriter: "h3 prompt",
H3ClaudeCodeRefWriter: "h3 prompt",
H3ClaudeCodeRefiner: "h3 prompt",
H3ClaudeCodeContinueWriter: "h3 prompt",
H3BasePromptWriter: "h3 prompt",
H3RefPromptWriter: "h3 prompt",
H3ScenePick: "h3 prompt",
};
function categoryOf(type) {
if (!type) return null;
if (KNOWN[type]) return KNOWN[type];
if (type.endsWith("PromptNode")) {
return type
.slice(0, -"PromptNode".length)
.replace(/(?!^)([A-Z])/g, " $1")
.toLowerCase();
}
return null;
}
function findInput(node, i) {
return (node.inputs || []).findIndex((input) => input && input.name === slotName(i));
}
function relabel(node) {
const graph = node.graph || app.graph;
for (let i = 1; i <= MAX_CONTEXT; i++) {
const idx = findInput(node, i);
if (idx < 0) continue;
const input = node.inputs[idx];
let label = null;
if (input.link != null && graph) {
const link = graph.links?.[input.link];
const origin = link ? graph.getNodeById(link.origin_id) : null;
const cat = categoryOf(origin?.type);
label = cat ? `context: ${cat}` : "context";
}
input.label = label || undefined;
}
}
function syncContextSlots(node) {
if (!node || node._apnextCtxSyncing) return;
node._apnextCtxSyncing = true;
try {
let highest = 0;
for (let i = 1; i <= MAX_CONTEXT; i++) {
const idx = findInput(node, i);
if (idx >= 0 && node.inputs[idx].link != null) highest = i;
}
const wanted = Math.min(MAX_CONTEXT, highest + 1);
const rowsBefore = (node.inputs || []).length;
let changed = false;
for (let i = 1; i <= wanted; i++) {
if (findInput(node, i) < 0) { node.addInput(slotName(i), "STRING"); changed = true; }
}
for (let i = MAX_CONTEXT; i > wanted; i--) {
const idx = findInput(node, i);
if (idx >= 0 && node.inputs[idx].link == null) { node.removeInput(idx); changed = true; }
}
relabel(node);
if (changed) {
const slotHeight = (globalThis.LiteGraph && globalThis.LiteGraph.NODE_SLOT_HEIGHT) || 20;
const minimum = node.computeSize();
const height = Math.max(minimum[1], node.size[1] + ((node.inputs || []).length - rowsBefore) * slotHeight);
node.setSize([Math.max(node.size[0], minimum[0]), height]);
}
node.setDirtyCanvas(true, true);
} finally {
node._apnextCtxSyncing = false;
}
}
app.registerExtension({
name: "apnext.contextInputs",
beforeRegisterNodeDef(nodeType, nodeData) {
if (!NODE_CLASSES.has(nodeData?.name)) return;
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function () {
const result = onNodeCreated?.apply(this, arguments);
setTimeout(() => syncContextSlots(this), 0);
return result;
};
const onConfigure = nodeType.prototype.onConfigure;
nodeType.prototype.onConfigure = function () {
const result = onConfigure?.apply(this, arguments);
setTimeout(() => syncContextSlots(this), 0);
return result;
};
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
nodeType.prototype.onConnectionsChange = function (type, index, connected, linkInfo, ioSlot) {
const result = onConnectionsChange?.apply(this, arguments);
if (SLOT_RE.test(ioSlot?.name || "")) setTimeout(() => syncContextSlots(this), 0);
return result;
};
},
});
+54
View File
@@ -0,0 +1,54 @@
// APNext H3 Characters - franchise follows the picked character
//
// `franchise_filter` only narrows the pool when `character` is "random". When
// a named character is picked, this snaps the filter to that character's show
// so flipping to random afterwards draws a castmate instead of anyone at all.
// Labels look like "Character — Actor (Show)"; the show is matched against the
// filter's own option list, so shows containing parentheses still resolve.
import { app } from "../../../scripts/app.js";
const NODE_CLASS = "H3Characters";
const RANDOM_PREFIX = "🎲";
function franchiseOf(label, options) {
if (!label || label.startsWith(RANDOM_PREFIX)) return null;
let best = null;
for (const opt of options) {
if (opt === "(all)") continue;
if (label.endsWith(`(${opt})`) && (!best || opt.length > best.length)) best = opt;
}
return best;
}
app.registerExtension({
name: "apnext.h3.characters",
async beforeRegisterNodeDef(nodeType, nodeData) {
if (nodeData?.name !== NODE_CLASS) return;
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function () {
const r = onNodeCreated?.apply(this, arguments);
const character = this.widgets?.find((w) => w.name === "character");
const franchise = this.widgets?.find((w) => w.name === "franchise_filter");
if (!character || !franchise) return r;
const sync = () => {
const options = franchise.options?.values || [];
const show = franchiseOf(character.value, options);
if (show && franchise.value !== show) {
franchise.value = show;
this.setDirtyCanvas(true, true);
}
};
const prev = character.callback;
character.callback = function () {
const out = prev?.apply(this, arguments);
sync();
return out;
};
return r;
};
},
});
+9 -1
View File
@@ -21,6 +21,8 @@ const CSS = `
flex-direction: column;
width: 100%;
height: 100%;
max-height: 100%;
min-height: 0;
box-sizing: border-box;
background: #16181d;
border: 1px solid #2c3038;
@@ -33,6 +35,7 @@ const CSS = `
}
.apnext-h3-bar {
display: flex;
flex: 0 0 auto;
align-items: center;
gap: 6px;
flex-wrap: wrap;
@@ -75,8 +78,12 @@ const CSS = `
.apnext-h3-bar button:hover { background: #313847; }
.apnext-h3-bar button.apnext-ok { background: #1f4d34; border-color: #2f7a52; }
.apnext-h3-body {
flex: 1;
/* min-height: 0 lets the flex item shrink below its content height, so a
short node scrolls the prompt instead of clipping it. */
flex: 1 1 0;
min-height: 0;
overflow: auto;
overscroll-behavior: contain;
padding: 8px 10px 10px;
white-space: pre-wrap;
word-break: break-word;
@@ -86,6 +93,7 @@ const CSS = `
.apnext-h3-body::selection, .apnext-h3-body *::selection { background: #3d5a99; color: #fff; }
.apnext-h3-empty { color: #6b7280; font-style: italic; }
.apnext-h3-meta {
flex: 0 0 auto;
margin-top: 0;
padding-top: 5px;
border-top: 1px dashed #2c3038;