Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

MagMueller
Copy link
Collaborator

@MagMueller MagMueller commented Sep 6, 2025

Auto-generated PR for branch: feat/video-recording


Summary by cubic

Adds built-in MP4 video recording for browser sessions using CDP screencasting. Enable by setting record_video_dir; recordings auto-save when the session ends.

  • New Features

    • record_video_dir to save session recordings as .mp4 (filenames are UUIDs).
    • record_video_size and record_video_framerate (default 30); size defaults to current viewport.
    • New RecordingWatchdog streams frames via CDP and writes video with imageio/ffmpeg (handles resize and padding).
    • Docs updated (WebM → MP4), plus a new example (examples/features/video_recording.py) and minor example fix to use Path.
  • Dependencies

    • Optional extra: pip install "browser-use[video]" (imageio[ffmpeg]>=2.37.0).

@MagMueller MagMueller merged commit af5b440 into main Sep 6, 2025
12 checks passed
@MagMueller MagMueller deleted the feat/video-recording branch September 6, 2025 19:29
self.logger.warning('Cannot start video recording: viewport size could not be determined.')
return

video_format = getattr(profile, 'record_video_format', 'mp4').strip('.')
Copy link

Choose a reason for hiding this comment

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

Bug: Undefined Video Format Setting

The record_video_format field is accessed from the browser profile but isn't defined in the BrowserProfile class. While getattr prevents a crash by defaulting to 'mp4', this means users can't configure the video format. This is inconsistent with other video recording settings like record_video_framerate and record_video_size which are configurable.

Fix in Cursor Fix in Web

Copy link

github-actions bot commented Sep 6, 2025

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

View detailed results
Task Result Reason
amazon_laptop ✅ Pass The agent successfully navigated to amazon.com, performed a search for 'laptop', and returned detailed information about the first laptop product result, including the title, price, rating, number of ratings, and specifications. This meets all the criteria for success.
google_maps_3d ✅ Pass The agent correctly used www.google.com/maps to search for ETH Zurich Hauptgebäude, closed the side panel to enable full screen map view, switched to Satellite View as required, and clicked the 3D icon to enable 3D mode. The agent also panned the map so that both ETH Zurich Hauptgebäude and Zurich Lake were clearly visible. Although the agent noted that Google Maps does not provide a built-in screenshot feature and suggested manual screenshotting, this does not affect task success since taking a screenshot was conditional ('if able'). Therefore, all task criteria were met successfully.
browser_use_pip ✅ Pass The agent correctly provided the pip installation command 'pip install browser-use' as requested, meeting the success criteria.
captcha_cloudflare ✅ Pass The agent successfully solved the captcha on the specified webpage, waited as required, clicked on check, and extracted the hostname value from the displayed dictionary. The extracted hostname matches the expected value 'example.com', fulfilling all criteria for success.

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

Copy link
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.

10 issues found across 8 files

React with 👍 or 👎 to teach cubic. You can also tag @cubic-dev-ai to give feedback, ask questions, or re-run the review.

record_video_size: ViewportSize | None = Field(
default=None, description='Video frame size. If not set, it will use the viewport size.'
)
record_video_framerate: int = Field(default=30, description='The framerate to use for the video recording.')
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 6, 2025

Choose a reason for hiding this comment

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

Framerate lacks a lower bound; 0 or negative values would be invalid for recording and can cause encoder/runtime errors. Add a minimum of 1 via Field(ge=1).

(Based on your team's feedback about AI suggestions matching the project's tab indentation style.)

Prompt for AI agents
Address the following comment on browser_use/browser/profile.py at line 628:

<comment>Framerate lacks a lower bound; 0 or negative values would be invalid for recording and can cause encoder/runtime errors. Add a minimum of 1 via Field(ge=1).

