from playwright.async_api import async_playwright from app.config import CUSTOM_USER_AGENT import asyncio async def wait_for_network_idle(page): """Wait until no network requests are in flight""" await page.wait_for_load_state('networkidle') async def safe_browser_operation(url, operation_func): """Safely perform browser operations with proper cleanup""" browser = None context = None page = None try: # Get or create playwright instance playwright = await async_playwright().start() browser = await playwright.chromium.launch( headless=True, args=['--no-sandbox', '--disable-setuid-sandbox'], ) # Create context and page context = await browser.new_context( user_agent=CUSTOM_USER_AGENT, viewport={'width': 1920, 'height': 1080}, ignore_https_errors=True, ) page = await context.new_page() page.set_default_timeout(30000) # Call the operation function that uses the page result = await operation_func(page) return result except Exception as e: print(f"Error during browser operation: {e}") # Return error result instead of re-raising to allow graceful handling return { "status": "error", "error": str(e), "url": url } finally: # Ensure page is closed properly if page: try: await page.close() except Exception as e: print(f"Error closing page: {e}") # Ensure context is closed properly if context: try: await context.close() except Exception as e: print(f"Error closing context: {e}") # Ensure browser is closed properly if browser: try: await browser.close() except Exception as e: print(f"Error closing browser: {e}") # We don't re-raise here to avoid masking the original error