From 2881e6161081439b1c3fb3b6c1f51b3d272da710 Mon Sep 17 00:00:00 2001 From: comfyanonymous <121283862+comfyanonymous@users.noreply.github.com> Date: Sat, 1 Aug 2026 00:21:28 -0700 Subject: [PATCH] Store mp4 metadata at the beginning of the file when possible. (#15195) --- comfy_api/latest/_input_impl/video_types.py | 10 +++++++--- tests-unit/comfy_api_test/input_impl_test.py | 5 +++-- tests-unit/comfy_api_test/video_types_test.py | 12 ++++++++++++ 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/comfy_api/latest/_input_impl/video_types.py b/comfy_api/latest/_input_impl/video_types.py index 14d663881..cf4119250 100644 --- a/comfy_api/latest/_input_impl/video_types.py +++ b/comfy_api/latest/_input_impl/video_types.py @@ -36,13 +36,15 @@ def get_open_write_kwargs( dest: str | io.BytesIO, container_format: str, to_format: str | None ) -> dict: """Get kwargs for writing a `VideoFromFile` to a file/stream with `av.open`""" + is_write_to_buffer = isinstance(dest, io.BytesIO) + is_mp4_file = not is_write_to_buffer and os.path.splitext(dest)[1].lower() == ".mp4" + movflags = "use_metadata_tags+faststart" if is_mp4_file else "use_metadata_tags" open_kwargs = { "mode": "w", # If isobmff, preserve custom metadata tags (workflow, prompt, extra_pnginfo) - "options": {"movflags": "use_metadata_tags"}, + "options": {"movflags": movflags}, } - is_write_to_buffer = isinstance(dest, io.BytesIO) if is_write_to_buffer: # Set output format explicitly, since it cannot be inferred from file extension if to_format == VideoContainer.AUTO: @@ -103,7 +105,9 @@ def mp4_output_open_kwargs(path: str | io.BytesIO, format: VideoContainer, codec raise ValueError("Only MP4 format is supported for now") if codec != VideoCodec.AUTO and codec != VideoCodec.H264: raise ValueError("Only H264 codec is supported for now") - open_kwargs = {"mode": "w", "options": {"movflags": "use_metadata_tags"}} + # FFmpeg's faststart pass reopens the output by filename, so it cannot be used with file-like objects. + movflags = "use_metadata_tags+faststart" if isinstance(path, (str, os.PathLike)) else "use_metadata_tags" + open_kwargs = {"mode": "w", "options": {"movflags": movflags}} if isinstance(format, VideoContainer) and format != VideoContainer.AUTO: open_kwargs["format"] = format.value elif isinstance(path, io.BytesIO): diff --git a/tests-unit/comfy_api_test/input_impl_test.py b/tests-unit/comfy_api_test/input_impl_test.py index 5fc21a9a7..f1924f163 100644 --- a/tests-unit/comfy_api_test/input_impl_test.py +++ b/tests-unit/comfy_api_test/input_impl_test.py @@ -36,6 +36,7 @@ def test_get_open_write_kwargs_filepath_no_format(): kwargs_specific = get_open_write_kwargs("output.avi", "mp4", "avi") fail_msg = "Format should not be set for file paths (Specific)" assert "format" not in kwargs_specific, fail_msg + assert kwargs_specific["options"]["movflags"] == "use_metadata_tags" def test_get_open_write_kwargs_base_options_mode(): @@ -43,9 +44,9 @@ def test_get_open_write_kwargs_base_options_mode(): kwargs = get_open_write_kwargs("output.mp4", "mp4", VideoContainer.AUTO) assert kwargs["mode"] == "w", "mode should be set to write" - fail_msg = "movflags should be set to preserve custom metadata tags" + fail_msg = "movflags should preserve custom metadata tags and enable faststart for MP4 files" assert "movflags" in kwargs["options"], fail_msg - assert kwargs["options"]["movflags"] == "use_metadata_tags", fail_msg + assert kwargs["options"]["movflags"] == "use_metadata_tags+faststart", fail_msg def test_get_open_write_kwargs_bytesio_auto_format(): diff --git a/tests-unit/comfy_api_test/video_types_test.py b/tests-unit/comfy_api_test/video_types_test.py index dd95dc843..f688d3eca 100644 --- a/tests-unit/comfy_api_test/video_types_test.py +++ b/tests-unit/comfy_api_test/video_types_test.py @@ -258,6 +258,18 @@ def test_save_to_h264_crf_controls_quality(tmp_path): assert os.path.getsize(transcoded) < os.path.getsize(high_quality) +def test_save_to_mp4_writes_metadata_before_media(video_components, tmp_path): + encoded = tmp_path / "encoded.mp4" + remuxed = tmp_path / "remuxed.mp4" + + VideoFromComponents(video_components).save_to(str(encoded), metadata={"prompt": {"test": "value"}}) + VideoFromFile(str(encoded)).save_to(str(remuxed), metadata={"prompt": {"test": "value"}}) + + for path in (encoded, remuxed): + data = path.read_bytes() + assert data.index(b"moov") < data.index(b"mdat") + + def create_transcode_source( width=64, height=64, frames=30, fps=30, audio_streams=1, undecodable_audio=0, rotation=False, container_format="mov", audio_codec="pcm_s16le",