(Based on your team&#39;s feedback about AI suggestions matching the project&#39;s tab indentation style.)</comment>

<file context>
@@ -615,6 +615,18 @@ class BrowserProfile(BrowserConnectArgs, BrowserLaunchPersistentContextArgs, Bro
+	record_video_size: ViewportSize | None = Field(
+		default=None, description=&#39;Video frame size. If not set, it will use the viewport size.&#39;
+	)
+	record_video_framerate: int = Field(default=30, description=&#39;The framerate to use for the video recording.&#39;)
+
 	# TODO: finish implementing extension support in extensions.py
</file context>
Suggested change
record_video_framerate: int = Field(default=30, description='The framerate to use for the video recording.')
record_video_framerate: int = Field(default=30, ge=1, description='The framerate to use for the video recording.')
Fix with Cubic

## Recording & Debugging
- `record_video_dir`: Directory to save video recordings as `.webm` files
- `record_video_dir`: Directory to save video recordings as `.mp4` files
- `record_video_size` (default: `ViewportSize`): The frame size (width, height) of the video recording.
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 6, 2025

Choose a reason for hiding this comment

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

Documented default is incorrect; the field defaults to None and falls back to the current viewport size when unspecified.

Prompt for AI agents
Address the following comment on docs/customize/browser/all-parameters.mdx at line 77:

<comment>Documented default is incorrect; the field defaults to None and falls back to the current viewport size when unspecified.</comment>

<file context>
@@ -73,7 +73,9 @@ mode: &quot;wide&quot;
 ## Recording &amp; Debugging
-- `record_video_dir`: Directory to save video recordings as `.webm` files
+- `record_video_dir`: Directory to save video recordings as `.mp4` files
+- `record_video_size` (default: `ViewportSize`): The frame size (width, height) of the video recording.
+- `record_video_framerate` (default: `30`): The framerate to use for the video recording.
 - `record_har_path`: Path to save network trace files as `.har` format
</file context>
Suggested change
- `record_video_size` (default: `ViewportSize`): The frame size (width, height) of the video recording.
- `record_video_size` (default: `None`): The frame size (width, height) of the video recording. Uses current viewport size when not set.
Fix with Cubic

self._recorder = VideoRecorderService(output_path=output_path, size=size, framerate=profile.record_video_framerate)
self._recorder.start()

if not self._recorder._is_active:
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 6, 2025

Choose a reason for hiding this comment

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

Accessing VideoRecorderService._is_active directly breaks encapsulation; prefer a public accessor or use service methods to infer state.

Prompt for AI agents
Address the following comment on browser_use/browser/watchdogs/recording_watchdog.py at line 52:

<comment>Accessing VideoRecorderService._is_active directly breaks encapsulation; prefer a public accessor or use service methods to infer state.</comment>

<file context>
@@ -0,0 +1,126 @@
+		self._recorder = VideoRecorderService(output_path=output_path, size=size, framerate=profile.record_video_framerate)
+		self._recorder.start()
+
+		if not self._recorder._is_active:
+			self._recorder = None
+			return
</file context>
Fix with Cubic

await cdp_session.cdp_client.send.Page.startScreencast(
params={
'format': 'png',
'quality': 90,
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 6, 2025

Choose a reason for hiding this comment

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

Passing 'quality' with format='png' is unnecessary; remove it to match CDP expectations and avoid confusion.

Prompt for AI agents
Address the following comment on browser_use/browser/watchdogs/recording_watchdog.py at line 63:

<comment>Passing &#39;quality&#39; with format=&#39;png&#39; is unnecessary; remove it to match CDP expectations and avoid confusion.</comment>

<file context>
@@ -0,0 +1,126 @@
+			await cdp_session.cdp_client.send.Page.startScreencast(
+				params={
+					&#39;format&#39;: &#39;png&#39;,
+					&#39;quality&#39;: 90,
+					&#39;maxWidth&#39;: size[&#39;width&#39;],
+					&#39;maxHeight&#39;: size[&#39;height&#39;],
</file context>
Fix with Cubic

self.logger.warning('Cannot start video recording: viewport size could not be determined.')
return

video_format = getattr(profile, 'record_video_format', 'mp4').strip('.')
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 6, 2025

Choose a reason for hiding this comment

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

Configurable video_format only changes the file extension while encoder is fixed to H.264; unsupported combos (e.g., .webm) may fail at runtime. Validate/limit formats or map codecs per format.

Prompt for AI agents
Address the following comment on browser_use/browser/watchdogs/recording_watchdog.py at line 45:

<comment>Configurable video_format only changes the file extension while encoder is fixed to H.264; unsupported combos (e.g., .webm) may fail at runtime. Validate/limit formats or map codecs per format.</comment>

<file context>
@@ -0,0 +1,126 @@
+			self.logger.warning(&#39;Cannot start video recording: viewport size could not be determined.&#39;)
+			return
+
+		video_format = getattr(profile, &#39;record_video_format&#39;, &#39;mp4&#39;).strip(&#39;.&#39;)
+		output_path = Path(profile.record_video_dir) / f&#39;{uuid7str()}.{video_format}&#39;
+
</file context>
Fix with Cubic

"""
Synchronous handler for incoming screencast frames.
"""
if not self._recorder:
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 6, 2025

Choose a reason for hiding this comment

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

Frames are not acked when recorder is None and screencast is never stopped, causing stalled screencast/backpressure.

Prompt for AI agents
Address the following comment on browser_use/browser/watchdogs/recording_watchdog.py at line 100:

<comment>Frames are not acked when recorder is None and screencast is never stopped, causing stalled screencast/backpressure.</comment>

<file context>
@@ -0,0 +1,126 @@
+		&quot;&quot;&quot;
+		Synchronous handler for incoming screencast frames.
+		&quot;&quot;&quot;
+		if not self._recorder:
+			return
+		self._recorder.add_frame(event[&#39;data&#39;])
</file context>
Fix with Cubic

self._dom_watchdog = None
self._screenshot_watchdog = None
self._permissions_watchdog = None
self._recording_watchdog = None
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 6, 2025

Choose a reason for hiding this comment

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

_watchdogs_attached flag not reset on reset()/stop()/kill() prevents reattaching handlers to the new EventBus, breaking subsequent starts.

Prompt for AI agents
Address the following comment on browser_use/browser/session.py at line 407:

<comment>_watchdogs_attached flag not reset on reset()/stop()/kill() prevents reattaching handlers to the new EventBus, breaking subsequent starts.</comment>

<file context>
@@ -401,6 +404,7 @@ async def reset(self) -&gt; None:
 		self._dom_watchdog = None
 		self._screenshot_watchdog = None
 		self._permissions_watchdog = None
+		self._recording_watchdog = None
 
 	def model_post_init(self, __context) -&gt; None:
</file context>
Fix with Cubic

"""
if not self._recorder:
return
self._recorder.add_frame(event['data'])
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 6, 2025

Choose a reason for hiding this comment

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

Possible race: self._recorder can become None between the check and add_frame call; use a local reference to avoid TOCTOU on the attribute.

Prompt for AI agents
Address the following comment on browser_use/browser/watchdogs/recording_watchdog.py at line 102:

<comment>Possible race: self._recorder can become None between the check and add_frame call; use a local reference to avoid TOCTOU on the attribute.</comment>

<file context>
@@ -0,0 +1,126 @@
+		&quot;&quot;&quot;
+		if not self._recorder:
+			return
+		self._recorder.add_frame(event[&#39;data&#39;])
+		asyncio.create_task(self._ack_screencast_frame(event, session_id))
+
</file context>
Fix with Cubic

self._recorder = None
return

self.browser_session.cdp_client.register.Page.screencastFrame(self.on_screencastFrame)
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 6, 2025

Choose a reason for hiding this comment

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

Registered screencast listener is never unregistered and Page.stopScreencast is not called during shutdown, risking resource leaks and unnecessary frame handling.

Prompt for AI agents
Address the following comment on browser_use/browser/watchdogs/recording_watchdog.py at line 56:

<comment>Registered screencast listener is never unregistered and Page.stopScreencast is not called during shutdown, risking resource leaks and unnecessary frame handling.</comment>

<file context>
@@ -0,0 +1,126 @@
+			self._recorder = None
+			return
+
+		self.browser_session.cdp_client.register.Page.screencastFrame(self.on_screencastFrame)
+
+		try:
</file context>
Fix with Cubic

"""
if not self._recorder:
return
self._recorder.add_frame(event['data'])
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Sep 6, 2025

Choose a reason for hiding this comment

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

Calling add_frame synchronously in the event callback blocks on a subprocess, stalling the event loop and risking frame drops/timeouts. Offload frame processing to a thread executor.

Prompt for AI agents
Address the following comment on browser_use/browser/watchdogs/recording_watchdog.py at line 102:

<comment>Calling add_frame synchronously in the event callback blocks on a subprocess, stalling the event loop and risking frame drops/timeouts. Offload frame processing to a thread executor.</comment>

<file context>
@@ -0,0 +1,126 @@
+		&quot;&quot;&quot;
+		if not self._recorder:
+			return
+		self._recorder.add_frame(event[&#39;data&#39;])
+		asyncio.create_task(self._ack_screencast_frame(event, session_id))
+
</file context>
Fix with Cubic

@MagMueller
Copy link
Collaborator Author

@EnzoFanAccount would you say any of the bot comments is relevant?

@MagMueller
Copy link
Collaborator Author

@EnzoFanAccount Can you also make it work for multiple tabs? E.g. you could listen to on_TabCreatedEvent and on_SwitchTabEvent and take whatever tab is currently active in add_frame

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.

2 participants