Skip to content

Commit 92069a5

Browse files
author
katiue
committed
new stream function without the need to modify custom context
1 parent 585800f commit 92069a5

File tree

4 files changed

+193
-156
lines changed

4 files changed

+193
-156
lines changed

src/browser/custom_browser.py

Lines changed: 104 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@
44
# @ProjectName: browser-use-webui
55
# @FileName: browser.py
66

7+
import asyncio
8+
9+
from playwright.async_api import Browser as PlaywrightBrowser
10+
from playwright.async_api import (
11+
BrowserContext as PlaywrightBrowserContext,
12+
)
13+
from playwright.async_api import (
14+
Playwright,
15+
async_playwright,
16+
)
717
from browser_use.browser.browser import Browser
818
from browser_use.browser.context import BrowserContext, BrowserContextConfig
919
from playwright.async_api import BrowserContext as PlaywrightBrowserContext
@@ -15,36 +25,102 @@
1525
logger = logging.getLogger(__name__)
1626

1727
class CustomBrowser(Browser):
18-
_global_context = None
1928

2029
async def new_context(
2130
self,
22-
config: BrowserContextConfig = BrowserContextConfig(),
23-
context: PlaywrightBrowserContext = None,
31+
config: BrowserContextConfig = BrowserContextConfig()
2432
) -> CustomBrowserContext:
25-
"""Create a browser context with persistence support"""
26-
persistence_config = BrowserPersistenceConfig.from_env()
27-
28-
if persistence_config.persistent_session:
29-
if CustomBrowser._global_context is not None:
30-
logger.info("Reusing existing persistent browser context")
31-
return CustomBrowser._global_context
32-
33-
context_instance = CustomBrowserContext(config=config, browser=self, context=context)
34-
CustomBrowser._global_context = context_instance
35-
logger.info("Created new persistent browser context")
36-
return context_instance
37-
38-
logger.info("Creating non-persistent browser context")
39-
return CustomBrowserContext(config=config, browser=self, context=context)
40-
41-
async def close(self):
42-
"""Override close to respect persistence setting"""
43-
persistence_config = BrowserPersistenceConfig.from_env()
44-
if not persistence_config.persistent_session:
45-
if CustomBrowser._global_context is not None:
46-
await CustomBrowser._global_context.close()
47-
CustomBrowser._global_context = None
48-
await super().close()
33+
return CustomBrowserContext(config=config, browser=self)
34+
35+
async def _setup_browser(self, playwright: Playwright) -> PlaywrightBrowser:
36+
"""Sets up and returns a Playwright Browser instance with anti-detection measures."""
37+
if self.config.wss_url:
38+
browser = await playwright.chromium.connect(self.config.wss_url)
39+
return browser
40+
elif self.config.chrome_instance_path:
41+
import subprocess
42+
43+
import requests
44+
45+
try:
46+
# Check if browser is already running
47+
response = requests.get('http://localhost:9222/json/version', timeout=2)
48+
if response.status_code == 200:
49+
logger.info('Reusing existing Chrome instance')
50+
browser = await playwright.chromium.connect_over_cdp(
51+
endpoint_url='http://localhost:9222',
52+
timeout=20000, # 20 second timeout for connection
53+
)
54+
return browser
55+
except requests.ConnectionError:
56+
logger.debug('No existing Chrome instance found, starting a new one')
57+
58+
# Start a new Chrome instance
59+
subprocess.Popen(
60+
[
61+
self.config.chrome_instance_path,
62+
'--remote-debugging-port=9222',
63+
],
64+
stdout=subprocess.DEVNULL,
65+
stderr=subprocess.DEVNULL,
66+
)
67+
68+
# Attempt to connect again after starting a new instance
69+
for _ in range(10):
70+
try:
71+
response = requests.get('http://localhost:9222/json/version', timeout=2)
72+
if response.status_code == 200:
73+
break
74+
except requests.ConnectionError:
75+
pass
76+
await asyncio.sleep(1)
77+
78+
try:
79+
browser = await playwright.chromium.connect_over_cdp(
80+
endpoint_url='http://localhost:9222',
81+
timeout=20000, # 20 second timeout for connection
82+
)
83+
return browser
84+
except Exception as e:
85+
logger.error(f'Failed to start a new Chrome instance.: {str(e)}')
86+
raise RuntimeError(
87+
' To start chrome in Debug mode, you need to close all existing Chrome instances and try again otherwise we can not connect to the instance.'
88+
)
89+
4990
else:
50-
logger.info("Skipping browser close due to persistent session")
91+
try:
92+
disable_security_args = []
93+
if self.config.disable_security:
94+
disable_security_args = [
95+
'--disable-web-security',
96+
'--disable-site-isolation-trials',
97+
'--disable-features=IsolateOrigins,site-per-process',
98+
]
99+
100+
browser = await playwright.chromium.launch(
101+
headless=self.config.headless,
102+
args=[
103+
'--no-sandbox',
104+
'--disable-blink-features=AutomationControlled',
105+
'--disable-infobars',
106+
'--disable-background-timer-throttling',
107+
'--disable-popup-blocking',
108+
'--disable-backgrounding-occluded-windows',
109+
'--disable-renderer-backgrounding',
110+
'--disable-window-activation',
111+
'--disable-focus-on-load',
112+
'--no-first-run',
113+
'--no-default-browser-check',
114+
'--no-startup-window',
115+
'--window-position=0,0',
116+
# '--window-size=1280,1000',
117+
]
118+
+ disable_security_args
119+
+ self.config.extra_chromium_args,
120+
proxy=self.config.proxy,
121+
)
122+
123+
return browser
124+
except Exception as e:
125+
logger.error(f'Failed to initialize Playwright browser: {str(e)}')
126+
raise

