send-img-to-device.js 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. /**
  2. * adb method: send-img-to-device — 将本地图片推送到手机并加入相册(1+ 等 Android 通用)
  3. * inVars: [本地图片路径] 路径可为相对 folderPath 或绝对
  4. */
  5. const { spawnSync } = require('child_process')
  6. const path = require('path')
  7. const fs = require('fs')
  8. const projectRoot = path.resolve(__dirname, '..', '..', '..', '..')
  9. const configPath = path.join(projectRoot, 'configs', 'config.js')
  10. const config = fs.existsSync(configPath) ? require(configPath) : {}
  11. const adbPath = config.adbPath?.path
  12. ? (path.isAbsolute(config.adbPath.path) ? config.adbPath.path : path.resolve(projectRoot, config.adbPath.path))
  13. : path.join(projectRoot, 'lib', 'scrcpy-adb', process.platform === 'win32' ? 'adb.exe' : 'adb')
  14. // Android 10+ 相册对 Pictures 目录更友好,1+ 等机型也常从此处读
  15. const DEVICE_PICTURES = '/sdcard/Pictures/'
  16. const DEVICE_DCIM = '/sdcard/DCIM/'
  17. /**
  18. * 将本地图片推送到设备相册(供 run 与 CLI 复用)
  19. * @param {string} localPath - 本地图片绝对路径
  20. * @param {string} deviceId - 设备 ID
  21. * @param {string} [adbExe] - 可选,默认用 config
  22. */
  23. function doSendImageToDevice(localPath, deviceId, adbExe = adbPath) {
  24. const resolved = path.resolve(localPath)
  25. if (!fs.existsSync(resolved)) {
  26. return { success: false, error: `本地文件不存在: ${resolved}` }
  27. }
  28. const basename = path.basename(resolved)
  29. // 优先推送到 Pictures,Android 10+ 相册更容易识别
  30. const deviceFile = DEVICE_PICTURES + basename
  31. const localForAdb = resolved.replace(/\\/g, '/')
  32. const args = deviceId ? ['-s', deviceId, 'push', localForAdb, deviceFile] : ['push', localForAdb, deviceFile]
  33. const push = spawnSync(adbExe, args, { encoding: 'utf-8', timeout: 30000 })
  34. if (push.status !== 0) {
  35. const err = (push.stderr || push.stdout || '').trim() || `adb push 退出码 ${push.status}`
  36. return { success: false, error: err }
  37. }
  38. // Android 10/11+ 需带 --receiver-include-background 扫描才易在相册中显示
  39. const scan = (d) => {
  40. const base = ['shell', 'am', 'broadcast', '-a', 'android.intent.action.MEDIA_SCANNER_SCAN_FILE', '-d', d, '--receiver-include-background']
  41. const a = deviceId ? ['-s', deviceId, ...base] : base
  42. spawnSync(adbExe, a, { encoding: 'utf-8', timeout: 8000 })
  43. }
  44. scan(`file://${deviceFile}`)
  45. scan('file:///sdcard/Pictures')
  46. scan('file:///sdcard/DCIM')
  47. return { success: true, devicePath: deviceFile }
  48. }
  49. async function run(action, ctx) {
  50. const { device, folderPath, variableContext, extractVarName, logMessage } = ctx
  51. const inVars = action.inVars || []
  52. let localPath = inVars.length > 0 ? (variableContext[extractVarName(inVars[0])] || inVars[0]) : action.value
  53. if (!localPath) return { success: false, error: 'send-img-to-device 操作缺少本地图片路径' }
  54. if (!device || String(device).trim() === '') return { success: false, error: 'send-img-to-device 需要设备 ID,请确保流程已连接设备' }
  55. const fullPath = localPath.startsWith('/') || (localPath.length >= 2 && localPath[1] === ':') ? localPath : path.resolve(folderPath, localPath)
  56. if (!fs.existsSync(fullPath)) return { success: false, error: `本地文件不存在: ${fullPath}(流程目录: ${folderPath})` }
  57. const result = doSendImageToDevice(fullPath, device, adbPath)
  58. if (!result.success) return result
  59. if (logMessage && folderPath) await logMessage(`[send-img-to-device] pushed: ${result.devicePath}`, folderPath).catch(() => {})
  60. return { success: true, devicePath: result.devicePath }
  61. }
  62. module.exports = { run, doSendImageToDevice }