File size: 3,737 Bytes
c5f9050
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# backend/cdp_streamer.py
import asyncio
import json
import websockets
from playwright.async_api import CDPSession

class CDPBrowserStreamer:
    def __init__(self, page):
        self.page = page
        self.cdp_session = CDPSession()
        self.streaming = False
        
    async def start_streaming(self, websocket_port: int = 8080):
        """Start CDP-based streaming"""
        try:
            # Get CDP session from Playwright page
            self.cdp_session = await self.page.context.new_cdp_session(self.page)
            
            # Enable necessary CDP domains
            await self.cdp_session.send('Runtime.enable')
            await self.cdp_session.send('Page.enable')
            await self.cdp_session.send('Page.startScreencast', {
                'format': 'jpeg',
                'quality': 80,
                'maxWidth': 1280,
                'maxHeight': 800,
                'everyNthFrame': 1  # Stream every frame for real-time
            })
            
            # Start WebSocket server for streaming
            await websockets.serve(self.handle_client, "localhost", websocket_port)
            print(f"πŸŽ₯ CDP Streaming started on port {websocket_port}")
            
        except Exception as e:
            print(f"❌ Failed to start CDP streaming: {e}")
            
    async def handle_client(self, websocket, path):
        """Handle WebSocket clients for streaming"""
        print("πŸ”— Client connected to CDP stream")
        
        try:
            # Listen for screencast frames
            self.cdp_session.on('Page.screencastFrame', lambda params: 
                asyncio.create_task(self.send_frame(websocket, params)))
            
            # Keep connection alive and handle client messages
            async for message in websocket:
                data = json.loads(message)
                if data['type'] == 'mouse':
                    await self.handle_mouse_event(data)
                elif data['type'] == 'keyboard':
                    await self.handle_keyboard_event(data)
                    
        except websockets.exceptions.ConnectionClosed:
            print("πŸ”Œ Client disconnected from CDP stream")
            
    async def send_frame(self, websocket, params):
        """Send screencast frame to client"""
        try:
            frame_data = {
                'type': 'frame',
                'data': params['data'],  # Base64 encoded JPEG
                'metadata': {
                    'sessionId': params['sessionId'],
                    'timestamp': params.get('timestamp')
                }
            }
            await websocket.send(json.dumps(frame_data))
            
            # Acknowledge frame
            await self.cdp_session.send('Page.screencastFrameAck', {
                'sessionId': params['sessionId']
            })
        except Exception as e:
            print(f"❌ Error sending frame: {e}")
            
    async def handle_mouse_event(self, data):
        """Handle mouse events from client"""
        await self.cdp_session.send('Input.dispatchMouseEvent', {
            'type': data['eventType'],  # 'mousePressed', 'mouseReleased', 'mouseMoved'
            'x': data['x'],
            'y': data['y'],
            'button': data.get('button', 'left'),
            'clickCount': data.get('clickCount', 1)
        })
        
    async def handle_keyboard_event(self, data):
        """Handle keyboard events from client"""
        await self.cdp_session.send('Input.dispatchKeyEvent', {
            'type': data['eventType'],  # 'keyDown', 'keyUp', 'char'
            'text': data.get('text', ''),
            'key': data.get('key', ''),
            'code': data.get('code', '')
        })