unrealon 1.1.1__py3-none-any.whl → 1.1.4__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.
Files changed (83) hide show
  1. unrealon/__init__.py +16 -6
  2. unrealon-1.1.4.dist-info/METADATA +658 -0
  3. unrealon-1.1.4.dist-info/RECORD +54 -0
  4. {unrealon-1.1.1.dist-info → unrealon-1.1.4.dist-info}/entry_points.txt +1 -1
  5. unrealon_browser/__init__.py +3 -6
  6. unrealon_browser/core/browser_manager.py +86 -84
  7. unrealon_browser/dto/models/config.py +2 -0
  8. unrealon_browser/managers/captcha.py +165 -185
  9. unrealon_browser/managers/cookies.py +57 -28
  10. unrealon_browser/managers/logger_bridge.py +94 -34
  11. unrealon_browser/managers/profile.py +186 -158
  12. unrealon_browser/managers/stealth.py +58 -47
  13. unrealon_driver/__init__.py +8 -21
  14. unrealon_driver/exceptions.py +5 -0
  15. unrealon_driver/html_analyzer/__init__.py +32 -0
  16. unrealon_driver/{parser/managers/html.py → html_analyzer/cleaner.py} +330 -405
  17. unrealon_driver/html_analyzer/config.py +64 -0
  18. unrealon_driver/html_analyzer/manager.py +247 -0
  19. unrealon_driver/html_analyzer/models.py +115 -0
  20. unrealon_driver/html_analyzer/websocket_analyzer.py +157 -0
  21. unrealon_driver/models/__init__.py +31 -0
  22. unrealon_driver/models/websocket.py +98 -0
  23. unrealon_driver/parser/__init__.py +4 -23
  24. unrealon_driver/parser/cli_manager.py +6 -5
  25. unrealon_driver/parser/daemon_manager.py +242 -66
  26. unrealon_driver/parser/managers/__init__.py +0 -21
  27. unrealon_driver/parser/managers/config.py +15 -3
  28. unrealon_driver/parser/parser_manager.py +225 -395
  29. unrealon_driver/smart_logging/__init__.py +24 -0
  30. unrealon_driver/smart_logging/models.py +44 -0
  31. unrealon_driver/smart_logging/smart_logger.py +406 -0
  32. unrealon_driver/smart_logging/unified_logger.py +525 -0
  33. unrealon_driver/websocket/__init__.py +31 -0
  34. unrealon_driver/websocket/client.py +249 -0
  35. unrealon_driver/websocket/config.py +188 -0
  36. unrealon_driver/websocket/manager.py +90 -0
  37. unrealon-1.1.1.dist-info/METADATA +0 -722
  38. unrealon-1.1.1.dist-info/RECORD +0 -82
  39. unrealon_bridge/__init__.py +0 -114
  40. unrealon_bridge/cli.py +0 -316
  41. unrealon_bridge/client/__init__.py +0 -93
  42. unrealon_bridge/client/base.py +0 -78
  43. unrealon_bridge/client/commands.py +0 -89
  44. unrealon_bridge/client/connection.py +0 -90
  45. unrealon_bridge/client/events.py +0 -65
  46. unrealon_bridge/client/health.py +0 -38
  47. unrealon_bridge/client/html_parser.py +0 -146
  48. unrealon_bridge/client/logging.py +0 -139
  49. unrealon_bridge/client/proxy.py +0 -70
  50. unrealon_bridge/client/scheduler.py +0 -450
  51. unrealon_bridge/client/session.py +0 -70
  52. unrealon_bridge/configs/__init__.py +0 -14
  53. unrealon_bridge/configs/bridge_config.py +0 -212
  54. unrealon_bridge/configs/bridge_config.yaml +0 -39
  55. unrealon_bridge/models/__init__.py +0 -138
  56. unrealon_bridge/models/base.py +0 -28
  57. unrealon_bridge/models/command.py +0 -41
  58. unrealon_bridge/models/events.py +0 -40
  59. unrealon_bridge/models/html_parser.py +0 -79
  60. unrealon_bridge/models/logging.py +0 -55
  61. unrealon_bridge/models/parser.py +0 -63
  62. unrealon_bridge/models/proxy.py +0 -41
  63. unrealon_bridge/models/requests.py +0 -95
  64. unrealon_bridge/models/responses.py +0 -88
  65. unrealon_bridge/models/scheduler.py +0 -592
  66. unrealon_bridge/models/session.py +0 -28
  67. unrealon_bridge/server/__init__.py +0 -91
  68. unrealon_bridge/server/base.py +0 -171
  69. unrealon_bridge/server/handlers/__init__.py +0 -23
  70. unrealon_bridge/server/handlers/command.py +0 -110
  71. unrealon_bridge/server/handlers/html_parser.py +0 -139
  72. unrealon_bridge/server/handlers/logging.py +0 -95
  73. unrealon_bridge/server/handlers/parser.py +0 -95
  74. unrealon_bridge/server/handlers/proxy.py +0 -75
  75. unrealon_bridge/server/handlers/scheduler.py +0 -545
  76. unrealon_bridge/server/handlers/session.py +0 -66
  77. unrealon_driver/browser/__init__.py +0 -8
  78. unrealon_driver/browser/config.py +0 -74
  79. unrealon_driver/browser/manager.py +0 -416
  80. unrealon_driver/parser/managers/browser.py +0 -51
  81. unrealon_driver/parser/managers/logging.py +0 -609
  82. {unrealon-1.1.1.dist-info → unrealon-1.1.4.dist-info}/WHEEL +0 -0
  83. {unrealon-1.1.1.dist-info → unrealon-1.1.4.dist-info}/licenses/LICENSE +0 -0