src/browser/custom_context.py

Lines changed: 14 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -15,41 +15,28 @@
1515
from playwright.async_api import BrowserContext as PlaywrightBrowserContext
1616

1717
from .config import BrowserPersistenceConfig
18-
1918
logger = logging.getLogger(__name__)
2019

2120

2221
class CustomBrowserContext(BrowserContext):
2322
def __init__(
2423
self,
2524
browser: "Browser",
26-
config: BrowserContextConfig = BrowserContextConfig(),
27-
context: PlaywrightBrowserContext = None,
25+
config: BrowserContextConfig = BrowserContextConfig()
2826
):
2927
super(CustomBrowserContext, self).__init__(browser=browser, config=config)
30-
self.context = context
31-
self._page = None
32-
self._persistence_config = BrowserPersistenceConfig.from_env()
33-
34-
@property
35-
def impl_context(self) -> PlaywrightBrowserContext:
36-
"""Returns the underlying Playwright context implementation"""
37-
if self.context is None:
38-
raise RuntimeError("Failed to create or retrieve a browser context.")
39-
return self.context
4028

4129
async def _create_context(self, browser: PlaywrightBrowser) -> PlaywrightBrowserContext:
4230
"""Creates a new browser context with anti-detection measures and loads cookies if available."""
43-
if self.context:
44-
return self.context
31+
# If we have a context, return it directly
4532

