send-img-to-device.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  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. // .../nodejs/ef-compiler/actions/fun/adb/send-img-to-device.js → 上溯 5 层到仓库根
  9. const defaultRoot = path.resolve(__dirname, '..', '..', '..', '..', '..')
  10. const configPath = process.env.STATIC_ROOT
  11. ? path.join(path.dirname(path.resolve(process.env.STATIC_ROOT)), 'config.js')
  12. : path.join(defaultRoot, 'config.js')
  13. const config = fs.existsSync(configPath) ? require(configPath) : {}
  14. const projectRoot = (config.projectRoot && fs.existsSync(config.projectRoot)) ? config.projectRoot : defaultRoot
  15. const adbPath = config.adbPath?.path
  16. ? (path.isAbsolute(config.adbPath.path) ? config.adbPath.path : path.resolve(projectRoot, config.adbPath.path))
  17. : path.join(projectRoot, 'lib', 'scrcpy-adb', process.platform === 'win32' ? 'adb.exe' : 'adb')
  18. // Android 10+ 相册对 Pictures 目录更友好,1+ 等机型也常从此处读
  19. const DEVICE_PICTURES = '/sdcard/Pictures/'
  20. const DEVICE_DCIM = '/sdcard/DCIM/'
  21. /**
  22. * 将本地图片推送到设备相册(供 run 与 CLI 复用)
  23. * @param {string} localPath - 本地图片绝对路径
  24. * @param {string} deviceId - 设备 ID
  25. * @param {string} [adbExe] - 可选,默认用 config
  26. */
  27. function doSendImageToDevice(localPath, deviceId, adbExe = adbPath) {
  28. const resolved = path.resolve(localPath)
  29. if (!fs.existsSync(resolved)) {
  30. return { success: false, error: `本地文件不存在: ${resolved}` }
  31. }
  32. const basename = path.basename(resolved)
  33. // 优先推送到 Pictures,Android 10+ 相册更容易识别
  34. const deviceFile = DEVICE_PICTURES + basename
  35. const localForAdb = resolved.replace(/\\/g, '/')
  36. const args = deviceId ? ['-s', deviceId, 'push', localForAdb, deviceFile] : ['push', localForAdb, deviceFile]
  37. const push = spawnSync(adbExe, args, { encoding: 'utf-8', timeout: 60000 })
  38. const ok = push.status === 0
  39. if (!ok) {
  40. const msg = (push.stderr || push.stdout || '').trim()
  41. const code = push.status
  42. const sig = push.signal
  43. let err = msg || (code != null ? `adb push 退出码 ${code}` : sig ? `adb push 被终止 (${sig})` : 'adb push 未正常结束(可能超时或 adb 未找到)')
  44. if (code == null && !msg) err += '。请检查设备连接与 adb 路径'
  45. return { success: false, error: err }
  46. }
  47. // Android 10/11+ 需带 --receiver-include-background 扫描才易在相册中显示
  48. const scan = (d) => {
  49. const base = ['shell', 'am', 'broadcast', '-a', 'android.intent.action.MEDIA_SCANNER_SCAN_FILE', '-d', d, '--receiver-include-background']
  50. const a = deviceId ? ['-s', deviceId, ...base] : base
  51. spawnSync(adbExe, a, { encoding: 'utf-8', timeout: 8000 })
  52. }
  53. scan(`file://${deviceFile}`)
  54. scan('file:///sdcard/Pictures')
  55. scan('file:///sdcard/DCIM')
  56. return { success: true, devicePath: deviceFile }
  57. }
  58. async function run(action, ctx) {
  59. const { device, folderPath, variableContext, extractVarName, logMessage } = ctx
  60. const inVars = action.inVars || []
  61. let localPath = inVars.length > 0 ? (variableContext[extractVarName(inVars[0])] || inVars[0]) : action.value
  62. if (!localPath) return { success: false, error: 'send-img-to-device 操作缺少本地图片路径' }
  63. if (!device || String(device).trim() === '') return { success: false, error: 'send-img-to-device 需要设备 ID,请确保流程已连接设备' }
  64. const fullPath = localPath.startsWith('/') || (localPath.length >= 2 && localPath[1] === ':') ? localPath : path.resolve(folderPath, localPath)
  65. if (!fs.existsSync(fullPath)) return { success: false, error: `本地文件不存在: ${fullPath}(流程目录: ${folderPath})` }
  66. const result = doSendImageToDevice(fullPath, device, adbPath)
  67. if (!result.success) return result
  68. return { success: true, devicePath: result.devicePath }
  69. }
  70. module.exports = { run, doSendImageToDevice }