Simplify H3 beat timing and cleanup
This commit is contained in:
+14
-58
@@ -10,7 +10,7 @@ The node does two jobs at once:
|
||||
Think of it like this:
|
||||
|
||||
- `prompt` decides what happens.
|
||||
- `shot_seconds`, `per_beat_length`, and beat-level `seconds:` decide how long each beat is allowed to be.
|
||||
- `shot_seconds` and beat-level `seconds:` decide how long each beat is allowed to be.
|
||||
- `first_frame` and the handoff system decide continuity.
|
||||
- `ref_1` to `ref_9` decide identity and location reference conditioning.
|
||||
|
||||
@@ -375,60 +375,38 @@ This is the most important group to understand.
|
||||
These settings work together:
|
||||
|
||||
- `shot_seconds`
|
||||
- `per_beat_length`
|
||||
- beat-level `seconds:` or `duration:`
|
||||
- `allow_oversize_shots`
|
||||
- `megapixels`
|
||||
- `vram_headroom_gb`
|
||||
- `allow_res_backoff`
|
||||
|
||||
### The Short Version
|
||||
|
||||
`shot_seconds` is a ceiling, not a promise.
|
||||
`shot_seconds` is the socket-defined maximum.
|
||||
|
||||
A beat-level `seconds:` line is a request, not absolute law.
|
||||
A beat-level `seconds:` line wins for that beat.
|
||||
|
||||
The final beat length is limited by:
|
||||
|
||||
- H3's hard single-shot max
|
||||
- the VRAM budget
|
||||
- the current resolution and pixel budget
|
||||
If a requested length is too much for the hardware, the node keeps it and lets the render fail instead of quietly shrinking it.
|
||||
|
||||
### Exact Decision Order
|
||||
|
||||
For each beat, the node decides length like this:
|
||||
|
||||
1. If the beat has `seconds:` or `duration:`, use that as the requested length.
|
||||
2. Otherwise, if `per_beat_length` is on, estimate the beat length from its content.
|
||||
3. Otherwise, use the global shot ceiling.
|
||||
4. Clamp that result to the global `shot_seconds` ceiling if one is set.
|
||||
5. Clamp again if H3 or VRAM will not allow that size.
|
||||
6. Snap the result to H3's frame grid.
|
||||
2. Otherwise, estimate the beat length from its content.
|
||||
3. Snap the result to H3's frame grid.
|
||||
|
||||
### `shot_seconds`
|
||||
|
||||
Global per-shot ceiling.
|
||||
Global per-shot maximum.
|
||||
|
||||
Important:
|
||||
|
||||
- This does not mean every beat will be exactly this long.
|
||||
- It means no beat is allowed to be longer than this, unless other limits clamp it first.
|
||||
- It means beats are sized against this socket value when the node needs a maximum.
|
||||
|
||||
If left unconnected or set to `0`, the node uses the largest safe shot length it can budget automatically.
|
||||
|
||||
### `per_beat_length`
|
||||
|
||||
When on, the node estimates beat length from the beat content if there is no explicit beat-level `seconds:` line.
|
||||
|
||||
That means:
|
||||
|
||||
- short simple beats become shorter shots
|
||||
- more complex beats become longer shots
|
||||
|
||||
When off:
|
||||
|
||||
- every beat uses the full global ceiling unless that beat has its own explicit `seconds:` line
|
||||
|
||||
### Beat-Level `seconds:` Or `duration:`
|
||||
|
||||
Inside a beat, you can write:
|
||||
@@ -445,29 +423,7 @@ duration: 8
|
||||
|
||||
This has the highest priority for that beat.
|
||||
|
||||
But it can still be clamped down if:
|
||||
|
||||
- it exceeds the global `shot_seconds` ceiling
|
||||
- it exceeds H3's hard max shot length
|
||||
- it exceeds the VRAM budget and `allow_oversize_shots` is off
|
||||
|
||||
### `allow_oversize_shots`
|
||||
|
||||
This only affects forced global `shot_seconds`.
|
||||
|
||||
When off:
|
||||
|
||||
- too-long requests are clamped down to what fits
|
||||
|
||||
When on:
|
||||
|
||||
- the node will try to honor the requested global ceiling even if it exceeds the safe VRAM budget
|
||||
|
||||
Trade-off:
|
||||
|
||||
- slower renders
|
||||
- possible spill to system RAM
|
||||
- possible OOM
|
||||
It is not silently shortened by the node. If you ask for more than your hardware can handle, the render can fail.
|
||||
|
||||
This does not bypass H3's hard max shot size.
|
||||
|
||||
@@ -1287,9 +1243,9 @@ Useful on tighter cards at larger resolutions.
|
||||
|
||||
### `cleanup_between_shots`
|
||||
|
||||
Moves completed shot data off the GPU and clears memory between beats.
|
||||
The node always moves completed shot data off the GPU and clears memory between beats.
|
||||
|
||||
Good default for long chains.
|
||||
This is hard-coded for long-chain stability.
|
||||
|
||||
### `upscale`
|
||||
|
||||
@@ -1313,7 +1269,7 @@ Use them in this order:
|
||||
|
||||
1. Get the render stable.
|
||||
2. If decode fails, use decode tiling.
|
||||
3. If shot length is too short, reduce pixel cost before forcing oversize.
|
||||
3. If shot length is too short, reduce pixel cost before forcing a longer shot.
|
||||
4. Only upscale after the base render is working.
|
||||
|
||||
## Group 10: Overlay Controls
|
||||
@@ -1408,7 +1364,7 @@ Recommended pattern:
|
||||
- one character ref in `ref_1`
|
||||
- `ref_mode = auto ref2v`
|
||||
- no explicit `<Picture 1>` tags needed
|
||||
- `per_beat_length = on`
|
||||
- put `seconds:` on a beat when you want a specific length
|
||||
|
||||
### Two Characters Plus One Location
|
||||
|
||||
@@ -1422,7 +1378,7 @@ Recommended pattern:
|
||||
- use `shot_seconds` as a safe ceiling
|
||||
- put `seconds:` inside beats that need exact control
|
||||
- check `plan_only` first
|
||||
- expect clamping if you ask for more than H3 or VRAM allows
|
||||
- expect an OOM if you ask for more than H3 or your hardware allows
|
||||
|
||||
## Best Way To Check What Will Really Happen
|
||||
|
||||
|
||||
+33
-64
@@ -271,7 +271,7 @@ def split_paragraphs(text, delimiter):
|
||||
# APPEND widget names to this tuple; never insert into the middle. Pure sockets
|
||||
# that carry no widget value can stay grouped in INPUT_TYPES without being listed.
|
||||
ADDED_WIDGETS = (
|
||||
"beat_split", "per_beat_length",
|
||||
"beat_split",
|
||||
"watermark_text", "watermark_position", "watermark_size", "watermark_opacity",
|
||||
"watermark_margin", "intro_text", "intro_position", "intro_seconds",
|
||||
"intro_fade", "intro_size", "overlay_font", "overlay_stroke",
|
||||
@@ -3093,19 +3093,18 @@ def beat_seconds_directive(beat):
|
||||
beat_seconds_directive = lru_cache(maxsize=2048)(beat_seconds_directive)
|
||||
|
||||
|
||||
def plan_beat_frames(beats, fps, budget, per_beat=True):
|
||||
def plan_beat_frames(beats, fps, budget):
|
||||
"""Per-beat shot lengths in frames. Returns (lengths, notes).
|
||||
|
||||
`budget` is the CEILING -- the VRAM budget, or a forced shot_seconds already
|
||||
clamped to it. Per-beat sizing can only ever make a shot shorter than that
|
||||
ceiling, never longer. Priority per beat:
|
||||
`budget` is the socket-defined ceiling used for the beat-shot maximum.
|
||||
Priority per beat:
|
||||
|
||||
1. an explicit 'seconds: N' line in the beat -- always honored, down to
|
||||
H3's real 5-frame minimum, because you stated a duration outright;
|
||||
2. its own content -- action clauses and quoted dialogue (see
|
||||
estimate_beat_seconds), floored at MIN_CONTENT_FRAMES so a shot always
|
||||
has room for one action;
|
||||
3. with per_beat off, the ceiling, exactly as before.
|
||||
3. otherwise its own content estimate.
|
||||
|
||||
Why estimate at all, when action prose has no *reliable* duration? Because the
|
||||
alternative is not "no guess" -- it is "guess the maximum", which is what giving
|
||||
@@ -3115,11 +3114,8 @@ def plan_beat_frames(beats, fps, budget, per_beat=True):
|
||||
shot continues from the handoff frame; leaning long costs a jacket that takes
|
||||
itself off and puts itself back on."""
|
||||
beats = beats if beats else [""]
|
||||
# MIN_SHOT_FRAMES is the floor of the *VRAM budget* -- the shortest shot the node
|
||||
# falls back to when it has to guess with no information at all. It must not raise
|
||||
# a length that came from you or from the beat's own content: `max(floor, ...)`
|
||||
# silently turned every request below ~5.2s into 124f, so 1s/2s/3s/4s all rendered
|
||||
# identically and both the widget and the `seconds:` directive looked broken.
|
||||
# MIN_SHOT_FRAMES is the floor when the node has to guess with no information
|
||||
# at all. A stated or estimated length is not silently rewritten upward.
|
||||
cap = max(5, int(budget))
|
||||
content_floor = align_frame_count(MIN_CONTENT_FRAMES)
|
||||
out, notes = [], []
|
||||
@@ -3128,12 +3124,12 @@ def plan_beat_frames(beats, fps, budget, per_beat=True):
|
||||
want, src, floor = beat_seconds_directive(b), "seconds:", 5
|
||||
snap = align_frame_count # a stated length is never rounded DOWN
|
||||
if want is None:
|
||||
want = estimate_beat_seconds(b) if per_beat else 0.0
|
||||
want = estimate_beat_seconds(b)
|
||||
src, floor, snap = "content", content_floor, align_frame_count_nearest
|
||||
if want <= 0: # no signal -> the ceiling
|
||||
out.append(cap)
|
||||
continue
|
||||
n = min(cap, max(floor, snap(int(round(want * fps)))))
|
||||
n = max(floor, snap(int(round(want * fps))))
|
||||
out.append(n)
|
||||
if n != cap:
|
||||
notes.append(f"shot {i}: {n}f (~{n / fps:.1f}s, from {src})")
|
||||
@@ -3879,23 +3875,23 @@ def estimate_shot_frames(total_gb, resident_gb, headroom_gb, pixels=None, free_g
|
||||
|
||||
|
||||
def resolve_shot_frames(shot_seconds, fps, total_gb, resident_gb, headroom_gb,
|
||||
allow_oversize=False, pixels=None, free_gb=None):
|
||||
pixels=None, free_gb=None):
|
||||
"""Returns (frames, note).
|
||||
|
||||
Auto mode (shot_seconds <= 0): frames = the VRAM budget estimate (resolution-
|
||||
scaled). Forced mode: the requested length is clamped DOWN to the budget
|
||||
unless allow_oversize is set. When VRAM is unknown the request is honored."""
|
||||
scaled). Forced mode: the requested length is honored as requested. When VRAM
|
||||
is unknown the request is also honored."""
|
||||
budget = estimate_shot_frames(total_gb, resident_gb, headroom_gb, pixels, free_gb)
|
||||
if not (shot_seconds and float(shot_seconds) > 0):
|
||||
return budget, ""
|
||||
requested = align_frame_count(min(H3_MAX_FRAMES, max(5, round(float(shot_seconds) * fps))))
|
||||
if total_gb <= 0 or requested <= budget:
|
||||
if total_gb <= 0:
|
||||
return requested, ""
|
||||
if allow_oversize:
|
||||
return requested, (f"OVERSIZE: {requested}f requested vs {budget}f budget -- honoring it; "
|
||||
f"may spill to system RAM (slow) or OOM")
|
||||
return budget, (f"requested {requested}f (~{requested/max(1,fps):.1f}s) exceeds the ~{budget}f VRAM "
|
||||
f"budget -- clamped to {budget}f (~{budget/max(1,fps):.1f}s). Set allow_oversize_shots to override")
|
||||
if requested > budget:
|
||||
return requested, (f"requested {requested}f (~{requested/max(1,fps):.1f}s) exceeds the "
|
||||
f"~{budget}f VRAM budget -- honoring it; may spill to system RAM "
|
||||
f"(slow) or OOM")
|
||||
return requested, ""
|
||||
|
||||
|
||||
def _is_oom(e):
|
||||
@@ -6047,19 +6043,14 @@ class H3LongVideos:
|
||||
"mid-word open-mouth pose. Trims the matching audio tail too. 0 = last frame."}),
|
||||
"shot_seconds": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 15.1, "step": 0.5,
|
||||
"forceInput": True,
|
||||
"tooltip": "GLOBAL per-shot ceiling in seconds, not 'force every beat to exactly "
|
||||
"this length'. H3 Shot Length is the intended source because it snaps to "
|
||||
"H3's frame grid and reports the real duration.\n\n"
|
||||
"tooltip": "GLOBAL per-shot maximum in seconds. H3 Shot Length is the intended "
|
||||
"source because it snaps to H3's frame grid and reports the real duration.\n\n"
|
||||
"Leave UNCONNECTED for auto: the node picks the largest shot that fits "
|
||||
"the current size/VRAM budget. Connect a value to cap every beat at that "
|
||||
"length. A beat's own `seconds:` directive can still ask for less, and any "
|
||||
"request above H3's hard ~15.1s single-shot limit is clamped. Total video "
|
||||
"length is the sum of the beat shots, not simply beat-count x this value."}),
|
||||
"allow_oversize_shots": ("BOOLEAN", {"default": False,
|
||||
"tooltip": "OFF (default): a forced shot_seconds that won't fit VRAM is clamped DOWN to "
|
||||
"what fits, and the clamp is reported in info. ON: honor the requested length "
|
||||
"even if it exceeds the budget -- the render may spill into system RAM (slow) "
|
||||
"or OOM. Only affects forced shot_seconds, not auto."}),
|
||||
"length. A beat's own `seconds:` directive can still ask for less. If the "
|
||||
"requested duration exceeds your hardware, the node will keep it and let the "
|
||||
"render fail instead of shrinking it. Total video length is the sum of the "
|
||||
"beat shots, not simply beat-count x this value."}),
|
||||
"vram_headroom_gb": ("FLOAT", {"default": 1.5, "min": 0.0, "max": 32.0, "step": 0.5,
|
||||
"tooltip": "Safety margin RESERVED from free VRAM before the node budgets shot length. "
|
||||
"Higher = shorter safer shots. Lower = longer shots but more risk of spill or "
|
||||
@@ -6093,11 +6084,6 @@ class H3LongVideos:
|
||||
"decode_tile_size": ("INT", {"default": 0, "min": 0, "max": 1024, "step": 32,
|
||||
"tooltip": "Spatial tile size for the VAE decode (tile_x/tile_y). 0 = ComfyUI default. "
|
||||
"Try 256 on a tight card at 1344x768."}),
|
||||
"cleanup_between_shots": ("BOOLEAN", {"default": True,
|
||||
"tooltip": "Between beats, move each shot's decoded video+audio to system RAM and run "
|
||||
"a full VRAM+RAM purge (GC + CUDA cache), so a long chain doesn't accumulate "
|
||||
"on the GPU and OOM. Recommended on 16GB. Turn off only on a big card where "
|
||||
"you want to skip the per-shot cleanup cost."}),
|
||||
"upscale": (["off", "rtx", "model", "lanczos"], {"default": "off",
|
||||
"tooltip": "Optional post-pass on the finished frames. 'rtx' = NVIDIA RTX Video Super "
|
||||
"Resolution (Tensor Cores -- fastest and best for video; needs the "
|
||||
@@ -6195,20 +6181,6 @@ class H3LongVideos:
|
||||
"When this is filled in, EVERY paragraph of the prompt box is a beat/shot -- "
|
||||
"nothing is consumed as the identity anchor. Put the permanent identity here "
|
||||
"(hair, face, build, age) and the clothing in character_memory."}),
|
||||
"per_beat_length": ("BOOLEAN", {"default": True,
|
||||
"tooltip": "PACING. Size each shot from what its beat actually stages, instead of giving "
|
||||
"every shot the same length. ON (default): a beat's time is ~2s of setup plus "
|
||||
"~2.5s per action clause, or its spoken line, whichever is longer -- so 'she "
|
||||
"takes off her jacket and drops it on the bench' gets ~7s and a three-part "
|
||||
"beat gets more. OFF: every shot gets the full ceiling. WHY IT MATTERS: a 3s "
|
||||
"action in a 12s shot leaves 9 seconds the model was told nothing about, and "
|
||||
"it fills them by repeating or REVERSING the action -- which is why clothing "
|
||||
"comes off and goes back on. The estimate leans SHORT on purpose: an "
|
||||
"unfinished action is continued by the next shot from the handoff frame, "
|
||||
"while an overlong one is unrecoverable. Never exceeds the ceiling "
|
||||
"(shot_seconds or the VRAM budget) and always lands on the 17n+5 grid. "
|
||||
"Override any single beat with 'seconds: 8' on its own line inside that "
|
||||
"paragraph -- that wins over everything, including this toggle."}),
|
||||
"auto_soundscape": (["off", "fill if blank", "always"], {"default": "fill if blank",
|
||||
"tooltip": "Build the ambient bed from the scene instead of typing one. Reads the "
|
||||
"ANCHOR (the soundscape is global, so it must describe the PLACE, not "
|
||||
@@ -6509,9 +6481,8 @@ class H3LongVideos:
|
||||
shift_video=12.0, shift_audio=3.0, trim_seam=True, vary_seed_per_shot=False,
|
||||
handoff_offset=0, vram_headroom_gb=1.5, allow_res_backoff=True,
|
||||
decode_tile_frames=0, decode_tile_size=0,
|
||||
cleanup_between_shots=True,
|
||||
anchor_override="", shot_seconds=0.0, allow_oversize_shots=False,
|
||||
per_beat_length=True, beat_split="auto",
|
||||
anchor_override="", shot_seconds=0.0,
|
||||
beat_split="auto",
|
||||
character_memory="", auto_wardrobe=True, auto_props=True, prevent_nudity=True,
|
||||
exposed_terms="", anatomy_guard="auto", lock_restraints=True,
|
||||
solidity_guard="auto", motion_guard="auto", contact_guard="auto",
|
||||
@@ -6661,7 +6632,7 @@ class H3LongVideos:
|
||||
lora_gb = lora_overhead_gb(model)
|
||||
eff_headroom = vram_headroom_gb + lora_gb
|
||||
ln, ln_note = resolve_shot_frames(shot_seconds, fps, total_gb, resident_gb,
|
||||
eff_headroom, allow_oversize_shots, w * h, free_gb)
|
||||
eff_headroom, w * h, free_gb)
|
||||
if lora_gb:
|
||||
ln_note = ((ln_note + " ") if ln_note else "") + (
|
||||
f"reserved ~{lora_gb:.1f}GB for bypass-LoRA adapters (they stay resident in bf16 "
|
||||
@@ -6733,14 +6704,13 @@ class H3LongVideos:
|
||||
# this". Forcing a length used to DISABLE per-beat sizing entirely, which is
|
||||
# why a plan made with a forced length disagreed with the auto render: two
|
||||
# different code paths for the same question.
|
||||
lens, len_notes = plan_beat_frames(beats, fps, ln, per_beat=bool(per_beat_length))
|
||||
lens, len_notes = plan_beat_frames(beats, fps, ln)
|
||||
secs = [n / fps for n in lens]
|
||||
if len_notes:
|
||||
n_short = sum(1 for n in lens if n < ln)
|
||||
ln_note = ((ln_note + " ") if ln_note else "") + (
|
||||
f"per-beat pacing sized {n_short} of {len(lens)} shot(s) under the {ln}f "
|
||||
f"(~{ln / fps:.1f}s) ceiling from their own content: " + "; ".join(len_notes)
|
||||
+ ". Turn per_beat_length OFF to give every shot the full ceiling")
|
||||
f"content pacing sized {n_short} of {len(lens)} shot(s) under the {ln}f "
|
||||
f"(~{ln / fps:.1f}s) ceiling from their own content: " + "; ".join(len_notes))
|
||||
# With pacing OFF, every beat gets the ceiling whether it has anything to fill
|
||||
# it with or not -- so say which beats are too thin for the length they got.
|
||||
# This is the failure that reads as an action repeating or playing backwards.
|
||||
@@ -6749,16 +6719,14 @@ class H3LongVideos:
|
||||
ln_note = ((ln_note + " ") if ln_note else "") + (
|
||||
"THIN BEATS -- the model must invent the remaining time, which it fills by "
|
||||
"repeating or REVERSING the action: " + "; ".join(pace_warnings)
|
||||
+ ". Add a second clause to the beat, set 'seconds:' on it, or turn "
|
||||
"per_beat_length ON to size shots from their content")
|
||||
+ ". Add a second clause to the beat, or set 'seconds:' on it")
|
||||
fit_warnings = dialogue_fit_warnings(beats, secs)
|
||||
# The opposite error, and the one that babbles: far more shot than line.
|
||||
filler_warnings = dialogue_filler_warnings(beats, secs)
|
||||
if filler_warnings:
|
||||
ln_note = ((ln_note + " ") if ln_note else "") + (
|
||||
"BABBLE RISK -- " + "; ".join(filler_warnings)
|
||||
+ ". Turn per_beat_length ON to size these shots from their line, or set "
|
||||
"'seconds:' on the beat")
|
||||
+ ". Set 'seconds:' on the beat if you want a fixed longer take")
|
||||
wardrobe_notes = []
|
||||
strip_shots = [] # shots that newly bared a zone -> the NEXT shot starts fresh
|
||||
raw_gens = distribute_generations(anchor, beats, global_soundscape.strip(),
|
||||
@@ -6947,6 +6915,7 @@ class H3LongVideos:
|
||||
continuity_used = []
|
||||
ref_aug_used = []
|
||||
shot_timings = []
|
||||
cleanup_between_shots = True
|
||||
if cleanup_between_shots:
|
||||
_deep_cleanup() # start the first (heaviest) shot with max free VRAM
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ const GROUPS = [
|
||||
id: "prompt",
|
||||
label: "Prompt",
|
||||
defaultCollapsed: false,
|
||||
widgets: ["prompt", "resolution", "megapixels", "beat_split", "anchor_override", "per_beat_length", "shot_seconds", "plan_only", "fps"],
|
||||
widgets: ["prompt", "resolution", "megapixels", "beat_split", "anchor_override", "shot_seconds", "plan_only", "fps"],
|
||||
},
|
||||
{
|
||||
id: "refs",
|
||||
@@ -27,8 +27,8 @@ const GROUPS = [
|
||||
widgets: [
|
||||
"steps", "cfg", "sampler_name", "scheduler", "seed",
|
||||
"apply_model_sampling", "shift_video", "shift_audio",
|
||||
"allow_oversize_shots", "vram_headroom_gb", "allow_res_backoff",
|
||||
"decode_tile_frames", "decode_tile_size", "cleanup_between_shots",
|
||||
"vram_headroom_gb", "allow_res_backoff",
|
||||
"decode_tile_frames", "decode_tile_size",
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -734,14 +734,27 @@ class DumasH3LongVideosHelperTests(unittest.TestCase):
|
||||
|
||||
for index in range(1, 10):
|
||||
self.assertNotIn(f"ref_image_{index}", optional)
|
||||
self.assertNotIn("per_beat_length", optional)
|
||||
self.assertNotIn("cleanup_between_shots", optional)
|
||||
|
||||
def test_shot_seconds_tooltip_describes_ceiling_behavior(self):
|
||||
optional = self.module.H3LongVideos.INPUT_TYPES()["optional"]
|
||||
tooltip = optional["shot_seconds"][1]["tooltip"]
|
||||
|
||||
self.assertIn("GLOBAL per-shot ceiling", tooltip)
|
||||
self.assertIn("not 'force every beat to exactly this length'", tooltip)
|
||||
self.assertIn("hard ~15.1s single-shot limit", tooltip)
|
||||
self.assertIn("GLOBAL per-shot maximum", tooltip)
|
||||
self.assertIn("A beat's own `seconds:` directive can still ask for less", tooltip)
|
||||
self.assertIn("honoring it; may spill to system RAM (slow) or OOM", tooltip)
|
||||
|
||||
def test_resolve_shot_frames_honors_forced_request_over_budget(self):
|
||||
original_estimate_shot_frames = self.module.estimate_shot_frames
|
||||
try:
|
||||
self.module.estimate_shot_frames = lambda *_args, **_kwargs: 73
|
||||
frames, note = self.module.resolve_shot_frames(10.0, 24, 16.0, 8.0, 1.5)
|
||||
|
||||
self.assertEqual(frames, 243)
|
||||
self.assertIn("honoring it", note)
|
||||
finally:
|
||||
self.module.estimate_shot_frames = original_estimate_shot_frames
|
||||
|
||||
def test_run_defaults_match_declared_ref_widget_defaults(self):
|
||||
node = self.module.H3LongVideos()
|
||||
@@ -1152,7 +1165,7 @@ class DumasH3LongVideosHelperTests(unittest.TestCase):
|
||||
module.quant_accel_note = lambda *_args, **_kwargs: ""
|
||||
module.lora_active = lambda _model: False
|
||||
module.resolve_shot_frames = lambda *_args, **_kwargs: (73, "")
|
||||
module.plan_beat_frames = lambda beats, fps, budget, per_beat=True: ([73] * len(beats), [])
|
||||
module.plan_beat_frames = lambda beats, fps, budget: ([73] * len(beats), [])
|
||||
module.dialogue_fit_warnings = lambda *_args, **_kwargs: []
|
||||
module.dialogue_filler_warnings = lambda *_args, **_kwargs: []
|
||||
module.distribute_generations = lambda anchor, beats, *_args, **_kwargs: [
|
||||
|
||||
Reference in New Issue
Block a user