diff --git a/examples/cdp_mode/ReadMe.md b/examples/cdp_mode/ReadMe.md index dc059a18f57..d414567b5f5 100644 --- a/examples/cdp_mode/ReadMe.md +++ b/examples/cdp_mode/ReadMe.md @@ -368,6 +368,9 @@ sb.cdp.get_element_attribute(selector, attribute) sb.cdp.get_element_html(selector) sb.cdp.set_locale(locale) sb.cdp.set_attributes(selector, attribute, value) +sb.cdp.gui_press_key(key) +sb.cdp.gui_press_keys(keys) +sb.cdp.gui_write(text) sb.cdp.gui_click_x_y(x, y) sb.cdp.gui_click_element(selector) sb.cdp.internalize_links() diff --git a/examples/cdp_mode/raw_albertsons.py b/examples/cdp_mode/raw_albertsons.py new file mode 100644 index 00000000000..68bd05be6d4 --- /dev/null +++ b/examples/cdp_mode/raw_albertsons.py @@ -0,0 +1,35 @@ +from seleniumbase import SB + +with SB(uc=True, test=True, locale_code="en") as sb: + url = "https://www.albertsons.com/recipes/" + sb.activate_cdp_mode(url) + sb.sleep(2.5) + sb.remove_element("div > div > article") + sb.cdp.scroll_into_view('input[type="search"]') + sb.cdp.click_if_visible("button.banner-close-button") + sb.cdp.click("input#search-suggestion-input") + sb.sleep(0.2) + search = "Avocado Smoked Salmon" + required_text = "Salmon" + sb.cdp.press_keys("input#search-suggestion-input", search) + sb.sleep(0.8) + sb.cdp.click("#suggestion-0 a span") + sb.sleep(3.2) + sb.cdp.click_if_visible("button.banner-close-button") + sb.sleep(1.2) + print('*** Albertsons Search for "%s":' % search) + print(' (Results must contain "%s".)' % required_text) + unique_item_text = [] + item_selector = 'a[href*="/meal-plans-recipes/shop/"]' + info_selector = 'span[data-test-id*="recipe-thumb-title"]' + items = sb.cdp.find_elements("%s %s" % (item_selector, info_selector)) + for item in items: + sb.sleep(0.03) + item.scroll_into_view() + sb.sleep(0.025) + if required_text in item.text: + item.flash() + sb.sleep(0.025) + if item.text not in unique_item_text: + unique_item_text.append(item.text) + print("* " + item.text) diff --git a/requirements.txt b/requirements.txt index 572d07dae98..8848e2ff77b 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,13 @@ pip>=24.2 -packaging>=24.1 +packaging>=24.2 setuptools~=70.2;python_version<"3.10" setuptools>=73.0.1;python_version>="3.10" -wheel>=0.44.0 +wheel>=0.45.0 attrs>=24.2.0 certifi>=2024.8.30 exceptiongroup>=1.2.2 -websockets>=13.1 +websockets~=13.1;python_version<"3.9" +websockets>=14.0;python_version>="3.9" filelock>=3.16.1 fasteners>=0.19 mycdp>=1.0.1 diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index ebeb01572dd..9502cb9d3c2 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.32.8" +__version__ = "4.32.9" diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index 1f4897c780f..37d67d0fe9e 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -620,6 +620,9 @@ def uc_open_with_cdp_mode(driver, url=None): cdp.reset_window_size = CDPM.reset_window_size cdp.set_locale = CDPM.set_locale cdp.set_attributes = CDPM.set_attributes + cdp.gui_press_key = CDPM.gui_press_key + cdp.gui_press_keys = CDPM.gui_press_keys + cdp.gui_write = CDPM.gui_write cdp.gui_click_x_y = CDPM.gui_click_x_y cdp.gui_click_element = CDPM.gui_click_element cdp.internalize_links = CDPM.internalize_links @@ -721,6 +724,9 @@ def uc_click( timeout=settings.SMALL_TIMEOUT, reconnect_time=None, ): + if __is_cdp_swap_needed(driver): + driver.cdp.click(selector) + return with suppress(Exception): rct = float(by) # Add shortcut: driver.uc_click(selector, RCT) if not reconnect_time: diff --git a/seleniumbase/core/sb_cdp.py b/seleniumbase/core/sb_cdp.py index b8afadd113b..ed432cfe82f 100644 --- a/seleniumbase/core/sb_cdp.py +++ b/seleniumbase/core/sb_cdp.py @@ -942,6 +942,45 @@ def __get_configured_pyautogui(self, pyautogui_copy): ) return pyautogui_copy + def gui_press_key(self, key): + self.__install_pyautogui_if_missing() + import pyautogui + pyautogui = self.__get_configured_pyautogui(pyautogui) + gui_lock = fasteners.InterProcessLock( + constants.MultiBrowser.PYAUTOGUILOCK + ) + with gui_lock: + pyautogui.press(key) + time.sleep(0.0375) + self.__slow_mode_pause_if_set() + self.loop.run_until_complete(self.page.wait()) + + def gui_press_keys(self, keys): + self.__install_pyautogui_if_missing() + import pyautogui + pyautogui = self.__get_configured_pyautogui(pyautogui) + gui_lock = fasteners.InterProcessLock( + constants.MultiBrowser.PYAUTOGUILOCK + ) + with gui_lock: + for key in keys: + pyautogui.press(key) + time.sleep(0.0375) + self.__slow_mode_pause_if_set() + self.loop.run_until_complete(self.page.wait()) + + def gui_write(self, text): + self.__install_pyautogui_if_missing() + import pyautogui + pyautogui = self.__get_configured_pyautogui(pyautogui) + gui_lock = fasteners.InterProcessLock( + constants.MultiBrowser.PYAUTOGUILOCK + ) + with gui_lock: + pyautogui.write(text) + self.__slow_mode_pause_if_set() + self.loop.run_until_complete(self.page.wait()) + def __gui_click_x_y(self, x, y, timeframe=0.25, uc_lock=False): self.__install_pyautogui_if_missing() import pyautogui diff --git a/seleniumbase/plugins/driver_manager.py b/seleniumbase/plugins/driver_manager.py index b47bc1acbfc..2b7928c68da 100644 --- a/seleniumbase/plugins/driver_manager.py +++ b/seleniumbase/plugins/driver_manager.py @@ -550,11 +550,7 @@ def Driver( or uc_sub ): undetectable = True - if ( - (undetectable or undetected or uc) - and (uc_subprocess is None) - and (uc_sub is None) - ): + if undetectable or undetected or uc: uc_subprocess = True # Use UC as a subprocess by default. elif ( "--undetectable" in sys_argv diff --git a/seleniumbase/plugins/sb_manager.py b/seleniumbase/plugins/sb_manager.py index a7ba66bffb1..8a8839e9603 100644 --- a/seleniumbase/plugins/sb_manager.py +++ b/seleniumbase/plugins/sb_manager.py @@ -608,11 +608,7 @@ def SB( or uc_sub ): undetectable = True - if ( - (undetectable or undetected or uc) - and (uc_subprocess is None) - and (uc_sub is None) - ): + if undetectable or undetected or uc: uc_subprocess = True # Use UC as a subprocess by default. elif ( "--undetectable" in sys_argv diff --git a/seleniumbase/undetected/cdp_driver/connection.py b/seleniumbase/undetected/cdp_driver/connection.py index 7eba23cb534..da9df3da6ac 100644 --- a/seleniumbase/undetected/cdp_driver/connection.py +++ b/seleniumbase/undetected/cdp_driver/connection.py @@ -18,6 +18,7 @@ TypeVar, ) import websockets +from websockets.protocol import State from . import cdp_util as util import mycdp as cdp import mycdp.network @@ -261,7 +262,7 @@ async def aopen(self, **kw): """ Opens the websocket connection. Shouldn't be called manually by users. """ - if not self.websocket or self.websocket.closed: + if not self.websocket or self.websocket.state is State.CLOSED: try: self.websocket = await websockets.connect( self.websocket_url, @@ -288,7 +289,7 @@ async def aclose(self): """ Closes the websocket connection. Shouldn't be called manually by users. """ - if self.websocket and not self.websocket.closed: + if self.websocket and self.websocket.state is not State.CLOSED: if self.listener and self.listener.running: self.listener.cancel() self.enabled_domains.clear() @@ -393,7 +394,7 @@ async def send( when multiple calls to connection.send() are made. """ await self.aopen() - if not self.websocket or self.closed: + if not self.websocket or self.websocket.state is State.CLOSED: return if self._owner: browser = self._owner diff --git a/setup.py b/setup.py index 04ecc51fa0a..bf7861f5eed 100755 --- a/setup.py +++ b/setup.py @@ -148,14 +148,15 @@ python_requires=">=3.8", install_requires=[ 'pip>=24.2', - 'packaging>=24.1', + 'packaging>=24.2', 'setuptools~=70.2;python_version<"3.10"', # Newer ones had issues 'setuptools>=73.0.1;python_version>="3.10"', - 'wheel>=0.44.0', + 'wheel>=0.45.0', 'attrs>=24.2.0', "certifi>=2024.8.30", "exceptiongroup>=1.2.2", - "websockets>=13.1", + 'websockets~=13.1;python_version<"3.9"', + 'websockets>=14.0;python_version>="3.9"', 'filelock>=3.16.1', 'fasteners>=0.19', "mycdp>=1.0.1",