refactor(video): carry output resolution on the video result

Both runners round the requested resolution and the two-stage LTX path derives
its own, so the request never recorded what came out. Read it off the decoded
frames instead.
This commit is contained in:
CalamitousFelicitousness
2026-08-19 01:39:45 +01:00
parent 7dc10e0b5f
commit 5837dcdd63
2 changed files with 26 additions and 2 deletions
+6 -2
View File
@@ -29,6 +29,8 @@ class VideoResult:
has_audio: bool
still: bool
processed: processing.Processed
width: int = 0 # what was generated, which is not what was requested whenever a runner rounds or a model picks
height: int = 0
def resolve_model(engine: str | None, model: str | None) -> tuple[models_def.Model, bool]:
@@ -302,7 +304,8 @@ def run(selected: models_def.Model, *,
if getattr(p, 'video_still', False):
stills = processed.images[:1] # already trimmed in process_decode; defensive
return VideoResult(images=stills, video_path=None, thumb_path=None, num_frames=len(stills), fps=0.0, has_audio=False, still=True, processed=processed)
still_w, still_h = video_utils.pixel_size(stills, fallback=(p.width, p.height))
return VideoResult(images=stills, video_path=None, thumb_path=None, num_frames=len(stills), fps=0.0, has_audio=False, still=True, processed=processed, width=still_w, height=still_h)
if hasattr(processed, 'images') and processed.images is not None:
pixels = video_save.images_to_tensor(processed.images)
@@ -341,8 +344,9 @@ def run(selected: models_def.Model, *,
mp4_interpolate=mp4_interpolate,
metadata={},
)
out_w, out_h = video_utils.pixel_size(processed.images, fallback=(p.width, p.height))
del pixels
return VideoResult(images=processed.images, video_path=video_file, thumb_path=thumb_file, num_frames=num_frames, fps=float(save_fps), has_audio=waveform is not None, still=False, processed=processed)
return VideoResult(images=processed.images, video_path=video_file, thumb_path=thumb_file, num_frames=num_frames, fps=float(save_fps), has_audio=waveform is not None, still=False, processed=processed, width=out_w, height=out_h)
def generate(task_id, ui_state,
+20
View File
@@ -73,6 +73,26 @@ def has_torchaudio():
return False
def pixel_size(pixels, fallback: tuple[int, int] = (0, 0)) -> tuple[int, int]:
"""Width and height of decoded frames, whether they arrive as PIL images or as a tensor.
The runners request a resolution and the model answers with another one often enough that
the request is not a usable substitute, so read it off the pixels and keep the fallback for
the case where nothing was decoded at all.
"""
if isinstance(pixels, list):
return pixels[0].size if len(pixels) > 0 and hasattr(pixels[0], 'size') else fallback
ndim = getattr(pixels, 'ndim', None)
if ndim == 5: # NCTHW
return int(pixels.shape[-1]), int(pixels.shape[-2])
if ndim == 4: # NHWC
return int(pixels.shape[2]), int(pixels.shape[1])
shape = getattr(pixels, 'shape', None)
if shape is not None and len(shape) >= 2:
return int(shape[-1]), int(shape[-2])
return fallback
def classify_extension(fn: str):
"""Media kind of a filename, None when the extension is not one sdnext reads."""
lower = str(fn).lower()