Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions tests/tools/test_remotion_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,122 @@ def fake_run_command(cmd, *a, **k):

assert not any(str(c).startswith("--timeout") for c in seen["cmd"])
assert seen["timeout"] == 600


def test_remotion_browser_executable_is_passed_through(tool, tmp_path, monkeypatch):
seen = {}
browser = tmp_path / "custom_chrome.exe"
browser.write_text("dummy binary")

def fake_run_command(cmd, *a, **k):
seen["cmd"] = cmd
return None

monkeypatch.setattr(tool, "run_command", fake_run_command)
tool._remotion_render(
{
"composition_data": {"cuts": []},
"output_path": str(tmp_path / "out.mp4"),
"remotion_browser_executable": str(browser),
}
)

assert f"--browser-executable={browser.resolve()}" in seen["cmd"]


def test_remotion_browser_executable_fails_on_missing_file(tool, tmp_path):
missing = tmp_path / "nonexistent_chrome.exe"
result = tool._remotion_render(
{
"composition_data": {"cuts": []},
"output_path": str(tmp_path / "out.mp4"),
"remotion_browser_executable": str(missing),
}
)

assert result.success is False
assert "remotion_browser_executable does not exist or is not a file" in result.error


def test_high_level_render_forwards_browser_executable(tool, tmp_path, monkeypatch):
captured = {}
monkeypatch.setattr(tool, "_pre_compose_validation", lambda *a, **k: None)
monkeypatch.setattr(tool, "_needs_remotion", lambda *a, **k: True)

def fake_remotion_render(inputs):
captured.update(inputs)
from tools.base_tool import ToolResult

return ToolResult(success=True, data={}, artifacts=[])

monkeypatch.setattr(tool, "_remotion_render", fake_remotion_render)
monkeypatch.setattr(tool, "_run_final_review", lambda *a, **k: {})

tool._render(
{
"edit_decisions": {
"render_runtime": "remotion",
"renderer_family": "explainer-data",
"cuts": [{"id": "c1", "source": "a1", "in_seconds": 0, "out_seconds": 2}],
},
"asset_manifest": {"assets": [{"id": "a1", "path": "/tmp/a1.mp4"}]},
"output_path": str(tmp_path / "out.mp4"),
"remotion_browser_executable": "/custom/browser",
}
)

assert captured.get("remotion_browser_executable") == "/custom/browser"


def test_no_browser_executable_flag_when_not_requested(tool, tmp_path, monkeypatch):
seen = {}

def fake_run_command(cmd, *a, **k):
seen["cmd"] = cmd
return None

monkeypatch.setattr(tool, "run_command", fake_run_command)
tool._remotion_render(
{"composition_data": {"cuts": []}, "output_path": str(tmp_path / "out.mp4")}
)

assert not any(str(c).startswith("--browser-executable") for c in seen["cmd"])


def test_atelier_render_appends_browser_executable(tool, tmp_path, monkeypatch):
seen = {}
browser = tmp_path / "browser_atelier.exe"
browser.write_text("dummy binary")

entry = tmp_path / "entry.tsx"
entry.write_text("// dummy")

def fake_run_command(cmd, *a, **k):
seen["cmd"] = cmd
return None

orig_exists = Path.exists

def fake_exists(self):
if "remotion-composer" in str(self):
return True
return orig_exists(self)

monkeypatch.setattr(tool, "run_command", fake_run_command)
monkeypatch.setattr(Path, "exists", fake_exists)

tool._render_via_atelier(
inputs={
"output_path": str(tmp_path / "out.mp4"),
"remotion_browser_executable": str(browser),
},
edit_decisions={
"bespoke": {
"entry": str(entry),
"composition_id": "CompId",
}
},
)

assert f"--browser-executable={browser.resolve()}" in seen["cmd"]

37 changes: 37 additions & 0 deletions tools/video/video_compose.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,15 @@ class VideoCompose(BaseTool):
"networks). The subprocess timeout is widened to match."
),
},
"remotion_browser_executable": {
"type": "string",
"description": (
"Optional absolute path to a browser executable for Remotion "
"renders, passed through as `--browser-executable=<path>`. "
"Use when running on machines without bundled Chromium or "
"when a pinned enterprise browser binary is required."
),
},
},
}

Expand Down Expand Up @@ -930,6 +939,23 @@ def visit(node: Any, parent_key: str | None = None) -> Any:
visit(value)
return len(staged_by_source)

@staticmethod
def _append_remotion_browser_executable(
cmd: list[str], inputs: dict[str, Any]
) -> str | None:
"""Validate and append a job-scoped Remotion browser executable."""
requested = inputs.get("remotion_browser_executable")
if requested is None:
return None
browser = Path(str(requested)).expanduser().resolve()
if not browser.is_file():
raise ValueError(
"remotion_browser_executable does not exist or is not a file: "
f"{browser}"
)
cmd.append(f"--browser-executable={browser}")
return str(browser)

def _render_via_atelier(
self,
inputs: dict[str, Any],
Expand Down Expand Up @@ -1021,6 +1047,10 @@ def _render_via_atelier(
output_path.parent.mkdir(parents=True, exist_ok=True)

cmd = ["npx", "remotion", "render", str(effective_entry), str(comp_id), str(output_path)]
try:
self._append_remotion_browser_executable(cmd, inputs)
except ValueError as e:
return ToolResult(success=False, error=str(e))

props_path = bespoke.get("props_path")
if props_path:
Expand Down Expand Up @@ -1668,6 +1698,8 @@ def _render(self, inputs: dict[str, Any]) -> ToolResult:
# would only take effect on a direct _remotion_render() call.
if inputs.get("remotion_timeout_ms") is not None:
remotion_inputs["remotion_timeout_ms"] = inputs["remotion_timeout_ms"]
if inputs.get("remotion_browser_executable") is not None:
remotion_inputs["remotion_browser_executable"] = inputs["remotion_browser_executable"]
if inputs.get("public_dir") is not None:
remotion_inputs["public_dir"] = inputs["public_dir"]
render_result = self._remotion_render(remotion_inputs)
Expand Down Expand Up @@ -2062,6 +2094,11 @@ def _remotion_render(self, inputs: dict[str, Any]) -> ToolResult:
if public_dir is not None:
cmd.append(f"--public-dir={public_dir}")

try:
self._append_remotion_browser_executable(cmd, inputs)
except ValueError as e:
return ToolResult(success=False, error=str(e))

# Apply media profile dimensions
if profile_name:
try:
Expand Down
Loading