4633
# Check if we should use existing context for persistence
47-
if self._persistence_config.persistent_session and len(browser.contexts) > 0:
48-
logger.info("Using existing persistent context.")
49-
self.context = browser.contexts[0]
34+
if self.browser.config.chrome_instance_path and len(browser.contexts) > 0:
35+
# Connect to existing Chrome instance instead of creating new one
36+
context = browser.contexts[0]
5037
else:
51-
logger.info("Creating a new browser context.")
52-
self.context = await browser.new_context(
38+
# Original code for creating new context
39+
context = await browser.new_context(
5340
viewport=self.config.browser_window_size,
5441
no_viewport=False,
5542
user_agent=(
@@ -63,19 +50,20 @@ async def _create_context(self, browser: PlaywrightBrowser) -> PlaywrightBrowser
6350
record_video_size=self.config.browser_window_size,
6451
)
6552

66-
# Handle tracing
6753
if self.config.trace_path:
68-
await self.context.tracing.start(screenshots=True, snapshots=True, sources=True)
54+
await context.tracing.start(screenshots=True, snapshots=True, sources=True)
6955

7056
# Load cookies if they exist
7157
if self.config.cookies_file and os.path.exists(self.config.cookies_file):
7258
with open(self.config.cookies_file, "r") as f:
7359
cookies = json.load(f)
74-
logger.info(f"Loaded {len(cookies)} cookies from {self.config.cookies_file}.")
75-
await self.context.add_cookies(cookies)
60+
logger.info(
61+
f"Loaded {len(cookies)} cookies from {self.config.cookies_file}"
62+
)
63+
await context.add_cookies(cookies)
7664

7765
# Expose anti-detection scripts
78-
await self.context.add_init_script(
66+
await context.add_init_script(
7967
"""
8068
// Webdriver property
8169
Object.defineProperty(navigator, 'webdriver', {
@@ -105,41 +93,4 @@ async def _create_context(self, browser: PlaywrightBrowser) -> PlaywrightBrowser
10593
"""
10694
)
10795

108-
# Create initial page if none exists
109-
if not self.context.pages:
110-
self._page = await self.context.new_page()
111-
await self._page.goto('about:blank')
112-
113-
return self.context
114-
115-
async def new_page(self):
116-
"""Creates and returns a new page in this context."""
117-
if not self.context:
118-
await self._create_context(await self.browser.get_playwright_browser())
119-
return await self.context.new_page()
120-
121-
async def get_current_page(self):
122-
"""Returns the current page or creates one if none exists."""
123-
if not self.context:
124-
await self._create_context(await self.browser.get_playwright_browser())
125-
if not self.context:
126-
raise RuntimeError("Browser context is not initialized.")
127-
pages = self.context.pages
128-
if not pages:
129-
logger.warning("No existing pages in the context. Creating a new page.")
130-
return await self.context.new_page()
131-
return pages[0]
132-
133-
async def close(self):
134-
"""Override close to respect persistence setting."""
135-
if not self._persistence_config.persistent_session and self.context:
136-
await self.context.close()
137-
self.context = None
138-
139-
@property
140-
def pages(self):
141-
"""Returns list of pages in the context."""
142-
if not self.context:
143-
logger.warning("Attempting to access pages but context is not initialized.")
144-
return []
145-
return self.context.pages
96+
return context

src/utils/utils.py

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from langchain_ollama import ChatOllama
1616
from langchain_openai import AzureChatOpenAI, ChatOpenAI
1717
import gradio as gr
18+
from src.browser.custom_context import CustomBrowserContext
1819

1920
def get_llm_model(provider: str, **kwargs):
2021
"""
@@ -164,36 +165,30 @@ def get_latest_files(directory: str, file_types: list = ['.webm', '.zip']) -> Di
164165
print(f"Error getting latest {file_type} file: {e}")
165166

166167
return latest_files
167-
168-
async def capture_screenshot(browser_context) -> str:
168+
async def capture_screenshot(browser_context: CustomBrowserContext) -> str:
169169
"""Capture and encode a screenshot"""
170+
latest_screenshot = ""
170171
try:
171-
# Get the implementation context - handle both direct Playwright context and wrapped context
172-
context = browser_context
173-
if hasattr(browser_context, 'context'):
174-
context = browser_context.context
175-
176-
if not context:
177-
return "<div>No browser context available</div>"
178-
179-
# Get all pages
180-
pages = context.pages
181-
if not pages:
182-
return "<div>Waiting for page to be available...</div>"
183-
184-
# Use the first non-blank page or fallback to first page
185-
active_page = None
186-
for page in pages:
187-
if page.url != 'about:blank':
188-
active_page = page
189-
break
190-
191-
if not active_page and pages:
192-
active_page = pages[0]
193-
194-
if not active_page:
195-
return "<div>No active page available</div>"
172+
# Extract the Playwright browser instance
173+
playwright_browser = browser_context.browser.playwright_browser # Ensure this is correct.
196174

175+
# Check if the browser instance is valid and if an existing context can be reused
176+
if playwright_browser and playwright_browser.contexts:
177+
playwright_context = playwright_browser.contexts[0]
178+
else:
179+
return latest_screenshot
180+
181+
# Access pages in the context
182+
if playwright_context:
183+
pages = playwright_context.pages
184+
185+
# Use an existing page or create a new one if none exist
186+
if pages:
187+
active_page = pages[0]
188+
for page in pages:
189+
if page.url != "about:blank":
190+
active_page = page
191+
197192
# Take screenshot
198193
try:
199194
screenshot = await active_page.screenshot(
@@ -202,9 +197,9 @@ async def capture_screenshot(browser_context) -> str:
202197
scale="css"
203198
)
204199
encoded = base64.b64encode(screenshot).decode('utf-8')
205-
return f'<img src="data:image/jpeg;base64,{encoded}" style="width:100%; max-width:1200px; border:1px solid #ccc;">'
200+
return f'<img src="data:image/jpeg;base64,{encoded}" style="width:80vw; height:90vh ; border:1px solid #ccc;">'
206201
except Exception as e:
207-
return f"<div class='error'>Screenshot failed: {str(e)}</div>"
208-
202+
return f"<div class='error' style='width:80vw; height:90vh'>Screenshot failed: {str(e)}</div>"
203+
209204
except Exception as e:
210-
return f"<div class='error'>Screenshot error: {str(e)}</div>"
205+
return f"<div class='error' style='width:80vw; height:90vh'>Screenshot error: {str(e)}</div>"

0 commit comments

Comments
 (0)