110 lines
3.3 KiB
Python
110 lines
3.3 KiB
Python
# H3-LongVideos -- https://github.com/Smite79/MiniMax-H3-LongVideos
|
|
# Copyright (c) 2026 Smite79. All rights reserved.
|
|
# Redistribution, in whole or in part, requires written permission.
|
|
# This notice may not be removed or altered. See LICENSE.
|
|
"""Per-shot records passed from prompt planning to rendering."""
|
|
|
|
from dataclasses import dataclass, field
|
|
|
|
|
|
@dataclass
|
|
class Shot:
|
|
prompt: str
|
|
cast: list[str]
|
|
speech: bool
|
|
sounded: bool
|
|
voiced_only: bool
|
|
events: list[str]
|
|
frame_count: int = 0
|
|
refs: list[object] = field(default_factory=list)
|
|
line_seconds: float = 0.0 # the planner's estimate of the spoken line, words / WORDS_PER_SEC
|
|
|
|
|
|
@dataclass
|
|
class ShotPlan:
|
|
shots: list[Shot] = field(default_factory=list)
|
|
|
|
@property
|
|
def prompts(self):
|
|
return [shot.prompt for shot in self.shots]
|
|
|
|
def add(self, prompt, cast, speech, sounded, voiced_only, events):
|
|
shot = Shot(prompt, list(cast or ()), bool(speech), bool(sounded),
|
|
bool(voiced_only), list(events or ()))
|
|
self.shots.append(shot)
|
|
|
|
def set_frame_counts(self, counts):
|
|
counts = [int(n) for n in counts]
|
|
if len(counts) != len(self.shots) or any(n <= 0 for n in counts):
|
|
raise ValueError("frame counts must be positive and match the planned shots")
|
|
for shot, count in zip(self.shots, counts):
|
|
shot.frame_count = count
|
|
|
|
def validate(self):
|
|
if any(shot.frame_count <= 0 for shot in self.shots):
|
|
raise ValueError("each shot needs a positive frame count before rendering")
|
|
return self
|
|
|
|
def __len__(self):
|
|
return len(self.shots)
|
|
|
|
|
|
@dataclass
|
|
class PreparedVideo:
|
|
"""Resolved inputs consumed by the render stage; model/tensor handles are shared."""
|
|
_placed_shots: object
|
|
_first_is_plate: object
|
|
_returns: object
|
|
_soft_landing: object
|
|
_tagged_names: object
|
|
ambient_audio: object
|
|
ambient_level: float
|
|
apply_model_sampling: bool
|
|
audio_vae: object
|
|
auto_sound: bool
|
|
bared_shots: object
|
|
cfg: float
|
|
cleanup_between_shots: bool
|
|
clip: object
|
|
first_frame: object
|
|
foley_level: float
|
|
h: int
|
|
latent_upscale: str
|
|
latent_upscale_scale: float
|
|
megapixels: float
|
|
model: object
|
|
moved_shots: object
|
|
negative: object
|
|
notes: list[str]
|
|
plan: ShotPlan
|
|
ref_noise_aug: float | None
|
|
restart_after_removal: bool
|
|
revealed_shots: object
|
|
sampler_name: str
|
|
scheduler: str
|
|
seed: int
|
|
shift_audio: float
|
|
shift_video: float
|
|
sigmas: object
|
|
silence_nonspeech: bool
|
|
speech_lead_seconds: float
|
|
speech_tail_seconds: float
|
|
hold_levels: float
|
|
handoff_frames: int
|
|
staging_shots: object
|
|
steps: int
|
|
stripped_shots: object
|
|
cut_shots: object
|
|
tiled_decode: bool
|
|
trim_seam: bool
|
|
upscale: str
|
|
upscale_batch: int
|
|
upscale_model: str
|
|
upscale_target_short_edge: int
|
|
vae: object
|
|
w: int
|
|
shot_rooms: object = None # {0-based shot: (room it opens in, room it ends in)}
|
|
hardware_changed: object = None # 1-based shots that put hardware on or take it off
|
|
shot_frames: object = None # {0-based shot: (who its frames show, who is still there at its end)}
|
|
reentry_shots: object = None # {0-based shot: who walks in while the keyframe still has them}
|