Skip to content

Add record start/stop CLI command for session video capture#4710

Merged
sauravpanda merged 3 commits into
mainfrom
feat/cli-record-start-stop
Apr 20, 2026
Merged

Add record start/stop CLI command for session video capture#4710
sauravpanda merged 3 commits into
mainfrom
feat/cli-record-start-stop

Conversation

@sauravpanda
Copy link
Copy Markdown
Collaborator

@sauravpanda sauravpanda commented Apr 20, 2026

Closes #4533.

Summary

Adds browser-use record start <path> / record stop / record status to capture the current session as an MP4 via CDP screencasting — all the underlying machinery (Page.startScreencast, VideoRecorderService) already existed in the repo; this just exposes it on the CLI.

  • RecordingWatchdog gains a public start_recording(path, size?, framerate?) / stop_recording() -> Path / is_recording API. The existing BrowserConnectedEvent/BrowserStopEvent handler is refactored to use it, so profile-driven recording (record_video_dir=...) is unchanged.
  • New record subcommand wired through argparse (skill_cli/main.py), the daemon dispatch allowlist, and skill_cli/commands/browser.py. Works with --session NAME via the existing named-daemon infrastructure. record stop prints the saved file path so it can be captured programmatically (as requested in the issue).
  • CLIBrowserSession intentionally skips watchdogs; the handler lazily attaches RecordingWatchdog on first record start so non-recording sessions pay no cost.
  • Output is .mp4 (libx264) — matches the existing encoder. Gated behind the existing browser-use[video] optional extra; the CLI returns a helpful error if deps are missing.

Example

browser-use --session demo record start /tmp/demo.mp4
browser-use --session demo open https://example.com
browser-use --session demo click 3
browser-use --session demo record stop
# /tmp/demo.mp4

Test plan

  • uv run pytest -vxs tests/ci/test_action_record.py — 6 new tests, all pass (~23s). Covers: full start/stop cycle against a real headless browser (produces a decodable MP4), double-start rejection, stop-without-start returns None, profile-driven flow unchanged, argparse parsing, dispatch registration.
  • uv run pyright on changed files — clean.
  • uv run ruff check / ruff format — clean.
  • Live end-to-end CLI smoke test: record startopenrecord stop produced a valid ~11 KB MP4.
  • CI green.

Summary by cubic

Adds record start/stop/status to the browser-use CLI to capture the current session as an .mp4 via CDP screencasting, with simple start/stop APIs on RecordingWatchdog and reliable shutdown that finalizes recordings.

  • New Features

    • browser-use record start <path>, stop, and status; start supports --framerate, stop prints the saved path, and status returns path, framerate, and size.
    • Works with --session NAME; lazily attaches RecordingWatchdog so non-recording sessions have no overhead.
    • Outputs .mp4 (libx264) via the existing encoder; gated behind browser-use[video] with a clear error if missing.
  • Bug Fixes

    • on_BrowserConnectedEvent degrades gracefully when recording cannot start (e.g., missing browser-use[video] or undetectable viewport) so sessions still launch with record_video_dir set.
    • Daemon shutdown now awaits stop_recording() (no timeout) and finalizes any in-progress recording, preventing truncated MP4s.

Written for commit 44f7ead. Summary will update on new commits.

Closes #4533.

- `RecordingWatchdog` gains public `start_recording(path, size?, framerate?)`,
  `stop_recording() -> Path`, and `is_recording`; the existing
  `BrowserConnectedEvent`/`BrowserStopEvent` path is refactored to use them,
  so profile-driven recording behavior is unchanged.
- `browser-use record start <path>` / `record stop` / `record status`
  subcommands wired through argparse, daemon dispatch, and the browser
  command handler. `record stop` prints the saved file path so it can be
  captured programmatically, matching the issue's requested UX. Works with
  `--session NAME` via the existing named-daemon infrastructure.
- The CLI's `CLIBrowserSession` intentionally skips watchdogs; the handler
  lazily instantiates `RecordingWatchdog` on first `record start` so CLI
  recording doesn't pay the watchdog-setup cost for non-recording sessions.
- Output format is `.mp4` (libx264) since that's what the existing
  `VideoRecorderService` encodes; optional dependency gate is unchanged
  (`pip install "browser-use[video]"`).