@@ -9,41 +9,65 @@ import sqlite3
9
9
  from datetime import datetime, timezone
10
10
  from pathlib import Path
11
11
  from typing import Dict, Any, Optional, List, Union
12
-
12
+ import logging
13
13
  from unrealon_browser.dto import ProfileType, ProxyInfo
14
14
 
15
+ logger = logging.getLogger(__name__)
16
+
15
17
 
16
18
  class ProfileManager:
17
19
  """
18
20
  Browser profile lifecycle management with proxy binding
19
-
21
+
20
22
  Layer 2: Profile management capabilities
21
23
  - Proxy-based profile generation
22
24
  - Profile metadata tracking
23
25
  - Automatic cleanup policies
24
26
  - Profile health monitoring
25
27
  """
26
-
27
- def __init__(self, profiles_dir: str = "./browser_profiles"):
28
+
29
+ def __init__(self, profiles_dir: str = "./browser_profiles", logger_bridge=None):
28
30
  """Initialize profile manager"""
29
31
  self.profiles_dir = Path(profiles_dir)
30
32
  self.profiles_dir.mkdir(parents=True, exist_ok=True)
31
-
33
+ self.logger_bridge = logger_bridge
34
+
32
35
  # Initialize metadata database
33
36
  self.db_path = self.profiles_dir / "profiles_metadata.db"
34
37
  self._init_metadata_db()
35
-
38
+
36
39
  self.current_profile_path: Optional[Path] = None
37
40
  self.current_profile_type: Optional[ProfileType] = None
38
41
  self.current_proxy_info: Optional[ProxyInfo] = None
39
-
40
- print(f"📁 Profile manager initialized: {self.profiles_dir}")
41
-
42
+
43
+ self._logger(f"📁 Profile manager initialized: {self.profiles_dir}", "info")
44
+
45
+ def _logger(self, message: str, level: str = "info") -> None:
46
+ if self.logger_bridge:
47
+ if level == "info":
48
+ self.logger_bridge.log_info(message)
49
+ elif level == "error":
50
+ self.logger_bridge.log_error(message)
51
+ elif level == "warning":
52
+ self.logger_bridge.log_warning(message)
53
+ else:
54
+ self.logger_bridge.log_info(message)
55
+ else:
56
+ if level == "info":
57
+ logger.info(message)
58
+ elif level == "error":
59
+ logger.error(message)
60
+ elif level == "warning":
61
+ logger.warning(message)
62
+ else:
63
+ logger.info(message)
64
+
42
65
  def _init_metadata_db(self) -> None:
43
66
  """Initialize SQLite metadata database"""
44
67
  try:
45
68
  with sqlite3.connect(self.db_path) as conn:
