img-cropping.js 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. /**
  2. * fun 标签:img-cropping
  3. * 根据区域坐标裁剪截图指定区域并保存
  4. */
  5. const path = require('path')
  6. const fs = require('fs')
  7. const { spawnSync } = require('child_process')
  8. const { captureScreenshot } = require('../../adb/adb-screencap.js')
  9. const tagName = 'img-cropping'
  10. const projectRoot = path.resolve(__dirname, '..', '..', '..')
  11. const config = require(path.join(projectRoot, 'configs', 'config.js'))
  12. const imgCropScriptPath = path.join(projectRoot, 'python', 'scripts', 'img-crop.py')
  13. /** 解析 area 为 { x, y, width, height } */
  14. function parseAreaToRect(area) {
  15. const obj = typeof area === 'string' ? JSON.parse(area) : area
  16. if (obj.topLeft && obj.bottomRight) {
  17. return {
  18. x: parseInt(obj.topLeft.x, 10),
  19. y: parseInt(obj.topLeft.y, 10),
  20. width: parseInt(obj.bottomRight.x - obj.topLeft.x, 10),
  21. height: parseInt(obj.bottomRight.y - obj.topLeft.y, 10),
  22. }
  23. }
  24. if (obj.topLeft && obj.topRight && obj.bottomLeft && obj.bottomRight) {
  25. return {
  26. x: parseInt(obj.topLeft.x, 10),
  27. y: parseInt(obj.topLeft.y, 10),
  28. width: parseInt(obj.bottomRight.x - obj.topLeft.x, 10),
  29. height: parseInt(obj.bottomRight.y - obj.topLeft.y, 10),
  30. }
  31. }
  32. if (obj.x != null && obj.y != null && obj.width != null && obj.height != null) {
  33. return {
  34. x: parseInt(obj.x, 10),
  35. y: parseInt(obj.y, 10),
  36. width: parseInt(obj.width, 10),
  37. height: parseInt(obj.height, 10),
  38. }
  39. }
  40. return null
  41. }
  42. /** 构建截图路径 */
  43. function buildScreenshotPath(folderPath) {
  44. return path.join(folderPath, 'history', 'ScreenShot.png')
  45. }
  46. /** 构建保存路径 */
  47. function buildSavePath(savePath, folderPath) {
  48. return savePath.includes(':') ? savePath : path.join(folderPath, savePath)
  49. }
  50. /** 调用 Python 裁剪图片并保存 */
  51. function cropAndSaveImage(inputPath, outputPath, x, y, width, height) {
  52. const envVenv = path.join(config.pythonEnvPath, 'env', 'Scripts', 'python.exe')
  53. const venvScripts = path.join(config.pythonEnvPath, 'Scripts', 'python.exe')
  54. const pyEmbedded = path.join(config.pythonEnvPath, 'py', 'python.exe')
  55. const pythonExe = fs.existsSync(envVenv) ? envVenv : (fs.existsSync(venvScripts) ? venvScripts : pyEmbedded)
  56. const r = spawnSync(pythonExe, [imgCropScriptPath, inputPath, outputPath, String(x), String(y), String(width), String(height)], {
  57. encoding: 'utf-8',
  58. timeout: 10000,
  59. cwd: projectRoot
  60. })
  61. if (r.status !== 0) {
  62. return { success: false, error: (r.stderr || r.stdout || '').trim() || '裁剪失败' }
  63. }
  64. return { success: true }
  65. }
  66. /** 执行 img-cropping */
  67. async function executeImgCropping({ area, savePath, folderPath, device }) {
  68. const rect = parseAreaToRect(area)
  69. if (!rect || rect.width <= 0 || rect.height <= 0) {
  70. return { success: false, error: '区域坐标格式不正确' }
  71. }
  72. const screenshotPath = buildScreenshotPath(folderPath)
  73. const outputPath = buildSavePath(savePath, folderPath)
  74. if (device) {
  75. const cap = captureScreenshot(device, screenshotPath)
  76. if (!cap.success) return { success: false, error: cap.error }
  77. }
  78. const crop = cropAndSaveImage(screenshotPath, outputPath, rect.x, rect.y, rect.width, rect.height)
  79. if (!crop.success) return { success: false, error: crop.error }
  80. return { success: true }
  81. }
  82. module.exports = { tagName, executeImgCropping, cropAndSaveImage }