- New `tests/ci/test_action_record.py` exercises the full stack against a
  real headless browser + `pytest-httpserver`, verifying decodable MP4
  output, double-start rejection, stop-without-start no-op, that the
  existing `profile.record_video_dir` flow still works, and the argparse /
  dispatch wiring.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 20, 2026

Agent Task Evaluation Results: 2/2 (100%)

View detailed results
Task Result Reason
amazon_laptop ✅ Pass Skipped - API key not available (fork PR or missing secret)
browser_use_pip ✅ Pass Skipped - API key not available (fork PR or missing secret)

Check the evaluate-tasks job for detailed task execution logs.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b1d933258c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread browser_use/browser/watchdogs/recording_watchdog.py
Comment thread browser_use/skill_cli/commands/browser.py
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 4 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="browser_use/skill_cli/commands/browser.py">

<violation number="1" location="browser_use/skill_cli/commands/browser.py:782">
P1: The lazily created `RecordingWatchdog` is never attached to the session event bus, so its `AgentFocusChangedEvent`/`BrowserStopEvent` handlers do not run. Recording can miss tab switches and may not finalize cleanly on browser stop.</violation>
</file>

<file name="browser_use/browser/watchdogs/recording_watchdog.py">

<violation number="1" location="browser_use/browser/watchdogs/recording_watchdog.py:41">
P1: The refactored `on_BrowserConnectedEvent` now calls `start_recording()` which raises `RuntimeError` on failure (missing deps, viewport detection failure). Previously, both conditions were handled with early returns/warnings, allowing sessions with `record_video_dir` to degrade gracefully. Wrap this call in `try/except RuntimeError` and log a warning to preserve the prior behavior.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Comment thread browser_use/skill_cli/commands/browser.py
Comment thread browser_use/browser/watchdogs/recording_watchdog.py Outdated
- `on_BrowserConnectedEvent` now catches `RuntimeError` from
  `start_recording()` so sessions with `record_video_dir` configured but
  missing `[video]` extras (or a viewport that can't be sized) keep
  starting — prior graceful-degradation behavior is restored.
- Lazy `RecordingWatchdog` in the CLI handler now calls
  `attach_to_session()`, so `AgentFocusChangedEvent` / `BrowserStopEvent`
  handlers are wired correctly if the session dispatches them.
- Daemon shutdown finalizes any in-progress recording before tearing the
  browser down, preventing truncated MP4s on `close`, idle timeout, or
  signal-driven exit.
- Added regression test that monkeypatches `start_recording` to raise and
  asserts `on_BrowserConnectedEvent` swallows it without breaking startup.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 132756dabb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread browser_use/skill_cli/daemon.py Outdated
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="browser_use/skill_cli/daemon.py">

<violation number="1" location="browser_use/skill_cli/daemon.py:457">
P2: `asyncio.wait_for` actively cancels the wrapped coroutine on timeout — meaning the ffmpeg encoder close inside `stop_recording()` will be interrupted, potentially leaving a truncated/corrupt MP4. For longer recordings or slow storage, 5 seconds may not be enough for finalization. Consider using `asyncio.shield()` around `stop_recording()` or increasing the timeout significantly (e.g., 30s) to avoid data loss during graceful shutdown.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Comment thread browser_use/skill_cli/daemon.py Outdated
`asyncio.wait_for(stop_recording(), timeout=5.0)` could expire while the
ffmpeg encoder was still flushing, leading the daemon's subsequent
`os._exit(0)` to kill the executor thread mid-write and leave the exact
truncated MP4 this hook was meant to prevent. `stop_recording()` already
offloads the blocking close to an executor, so awaiting it directly is
safe — and if it genuinely hangs, a stuck daemon is a clearer failure
signal than silent video corruption.

Verified end-to-end: start recording → `open` → `close` (no explicit
`record stop`) now produces a decodable MP4 with the captured frames.
@sauravpanda sauravpanda merged commit 8ad6f89 into main Apr 20, 2026
88 checks passed
@sauravpanda sauravpanda deleted the feat/cli-record-start-stop branch April 20, 2026 22:49
r266-tech added a commit to r266-tech/browser-use that referenced this pull request May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: browser-use record command for session video capture

1 participant