""" Browser automation controller for CAPTCHA solving workflows. Supports Playwright and Selenium when installed; otherwise operates in manual mode. """ import logging from dataclasses import dataclass from typing import Any, Optional, Tuple logger = logging.getLogger(__name__) @dataclass class BrowserConfig: engine: str = "Playwright" headless: bool = False viewport: Tuple[int, int] = (1280, 720) mouse_delay_ms: int = 50 retry_limit: int = 3 class BrowserController: """Launches and controls a browser session for CAPTCHA automation.""" def __init__(self, config: Optional[BrowserConfig] = None): self.config = config or BrowserConfig() self._driver: Any = None self._playwright: Any = None self._browser: Any = None self._page: Any = None @property def is_running(self) -> bool: return self._driver is not None or self._page is not None @property def active_driver(self) -> Any: """Return the active Playwright page or Selenium WebDriver.""" return self._page or self._driver def launch(self, url: Optional[str] = None) -> bool: engine = (self.config.engine or "None (Manual)").lower() if "playwright" in engine: return self._launch_playwright(url) if "selenium" in engine: return self._launch_selenium(url) logger.info("Browser automation disabled — manual mode") return False def _launch_playwright(self, url: Optional[str]) -> bool: try: from playwright.sync_api import sync_playwright except ImportError: logger.warning("Playwright not installed. Install with: pip install playwright") return False try: self._playwright = sync_playwright().start() self._browser = self._playwright.chromium.launch(headless=self.config.headless) context = self._browser.new_context( viewport={ "width": self.config.viewport[0], "height": self.config.viewport[1], } ) self._page = context.new_page() if url: self._page.goto(url, wait_until="domcontentloaded") logger.info("Playwright browser launched") return True except Exception as e: logger.error(f"Failed to launch Playwright: {e}") self.close() return False def _launch_selenium(self, url: Optional[str]) -> bool: try: from selenium import webdriver from selenium.webdriver.chrome.options import Options except ImportError: logger.warning("Selenium not installed. Install with: pip install selenium") return False try: options = Options() if self.config.headless: options.add_argument("--headless=new") options.add_argument( f"--window-size={self.config.viewport[0]},{self.config.viewport[1]}" ) self._driver = webdriver.Chrome(options=options) if url: self._driver.get(url) logger.info("Selenium browser launched") return True except Exception as e: logger.error(f"Failed to launch Selenium: {e}") self.close() return False def navigate(self, url: str) -> bool: driver = self.active_driver if not driver: return False try: if self._page is not None: self._page.goto(url, wait_until="domcontentloaded") else: driver.get(url) return True except Exception as e: logger.error(f"Navigation failed: {e}") return False def close(self): try: if self._page is not None: self._page.close() if self._browser is not None: self._browser.close() if self._playwright is not None: self._playwright.stop() if self._driver is not None: self._driver.quit() except Exception as e: logger.warning(f"Browser shutdown warning: {e}") finally: self._driver = None self._page = None self._browser = None self._playwright = None