import { screen, Region } from '@nut-tree-fork/nut-js'; import type { ScreenRegion } from '../../../domain/value-objects/ScreenRegion'; import type { ScreenCaptureResult } from '../../../application/ports/IScreenAutomation'; import type { ILogger } from '../../../application/ports/ILogger'; import { NoOpLogAdapter } from '../logging/NoOpLogAdapter'; /** * Service for capturing screen regions using nut.js. * Provides screen capture functionality for template matching and visual automation. */ export class ScreenRecognitionService { private logger: ILogger; constructor(logger?: ILogger) { this.logger = logger ?? new NoOpLogAdapter(); } /** * Capture the entire screen. * @returns ScreenCaptureResult with image buffer data */ async captureFullScreen(): Promise { const startTime = Date.now(); try { const width = await screen.width(); const height = await screen.height(); this.logger.debug('Capturing full screen', { width, height }); const image = await screen.grab(); const data = await image.toRGB(); const durationMs = Date.now() - startTime; this.logger.debug('Screen capture completed', { durationMs, width, height }); return { success: true, data: Buffer.from(data.data), width: data.width, height: data.height, }; } catch (error) { const errorMsg = `Screen capture failed: ${error}`; this.logger.error('Screen capture failed', error instanceof Error ? error : new Error(errorMsg)); return { success: false, error: errorMsg, }; } } /** * Capture a specific region of the screen. * @param region - The rectangular region to capture * @returns ScreenCaptureResult with image buffer data */ async captureRegion(region: ScreenRegion): Promise { const startTime = Date.now(); try { this.logger.debug('Capturing screen region', { region }); const nutRegion = new Region(region.x, region.y, region.width, region.height); const image = await screen.grabRegion(nutRegion); const data = await image.toRGB(); const durationMs = Date.now() - startTime; this.logger.debug('Region capture completed', { durationMs, region }); return { success: true, data: Buffer.from(data.data), width: data.width, height: data.height, }; } catch (error) { const errorMsg = `Region capture failed: ${error}`; this.logger.error('Region capture failed', error instanceof Error ? error : new Error(errorMsg), { region }); return { success: false, error: errorMsg, }; } } /** * Get the current screen dimensions. * @returns Object with width and height, or null on error */ async getScreenDimensions(): Promise<{ width: number; height: number } | null> { try { const width = await screen.width(); const height = await screen.height(); return { width, height }; } catch (error) { this.logger.error('Failed to get screen dimensions', error instanceof Error ? error : new Error(String(error))); return null; } } /** * Convert ScreenRegion to nut.js Region. * Utility method for internal use. */ toNutRegion(region: ScreenRegion): Region { return new Region(region.x, region.y, region.width, region.height); } /** * Convert nut.js Region to ScreenRegion. * Utility method for internal use. */ fromNutRegion(nutRegion: Region): ScreenRegion { return { x: nutRegion.left, y: nutRegion.top, width: nutRegion.width, height: nutRegion.height, }; } }