Playwright_Videoabspielen
Dieser Commit ist enthalten in:
@ -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 <video>-Elementen zu starten.
|
||||
- Klickt das erste Video (simuliert User-Gesture)
|
||||
- Setzt muted=true und ruft play() auf allen Videos auf
|
||||
"""
|
||||
if self.page is None:
|
||||
return
|
||||
try:
|
||||
# Kurzes Warten auf eventuelle Video-Elemente
|
||||
self.page.wait_for_selector('video', timeout=1500)
|
||||
except Exception:
|
||||
# Kein Video gefunden – nichts zu tun
|
||||
return
|
||||
|
||||
# Optional: Klick auf erstes Video-Element
|
||||
try:
|
||||
if self.auto_click_video if self.auto_click_video is not None else self.browser_config.get("auto_click_video", True):
|
||||
self.page.click('video', timeout=1000)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Erzwinge (stumm) die Wiedergabe aller Videos über JS
|
||||
try:
|
||||
muted = self.auto_mute_on_play if self.auto_mute_on_play is not None else self.browser_config.get("auto_mute_on_play", True)
|
||||
js = (
|
||||
"() => {\n"
|
||||
" const vids = Array.from(document.querySelectorAll('video'));\n"
|
||||
" let started = 0;\n"
|
||||
" for (const v of vids) {\n"
|
||||
" try {\n"
|
||||
" v.playsInline = true;\n"
|
||||
" v.setAttribute('playsinline', '');\n"
|
||||
+ (" v.muted = true;\n" if muted else " v.muted = false;\n") +
|
||||
" const p = v.play();\n"
|
||||
" if (p && typeof p.catch === 'function') p.catch(() => {});\n"
|
||||
" started++;\n"
|
||||
" } catch (e) {}\n"
|
||||
" }\n"
|
||||
" return started;\n"
|
||||
"}"
|
||||
)
|
||||
started = self.page.evaluate(js)
|
||||
logger.debug(f"Video-Wiedergabe gestartet für {started} Elemente")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def wait_for_selector(self, selector: str, timeout: int = 30000) -> Optional[ElementHandle]:
|
||||
"""
|
||||
@ -903,4 +1030,4 @@ if __name__ == "__main__":
|
||||
# Browser starten und zu einer Seite navigieren
|
||||
with PlaywrightManager(headless=False) as manager:
|
||||
manager.navigate_to("https://www.instagram.com")
|
||||
time.sleep(5) # Kurze Pause zum Anzeigen der Seite
|
||||
time.sleep(5) # Kurze Pause zum Anzeigen der Seite
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"current_version": "1.0.0",
|
||||
"last_check": "2025-07-19T01:32:14.527285",
|
||||
"last_check": "2025-10-18T21:12:40.353264",
|
||||
"channel": "stable",
|
||||
"auto_check": true,
|
||||
"auto_download": false
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
"channel": "chrome",
|
||||
"enable_autoplay_flag": true,
|
||||
"auto_click_video": true,
|
||||
"auto_mute_on_play": true,
|
||||
"enable_video_enhancements": false
|
||||
}
|
||||
|
||||
Binäre Datei nicht angezeigt.
@ -43,8 +43,15 @@ def setup_logger(name="main", level=logging.DEBUG):
|
||||
|
||||
logger.setLevel(level)
|
||||
|
||||
# Datehandler
|
||||
log_file = os.path.join("logs", f"{name}.log")
|
||||
# Dateihandler: Stelle sicher, dass das Log-Verzeichnis existiert
|
||||
log_dir = os.path.join("logs")
|
||||
try:
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
except Exception:
|
||||
# Falls das Anlegen fehlschlägt, weiter mit aktuellem Verzeichnis
|
||||
pass
|
||||
|
||||
log_file = os.path.join(log_dir, f"{name}.log")
|
||||
file_handler = logging.FileHandler(log_file)
|
||||
file_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren