manage-your-printer/scripts/screenshot_tool.py
2025-06-04 10:03:22 +02:00

728 lines
29 KiB
Python

#!/usr/bin/env python3
"""
Automatisches Screenshot-Tool für Mitarbeiterschulungen
======================================================
Dieses Tool erstellt automatisch Screenshots von allen verfügbaren Seiten
der Webanwendung für Schulungszwecke und Präsentationsmaterial.
Funktionen:
- Automatische Erkennung aller verfügbaren Routen
- Screenshots in verschiedenen Auflösungen (Desktop, Tablet, Mobile)
- Strukturierte Ordnerorganisation für Schulungen
- Automatischer Login als Admin
- Detaillierter Bericht über erstellte Screenshots
- Behandlung von geschützten und öffentlichen Bereichen
Autor: KI-Assistant
Datum: 2025-01-16
"""
import os
import sys
import time
import json
import logging
from datetime import datetime, timedelta
from typing import List, Dict, Tuple, Optional, Set
from urllib.parse import urljoin, urlparse
import traceback
import subprocess
# Selenium Imports
try:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options as ChromeOptions
from selenium.webdriver.firefox.options import Options as FirefoxOptions
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.firefox.service import Service as FirefoxService
from selenium.common.exceptions import TimeoutException, WebDriverException, NoSuchElementException
SELENIUM_AVAILABLE = True
except ImportError as e:
print(f"⚠️ Selenium nicht verfügbar: {e}")
print("💡 Installieren Sie Selenium: pip install selenium")
SELENIUM_AVAILABLE = False
# Flask App Import
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
try:
from app import app, User
from models import get_db_session
FLASK_APP_AVAILABLE = True
except ImportError as e:
print(f"⚠️ Flask-App nicht verfügbar: {e}")
FLASK_APP_AVAILABLE = False
# Logging-Konfiguration
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('screenshot_tool.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
class ScreenshotConfiguration:
"""Konfiguration für das Screenshot-Tool"""
# Basis-Konfiguration
BASE_URL = "http://localhost:5000"
ADMIN_EMAIL = "admin@example.com" # Anpassen falls nötig
ADMIN_PASSWORD = "admin123" # Anpassen falls nötig
# Screenshot-Ordner
SCREENSHOT_BASE_DIR = os.path.join("docs", "schulung", "screenshots")
# Browser-Konfiguration
BROWSER_TYPE = "chrome" # "chrome" oder "firefox"
HEADLESS = True # Für Server ohne GUI
# Auflösungen für verschiedene Geräte
RESOLUTIONS = {
"desktop": (1920, 1080),
"tablet": (1024, 768),
"mobile": (375, 667)
}
# Wartezeiten
PAGE_LOAD_TIMEOUT = 15
ELEMENT_WAIT_TIMEOUT = 10
SCREENSHOT_DELAY = 2
# Zu ignorierende Routen (z.B. API-Endpunkte)
IGNORED_ROUTES = {
"/api/", "/auth/api/", "/static/", "/favicon.ico",
"/robots.txt", "/sitemap.xml"
}
# Routen die spezielle Behandlung benötigen
SPECIAL_ROUTES = {
"/admin": "Benötigt Admin-Rechte",
"/user/": "Benötigt User-Login",
"/guest": "Öffentlich zugänglich"
}
class RouteDiscovery:
"""Klasse zur automatischen Erkennung aller verfügbaren Routen"""
def __init__(self):
self.routes = set()
self.protected_routes = set()
self.public_routes = set()
def discover_routes_from_app(self) -> List[str]:
"""Extrahiert alle Routen aus der Flask-App"""
if not FLASK_APP_AVAILABLE:
logger.warning("Flask-App nicht verfügbar - verwende Standard-Routen")
return self._get_default_routes()
discovered_routes = []
try:
with app.app_context():
# Alle URL-Regeln der App durchgehen
for rule in app.url_map.iter_rules():
# Nur GET-Routen für Screenshots
if 'GET' in rule.methods:
route = str(rule.rule)
# Dynamische Parameter ersetzen
if '<' in route:
resolved_routes = self._resolve_dynamic_route(route)
if resolved_routes:
for resolved_route in resolved_routes:
discovered_routes.append(resolved_route)
self._categorize_route(resolved_route)
else:
discovered_routes.append(route)
self._categorize_route(route)
logger.info(f"{len(discovered_routes)} Routen aus Flask-App extrahiert")
return discovered_routes
except Exception as e:
logger.error(f"❌ Fehler beim Extrahieren der Routen: {e}")
return self._get_default_routes()
def _resolve_dynamic_route(self, route: str) -> Optional[List[str]]:
"""Löst dynamische Routen mit Beispieldaten auf"""
resolved_routes = []
try:
if not FLASK_APP_AVAILABLE:
return None
with app.app_context():
db_session = get_db_session()
# User-ID Parameter
if '<int:user_id>' in route:
users = db_session.query(User).limit(3).all()
for user in users:
resolved_routes.append(route.replace('<int:user_id>', str(user.id)))
# Printer-ID Parameter
elif '<int:printer_id>' in route:
# Beispiel-Printer-IDs (1, 2, 3)
for printer_id in [1, 2, 3]:
resolved_routes.append(route.replace('<int:printer_id>', str(printer_id)))
# Job-ID Parameter
elif '<int:job_id>' in route:
# Beispiel-Job-IDs (1, 2, 3)
for job_id in [1, 2, 3]:
resolved_routes.append(route.replace('<int:job_id>', str(job_id)))
# Andere Parameter mit Standard-Werten
elif '<' in route:
# Generische Behandlung
import re
pattern = r'<[^>]+>'
resolved_route = re.sub(pattern, '1', route)
resolved_routes.append(resolved_route)
db_session.close()
except Exception as e:
logger.warning(f"⚠️ Fehler beim Auflösen der dynamischen Route {route}: {e}")
return resolved_routes if resolved_routes else None
def _categorize_route(self, route: str):
"""Kategorisiert Routen als öffentlich oder geschützt"""
if any(protected in route for protected in ['/admin', '/user/', '/dashboard']):
self.protected_routes.add(route)
elif any(public in route for public in ['/guest', '/privacy', '/terms', '/imprint']):
self.public_routes.add(route)
else:
# Standardmäßig als geschützt behandeln
self.protected_routes.add(route)
def _get_default_routes(self) -> List[str]:
"""Fallback-Routen falls automatische Erkennung fehlschlägt"""
return [
"/",
"/dashboard",
"/admin",
"/admin-dashboard",
"/printers",
"/jobs",
"/jobs/new",
"/stats",
"/user/profile",
"/user/settings",
"/admin/users/add",
"/admin/printers/add",
"/reports",
"/maintenance",
"/guest",
"/guest-status",
"/privacy",
"/terms",
"/imprint",
"/demo"
]
class BrowserManager:
"""Verwaltet den Webbrowser für Screenshots"""
def __init__(self, config: ScreenshotConfiguration):
self.config = config
self.driver = None
def initialize_browser(self) -> bool:
"""Initialisiert den Webbrowser"""
try:
if self.config.BROWSER_TYPE.lower() == "chrome":
self.driver = self._setup_chrome()
elif self.config.BROWSER_TYPE.lower() == "firefox":
self.driver = self._setup_firefox()
else:
raise ValueError(f"Unbekannter Browser-Typ: {self.config.BROWSER_TYPE}")
# Browser-Konfiguration
self.driver.set_page_load_timeout(self.config.PAGE_LOAD_TIMEOUT)
self.driver.implicitly_wait(self.config.ELEMENT_WAIT_TIMEOUT)
logger.info(f"✅ Browser {self.config.BROWSER_TYPE} erfolgreich initialisiert")
return True
except Exception as e:
logger.error(f"❌ Fehler beim Initialisieren des Browsers: {e}")
return False
def _setup_chrome(self):
"""Konfiguriert Chrome-Browser"""
options = ChromeOptions()
if self.config.HEADLESS:
options.add_argument('--headless')
# Weitere Chrome-Optionen für Stabilität
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
options.add_argument('--disable-gpu')
options.add_argument('--window-size=1920,1080')
options.add_argument('--disable-notifications')
options.add_argument('--disable-popup-blocking')
try:
# Automatische Erkennung des ChromeDriver
return webdriver.Chrome(options=options)
except Exception as e:
logger.warning(f"⚠️ ChromeDriver automatisch nicht gefunden: {e}")
logger.info("💡 Installieren Sie ChromeDriver oder verwenden Sie webdriver-manager")
raise
def _setup_firefox(self):
"""Konfiguriert Firefox-Browser"""
options = FirefoxOptions()
if self.config.HEADLESS:
options.add_argument('--headless')
try:
return webdriver.Firefox(options=options)
except Exception as e:
logger.warning(f"⚠️ GeckoDriver nicht gefunden: {e}")
logger.info("💡 Installieren Sie GeckoDriver für Firefox")
raise
def set_resolution(self, resolution_name: str):
"""Setzt die Browser-Auflösung"""
if resolution_name in self.config.RESOLUTIONS:
width, height = self.config.RESOLUTIONS[resolution_name]
self.driver.set_window_size(width, height)
logger.debug(f"📱 Auflösung gesetzt: {resolution_name} ({width}x{height})")
def close(self):
"""Schließt den Browser"""
if self.driver:
try:
self.driver.quit()
logger.info("✅ Browser geschlossen")
except Exception as e:
logger.warning(f"⚠️ Fehler beim Schließen des Browsers: {e}")
class ScreenshotTool:
"""Hauptklasse für das Screenshot-Tool"""
def __init__(self, config: ScreenshotConfiguration = None):
self.config = config or ScreenshotConfiguration()
self.browser = BrowserManager(self.config)
self.route_discovery = RouteDiscovery()
self.results = {
"success": [],
"failed": [],
"skipped": [],
"total_screenshots": 0,
"start_time": None,
"end_time": None
}
def run_screenshot_session(self) -> Dict:
"""Führt eine komplette Screenshot-Session durch"""
logger.info("🚀 Starte automatische Screenshot-Erstellung für Schulungen")
self.results["start_time"] = datetime.now()
try:
# 1. Vorbereitung
if not self._prepare_environment():
return self.results
# 2. Browser initialisieren
if not self.browser.initialize_browser():
return self.results
# 3. Routen entdecken
routes = self.route_discovery.discover_routes_from_app()
logger.info(f"📋 {len(routes)} Routen gefunden")
# 4. Login durchführen
if not self._perform_admin_login():
logger.error("❌ Admin-Login fehlgeschlagen")
return self.results
# 5. Screenshots erstellen
self._create_screenshots_for_routes(routes)
# 6. Bericht generieren
self._generate_report()
except KeyboardInterrupt:
logger.info("⏹️ Screenshot-Session vom Benutzer abgebrochen")
except Exception as e:
logger.error(f"❌ Unerwarteter Fehler: {e}")
logger.debug(traceback.format_exc())
finally:
self.browser.close()
self.results["end_time"] = datetime.now()
return self.results
def _prepare_environment(self) -> bool:
"""Bereitet die Umgebung für Screenshots vor"""
try:
# Screenshot-Ordner erstellen
os.makedirs(self.config.SCREENSHOT_BASE_DIR, exist_ok=True)
# Unterordner für verschiedene Kategorien
categories = ["admin", "benutzer", "oeffentlich", "alle_auflösungen"]
for category in categories:
category_dir = os.path.join(self.config.SCREENSHOT_BASE_DIR, category)
os.makedirs(category_dir, exist_ok=True)
# Auflösungs-Unterordner
for resolution in self.config.RESOLUTIONS.keys():
resolution_dir = os.path.join(category_dir, resolution)
os.makedirs(resolution_dir, exist_ok=True)
logger.info(f"✅ Screenshot-Ordner vorbereitet: {self.config.SCREENSHOT_BASE_DIR}")
return True
except Exception as e:
logger.error(f"❌ Fehler bei der Umgebungsvorbereitung: {e}")
return False
def _perform_admin_login(self) -> bool:
"""Führt den Admin-Login durch"""
try:
login_url = urljoin(self.config.BASE_URL, "/auth/login")
self.browser.driver.get(login_url)
# Warten bis Login-Formular geladen ist
wait = WebDriverWait(self.browser.driver, self.config.ELEMENT_WAIT_TIMEOUT)
# Email eingeben
email_field = wait.until(EC.presence_of_element_located((By.NAME, "email")))
email_field.clear()
email_field.send_keys(self.config.ADMIN_EMAIL)
# Passwort eingeben
password_field = self.browser.driver.find_element(By.NAME, "password")
password_field.clear()
password_field.send_keys(self.config.ADMIN_PASSWORD)
# Login-Button klicken
login_button = self.browser.driver.find_element(By.CSS_SELECTOR, "button[type='submit']")
login_button.click()
# Warten auf Weiterleitung zum Dashboard
wait.until(lambda driver: "/dashboard" in driver.current_url or "/admin" in driver.current_url)
logger.info("✅ Admin-Login erfolgreich")
return True
except TimeoutException:
logger.error("❌ Timeout beim Admin-Login")
return False
except NoSuchElementException as e:
logger.error(f"❌ Login-Element nicht gefunden: {e}")
return False
except Exception as e:
logger.error(f"❌ Fehler beim Admin-Login: {e}")
return False
def _create_screenshots_for_routes(self, routes: List[str]):
"""Erstellt Screenshots für alle angegebenen Routen"""
total_routes = len(routes)
for i, route in enumerate(routes, 1):
logger.info(f"📸 Screenshot {i}/{total_routes}: {route}")
# Route ignorieren falls in Ignore-Liste
if any(ignored in route for ignored in self.config.IGNORED_ROUTES):
logger.debug(f"⏭️ Route ignoriert: {route}")
self.results["skipped"].append({"route": route, "reason": "In Ignore-Liste"})
continue
# Screenshots für alle Auflösungen erstellen
for resolution_name in self.config.RESOLUTIONS.keys():
try:
self._take_screenshot_for_route(route, resolution_name)
self.results["total_screenshots"] += 1
except Exception as e:
logger.error(f"❌ Fehler bei Screenshot für {route} ({resolution_name}): {e}")
self.results["failed"].append({
"route": route,
"resolution": resolution_name,
"error": str(e)
})
def _take_screenshot_for_route(self, route: str, resolution_name: str):
"""Erstellt einen Screenshot für eine spezifische Route und Auflösung"""
# Browser-Auflösung setzen
self.browser.set_resolution(resolution_name)
# Zur Seite navigieren
full_url = urljoin(self.config.BASE_URL, route)
self.browser.driver.get(full_url)
# Warten bis Seite geladen ist
time.sleep(self.config.SCREENSHOT_DELAY)
# Kategorie bestimmen
category = self._determine_route_category(route)
# Dateiname generieren
safe_route_name = self._sanitize_filename(route)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{safe_route_name}_{resolution_name}_{timestamp}.png"
# Screenshot-Pfad
screenshot_path = os.path.join(
self.config.SCREENSHOT_BASE_DIR,
category,
resolution_name,
filename
)
# Screenshot erstellen
self.browser.driver.save_screenshot(screenshot_path)
# Erfolg protokollieren
self.results["success"].append({
"route": route,
"resolution": resolution_name,
"file_path": screenshot_path,
"file_size": os.path.getsize(screenshot_path),
"timestamp": datetime.now().isoformat()
})
logger.debug(f"✅ Screenshot gespeichert: {screenshot_path}")
def _determine_route_category(self, route: str) -> str:
"""Bestimmt die Kategorie einer Route für die Ordnerorganisation"""
if any(admin_route in route for admin_route in ['/admin', '/admin-dashboard']):
return "admin"
elif any(user_route in route for user_route in ['/user/', '/dashboard', '/printers', '/jobs']):
return "benutzer"
elif any(public_route in route for public_route in ['/guest', '/privacy', '/terms', '/imprint', '/']):
return "oeffentlich"
else:
return "benutzer" # Standard-Kategorie
def _sanitize_filename(self, route: str) -> str:
"""Bereinigt Routennamen für Dateinamen"""
import re
# Entferne gefährliche Zeichen
safe_name = re.sub(r'[<>:"/\\|?*]', '_', route)
# Ersetze Slashes mit Unterstrichen
safe_name = safe_name.replace('/', '_')
# Entferne führende/nachfolgende Unterstriche
safe_name = safe_name.strip('_')
# Leere Namen behandeln
if not safe_name or safe_name == '_':
safe_name = "home"
return safe_name
def _generate_report(self):
"""Generiert einen detaillierten Bericht über die Screenshot-Session"""
duration = self.results["end_time"] - self.results["start_time"]
report = {
"session_info": {
"start_time": self.results["start_time"].isoformat(),
"end_time": self.results["end_time"].isoformat(),
"duration_seconds": duration.total_seconds(),
"duration_formatted": str(duration)
},
"statistics": {
"total_screenshots": self.results["total_screenshots"],
"successful_screenshots": len(self.results["success"]),
"failed_screenshots": len(self.results["failed"]),
"skipped_routes": len(self.results["skipped"])
},
"results": self.results
}
# Bericht als JSON speichern
report_path = os.path.join(self.config.SCREENSHOT_BASE_DIR, "screenshot_report.json")
with open(report_path, 'w', encoding='utf-8') as f:
json.dump(report, f, indent=2, ensure_ascii=False, default=str)
# Menschenlesbaren Bericht erstellen
self._generate_human_readable_report(report)
logger.info(f"📊 Bericht gespeichert: {report_path}")
def _generate_human_readable_report(self, report: Dict):
"""Erstellt einen menschenlesbaren Bericht"""
report_path = os.path.join(self.config.SCREENSHOT_BASE_DIR, "screenshot_bericht.md")
with open(report_path, 'w', encoding='utf-8') as f:
f.write("# Screenshot-Bericht für Mitarbeiterschulungen\n\n")
f.write(f"**Erstellt am:** {datetime.now().strftime('%d.%m.%Y um %H:%M:%S')}\n\n")
# Zusammenfassung
f.write("## Zusammenfassung\n\n")
f.write(f"- **Gesamte Screenshots:** {report['statistics']['total_screenshots']}\n")
f.write(f"- **Erfolgreich:** {report['statistics']['successful_screenshots']}\n")
f.write(f"- **Fehlgeschlagen:** {report['statistics']['failed_screenshots']}\n")
f.write(f"- **Übersprungen:** {report['statistics']['skipped_routes']}\n")
f.write(f"- **Dauer:** {report['session_info']['duration_formatted']}\n\n")
# Erfolgreiche Screenshots
if self.results["success"]:
f.write("## Erfolgreich erstellte Screenshots\n\n")
for item in self.results["success"]:
f.write(f"- **{item['route']}** ({item['resolution']})\n")
f.write(f" - Datei: `{item['file_path']}`\n")
f.write(f" - Größe: {item['file_size']:,} Bytes\n\n")
# Fehlgeschlagene Screenshots
if self.results["failed"]:
f.write("## Fehlgeschlagene Screenshots\n\n")
for item in self.results["failed"]:
f.write(f"- **{item['route']}** ({item['resolution']})\n")
f.write(f" - Fehler: {item['error']}\n\n")
# Verwendungshinweise
f.write("## Verwendung für Schulungen\n\n")
f.write("### Ordnerstruktur\n\n")
f.write("```\n")
f.write("docs/schulung/screenshots/\n")
f.write("├── admin/ # Administrator-Bereich\n")
f.write("│ ├── desktop/ # Desktop-Auflösung (1920x1080)\n")
f.write("│ ├── tablet/ # Tablet-Auflösung (1024x768)\n")
f.write("│ └── mobile/ # Mobile-Auflösung (375x667)\n")
f.write("├── benutzer/ # Benutzer-Bereich\n")
f.write("│ ├── desktop/\n")
f.write("│ ├── tablet/\n")
f.write("│ └── mobile/\n")
f.write("└── oeffentlich/ # Öffentlicher Bereich\n")
f.write(" ├── desktop/\n")
f.write(" ├── tablet/\n")
f.write(" └── mobile/\n")
f.write("```\n\n")
f.write("### Empfehlungen für Präsentationen\n\n")
f.write("1. **Desktop-Screenshots** für Hauptpräsentationen verwenden\n")
f.write("2. **Mobile-Screenshots** für Responsive-Design-Demonstrationen\n")
f.write("3. **Admin-Ordner** für Administratoren-Schulungen\n")
f.write("4. **Benutzer-Ordner** für allgemeine Mitarbeiterschulungen\n")
f.write("5. **Öffentlich-Ordner** für Gäste-/Kunden-Präsentationen\n\n")
logger.info(f"📝 Menschenlesbarer Bericht gespeichert: {report_path}")
def load_config_from_file(config_file: str = "screenshot_config.json") -> ScreenshotConfiguration:
"""Lädt Konfiguration aus JSON-Datei"""
config = ScreenshotConfiguration()
if os.path.exists(config_file):
try:
with open(config_file, 'r', encoding='utf-8') as f:
config_data = json.load(f)
# Server-Konfiguration laden
if 'server' in config_data:
server_config = config_data['server']
config.BASE_URL = server_config.get('base_url', config.BASE_URL)
config.ADMIN_EMAIL = server_config.get('admin_email', config.ADMIN_EMAIL)
config.ADMIN_PASSWORD = server_config.get('admin_password', config.ADMIN_PASSWORD)
# Browser-Konfiguration laden
if 'browser' in config_data:
browser_config = config_data['browser']
config.BROWSER_TYPE = browser_config.get('type', config.BROWSER_TYPE)
config.HEADLESS = browser_config.get('headless', config.HEADLESS)
config.PAGE_LOAD_TIMEOUT = browser_config.get('page_load_timeout', config.PAGE_LOAD_TIMEOUT)
config.ELEMENT_WAIT_TIMEOUT = browser_config.get('element_wait_timeout', config.ELEMENT_WAIT_TIMEOUT)
config.SCREENSHOT_DELAY = browser_config.get('screenshot_delay', config.SCREENSHOT_DELAY)
# Output-Konfiguration laden
if 'output' in config_data:
output_config = config_data['output']
config.SCREENSHOT_BASE_DIR = output_config.get('base_directory', config.SCREENSHOT_BASE_DIR)
# Auflösungen laden
if 'resolutions' in config_data:
resolutions_config = config_data['resolutions']
new_resolutions = {}
for name, res_data in resolutions_config.items():
if isinstance(res_data, dict) and 'width' in res_data and 'height' in res_data:
new_resolutions[name] = (res_data['width'], res_data['height'])
if new_resolutions:
config.RESOLUTIONS = new_resolutions
logger.info(f"✅ Konfiguration aus {config_file} geladen")
except Exception as e:
logger.warning(f"⚠️ Fehler beim Laden der Konfiguration aus {config_file}: {e}")
logger.info("💡 Verwende Standard-Konfiguration")
else:
logger.info(f"📋 Keine Konfigurationsdatei {config_file} gefunden - verwende Standard-Werte")
return config
def main():
"""Hauptfunktion des Screenshot-Tools"""
print("=" * 60)
print("🎯 AUTOMATISCHES SCREENSHOT-TOOL FÜR SCHULUNGEN")
print("=" * 60)
# Abhängigkeiten prüfen
if not SELENIUM_AVAILABLE:
print("❌ Selenium ist nicht installiert!")
print("💡 Führen Sie aus: pip install selenium")
return 1
# Konfiguration aus Datei laden
config = load_config_from_file()
# Benutzer-Eingaben für Konfiguration (optional)
print("\n📋 KONFIGURATION")
print("-" * 30)
base_url = input(f"Server-URL [{config.BASE_URL}]: ").strip()
if base_url:
config.BASE_URL = base_url
admin_email = input(f"Admin-Email [{config.ADMIN_EMAIL}]: ").strip()
if admin_email:
config.ADMIN_EMAIL = admin_email
admin_password = input(f"Admin-Passwort [{config.ADMIN_PASSWORD}]: ").strip()
if admin_password:
config.ADMIN_PASSWORD = admin_password
headless_input = input(f"Headless-Modus (ohne GUI) [{'Ja' if config.HEADLESS else 'Nein'}]: ").strip().lower()
if headless_input in ['nein', 'n', 'no', 'false']:
config.HEADLESS = False
elif headless_input in ['ja', 'j', 'yes', 'true']:
config.HEADLESS = True
print(f"\n✅ Konfiguration abgeschlossen")
print(f"📂 Screenshots werden gespeichert in: {config.SCREENSHOT_BASE_DIR}")
# Screenshot-Tool starten
tool = ScreenshotTool(config)
results = tool.run_screenshot_session()
# Ergebnisse anzeigen
print("\n" + "=" * 60)
print("📊 ERGEBNISSE")
print("=" * 60)
print(f"✅ Erfolgreich: {len(results['success'])} Screenshots")
print(f"❌ Fehlgeschlagen: {len(results['failed'])} Screenshots")
print(f"⏭️ Übersprungen: {len(results['skipped'])} Routen")
print(f"📸 Gesamt: {results['total_screenshots']} Screenshots")
if results["start_time"] and results["end_time"]:
duration = results["end_time"] - results["start_time"]
print(f"⏱️ Dauer: {duration}")
print(f"\n📁 Alle Screenshots verfügbar in:")
print(f" {os.path.abspath(config.SCREENSHOT_BASE_DIR)}")
return 0 if results["total_screenshots"] > 0 else 1
if __name__ == "__main__":
sys.exit(main())