diff --git a/browser/playwright_manager.py b/browser/playwright_manager.py index 1f9191e..d858482 100644 --- a/browser/playwright_manager.py +++ b/browser/playwright_manager.py @@ -26,10 +26,15 @@ class PlaywrightManager: headless: bool = False, proxy: Optional[Dict[str, str]] = None, browser_type: str = "chromium", + browser_channel: Optional[str] = None, user_agent: Optional[str] = None, screenshots_dir: str = "screenshots", slowmo: int = 0, - window_position: Optional[Tuple[int, int]] = None): + window_position: Optional[Tuple[int, int]] = None, + enable_autoplay_flag: bool = True, + auto_click_video: bool = True, + auto_mute_on_play: bool = True, + enable_video_enhancements: bool = False): """ Initialisiert den PlaywrightManager. @@ -45,10 +50,15 @@ class PlaywrightManager: self.headless = headless self.proxy = proxy self.browser_type = browser_type + self.browser_channel = browser_channel self.user_agent = user_agent self.screenshots_dir = screenshots_dir self.slowmo = slowmo self.window_position = window_position + self.enable_autoplay_flag = enable_autoplay_flag + self.auto_click_video = auto_click_video + self.auto_mute_on_play = auto_mute_on_play + self.enable_video_enhancements = enable_video_enhancements # Stelle sicher, dass das Screenshots-Verzeichnis existiert os.makedirs(self.screenshots_dir, exist_ok=True) @@ -62,7 +72,8 @@ class PlaywrightManager: # Zähler für Wiederhholungsversuche self.retry_counter = {} - # Lade Stealth-Konfigurationen + # Lade Browser-/Stealth-Konfigurationen + self.browser_config = self._load_browser_config() self.stealth_config = self._load_stealth_config() # Browser Protection Service @@ -92,6 +103,27 @@ class PlaywrightManager: "fingerprint_noise": True, "device_scale_factor": 1.0, } + + def _load_browser_config(self) -> Dict[str, Any]: + """Lädt Browser-bezogene Konfigurationen (Channel, Autoplay-Flags, Video-Helper).""" + try: + config_dir = Path(__file__).parent.parent / "config" + browser_config_path = config_dir / "browser_config.json" + if browser_config_path.exists() and browser_config_path.stat().st_size > 0: + with open(browser_config_path, 'r', encoding='utf-8') as f: + data = json.load(f) + return data if isinstance(data, dict) else {} + except Exception as e: + logger.warning(f"Konnte Browser-Konfiguration nicht laden: {e}") + + # Defaults: Chrome/Edge Channel und Autoplay erlauben + return { + "channel": "chrome", # Alternativ: "msedge" + "enable_autoplay_flag": True, + "auto_click_video": True, + "auto_mute_on_play": True, + "enable_video_enhancements": False + } def start(self) -> Page: """ @@ -124,8 +156,14 @@ class PlaywrightManager: '--disable-features=IsolateOrigins,site-per-process', '--disable-site-isolation-trials', ]) + # Autoplay-Blockade aufheben (für Video-Tests/Automation) + # Aktiviert nur, wenn konfiguriert + if self.enable_autoplay_flag if self.enable_autoplay_flag is not None else self.browser_config.get("enable_autoplay_flag", True): + browser_args.append('--autoplay-policy=no-user-gesture-required') # Browser-Launch-Optionen + # Speichere Args für mögliche Erweiterungen (z.B. Gmail Fresh Browser) + self.browser_args = browser_args launch_options = { "headless": self.headless, "args": browser_args, @@ -139,8 +177,33 @@ class PlaywrightManager: f'--window-position={x},{y}' ]) - # Browser starten - self.browser = browser_instance.launch(**launch_options) + # Für Chromium optional einen Channel (chrome/msedge) nutzen + # Fallback: Ohne Channel starten, wenn der Channel nicht verfügbar ist + if self.browser_type == "chromium": + # Bevorzugung: expliziter Parameter > Konfigurationswert + channel = self.browser_channel if self.browser_channel else self.browser_config.get("channel") + tried_channel = False + if channel: + try: + logger.info(f"Starte Chromium mit Channel '{channel}' (für H.264/AAC Unterstützung)") + self.browser = browser_instance.launch(channel=channel, **launch_options) + tried_channel = True + except Exception as e: + logger.warning(f"Start mit Channel '{channel}' fehlgeschlagen: {e}.") + # Zweiter Versuch mit alternativem Channel (msedge/chrome) + alt = 'msedge' if channel.startswith('chrome') else 'chrome' + try: + logger.info(f"Versuche alternativen Channel '{alt}'") + self.browser = browser_instance.launch(channel=alt, **launch_options) + tried_channel = True + except Exception as e2: + logger.warning(f"Auch alternativer Channel '{alt}' fehlgeschlagen: {e2}. Fallback auf gebundleten Chromium.") + # Fallback ohne Channel unten + if not tried_channel or self.browser is None: + self.browser = browser_instance.launch(**launch_options) + else: + # Andere Browser-Typen normal starten + self.browser = browser_instance.launch(**launch_options) # Kontext-Optionen für Stealth-Modus context_options = { @@ -161,6 +224,16 @@ class PlaywrightManager: # Browserkontext erstellen self.context = self.browser.new_context(**context_options) + + # Optional: Video Stealth/Kompatibilitäts-Verbesserungen anwenden + try: + enable_video_enh = self.enable_video_enhancements if self.enable_video_enhancements is not None else self.browser_config.get("enable_video_enhancements", False) + if enable_video_enh: + from browser.video_stealth_enhancement import VideoStealthEnhancement + VideoStealthEnhancement(self.context).apply_video_stealth() + logger.info("Video-Stealth-Verbesserungen aktiviert") + except Exception as e: + logger.warning(f"Konnte Video-Stealth-Verbesserungen nicht anwenden: {e}") # JavaScript-Fingerprinting-Schutz self._apply_stealth_scripts() @@ -288,11 +361,65 @@ class PlaywrightManager: try: logger.info(f"Navigiere zu: {url}") self.page.goto(url, wait_until=wait_until, timeout=timeout) + # Nach erfolgreicher Navigation: Versuche Videos zu starten (optional) + try: + enable_auto_kick = self.enable_autoplay_flag if self.enable_autoplay_flag is not None else self.browser_config.get("enable_autoplay_flag", True) + if enable_auto_kick: + self._kickstart_video_playback() + except Exception: + pass return True except Exception as e: logger.error(f"Fehler bei der Navigation zu {url}: {e}") self.take_screenshot(f"navigation_error_{int(time.time())}") return False + + def _kickstart_video_playback(self) -> None: + """ + Versucht, die Wiedergabe von