import { spawn } from 'child_process' import path from 'path' import fs from 'fs' import fsp from 'fs/promises' import { fileURLToPath } from 'url' import { app } from 'electron' // ESM 下没有 __dirname,使用 import.meta.url 计算当前目录 const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) const isWin = process.platform === 'win32' const adbBinary = isWin ? 'adb.exe' : 'adb' // 解析 adb 可执行文件路径(开发/打包均可用) function resolveAdbPath() { const resources = process.resourcesPath || '' const candidates = [ // 开发:项目根目录 resources/adbSDK/adb(.exe)(与 electron-builder extraResources 对应) path.join(process.cwd(), 'resources', 'adbSDK', adbBinary), // 开发:与本文件同级的 ../adbSDK/adb(.exe) path.join(__dirname, '..', 'adbSDK', adbBinary), // 开发:构建输出 out/adbSDK/adb(.exe)(如有) path.join(process.cwd(), 'out', 'adbSDK', adbBinary), // 生产:extraResources 复制到 resources/adbSDK path.join(resources, 'adbSDK', adbBinary), // 生产:若使用 asarUnpack,位于 app.asar.unpacked path.join(resources, 'app.asar.unpacked', 'adbSDK', adbBinary) ] for (const p of candidates) { try { if (fs.existsSync(p)) return p } catch {} } throw new Error( `adb executable not found. Tried:\n${candidates.join('\n')}\n` + 'Please ensure adbSDK exists in development (resources/adbSDK) and is listed in extraResources for production.' ) } const adbPath = resolveAdbPath() const adbDir = path.dirname(adbPath) function getAdbSerial(ip) { if (!ip) throw new Error('ip required') return ip.includes(':') ? ip : `${ip}:5555` } /** * 运行 adb 命令的通用封装 */ function runAdb(args, options = {}) { return new Promise((resolve, reject) => { // 先行校验,给出更明确的错误 if (!fs.existsSync(adbPath)) { return reject(new Error(`adb not found at ${adbPath}`)) } const child = spawn(adbPath, args, { cwd: adbDir, windowsHide: true, ...options }) let stdout = '' let stderr = '' child.stdout?.on('data', (d) => (stdout += d.toString())) child.stderr?.on('data', (d) => (stderr += d.toString())) child.on('error', (err) => reject(err)) child.on('close', (code) => { if (code === 0) return resolve({ stdout: stdout.trim(), stderr: stderr.trim() }) const err = new Error(`adb exited with ${code}: ${stderr || stdout}`) err.code = code err.stdout = stdout err.stderr = stderr reject(err) }) }) } // 下面其余 API 保持不变(仅把 fs/promises 改为 fsp 引用) async function connect(ip) { if (!ip) throw new Error('ip required') return runAdb(['connect', getAdbSerial(ip)]) } async function disconnect(ip) { if (ip) return runAdb(['disconnect', getAdbSerial(ip)]) return runAdb(['disconnect']) } async function pull(remotePath, destFilename, ip) { const dest = path.join(adbDir, destFilename || path.basename(remotePath)) await fsp.mkdir(adbDir, { recursive: true }) const serialArgs = ip ? ['-s', getAdbSerial(ip)] : [] await runAdb([...serialArgs, 'pull', remotePath, dest]) return dest } async function push(localPath, remotePath, ip) { const serialArgs = ip ? ['-s', getAdbSerial(ip)] : [] return runAdb([...serialArgs, 'push', localPath, remotePath]) } async function readLocalOLE() { const file = path.join(adbDir, 'OLE.ini') const text = await fsp.readFile(file, 'utf8') const lines = text.split(/\r?\n/).filter(Boolean) return lines[0] ? lines[0] : null } async function writeFirstOLE(line1) { const file = path.join(adbDir, 'OLE.ini') let second = '1.000000' try { const text = await fsp.readFile(file, 'utf8') const lines = text.split(/\r?\n/).filter(Boolean) if (lines.length >= 2) second = lines[1] } catch {} const content = `${line1}\n${second}\n` await fsp.writeFile(file, content, 'utf8') return file } async function getParametersFromDevice(ip) { if (ip) await connect(ip) await pull('/data/OLE.ini', 'OLE.ini', ip) return readLocalOLE() } async function setFirstParameter({ param, ip, toSystem = false }) { if (param == null) throw new Error('param required') const local = await writeFirstOLE(param) if (ip) await connect(ip) await push(local, '/data/', ip) if (toSystem) { await push(local, '/system/etc/', ip) } return { local } } export { adbPath, runAdb, connect, pull, push, readLocalOLE, writeFirstOLE, getParametersFromDevice, setFirstParameter, disconnect, getAdbSerial }