# screen_capturer.py """ Capture d'écran pour agent_v0. v0+ : - capture de l'écran principal en PNG - mode "full" : écran complet - mode "crop" : zone centrée autour d'une position (curseur) - stockage dans sessions//shots/shot_XXXX.png """ from __future__ import annotations import os import time from typing import Tuple, Optional from mss import mss from mss.tools import to_png from config import SESSIONS_ROOT from raw_session import RawSession class ScreenCapturer: """ Gère la capture d'écran pour une session donnée. """ def __init__( self, session: RawSession, base_dir: str = SESSIONS_ROOT, screenshot_mode: str = "full", # "full" | "crop" crop_width: int = 800, crop_height: int = 600, ) -> None: self.session = session self.base_dir = base_dir self._counter = 0 self.screenshot_mode = screenshot_mode self.crop_width = crop_width self.crop_height = crop_height # Résolution écran pour clamp la zone de crop self.screen_width, self.screen_height = self.detect_primary_resolution() def _get_session_shots_dir(self) -> str: session_dir = os.path.join(self.base_dir, self.session.session_id) shots_dir = os.path.join(session_dir, "shots") os.makedirs(shots_dir, exist_ok=True) return shots_dir def capture(self, focus_pos: Optional[Tuple[int, int]] = None) -> Tuple[str, str]: """ Capture l'écran : - si screenshot_mode == "crop" et focus_pos fourni -> zone centrée autour de focus_pos - sinon -> plein écran Retourne (screenshot_id, relative_path) """ self._counter += 1 screenshot_id = f"shot_{self._counter:04d}" shots_dir = self._get_session_shots_dir() filename = f"{screenshot_id}.png" file_path = os.path.join(shots_dir, filename) relative_path = os.path.join("shots", filename) with mss() as sct: if self.screenshot_mode == "crop" and focus_pos is not None: cx, cy = focus_pos w = min(self.crop_width, self.screen_width) h = min(self.crop_height, self.screen_height) left = max(0, cx - w // 2) top = max(0, cy - h // 2) if left + w > self.screen_width: left = self.screen_width - w if top + h > self.screen_height: top = self.screen_height - h region = {"left": int(left), "top": int(top), "width": int(w), "height": int(h)} img = sct.grab(region) else: monitor = sct.monitors[0] # écran principal complet img = sct.grab(monitor) with open(file_path, "wb") as f: f.write(to_png(img.rgb, img.size)) captured_at = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) self.session.add_screenshot( screenshot_id=screenshot_id, relative_path=relative_path, captured_at=captured_at, ) return screenshot_id, relative_path @staticmethod def detect_primary_resolution() -> Tuple[int, int]: """ Renvoie (width, height) de l'écran principal. Utile pour remplir RawSession.environment.screen.primary_resolution. """ with mss() as sct: mon = sct.monitors[0] return mon["width"], mon["height"]