46
- conn.execute("""
69
+ conn.execute(
70
+ """
47
71
  CREATE TABLE IF NOT EXISTS profiles (
48
72
  profile_name TEXT PRIMARY KEY,
49
73
  profile_type TEXT NOT NULL,
@@ -60,12 +84,13 @@ class ProfileManager:
60
84
  failure_count INTEGER DEFAULT 0,
61
85
  metadata_json TEXT
62
86
  )
63
- """)
87
+ """
88
+ )
64
89
  conn.commit()
65
- print(" ✅ Profile metadata database initialized")
90
+ self._logger(" ✅ Profile metadata database initialized", "info")
66
91
  except Exception as e:
67
- print(f" ❌ Failed to initialize metadata database: {e}")
68
-
92
+ self._logger(f"❌ Failed to initialize metadata database: {e}", "error")
93
+
69
94
  def _generate_profile_name(self, parser_name: str, proxy_info: Optional[ProxyInfo] = None) -> str:
70
95
  """Generate profile name based on parser and proxy"""
71
96
  if proxy_info:
@@ -75,13 +100,8 @@ class ProfileManager:
75
100
  # Direct connection profile name
76
101
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
77
102
  return f"{parser_name}_direct_{timestamp}"
78
-
79
- def create_profile(
80
- self,
81
- parser_name: str,
82
- proxy_info: Optional[ProxyInfo] = None,
83
- profile_type: ProfileType = ProfileType.PROXY_BOUND
84
- ) -> Path:
103
+
104
+ def create_profile(self, parser_name: str, proxy_info: Optional[ProxyInfo] = None, profile_type: ProfileType = ProfileType.PROXY_BOUND) -> Path:
85
105
  """
86
106
  Create browser profile directory
87
107
  Inspired by unrealparser's profile creation strategy
@@ -90,192 +110,192 @@ class ProfileManager:
90
110
  # Generate profile name
91
111
  profile_name = self._generate_profile_name(parser_name, proxy_info)
92
112
  profile_path = self.profiles_dir / profile_name
93
-
94
- print(f"📁 Creating profile: {profile_name}")
95
- print(f" Type: {profile_type.value}")
113
+
114
+ self._logger(f"📁 Creating profile: {profile_name}", "info")
115
+ self._logger(f" Type: {profile_type.value}", "info")
96
116
  if proxy_info:
97
- print(f" Proxy: {proxy_info.host}:{proxy_info.port}")
98
-
117
+ self._logger(f" Proxy: {proxy_info.host}:{proxy_info.port}", "info")
118
+
99
119
  # Create profile directory
100
120
  profile_path.mkdir(parents=True, exist_ok=True)
101
-
102
- # ✅ LOG: Profile path details for debugging
103
- print(f" 📂 Profile directory created:")
104
- print(f" Path: {profile_path}")
105
- print(f" Absolute: {profile_path.resolve()}")
106
- print(f" Exists: {profile_path.exists()}")
107
- print(f" Is directory: {profile_path.is_dir()}")
108
-
121
+
122
+ self._logger(f" 📂 Profile directory created:", "info")
123
+ self._logger(f" Path: {profile_path}", "info")
124
+ self._logger(f" Absolute: {profile_path.resolve()}", "info")
125
+ self._logger(f" Exists: {profile_path.exists()}", "info")
126
+ self._logger(f" Is directory: {profile_path.is_dir()}", "info")
127
+
109
128
  # Store current profile info
110
129
  self.current_profile_path = profile_path
111
130
  self.current_profile_type = profile_type
112
131
  self.current_proxy_info = proxy_info
113
-
132
+
114
133
  # Save profile metadata to database
115
- self._save_profile_metadata(
116
- profile_name=profile_name,
117
- profile_type=profile_type,
118
- proxy_info=proxy_info,
119
- profile_path=profile_path
120
- )
121
-
122
- print(f" ✅ Profile created: {profile_path}")
134
+ self._save_profile_metadata(profile_name=profile_name, profile_type=profile_type, proxy_info=proxy_info, profile_path=profile_path)
135
+
136
+ self._logger(f" ✅ Profile created: {profile_path}", "info")
123
137
  return profile_path
124
-
138
+
125
139
  except Exception as e:
126
- print(f"❌ Failed to create profile: {e}")
140
+ self._logger(f"❌ Failed to create profile: {e}", "error")
127
141
  raise
128
-
129
- def _save_profile_metadata(
130
- self,
131
- profile_name: str,
132
- profile_type: ProfileType,
133
- proxy_info: Optional[ProxyInfo],
134
- profile_path: Path
135
- ) -> None:
142
+
143
+ def _save_profile_metadata(self, profile_name: str, profile_type: ProfileType, proxy_info: Optional[ProxyInfo], profile_path: Path) -> None:
136
144
  """Save profile metadata to database"""
137
145
  try:
138
146
  # Calculate profile size
139
147
  size_bytes = self._calculate_profile_size(profile_path)
140
-
148
+
141
149
  # Prepare metadata
142
150
  metadata = {
143
151
  "profile_path": str(profile_path),
144
152
  "parser_name": profile_name.split("_")[0],
145
153
  "created_timestamp": datetime.now(timezone.utc).isoformat(),
146
154
  }
147
-
155
+
148
156
  with sqlite3.connect(self.db_path) as conn:
149
- conn.execute("""
157
+ conn.execute(
158
+ """
150
159
  INSERT OR REPLACE INTO profiles (
151
160
  profile_name, profile_type, proxy_host, proxy_port,
152
161
  proxy_username, proxy_password, proxy_ip,
153
162
  created_at, last_used_at, size_bytes, metadata_json
154
163
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
155
- """, (
156
- profile_name,
157
- profile_type.value,
158
- proxy_info.host if proxy_info else None,
159
- proxy_info.port if proxy_info else None,
160
- proxy_info.username if proxy_info else None,
161
- proxy_info.password if proxy_info else None,
162
- proxy_info.ip if proxy_info else None,
163
- datetime.now(timezone.utc).isoformat(),
164
- datetime.now(timezone.utc).isoformat(),
165
- size_bytes,
166
- json.dumps(metadata)
167
- ))
164
+ """,
165
+ (
166
+ profile_name,
167
+ profile_type.value,
168
+ proxy_info.host if proxy_info else None,
169
+ proxy_info.port if proxy_info else None,
170
+ proxy_info.username if proxy_info else None,
171
+ proxy_info.password if proxy_info else None,
172
+ proxy_info.ip if proxy_info else None,
173
+ datetime.now(timezone.utc).isoformat(),
174
+ datetime.now(timezone.utc).isoformat(),
175
+ size_bytes,
176
+ json.dumps(metadata),
177
+ ),
178
+ )
168
179
  conn.commit()
169
-
180
+
170
181
  except Exception as e:
171
- print(f"❌ Failed to save profile metadata: {e}")
172
-
182
+ self._logger(f"❌ Failed to save profile metadata: {e}", "error")
183
+
173
184
  def _calculate_profile_size(self, profile_path: Path) -> int:
174
185
  """Calculate total size of profile directory"""
175
186
  try:
176
187
  if not profile_path.exists():
177
188
  return 0
178
-
189
+
179
190
  total_size = 0
180
191
  for file_path in profile_path.rglob("*"):
181
192
  if file_path.is_file():
182
193
  total_size += file_path.stat().st_size
183
194
  return total_size
184
-
195
+
185
196
  except Exception as e:
186
- print(f"⚠️ Failed to calculate profile size: {e}")
197
+ self._logger(f"⚠️ Failed to calculate profile size: {e}", "warning")
187
198
  return 0
188
-
199
+
189
200
  def get_profile_for_proxy(self, parser_name: str, proxy_info: ProxyInfo) -> Optional[Path]:
190
201
  """Get existing profile for specific proxy"""
191
202
  try:
192
203
  profile_name = self._generate_profile_name(parser_name, proxy_info)
193
204
  profile_path = self.profiles_dir / profile_name
194
-
205
+
195
206
  if profile_path.exists():
196
- print(f"📁 Found existing profile: {profile_name}")
197
-
207
+ self._logger(f"📁 Found existing profile: {profile_name}", "info")
208
+
198
209
  # Update last used timestamp
199
210
  self._update_profile_usage(profile_name)
200
-
211
+
201
212
  # Store current profile info
202
213
  self.current_profile_path = profile_path
203
214
  self.current_profile_type = ProfileType.PROXY_BOUND
204
215
  self.current_proxy_info = proxy_info
205
-
216
+
206
217
  return profile_path
207
-
218
+
208
219
  return None
209
-
220
+
210
221
  except Exception as e:
211
- print(f"❌ Failed to get profile for proxy: {e}")
222
+ self._logger(f"❌ Failed to get profile for proxy: {e}", "error")
212
223
  return None
213
-
224
+
214
225
  def _update_profile_usage(self, profile_name: str) -> None:
215
226
  """Update profile usage statistics"""
216
227
  try:
217
228
  with sqlite3.connect(self.db_path) as conn:
218
- conn.execute("""
229
+ conn.execute(
230
+ """
219
231
  UPDATE profiles
220
232
  SET last_used_at = ?, session_count = session_count + 1
221
233
  WHERE profile_name = ?
222
- """, (datetime.now(timezone.utc).isoformat(), profile_name))
234
+ """,
235
+ (datetime.now(timezone.utc).isoformat(), profile_name),
236
+ )
223
237
  conn.commit()
224
-
238
+
225
239
  except Exception as e:
226
- print(f"⚠️ Failed to update profile usage: {e}")
227
-
240
+ self._logger(f"⚠️ Failed to update profile usage: {e}", "warning")
241
+
228
242
  def mark_session_success(self, success: bool = True) -> None:
229
243
  """Mark current session as success or failure"""
230
244
  if not self.current_profile_path:
231
245
  return
232
-
246
+
233
247
  try:
234
248
  profile_name = self.current_profile_path.name
235
-
249
+
236
250
  with sqlite3.connect(self.db_path) as conn:
237
251
  if success:
238
- conn.execute("""
252
+ conn.execute(
253
+ """
239
254
  UPDATE profiles
240
255
  SET success_count = success_count + 1
241
256
  WHERE profile_name = ?
242
- """, (profile_name,))
257
+ """,
258
+ (profile_name,),
259
+ )
243
260
  else:
244
- conn.execute("""
261
+ conn.execute(
262
+ """
245
263
  UPDATE profiles
246
264
  SET failure_count = failure_count + 1
247
265
  WHERE profile_name = ?
248
- """, (profile_name,))
266
+ """,
267
+ (profile_name,),
268
+ )
249
269
  conn.commit()
250
-
270
+
251
271
  except Exception as e:
252
- print(f"⚠️ Failed to mark session result: {e}")
253
-
272
+ self._logger(f"⚠️ Failed to mark session result: {e}", "warning")
273
+
254
274
  def list_profiles(self, parser_name: Optional[str] = None) -> List[Dict[str, Any]]:
255
275
  """List all profiles with metadata"""
256
276
  try:
257
277
  query = "SELECT * FROM profiles"
258
278
  params = []
259
-
279
+
260
280
  if parser_name:
261
281
  query += " WHERE profile_name LIKE ?"
262
282
  params.append(f"{parser_name}_%")
263
-
283
+
264
284
  query += " ORDER BY last_used_at DESC"
265
-
285
+
266
286
  with sqlite3.connect(self.db_path) as conn:
267
287
  conn.row_factory = sqlite3.Row
268
288
  cursor = conn.execute(query, params)
269
289
  profiles = []
270
-
290
+
271
291
  for row in cursor.fetchall():
272
292
  profile_data = dict(row)
273
-
293
+
274
294
  # Add calculated fields
275
295
  profile_path = Path(profile_data["profile_name"])
276
296
  profile_data["exists"] = (self.profiles_dir / profile_path).exists()
277
297
  profile_data["size_mb"] = profile_data["size_bytes"] / (1024 * 1024) if profile_data["size_bytes"] else 0
278
-
298
+
279
299
  # Calculate success rate
280
300
  total_sessions = profile_data["session_count"] or 0
281
301
  if total_sessions > 0:
@@ -283,104 +303,110 @@ class ProfileManager:
283
303
  profile_data["success_rate"] = success_rate
284
304
  else:
285
305
  profile_data["success_rate"] = 0.0
286
-
306
+
287
307
  profiles.append(profile_data)
288
-
308
+
289
309
  return profiles
290
-
310
+
291
311
  except Exception as e:
292
- print(f"❌ Failed to list profiles: {e}")
312
+ self._logger(f"❌ Failed to list profiles: {e}", "error")
293
313
  return []
294
-
314
+
295
315
  def cleanup_old_profiles(self, max_age_days: int = 30, max_size_mb: int = 1000) -> int:
296
316
  """
