124 lines
3.7 KiB
TypeScript
124 lines
3.7 KiB
TypeScript
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<ScreenCaptureResult> {
|
|
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<ScreenCaptureResult> {
|
|
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,
|
|
};
|
|
}
|
|
} |