Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
12 changes: 12 additions & 0 deletions cdp_use/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import re
import time
from typing import TYPE_CHECKING, Any, Dict, Optional
from cdp_use.recorder import Recorder

import websockets

Expand Down Expand Up @@ -400,3 +401,14 @@ async def emit_event(
produced by your application rather than the browser.
"""
return await self._event_registry.handle_event(method, params or {}, session_id)

async def start_recording(self, output_dir: str):
"""
Start recording browser session frames.

:param output_dir: Directory to save frames
:return: Recorder instance
"""
recorder = Recorder(self, output_dir)
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
await recorder.start()
return recorder
85 changes: 85 additions & 0 deletions cdp_use/recorder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import os
import base64
import asyncio
from typing import Any


class Recorder:
"""
Records browser frames using CDP screencast and saves them as JPEG images.
"""

def __init__(self, client: Any, output_dir: str):
self.client = client
self.output_dir = output_dir
self.frame_count = 0
self._running = False
self._queue: asyncio.Queue[dict[str, Any]] = asyncio.Queue()
self._worker_task: asyncio.Task | None = None

async def start(self) -> None:
"""
Start recording frames.
"""
os.makedirs(self.output_dir, exist_ok=True)
self._running = True

async def worker():
while self._running or not self._queue.empty():
event = await self._queue.get()

self.frame_count += 1

# Decode base64 frame into binary image
image_data = base64.b64decode(event["data"])

filename = os.path.join(
self.output_dir, f"frame_{self.frame_count:04d}.jpg"
)

with open(filename, "wb") as f:
f.write(image_data)

print(f"Saved {filename}")

# Acknowledge frame so Chrome continues sending frames
await self.client.send.Page.screencastFrameAck({
"sessionId": event["sessionId"]
})

self._queue.task_done()
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated

def on_frame(event: dict, session_id: str) -> None:
if self._running:
self._queue.put_nowait(event)

self.client.register.Page.screencastFrame(on_frame)

self._worker_task = asyncio.create_task(worker())

await self.client.send.Page.enable()

await self.client.send.Page.startScreencast({
"format": "jpeg",
"quality": 50,
"everyNthFrame": 1
})

async def stop(self) -> None:
"""
Stop recording and finalize frame saving.
"""
self._running = False

await self.client.send.Page.stopScreencast()

await self._queue.join()

if self._worker_task:
self._worker_task.cancel()
try:
await self._worker_task
except asyncio.CancelledError:
pass

print(f"Recording saved to {self.output_dir}")
21 changes: 21 additions & 0 deletions examples/record.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import asyncio
from cdp_use.client import CDPClient


async def main():
ws_url = "ws://localhost:9222/devtools/page/XXXX"

async with CDPClient(ws_url) as client:
# Start recording browser session
recorder = await client.start_recording("recording_output")

try:
# Perform actions while recording
await client.send.Page.navigate({"url": "https://youtube.com"})
await asyncio.sleep(5)
finally:
# Ensure recording is properly stopped
await recorder.stop()


asyncio.run(main())
Comment thread
Adarsh-Raj-Jaiswal marked this conversation as resolved.
Outdated