diff --git a/playwright_recaptcha/recaptchav2/async_solver.py b/playwright_recaptcha/recaptchav2/async_solver.py index 17d640f..60841de 100644 --- a/playwright_recaptcha/recaptchav2/async_solver.py +++ b/playwright_recaptcha/recaptchav2/async_solver.py @@ -9,10 +9,10 @@ from concurrent.futures import ThreadPoolExecutor from io import BytesIO from json import JSONDecodeError -from typing import Any, BinaryIO, Dict, Iterable, Optional, Union +from typing import Any, BinaryIO, Dict, Iterable, List, Optional, Union import speech_recognition -from playwright.async_api import APIResponse, Page, Response +from playwright.async_api import APIResponse, Locator, Page, Response from pydub import AudioSegment from tenacity import ( AsyncRetrying, @@ -36,31 +36,33 @@ class AsyncAudioFile(speech_recognition.AudioFile): Parameters ---------- - file : BinaryIO - The audio file. + file : Union[BinaryIO, str] + The audio file handle or file path. executor : Optional[ThreadPoolExecutor], optional The thread pool executor to use, by default None. """ def __init__( - self, file: BinaryIO, *, executor: Optional[ThreadPoolExecutor] = None + self, + file: Union[BinaryIO, str], + *, + executor: Optional[ThreadPoolExecutor] = None, ) -> None: super().__init__(file) + self._loop = asyncio.get_event_loop() self._executor = executor async def __aenter__(self) -> AsyncAudioFile: - loop = asyncio.get_event_loop() - await loop.run_in_executor(self._executor, super().__enter__) + await self._loop.run_in_executor(self._executor, super().__enter__) return self async def __aexit__(self, *args: Any) -> None: - loop = asyncio.get_event_loop() - await loop.run_in_executor(self._executor, super().__exit__, *args) + await self._loop.run_in_executor(self._executor, super().__exit__, *args) class AsyncSolver: """ - A class used to solve reCAPTCHA v2 asynchronously. + A class for solving reCAPTCHA v2 asynchronously with Playwright. Parameters ---------- @@ -94,7 +96,7 @@ def __repr__(self) -> str: async def __aenter__(self) -> AsyncSolver: return self - async def __aexit__(self, *args: Any) -> None: + async def __aexit__(self, *_: Any) -> None: self.close() @staticmethod @@ -258,7 +260,7 @@ async def _solve_tiles( CapSolverError If the CapSolver API returned an error. """ - changing_tiles = [] + changing_tiles: List[Locator] = [] for index in indexes: tile = recaptcha_box.tile_selector.nth(index) diff --git a/playwright_recaptcha/recaptchav2/recaptcha_box.py b/playwright_recaptcha/recaptchav2/recaptcha_box.py index 7624571..a9a1162 100644 --- a/playwright_recaptcha/recaptchav2/recaptcha_box.py +++ b/playwright_recaptcha/recaptchav2/recaptcha_box.py @@ -60,8 +60,10 @@ def _get_recaptcha_frame_pairs( frame_id = anchor_frame.name[2:] for bframe_frame in bframe_frames: - if frame_id in bframe_frame.name: - frame_pairs.append((anchor_frame, bframe_frame)) + if frame_id not in bframe_frame.name: + continue + + frame_pairs.append((anchor_frame, bframe_frame)) if not frame_pairs: raise RecaptchaNotFoundError diff --git a/playwright_recaptcha/recaptchav2/sync_solver.py b/playwright_recaptcha/recaptchav2/sync_solver.py index fe3d972..fe5efeb 100644 --- a/playwright_recaptcha/recaptchav2/sync_solver.py +++ b/playwright_recaptcha/recaptchav2/sync_solver.py @@ -6,10 +6,10 @@ import re from io import BytesIO from json import JSONDecodeError -from typing import Any, Dict, Iterable, Optional, Union +from typing import Any, Dict, Iterable, List, Optional, Union import speech_recognition -from playwright.sync_api import APIResponse, Page, Response +from playwright.sync_api import APIResponse, Locator, Page, Response from pydub import AudioSegment from tenacity import Retrying, retry_if_exception_type, stop_after_delay, wait_fixed @@ -24,7 +24,7 @@ class SyncSolver: """ - A class used to solve reCAPTCHA v2 synchronously. + A class for solving reCAPTCHA v2 synchronously with Playwright. Parameters ---------- @@ -58,7 +58,7 @@ def __repr__(self) -> str: def __enter__(self) -> SyncSolver: return self - def __exit__(self, *args: Any) -> None: + def __exit__(self, *_: Any) -> None: self.close() @staticmethod @@ -222,7 +222,7 @@ def _solve_tiles( CapSolverError If the CapSolver API returned an error. """ - changing_tiles = [] + changing_tiles: List[Locator] = [] for index in indexes: tile = recaptcha_box.tile_selector.nth(index) diff --git a/playwright_recaptcha/recaptchav3/async_solver.py b/playwright_recaptcha/recaptchav3/async_solver.py index cfbba06..695aca9 100644 --- a/playwright_recaptcha/recaptchav3/async_solver.py +++ b/playwright_recaptcha/recaptchav3/async_solver.py @@ -11,7 +11,7 @@ class AsyncSolver: """ - A class used to solve reCAPTCHA v3 asynchronously. + A class for solving reCAPTCHA v3 asynchronously with Playwright. Parameters ---------- @@ -26,7 +26,7 @@ def __init__(self, page: Page, timeout: int = 30) -> None: self._timeout = timeout self._token: Optional[str] = None - self._page.on("response", self._extract_token) + self._page.on("response", self._response_callback) def __repr__(self) -> str: return f"AsyncSolver(page={self._page!r}, timeout={self._timeout!r})" @@ -34,17 +34,17 @@ def __repr__(self) -> str: async def __aenter__(self) -> AsyncSolver: return self - async def __aexit__(self, *args: Any) -> None: + async def __aexit__(self, *_: Any) -> None: self.close() - async def _extract_token(self, response: Response) -> None: + async def _response_callback(self, response: Response) -> None: """ - Extract the `g-recaptcha-response` token from the reload response. + The callback for intercepting reload responses. Parameters ---------- response : Response - The response to extract the `g-recaptcha-response` token from. + The response. """ if re.search("/recaptcha/(api2|enterprise)/reload", response.url) is None: return @@ -57,7 +57,7 @@ async def _extract_token(self, response: Response) -> None: def close(self) -> None: """Remove the reload response listener.""" try: - self._page.remove_listener("response", self._extract_token) + self._page.remove_listener("response", self._response_callback) except KeyError: pass diff --git a/playwright_recaptcha/recaptchav3/sync_solver.py b/playwright_recaptcha/recaptchav3/sync_solver.py index bc8b7c7..100c2f9 100644 --- a/playwright_recaptcha/recaptchav3/sync_solver.py +++ b/playwright_recaptcha/recaptchav3/sync_solver.py @@ -11,7 +11,7 @@ class SyncSolver: """ - A class used to solve reCAPTCHA v3 synchronously. + A class for solving reCAPTCHA v3 synchronously with Playwright. Parameters ---------- @@ -26,7 +26,7 @@ def __init__(self, page: Page, timeout: int = 30) -> None: self._timeout = timeout self._token: Optional[str] = None - self._page.on("response", self._extract_token) + self._page.on("response", self._response_callback) def __repr__(self) -> str: return f"SyncSolver(page={self._page!r}, timeout={self._timeout!r})" @@ -34,17 +34,17 @@ def __repr__(self) -> str: def __enter__(self) -> SyncSolver: return self - def __exit__(self, *args: Any) -> None: + def __exit__(self, *_: Any) -> None: self.close() - def _extract_token(self, response: Response) -> None: + def _response_callback(self, response: Response) -> None: """ - Extract the `g-recaptcha-response` token from the reload response. + The callback for intercepting reload responses. Parameters ---------- response : Response - The response to extract the `g-recaptcha-response` token from. + The response. """ if re.search("/recaptcha/(api2|enterprise)/reload", response.url) is None: return @@ -57,7 +57,7 @@ def _extract_token(self, response: Response) -> None: def close(self) -> None: """Remove the reload response listener.""" try: - self._page.remove_listener("response", self._extract_token) + self._page.remove_listener("response", self._response_callback) except KeyError: pass