Spaces:
Sleeping
Sleeping
| """ | |
| Debug Recorder - записывает действия браузера для отладки | |
| """ | |
| import os | |
| import json | |
| import time | |
| from datetime import datetime | |
| from pathlib import Path | |
| from typing import Optional, List, Dict, Any | |
| class DebugRecorder: | |
| """ | |
| Записывает все действия браузера для последующего анализа. | |
| Сохраняет: скриншоты, HTML, логи, сетевые запросы. | |
| """ | |
| def __init__(self, session_name: str = None, output_dir: str = None): | |
| """ | |
| Args: | |
| session_name: Имя сессии (по умолчанию timestamp) | |
| output_dir: Директория для записи (по умолчанию autoreg/debug_sessions) | |
| """ | |
| self.enabled = os.environ.get('DEBUG_RECORDING', '0') == '1' | |
| if not self.enabled: | |
| return | |
| timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') | |
| self.session_name = session_name or f"session_{timestamp}" | |
| # Создаём директорию для сессии | |
| base_dir = Path(output_dir) if output_dir else Path(__file__).parent.parent / 'debug_sessions' | |
| self.session_dir = base_dir / self.session_name | |
| self.session_dir.mkdir(parents=True, exist_ok=True) | |
| # Счётчики для именования файлов | |
| self._step_counter = 0 | |
| self._screenshot_counter = 0 | |
| # Лог действий | |
| self._actions: List[Dict[str, Any]] = [] | |
| # Метаданные сессии | |
| self._metadata = { | |
| 'session_name': self.session_name, | |
| 'started_at': datetime.now().isoformat(), | |
| 'platform': os.name, | |
| } | |
| print(f"[DEBUG] Recording enabled: {self.session_dir}") | |
| def record_action(self, action_type: str, details: Dict[str, Any] = None, | |
| page=None, screenshot: bool = True): | |
| """ | |
| Записывает действие. | |
| Args: | |
| action_type: Тип действия (navigate, click, type, wait, etc.) | |
| details: Детали действия | |
| page: Объект страницы для скриншота | |
| screenshot: Делать ли скриншот | |
| """ | |
| if not self.enabled: | |
| return | |
| self._step_counter += 1 | |
| timestamp = datetime.now().isoformat() | |
| action = { | |
| 'step': self._step_counter, | |
| 'timestamp': timestamp, | |
| 'type': action_type, | |
| 'details': details or {}, | |
| } | |
| # Добавляем URL если есть page | |
| if page: | |
| try: | |
| action['url'] = page.url | |
| except: | |
| pass | |
| # Делаем скриншот | |
| if screenshot and page: | |
| screenshot_path = self._take_screenshot(page, f"step_{self._step_counter:03d}_{action_type}") | |
| if screenshot_path: | |
| action['screenshot'] = screenshot_path.name | |
| self._actions.append(action) | |
| # Сохраняем лог после каждого действия | |
| self._save_log() | |
| def record_page_html(self, page, name: str = None): | |
| """Сохраняет HTML страницы""" | |
| if not self.enabled: | |
| return | |
| try: | |
| html = page.html | |
| filename = f"{self._step_counter:03d}_{name or 'page'}.html" | |
| filepath = self.session_dir / filename | |
| filepath.write_text(html, encoding='utf-8') | |
| return filepath | |
| except Exception as e: | |
| print(f"[DEBUG] Failed to save HTML: {e}") | |
| return None | |
| def record_network(self, logs: List[Dict]): | |
| """Сохраняет сетевые логи""" | |
| if not self.enabled: | |
| return | |
| filepath = self.session_dir / 'network_logs.json' | |
| with open(filepath, 'w', encoding='utf-8') as f: | |
| json.dump(logs, f, indent=2, ensure_ascii=False) | |
| def record_error(self, error: str, page=None): | |
| """Записывает ошибку с полным контекстом""" | |
| if not self.enabled: | |
| return | |
| self.record_action('error', {'message': error}, page, screenshot=True) | |
| # Сохраняем HTML при ошибке | |
| if page: | |
| self.record_page_html(page, 'error_page') | |
| def _take_screenshot(self, page, name: str) -> Optional[Path]: | |
| """Делает скриншот""" | |
| try: | |
| self._screenshot_counter += 1 | |
| filename = f"{name}.png" | |
| filepath = self.session_dir / filename | |
| page.get_screenshot(path=str(filepath)) | |
| return filepath | |
| except Exception as e: | |
| print(f"[DEBUG] Screenshot failed: {e}") | |
| return None | |
| def _save_log(self): | |
| """Сохраняет лог действий""" | |
| log_data = { | |
| 'metadata': self._metadata, | |
| 'actions': self._actions, | |
| } | |
| filepath = self.session_dir / 'actions.json' | |
| with open(filepath, 'w', encoding='utf-8') as f: | |
| json.dump(log_data, f, indent=2, ensure_ascii=False) | |
| def finish(self): | |
| """Завершает запись сессии""" | |
| if not self.enabled: | |
| return | |
| self._metadata['finished_at'] = datetime.now().isoformat() | |
| self._metadata['total_steps'] = self._step_counter | |
| self._save_log() | |
| print(f"[DEBUG] Session saved: {self.session_dir}") | |
| print(f"[DEBUG] Total steps: {self._step_counter}") | |
| # Глобальный рекордер | |
| _recorder: Optional[DebugRecorder] = None | |
| def get_recorder() -> Optional[DebugRecorder]: | |
| """Возвращает глобальный рекордер""" | |
| return _recorder | |
| def init_recorder(session_name: str = None) -> DebugRecorder: | |
| """Инициализирует глобальный рекордер""" | |
| global _recorder | |
| _recorder = DebugRecorder(session_name) | |
| return _recorder | |
| def record(action_type: str, details: Dict = None, page=None, screenshot: bool = True): | |
| """Shortcut для записи действия""" | |
| if _recorder: | |
| _recorder.record_action(action_type, details, page, screenshot) | |