unrealon 2.0.10__py3-none-any.whl → 2.0.12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {unrealon-2.0.10.dist-info → unrealon-2.0.12.dist-info}/METADATA +1 -1
- {unrealon-2.0.10.dist-info → unrealon-2.0.12.dist-info}/RECORD +21 -20
- unrealon_browser/core/browser_manager.py +58 -0
- unrealon_browser/managers/logger_bridge.py +3 -3
- unrealon_browser/managers/script_manager.py +45 -26
- unrealon_browser/stealth/{bypass_techniques.pyc → bypass_techniques.py} +0 -0
- unrealon_browser/stealth/manager.py +507 -0
- unrealon_browser/stealth/nodriver_stealth.py +432 -0
- unrealon_browser/stealth/playwright_stealth.py +179 -0
- unrealon_browser/stealth/scanner_tester.py +355 -0
- unrealon_browser/stealth/undetected_chrome.py +361 -0
- unrealon_driver/__init__.py +12 -2
- unrealon_driver/driver/factory/manager_factory.py +8 -1
- unrealon_driver/managers/browser.py +16 -1
- unrealon_driver/managers/proxy.py +11 -4
- unrealon_driver/utils/__init__.py +13 -4
- unrealon_driver/utils/platform_compatibility.py +205 -0
- unrealon_browser/stealth/manager.pyc +0 -0
- unrealon_browser/stealth/nodriver_stealth.pyc +0 -0
- unrealon_browser/stealth/playwright_stealth.pyc +0 -0
- unrealon_browser/stealth/scanner_tester.pyc +0 -0
- unrealon_browser/stealth/undetected_chrome.pyc +0 -0
- {unrealon-2.0.10.dist-info → unrealon-2.0.12.dist-info}/LICENSE +0 -0
- {unrealon-2.0.10.dist-info → unrealon-2.0.12.dist-info}/WHEEL +0 -0
- {unrealon-2.0.10.dist-info → unrealon-2.0.12.dist-info}/entry_points.txt +0 -0
- {unrealon-2.0.10.dist-info → unrealon-2.0.12.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
"""
|
|
2
|
+
UndetectedChrome - Integration with undetected-chromedriver
|
|
3
|
+
|
|
4
|
+
Provides integration with undetected-chromedriver for advanced bot detection bypass:
|
|
5
|
+
- Automatic ChromeDriver patching
|
|
6
|
+
- Selenium stealth integration
|
|
7
|
+
- Advanced Chrome arguments
|
|
8
|
+
- BotD bypass optimization
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
from typing import Dict, Any, Optional, List
|
|
13
|
+
import random
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
logger.setLevel(logging.DEBUG)
|
|
17
|
+
|
|
18
|
+
class UndetectedChrome:
|
|
19
|
+
"""
|
|
20
|
+
Integration with undetected-chromedriver
|
|
21
|
+
|
|
22
|
+
Provides seamless integration with undetected-chromedriver
|
|
23
|
+
for maximum bot detection bypass effectiveness
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, logger_bridge=None):
|
|
27
|
+
"""Initialize UndetectedChrome"""
|
|
28
|
+
self.logger_bridge = logger_bridge
|
|
29
|
+
|
|
30
|
+
def _logger(self, message: str, level: str = "info") -> None:
|
|
31
|
+
"""Private wrapper for logger with bridge support"""
|
|
32
|
+
if self.logger_bridge:
|
|
33
|
+
if level == "info":
|
|
34
|
+
self.logger_bridge.log_info(message)
|
|
35
|
+
elif level == "error":
|
|
36
|
+
self.logger_bridge.log_error(message)
|
|
37
|
+
elif level == "warning":
|
|
38
|
+
self.logger_bridge.log_warning(message)
|
|
39
|
+
else:
|
|
40
|
+
self.logger_bridge.log_info(message)
|
|
41
|
+
else:
|
|
42
|
+
if level == "info":
|
|
43
|
+
logger.info(message)
|
|
44
|
+
elif level == "error":
|
|
45
|
+
logger.error(message)
|
|
46
|
+
elif level == "warning":
|
|
47
|
+
logger.warning(message)
|
|
48
|
+
else:
|
|
49
|
+
logger.info(message)
|
|
50
|
+
|
|
51
|
+
def get_undetected_chrome_options(self) -> Dict[str, Any]:
|
|
52
|
+
"""
|
|
53
|
+
Get optimized options for undetected-chromedriver
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Dict with Chrome options optimized for stealth
|
|
57
|
+
"""
|
|
58
|
+
return {
|
|
59
|
+
"headless": False, # Recommend headed mode for best results
|
|
60
|
+
"use_subprocess": False,
|
|
61
|
+
"version_main": None, # Auto-detect Chrome version
|
|
62
|
+
"driver_executable_path": None, # Auto-download
|
|
63
|
+
"browser_executable_path": None, # Use system Chrome
|
|
64
|
+
"user_data_dir": None, # Use temporary profile
|
|
65
|
+
"suppress_welcome": True,
|
|
66
|
+
"no_sandbox": True,
|
|
67
|
+
"disable_gpu": False, # Keep GPU for WebGL consistency
|
|
68
|
+
"log_level": 3, # Suppress logs
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
def get_enhanced_chrome_arguments(self) -> List[str]:
|
|
72
|
+
"""
|
|
73
|
+
Get enhanced Chrome arguments for undetected-chromedriver
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
List of Chrome arguments optimized for stealth
|
|
77
|
+
"""
|
|
78
|
+
return [
|
|
79
|
+
# Core stealth arguments
|
|
80
|
+
"--no-first-run",
|
|
81
|
+
"--no-default-browser-check",
|
|
82
|
+
"--disable-blink-features=AutomationControlled",
|
|
83
|
+
"--disable-features=VizDisplayCompositor",
|
|
84
|
+
|
|
85
|
+
# Enhanced stealth for BotD bypass
|
|
86
|
+
"--disable-web-security",
|
|
87
|
+
"--disable-client-side-phishing-detection",
|
|
88
|
+
"--disable-component-extensions-with-background-pages",
|
|
89
|
+
"--disable-default-apps",
|
|
90
|
+
"--disable-domain-reliability",
|
|
91
|
+
"--disable-background-networking",
|
|
92
|
+
"--disable-translate",
|
|
93
|
+
"--disable-ipc-flooding-protection",
|
|
94
|
+
|
|
95
|
+
# Performance and stability
|
|
96
|
+
"--no-sandbox",
|
|
97
|
+
"--disable-dev-shm-usage",
|
|
98
|
+
"--disable-background-timer-throttling",
|
|
99
|
+
"--disable-backgrounding-occluded-windows",
|
|
100
|
+
"--disable-renderer-backgrounding",
|
|
101
|
+
"--disable-field-trial-config",
|
|
102
|
+
"--disable-back-forward-cache",
|
|
103
|
+
|
|
104
|
+
# System integration
|
|
105
|
+
"--disable-hang-monitor",
|
|
106
|
+
"--disable-prompt-on-repost",
|
|
107
|
+
"--disable-sync",
|
|
108
|
+
"--metrics-recording-only",
|
|
109
|
+
"--no-report-upload",
|
|
110
|
+
|
|
111
|
+
# Realistic window settings
|
|
112
|
+
"--window-size=1366,768",
|
|
113
|
+
"--start-maximized",
|
|
114
|
+
|
|
115
|
+
# Audio and media
|
|
116
|
+
"--mute-audio",
|
|
117
|
+
"--disable-audio-output",
|
|
118
|
+
|
|
119
|
+
# Extensions (keep minimal for realism)
|
|
120
|
+
"--disable-extensions-except=",
|
|
121
|
+
|
|
122
|
+
# Memory and performance
|
|
123
|
+
"--max_old_space_size=4096",
|
|
124
|
+
"--memory-pressure-off",
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
def get_random_user_agent(self) -> str:
|
|
128
|
+
"""
|
|
129
|
+
Get random realistic user agent
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Random user agent string
|
|
133
|
+
"""
|
|
134
|
+
user_agents = [
|
|
135
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
136
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
|
|
137
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
138
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
|
|
139
|
+
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
140
|
+
]
|
|
141
|
+
return random.choice(user_agents)
|
|
142
|
+
|
|
143
|
+
def create_undetected_driver(self, headless: bool = False, **kwargs) -> Any:
|
|
144
|
+
"""
|
|
145
|
+
Create undetected ChromeDriver instance
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
headless: Whether to run in headless mode
|
|
149
|
+
**kwargs: Additional options for undetected_chromedriver
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Configured undetected ChromeDriver instance
|
|
153
|
+
"""
|
|
154
|
+
try:
|
|
155
|
+
import undetected_chromedriver as uc
|
|
156
|
+
from selenium_stealth import stealth
|
|
157
|
+
|
|
158
|
+
self._logger("🚗 Creating undetected ChromeDriver...", "info")
|
|
159
|
+
|
|
160
|
+
# Get base options
|
|
161
|
+
options = self.get_undetected_chrome_options()
|
|
162
|
+
options.update(kwargs)
|
|
163
|
+
options["headless"] = headless
|
|
164
|
+
|
|
165
|
+
# Create Chrome options
|
|
166
|
+
chrome_options = uc.ChromeOptions()
|
|
167
|
+
|
|
168
|
+
# Add enhanced arguments
|
|
169
|
+
for arg in self.get_enhanced_chrome_arguments():
|
|
170
|
+
chrome_options.add_argument(arg)
|
|
171
|
+
|
|
172
|
+
# Add random user agent
|
|
173
|
+
user_agent = self.get_random_user_agent()
|
|
174
|
+
chrome_options.add_argument(f'--user-agent={user_agent}')
|
|
175
|
+
|
|
176
|
+
# Disable automation indicators
|
|
177
|
+
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
|
|
178
|
+
chrome_options.add_experimental_option('useAutomationExtension', False)
|
|
179
|
+
|
|
180
|
+
# Create driver
|
|
181
|
+
driver = uc.Chrome(options=chrome_options, **options)
|
|
182
|
+
|
|
183
|
+
# Apply selenium-stealth
|
|
184
|
+
stealth(
|
|
185
|
+
driver,
|
|
186
|
+
languages=["en-US", "en"],
|
|
187
|
+
vendor="Google Inc.",
|
|
188
|
+
platform="Win32",
|
|
189
|
+
webgl_vendor="Intel Inc.",
|
|
190
|
+
renderer="Intel Iris OpenGL Engine",
|
|
191
|
+
fix_hairline=True,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# Remove webdriver property
|
|
195
|
+
driver.execute_script("""
|
|
196
|
+
Object.defineProperty(navigator, 'webdriver', {
|
|
197
|
+
get: () => undefined
|
|
198
|
+
});
|
|
199
|
+
""")
|
|
200
|
+
|
|
201
|
+
self._logger("✅ Undetected ChromeDriver created successfully", "info")
|
|
202
|
+
return driver
|
|
203
|
+
|
|
204
|
+
except ImportError as e:
|
|
205
|
+
self._logger(f"❌ undetected-chromedriver not available: {e}", "error")
|
|
206
|
+
return None
|
|
207
|
+
except Exception as e:
|
|
208
|
+
self._logger(f"❌ Failed to create undetected ChromeDriver: {e}", "error")
|
|
209
|
+
return None
|
|
210
|
+
|
|
211
|
+
def apply_selenium_stealth(self, driver) -> bool:
|
|
212
|
+
"""
|
|
213
|
+
Apply selenium-stealth to existing driver
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
driver: Selenium WebDriver instance
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
True if successful
|
|
220
|
+
"""
|
|
221
|
+
try:
|
|
222
|
+
from selenium_stealth import stealth
|
|
223
|
+
|
|
224
|
+
self._logger("🛡️ Applying selenium-stealth...", "info")
|
|
225
|
+
|
|
226
|
+
stealth(
|
|
227
|
+
driver,
|
|
228
|
+
languages=["en-US", "en"],
|
|
229
|
+
vendor="Google Inc.",
|
|
230
|
+
platform="Win32",
|
|
231
|
+
webgl_vendor="Intel Inc.",
|
|
232
|
+
renderer="Intel Iris OpenGL Engine",
|
|
233
|
+
fix_hairline=True,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
# Additional webdriver removal
|
|
237
|
+
driver.execute_script("""
|
|
238
|
+
// Enhanced webdriver removal
|
|
239
|
+
Object.defineProperty(navigator, 'webdriver', {
|
|
240
|
+
get: () => undefined
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Remove automation indicators
|
|
244
|
+
['__webdriver_evaluate', '__selenium_evaluate', '__webdriver_script_function',
|
|
245
|
+
'__fxdriver_evaluate', '__driver_unwrapped', '__webdriver_unwrapped',
|
|
246
|
+
'_Selenium_IDE_Recorder', '_selenium', 'calledSelenium'].forEach(prop => {
|
|
247
|
+
delete window[prop];
|
|
248
|
+
});
|
|
249
|
+
""")
|
|
250
|
+
|
|
251
|
+
self._logger("✅ Selenium-stealth applied successfully", "info")
|
|
252
|
+
return True
|
|
253
|
+
|
|
254
|
+
except ImportError as e:
|
|
255
|
+
self._logger(f"❌ selenium-stealth not available: {e}", "error")
|
|
256
|
+
return False
|
|
257
|
+
except Exception as e:
|
|
258
|
+
self._logger(f"❌ Failed to apply selenium-stealth: {e}", "error")
|
|
259
|
+
return False
|
|
260
|
+
|
|
261
|
+
def test_undetected_chrome(self, test_url: str) -> Dict[str, Any]:
|
|
262
|
+
"""
|
|
263
|
+
Test undetected ChromeDriver effectiveness
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
test_url: URL to test stealth effectiveness
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
Dict with test results
|
|
270
|
+
"""
|
|
271
|
+
driver = None
|
|
272
|
+
try:
|
|
273
|
+
self._logger(f"🧪 Testing undetected ChromeDriver on {test_url}...", "info")
|
|
274
|
+
|
|
275
|
+
# Create driver
|
|
276
|
+
driver = self.create_undetected_driver(headless=True)
|
|
277
|
+
if not driver:
|
|
278
|
+
return {
|
|
279
|
+
"success": False,
|
|
280
|
+
"error": "Failed to create undetected ChromeDriver"
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
# Navigate to test page
|
|
284
|
+
driver.get(test_url)
|
|
285
|
+
|
|
286
|
+
# Wait for page to load
|
|
287
|
+
import time
|
|
288
|
+
time.sleep(5)
|
|
289
|
+
|
|
290
|
+
# Extract basic detection info
|
|
291
|
+
try:
|
|
292
|
+
results = driver.execute_script("""
|
|
293
|
+
return {
|
|
294
|
+
userAgent: navigator.userAgent,
|
|
295
|
+
webdriver: navigator.webdriver !== undefined,
|
|
296
|
+
chrome: !!window.chrome,
|
|
297
|
+
plugins: navigator.plugins.length,
|
|
298
|
+
languages: navigator.languages ? navigator.languages.length : 0,
|
|
299
|
+
botDetectionResults: window.botDetectionResults || null
|
|
300
|
+
};
|
|
301
|
+
""")
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
"success": True,
|
|
305
|
+
"results": results,
|
|
306
|
+
"is_bot": results.get("webdriver", False),
|
|
307
|
+
"user_agent": results.get("userAgent", ""),
|
|
308
|
+
"chrome_object": results.get("chrome", False),
|
|
309
|
+
"plugins_count": results.get("plugins", 0),
|
|
310
|
+
"bot_detection": results.get("botDetectionResults")
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
except Exception as e:
|
|
314
|
+
return {
|
|
315
|
+
"success": False,
|
|
316
|
+
"error": f"Failed to extract results: {e}"
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
except Exception as e:
|
|
320
|
+
self._logger(f"❌ Undetected Chrome test failed: {e}", "error")
|
|
321
|
+
return {
|
|
322
|
+
"success": False,
|
|
323
|
+
"error": str(e)
|
|
324
|
+
}
|
|
325
|
+
finally:
|
|
326
|
+
if driver:
|
|
327
|
+
try:
|
|
328
|
+
driver.quit()
|
|
329
|
+
except:
|
|
330
|
+
pass
|
|
331
|
+
|
|
332
|
+
def get_stealth_info(self) -> Dict[str, Any]:
|
|
333
|
+
"""
|
|
334
|
+
Get information about undetected-chromedriver capabilities
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
Dict with capability information
|
|
338
|
+
"""
|
|
339
|
+
return {
|
|
340
|
+
"library": "undetected-chromedriver",
|
|
341
|
+
"selenium_stealth": "selenium-stealth",
|
|
342
|
+
"features": {
|
|
343
|
+
"automatic_chromedriver_patching": True,
|
|
344
|
+
"webdriver_property_removal": True,
|
|
345
|
+
"user_agent_spoofing": True,
|
|
346
|
+
"chrome_arguments_optimization": True,
|
|
347
|
+
"selenium_stealth_integration": True,
|
|
348
|
+
"random_user_agents": True,
|
|
349
|
+
"headless_support": True,
|
|
350
|
+
"headed_mode_recommended": True
|
|
351
|
+
},
|
|
352
|
+
"bypass_capabilities": {
|
|
353
|
+
"cloudflare": True,
|
|
354
|
+
"datadome": True,
|
|
355
|
+
"imperva": True,
|
|
356
|
+
"botd": "partial",
|
|
357
|
+
"recaptcha": True,
|
|
358
|
+
"basic_detection": True
|
|
359
|
+
},
|
|
360
|
+
"description": "Most effective solution for Selenium-based bot detection bypass"
|
|
361
|
+
}
|
unrealon_driver/__init__.py
CHANGED
|
@@ -60,15 +60,20 @@ from .decorators import (
|
|
|
60
60
|
timing
|
|
61
61
|
)
|
|
62
62
|
|
|
63
|
+
# Utilities - Cross-platform compatibility
|
|
64
|
+
from .utils import (
|
|
65
|
+
PlatformCompatibility,
|
|
66
|
+
ensure_platform_compatibility,
|
|
67
|
+
get_platform_info
|
|
68
|
+
)
|
|
69
|
+
|
|
63
70
|
__version__ = get_driver_version()
|
|
64
71
|
__author__ = "UnrealOn Team"
|
|
65
|
-
__phase__ = "Phase 3.8: Clean Refactor"
|
|
66
72
|
|
|
67
73
|
__all__ = [
|
|
68
74
|
# Version
|
|
69
75
|
"__version__",
|
|
70
76
|
"__author__",
|
|
71
|
-
"__phase__",
|
|
72
77
|
|
|
73
78
|
# Core API
|
|
74
79
|
"UniversalDriver",
|
|
@@ -102,4 +107,9 @@ __all__ = [
|
|
|
102
107
|
"retry",
|
|
103
108
|
"schedule",
|
|
104
109
|
"timing",
|
|
110
|
+
|
|
111
|
+
# Utilities
|
|
112
|
+
"PlatformCompatibility",
|
|
113
|
+
"ensure_platform_compatibility",
|
|
114
|
+
"get_platform_info",
|
|
105
115
|
]
|
|
@@ -81,11 +81,18 @@ class ManagerFactory:
|
|
|
81
81
|
@staticmethod
|
|
82
82
|
def _setup_browser_manager(driver: 'UniversalDriver', registry: ManagerRegistry):
|
|
83
83
|
"""Setup browser manager."""
|
|
84
|
+
# Get proxy URL from proxy manager if enabled
|
|
85
|
+
proxy_url = None
|
|
86
|
+
if driver.config.proxy_enabled and hasattr(driver, 'proxy') and driver.proxy:
|
|
87
|
+
proxy_url = driver.proxy.get_proxy()
|
|
88
|
+
|
|
84
89
|
browser_config = BrowserManagerConfig(
|
|
85
90
|
enabled=True,
|
|
86
91
|
headless=driver.config.browser_headless,
|
|
87
92
|
timeout=driver.config.browser_timeout,
|
|
88
|
-
parser_name=driver.driver_id
|
|
93
|
+
parser_name=driver.driver_id,
|
|
94
|
+
proxy_enabled=driver.config.proxy_enabled,
|
|
95
|
+
proxy_url=proxy_url
|
|
89
96
|
)
|
|
90
97
|
driver.browser = BrowserManager(browser_config)
|
|
91
98
|
registry.register(driver.browser)
|
|
@@ -17,6 +17,11 @@ class BrowserManagerConfig(ManagerConfig):
|
|
|
17
17
|
headless: bool = Field(default=True, description="Run headless")
|
|
18
18
|
parser_name: str = Field(..., description="Parser name for browser")
|
|
19
19
|
stealth_warmup_enabled: bool = Field(default=False, description="Enable stealth warmup")
|
|
20
|
+
|
|
21
|
+
# Proxy settings
|
|
22
|
+
proxy_enabled: bool = Field(default=False, description="Enable proxy usage")
|
|
23
|
+
proxy_url: Optional[str] = Field(default=None, description="Proxy URL (http://user:pass@host:port)")
|
|
24
|
+
proxy_timeout: int = Field(default=30, description="Proxy timeout in seconds")
|
|
20
25
|
|
|
21
26
|
|
|
22
27
|
class BrowserManager(BaseManager):
|
|
@@ -53,6 +58,12 @@ class BrowserManager(BaseManager):
|
|
|
53
58
|
try:
|
|
54
59
|
self.logger.info("🚀 Starting browser (lazy initialization)...")
|
|
55
60
|
|
|
61
|
+
# Log proxy status
|
|
62
|
+
if self.config.proxy_enabled and self.config.proxy_url:
|
|
63
|
+
self.logger.info(f"🔒 Proxy enabled: {self.config.proxy_url}")
|
|
64
|
+
else:
|
|
65
|
+
self.logger.info("🌐 No proxy configured")
|
|
66
|
+
|
|
56
67
|
# Create browser config with proper headless mode
|
|
57
68
|
browser_mode = BrowserMode.HEADLESS if self.config.headless else BrowserMode.HEADED
|
|
58
69
|
|
|
@@ -62,8 +73,12 @@ class BrowserManager(BaseManager):
|
|
|
62
73
|
stealth_warmup_enabled=self.config.stealth_warmup_enabled
|
|
63
74
|
)
|
|
64
75
|
|
|
65
|
-
# Create browser manager
|
|
76
|
+
# Create browser manager with proxy support
|
|
66
77
|
self.browser = CoreBrowserManager(browser_config)
|
|
78
|
+
|
|
79
|
+
# Set proxy if enabled (we'll need to modify CoreBrowserManager to support this)
|
|
80
|
+
if self.config.proxy_enabled and self.config.proxy_url:
|
|
81
|
+
self.browser._proxy_url = self.config.proxy_url
|
|
67
82
|
|
|
68
83
|
# Initialize browser
|
|
69
84
|
await self.browser.initialize_async()
|
|
@@ -13,6 +13,7 @@ from .base import BaseManager, ManagerConfig
|
|
|
13
13
|
class ProxyManagerConfig(ManagerConfig):
|
|
14
14
|
"""Proxy manager configuration."""
|
|
15
15
|
proxies: List[str] = Field(default_factory=list, description="List of proxy URLs")
|
|
16
|
+
single_proxy: Optional[str] = Field(default=None, description="Single proxy URL to use")
|
|
16
17
|
rotation_interval: int = Field(default=300, description="Rotation interval seconds")
|
|
17
18
|
health_check_interval: int = Field(default=60, description="Health check interval")
|
|
18
19
|
|
|
@@ -29,11 +30,17 @@ class ProxyManager(BaseManager):
|
|
|
29
30
|
|
|
30
31
|
async def _initialize(self) -> bool:
|
|
31
32
|
"""Initialize proxy manager."""
|
|
32
|
-
|
|
33
|
+
# Use single proxy if specified, otherwise use proxy list
|
|
34
|
+
if self.config.single_proxy:
|
|
35
|
+
self.active_proxies = [self.config.single_proxy]
|
|
36
|
+
self.current_proxy = self.config.single_proxy
|
|
37
|
+
else:
|
|
38
|
+
self.active_proxies = self.config.proxies.copy()
|
|
39
|
+
if self.active_proxies:
|
|
40
|
+
self.current_proxy = random.choice(self.active_proxies)
|
|
33
41
|
|
|
34
|
-
if
|
|
35
|
-
|
|
36
|
-
# Start rotation task
|
|
42
|
+
# Only start rotation if we have multiple proxies
|
|
43
|
+
if len(self.active_proxies) > 1:
|
|
37
44
|
self._rotation_task = asyncio.create_task(self._rotation_loop())
|
|
38
45
|
|
|
39
46
|
return True
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Utility
|
|
3
|
-
"""
|
|
2
|
+
Utility modules for UnrealOn Driver.
|
|
4
3
|
|
|
4
|
+
Contains cross-platform compatibility and other utility functions.
|
|
5
|
+
"""
|
|
5
6
|
from .time import utc_now
|
|
7
|
+
from .platform_compatibility import (
|
|
8
|
+
PlatformCompatibility,
|
|
9
|
+
ensure_platform_compatibility,
|
|
10
|
+
get_platform_info
|
|
11
|
+
)
|
|
6
12
|
|
|
7
13
|
__all__ = [
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
'utc_now',
|
|
15
|
+
'PlatformCompatibility',
|
|
16
|
+
'ensure_platform_compatibility',
|
|
17
|
+
'get_platform_info'
|
|
18
|
+
]
|