297
317
  Cleanup old or oversized profiles
298
318
  Inspired by unrealparser's cleanup strategy
299
319
  """
300
320
  try:
301
- print(f"🧹 Cleaning up profiles older than {max_age_days} days or larger than {max_size_mb}MB")
302
-
321
+ self._logger(f"🧹 Cleaning up profiles older than {max_age_days} days or larger than {max_size_mb}MB", "info")
322
+
303
323
  cutoff_date = datetime.now(timezone.utc).timestamp() - (max_age_days * 24 * 3600)
304
324
  cleaned_count = 0
305
-
325
+
306
326
  profiles = self.list_profiles()
307
-
327
+
308
328
  for profile in profiles:
309
329
  should_delete = False
310
330
  reason = ""
311
-
331
+
312
332
  # Check age
313
333
  if profile["created_at"]:
314
- created_timestamp = datetime.fromisoformat(profile["created_at"].replace('Z', '+00:00')).timestamp()
334
+ created_timestamp = datetime.fromisoformat(profile["created_at"].replace("Z", "+00:00")).timestamp()
315
335
  if created_timestamp < cutoff_date:
316
336
  should_delete = True
317
337
  reason = f"older than {max_age_days} days"
318
-
338
+
319
339
  # Check size
320
340
  if profile["size_mb"] > max_size_mb:
321
341
  should_delete = True
322
342
  reason = f"larger than {max_size_mb}MB ({profile['size_mb']:.1f}MB)"
323
-
343
+
324
344
  if should_delete and profile["exists"]:
325
345
  profile_path = self.profiles_dir / profile["profile_name"]
326
346
  try:
327
347
  shutil.rmtree(profile_path)
328
- print(f" 🗑️ Deleted profile {profile['profile_name']} ({reason})")
348
+ self._logger(f" 🗑️ Deleted profile {profile['profile_name']} ({reason})", "info")
329
349
  cleaned_count += 1
330
-
350
+
331
351
  # Remove from database
332
352
  with sqlite3.connect(self.db_path) as conn:
333
353
  conn.execute("DELETE FROM profiles WHERE profile_name = ?", (profile["profile_name"],))
334
354
  conn.commit()
335
-
355
+
336
356
  except Exception as e:
337
- print(f" ❌ Failed to delete profile {profile['profile_name']}: {e}")
338
-
339
- print(f"✅ Cleanup completed: {cleaned_count} profiles removed")
357
+ self._logger(f" ❌ Failed to delete profile {profile['profile_name']}: {e}", "error")
358
+
359
+ self._logger(f"✅ Cleanup completed: {cleaned_count} profiles removed", "info")
340
360
  return cleaned_count
341
-
361
+
342
362
  except Exception as e:
343
- print(f"❌ Profile cleanup failed: {e}")
363
+ self._logger(f"❌ Profile cleanup failed: {e}", "error")
344
364
  return 0
345
-
365
+
346
366
  def get_profile_statistics(self) -> Dict[str, Any]:
347
367
  """Get overall profile statistics"""
348
368
  try:
349
369
  with sqlite3.connect(self.db_path) as conn:
350
370
  conn.row_factory = sqlite3.Row
351
-
371
+
352
372
  # Basic counts
353
373
  cursor = conn.execute("SELECT COUNT(*) as total_profiles FROM profiles")
354
374
  total_profiles = cursor.fetchone()["total_profiles"]
355
-
375
+
356
376
  # Profile types
357
- cursor = conn.execute("""
377
+ cursor = conn.execute(
378
+ """
358
379
  SELECT profile_type, COUNT(*) as count
359
380
  FROM profiles
360
381
  GROUP BY profile_type
361
- """)
382
+ """
383
+ )
362
384
  profile_types = {row["profile_type"]: row["count"] for row in cursor.fetchall()}
363
-
385
+
364
386
  # Size statistics
365
- cursor = conn.execute("""
387
+ cursor = conn.execute(
388
+ """
366
389
  SELECT
367
390
  SUM(size_bytes) as total_size,
368
391
  AVG(size_bytes) as avg_size,
369
392
  MAX(size_bytes) as max_size
370
393
  FROM profiles
371
- """)
394
+ """
395
+ )
372
396
  size_stats = cursor.fetchone()
373
-
397
+
374
398
  # Session statistics
375
- cursor = conn.execute("""
399
+ cursor = conn.execute(
400
+ """
376
401
  SELECT
377
402
  SUM(session_count) as total_sessions,
378
403
  SUM(success_count) as total_successes,
379
404
  SUM(failure_count) as total_failures
380
405
  FROM profiles
381
- """)
406
+ """
407
+ )
382
408
  session_stats = cursor.fetchone()
383
-
409
+
384
410
  return {
385
411
  "total_profiles": total_profiles,
386
412
  "profile_types": profile_types,
@@ -390,47 +416,49 @@ class ProfileManager:
390
416
  "total_sessions": session_stats["total_sessions"] or 0,
391
417
  "total_successes": session_stats["total_successes"] or 0,
392
418
  "total_failures": session_stats["total_failures"] or 0,
393
- "overall_success_rate": (
394
- (session_stats["total_successes"] or 0) / (session_stats["total_sessions"] or 1)
395
- if session_stats["total_sessions"] else 0.0
396
- ),
419
+ "overall_success_rate": ((session_stats["total_successes"] or 0) / (session_stats["total_sessions"] or 1) if session_stats["total_sessions"] else 0.0),
397
420
  }
398
-
421
+
399
422
  except Exception as e:
400
- print(f"❌ Failed to get profile statistics: {e}")
423
+ self._logger(f"❌ Failed to get profile statistics: {e}", "error")
401
424
  return {}
402
-
425
+
403
426
  def print_profile_statistics(self) -> None:
404
427
  """Print profile statistics"""
405
428
  stats = self.get_profile_statistics()
406
-
429
+
407
430
  print("\n📁 Profile Manager Statistics:")
408
431
  print(f" Total profiles: {stats.get('total_profiles', 0)}")
409
432
  print(f" Total size: {stats.get('total_size_mb', 0):.1f}MB")
410
433
  print(f" Average size: {stats.get('avg_size_mb', 0):.1f}MB")
411
434
  print(f" Total sessions: {stats.get('total_sessions', 0)}")
412
435
  print(f" Success rate: {stats.get('overall_success_rate', 0):.1%}")
413
-
414
- profile_types = stats.get('profile_types', {})
436
+
437
+ profile_types = stats.get("profile_types", {})
415
438
  if profile_types:
416
- print(" Profile types:")
417
- for ptype, count in profile_types.items():
418
- print(f" {ptype}: {count}")
419
-
439
+ if self.logger_bridge:
440
+ self.logger_bridge.log_info(" Profile types:")
441
+ for ptype, count in profile_types.items():
442
+ self.logger_bridge.log_info(f" {ptype}: {count}")
443
+
420
444
  def get_current_profile_info(self) -> Optional[Dict[str, Any]]:
421
445
  """Get information about current active profile"""
422
446
  if not self.current_profile_path:
423
447
  return None
424
-
448
+
425
449
  return {
426
450
  "profile_path": str(self.current_profile_path),
427
451
  "profile_name": self.current_profile_path.name,
428
452
  "profile_type": self.current_profile_type.value if self.current_profile_type else None,
429
- "proxy_info": {
430
- "host": self.current_proxy_info.host,
431
- "port": self.current_proxy_info.port,
432
- "ip": self.current_proxy_info.ip,
433
- } if self.current_proxy_info else None,
453
+ "proxy_info": (
454
+ {
455
+ "host": self.current_proxy_info.host,
456
+ "port": self.current_proxy_info.port,
457
+ "ip": self.current_proxy_info.ip,
458
+ }
459
+ if self.current_proxy_info
460
+ else None
461
+ ),
434
462
  "exists": self.current_profile_path.exists(),
435
463
  "size_mb": self._calculate_profile_size(self.current_profile_path) / (1024 * 1024),
436
464
  }