#!/usr/bin/env node const { execSync } = require('child_process') const path = require('path') // 项目根 = 脚本所在目录的上一级(nodejs -> 项目根) // 打包后 nodejs 在 app.asar 内执行,但 lib/configs 在 app.asar.unpacked,需用 unpacked 路径才能找到 adb let PROJECT_ROOT = path.resolve(__dirname, '..') if (PROJECT_ROOT.includes('app.asar') && !PROJECT_ROOT.includes('app.asar.unpacked')) { PROJECT_ROOT = PROJECT_ROOT.replace('app.asar', 'app.asar.unpacked') } const TCPIP_PORT = 5555 /** 从配置解析并返回 ADB 可执行文件路径 */ function getAdbPath() { const configPath = path.join(PROJECT_ROOT, 'configs', 'config.js') const config = require(configPath) return config.adbPath?.path ? path.resolve(PROJECT_ROOT, config.adbPath.path) : path.join(PROJECT_ROOT, 'lib', 'scrcpy-adb', 'adb.exe') } /** 返回当前通过 USB 连接的设备 ID 列表(仅 status 为 device 且非 IP:port,排除无线设备) */ function getConnectedDeviceIds(adbPath) { const out = execSync(`"${adbPath}" devices`, { encoding: 'utf-8' }) return out .split('\n') .filter((line) => line.trim() && !line.startsWith('List') && line.includes('\tdevice')) .map((line) => line.trim().split('\t')[0]) .filter((id) => id && !id.includes(':')) } /** 操作一:在 USB 设备上开启无线调试(settings adb_wifi_enabled 1) */ function enableWirelessSetting(adbPath, deviceId) { execSync(`"${adbPath}" -s ${deviceId} shell settings put global adb_wifi_enabled 1`, { encoding: 'utf-8' }) } /** 操作二:在 USB 设备上激活 5555 端口(tcpip),用于无线连接 */ function enableTcpipPort(adbPath, deviceId, port) { return execSync(`"${adbPath}" -s ${deviceId} tcpip ${port}`, { encoding: 'utf-8' }).trim() } /** 从 adb shell 输出中匹配 inet 后的 IPv4(支持 inet 192.168.1.1/24 格式),可排除回环 */ function parseInetFromOutput(out, skipLoopback = false) { const re = /inet\s+(\d+\.\d+\.\d+\.\d+)/g const ips = [] let m while ((m = re.exec(out || '')) !== null) ips.push(m[1]) if (ips.length === 0) return null if (skipLoopback) { const nonLoop = ips.find((ip) => ip !== '127.0.0.1') return nonLoop || null } return ips[0] } /** 安全拼 adb -s 参数(deviceId 含空格时需引号) */ function adbShellCmd(adbPath, deviceId, shellCmd) { const id = deviceId.indexOf(' ') >= 0 ? `"${deviceId}"` : deviceId return `"${adbPath}" -s ${id} shell ${shellCmd}` } /** 获取设备当前 WiFi IP:多接口 + 多命令兼容不同机型 */ function getDeviceIp(adbPath, deviceId) { const run = (shellCmd) => { try { return execSync(adbShellCmd(adbPath, deviceId, shellCmd), { encoding: 'utf-8' }) } catch (e) { return '' } } // 1) wlan0 inet(最常见) let out = run('ip -4 addr show wlan0') let ip = parseInetFromOutput(out) if (ip) return ip // 2) eth0(部分机型/平板) out = run('ip -4 addr show eth0') ip = parseInetFromOutput(out) if (ip) return ip // 3) ip route get:src 或 from out = run('ip route get 1.1.1.1') let m = out.match(/\bsrc\s+(\d+\.\d+\.\d+\.\d+)\b/) if (m) return m[1] m = out.match(/\bfrom\s+(\d+\.\d+\.\d+\.\d+)\b/) if (m) return m[1] // 4) getprop for (const prop of ['dhcp.wlan0.ipaddress', 'net.wlan0.ipaddress', 'dhcp.eth0.ipaddress']) { out = run('getprop ' + prop) ip = (out || '').trim() if (/^\d+\.\d+\.\d+\.\d+$/.test(ip)) return ip } // 5) ip -4 addr show 全量,取第一个非回环 IP(避免 lo 的 127.0.0.1) out = run('ip -4 addr show') return parseInetFromOutput(out, true) } /** 主流程:取 USB 设备,先取 IP(与 getip.js 一致,此时 USB 稳定),再开无线与 tcpip,最后输出 */ function run() { const adbPath = getAdbPath() const devices = getConnectedDeviceIds(adbPath) if (devices.length === 0) { process.stderr.write('No devices found. Please connect a device via USB.\n') process.exit(1) } const deviceId = devices[0] // 先取 IP(在 tcpip 之前,USB 稳定时取,与 bat-tool/getip 行为一致) const ip = getDeviceIp(adbPath, deviceId) enableWirelessSetting(adbPath, deviceId) const tcpipOut = enableTcpipPort(adbPath, deviceId, TCPIP_PORT) process.stdout.write(tcpipOut + '\n') if (ip) { process.stdout.write('DEVICE_IP:' + ip + '\n') } process.exit(0) } run()