diff --git a/comfy_api/latest/_input_impl/video_types.py b/comfy_api/latest/_input_impl/video_types.py index 5f99e61fb..90dcc1a98 100644 --- a/comfy_api/latest/_input_impl/video_types.py +++ b/comfy_api/latest/_input_impl/video_types.py @@ -606,8 +606,13 @@ class VideoFromFile(VideoInput): duration_cap = math.ceil(duration * sample_rate) import comfy.utils - raw_duration = self._get_raw_duration() - window_seconds = duration if duration else max(raw_duration - start_time, 0.0) + if duration: + window_seconds = duration + else: + try: + window_seconds = max(self._get_raw_duration() - start_time, 0.0) + except ValueError: + window_seconds = 0.0 progress_total = max(1, int(round(window_seconds * float(rate)))) pbar = comfy.utils.ProgressBar(progress_total) @@ -707,6 +712,12 @@ class VideoFromFile(VideoInput): crop_rect = normalize_crop_rect(*self.__crop, out_width, out_height) if crop_rect is not None: out_width, out_height = crop_rect[2], crop_rect[3] + if (out_width % 2 or out_height % 2) and crop_rect is None: + even_width = out_width - out_width % 2 + even_height = out_height - out_height % 2 + if even_width > 0 and even_height > 0: + crop_rect = (0, 0, even_width, even_height) + out_width, out_height = even_width, even_height if out_width % 2 or out_height % 2: raise ValueError(f"H.264 output requires even dimensions, got {out_width}x{out_height}") source_size = (frame.width, frame.height) @@ -873,7 +884,7 @@ class VideoFromFile(VideoInput): duration=duration, crop=self.__crop, ) - if trimmed.get_duration() < duration and strict_duration: + if strict_duration and duration and trimmed.get_duration() < duration: return None return trimmed diff --git a/comfy_extras/nodes_video.py b/comfy_extras/nodes_video.py index a20b29999..2c4074d32 100644 --- a/comfy_extras/nodes_video.py +++ b/comfy_extras/nodes_video.py @@ -250,7 +250,7 @@ class LoadVideo(io.ComfyNode): video = apply_video_trim(source, (edit or {}).get("trim")) video = apply_video_crop(video, (edit or {}).get("crop")) if video is source: - return io.NodeOutput(video, ui=preview_input_video(file)) + return io.NodeOutput(video, ui=preview_input_video(file, source)) return io.NodeOutput(video, ui=save_video_preview(video)) @classmethod @@ -268,25 +268,41 @@ class LoadVideo(io.ComfyNode): return True -def preview_input_video(file: str) -> ui.PreviewVideo: +def preview_input_video(file: str, video: Input.Video | None = None) -> ui.PreviewVideo: name, _ = folder_paths.annotated_filepath(file) subfolder, _, filename = name.replace("\\", "/").rpartition("/") - return ui.PreviewVideo([ui.SavedResult(filename, subfolder, io.FolderType.input)]) + result = ui.SavedResult(filename, subfolder, io.FolderType.input) + if video is not None: + try: + video._preview_result = (folder_paths.get_annotated_filepath(file), result) + except AttributeError: + pass + return ui.PreviewVideo([result]) def save_video_preview(video: Input.Video) -> ui.PreviewVideo: + cached = getattr(video, "_preview_result", None) + if cached is not None and os.path.isfile(cached[0]): + return ui.PreviewVideo([cached[1]]) + width, height = video.get_dimensions() full_output_folder, filename, counter, subfolder, _ = folder_paths.get_save_image_path( "ComfyUI_temp_video", folder_paths.get_temp_directory(), width, height ) preview_format = Types.VideoContainer.MP4 file = f"{filename}_{counter:05}_.{Types.VideoContainer.get_extension(preview_format)}" + full_path = os.path.join(full_output_folder, file) video.save_to( - os.path.join(full_output_folder, file), + full_path, format=preview_format, codec="auto", ) - return ui.PreviewVideo([ui.SavedResult(file, subfolder, io.FolderType.temp)]) + result = ui.SavedResult(file, subfolder, io.FolderType.temp) + try: + video._preview_result = (full_path, result) + except AttributeError: + pass + return ui.PreviewVideo([result]) def apply_video_trim(video: Input.Video, trim, strict_duration: bool = False) -> Input.Video: