2023-11-02 08:08:19 +01:00
|
|
|
import { exec as _exec, spawn } from 'node:child_process'
|
2024-09-07 19:55:54 +02:00
|
|
|
import util from 'node:util'
|
2024-05-15 05:16:02 +02:00
|
|
|
import { adbPath, scrcpyPath } from '$electron/configs/index.js'
|
2024-09-07 19:55:54 +02:00
|
|
|
import appStore from '$electron/helpers/store.js'
|
|
|
|
import { replaceIP, sleep } from '$renderer/utils/index.js'
|
2024-10-23 10:14:25 +02:00
|
|
|
import commandHelper from '$renderer/utils/command/index.js'
|
2023-11-07 07:09:09 +01:00
|
|
|
|
|
|
|
let adbkit
|
2023-09-18 09:07:28 +02:00
|
|
|
|
2023-11-02 08:08:19 +01:00
|
|
|
const exec = util.promisify(_exec)
|
|
|
|
|
2024-10-23 10:14:25 +02:00
|
|
|
async function shell(command, { stdout, stderr } = {}) {
|
2023-10-26 12:11:52 +02:00
|
|
|
const spawnPath = appStore.get('common.scrcpyPath') || scrcpyPath
|
|
|
|
const ADB = appStore.get('common.adbPath') || adbPath
|
2023-10-17 11:54:29 +02:00
|
|
|
const args = command.split(' ')
|
2023-10-20 08:23:48 +02:00
|
|
|
|
2023-10-25 12:17:19 +02:00
|
|
|
const scrcpyProcess = spawn(`"${spawnPath}"`, args, {
|
2023-10-28 11:21:07 +02:00
|
|
|
env: { ...process.env, ADB },
|
2023-10-20 08:23:48 +02:00
|
|
|
shell: true,
|
2023-10-25 12:17:19 +02:00
|
|
|
encoding: 'utf8',
|
2023-10-20 08:23:48 +02:00
|
|
|
})
|
2023-09-18 09:07:28 +02:00
|
|
|
|
2023-10-17 11:54:29 +02:00
|
|
|
scrcpyProcess.stdout.on('data', (data) => {
|
|
|
|
const stringData = data.toString()
|
|
|
|
|
|
|
|
if (stdout) {
|
2023-10-20 12:17:58 +02:00
|
|
|
stdout(stringData, scrcpyProcess)
|
2023-10-17 11:54:29 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2023-11-14 04:06:36 +01:00
|
|
|
const stderrList = []
|
2023-10-17 11:54:29 +02:00
|
|
|
scrcpyProcess.stderr.on('data', (data) => {
|
|
|
|
const stringData = data.toString()
|
|
|
|
|
2023-11-14 04:06:36 +01:00
|
|
|
stderrList.push(stringData)
|
2023-11-14 03:53:58 +01:00
|
|
|
|
2023-10-17 11:54:29 +02:00
|
|
|
console.error('scrcpyProcess.stderr.data:', stringData)
|
|
|
|
|
|
|
|
if (stderr) {
|
2023-10-20 12:17:58 +02:00
|
|
|
stderr(stringData, scrcpyProcess)
|
2023-10-17 11:54:29 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
scrcpyProcess.on('close', (code) => {
|
|
|
|
if (code === 0) {
|
|
|
|
resolve()
|
|
|
|
}
|
|
|
|
else {
|
2023-11-14 04:06:36 +01:00
|
|
|
reject(
|
|
|
|
new Error(stderrList.join(',') || `Command failed with code ${code}`),
|
|
|
|
)
|
2023-10-17 11:54:29 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
scrcpyProcess.on('error', (err) => {
|
|
|
|
reject(err)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
2023-09-18 09:07:28 +02:00
|
|
|
|
2024-10-23 10:14:25 +02:00
|
|
|
async function execShell(command) {
|
2023-11-02 08:08:19 +01:00
|
|
|
const spawnPath = appStore.get('common.scrcpyPath') || scrcpyPath
|
|
|
|
const ADB = appStore.get('common.adbPath') || adbPath
|
|
|
|
|
|
|
|
const res = exec(`"${spawnPath}" ${command}`, {
|
|
|
|
env: { ...process.env, ADB },
|
|
|
|
shell: true,
|
|
|
|
encoding: 'utf8',
|
|
|
|
})
|
|
|
|
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2024-10-23 10:14:25 +02:00
|
|
|
async function getEncoders(serial) {
|
2023-11-02 08:08:19 +01:00
|
|
|
const res = await execShell(`--serial="${serial}" --list-encoders`)
|
|
|
|
const stdout = res.stdout
|
|
|
|
|
|
|
|
// 提取视频编码器列表
|
|
|
|
const videoEncoderRegex
|
|
|
|
= /--video-codec=([\w-]+)\s+--video-encoder='([^']+)'/g
|
|
|
|
const videoEncoders = [...stdout.matchAll(videoEncoderRegex)].map(
|
|
|
|
([, codec, encoder]) => ({ decoder: codec, encoder }),
|
|
|
|
)
|
|
|
|
|
|
|
|
// 提取音频编码器列表
|
|
|
|
const audioEncoderRegex
|
|
|
|
= /--audio-codec=([\w-]+)\s+--audio-encoder='([^']+)'/g
|
|
|
|
const audioEncoders = [...stdout.matchAll(audioEncoderRegex)].map(
|
|
|
|
([, codec, encoder]) => ({ decoder: codec, encoder }),
|
|
|
|
)
|
|
|
|
|
|
|
|
const value = {
|
|
|
|
audio: audioEncoders,
|
|
|
|
video: videoEncoders,
|
|
|
|
}
|
|
|
|
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
|
2024-10-23 10:14:25 +02:00
|
|
|
async function mirror(
|
2023-11-07 07:09:09 +01:00
|
|
|
serial,
|
|
|
|
{ title, args = '', exec = false, ...options } = {},
|
2024-10-23 10:14:25 +02:00
|
|
|
) {
|
|
|
|
const currentShell = exec ? execShell : shell
|
2023-11-07 07:09:09 +01:00
|
|
|
|
2024-10-23 10:14:25 +02:00
|
|
|
return currentShell(
|
2023-11-02 09:07:42 +01:00
|
|
|
`--serial="${serial}" --window-title="${title}" ${args}`,
|
|
|
|
options,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-10-23 10:14:25 +02:00
|
|
|
async function record(serial, { title, args = '', savePath, ...options } = {}) {
|
2023-11-02 09:07:42 +01:00
|
|
|
return shell(
|
|
|
|
`--serial="${serial}" --window-title="${title}" --record="${savePath}" ${args}`,
|
|
|
|
options,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-10-23 10:14:25 +02:00
|
|
|
async function mirrorGroup(serial, { openNum = 1, ...options } = {}) {
|
2023-11-07 07:09:09 +01:00
|
|
|
const overlayDisplay
|
|
|
|
= appStore.get(`scrcpy.${replaceIP(serial)}.--display-overlay`)
|
|
|
|
|| appStore.get('scrcpy.global.--display-overlay')
|
|
|
|
|| '1080x1920/320,secure'
|
|
|
|
|
|
|
|
const command = `settings put global overlay_display_devices "${[
|
2024-09-13 13:38:29 +02:00
|
|
|
...Array.from({ length: openNum }).keys(),
|
2023-11-07 07:09:09 +01:00
|
|
|
]
|
|
|
|
.map(() => overlayDisplay)
|
|
|
|
.join(';')}"`
|
|
|
|
|
|
|
|
await adbkit.deviceShell(serial, command)
|
|
|
|
|
|
|
|
await sleep()
|
|
|
|
|
2023-11-11 09:14:55 +01:00
|
|
|
const displayList = await adbkit.display(serial)
|
2023-11-07 07:09:09 +01:00
|
|
|
|
|
|
|
const filterList = displayList.filter(item => item !== '0')
|
|
|
|
|
|
|
|
const results = []
|
|
|
|
|
|
|
|
for (let index = 0; index < filterList.length; index++) {
|
|
|
|
const displayId = filterList[index]
|
|
|
|
|
|
|
|
let args = options.args || ''
|
|
|
|
|
|
|
|
if (args.includes('--display-id')) {
|
|
|
|
args = args.replace(/(--display-id=)"[^"]*"/, `$1"${displayId}"`)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
args += ` --display-id="${displayId}"`
|
|
|
|
}
|
|
|
|
|
|
|
|
const title = options?.title?.({ displayId, index }) || options?.title
|
|
|
|
|
|
|
|
const promise = mirror(serial, {
|
|
|
|
...options,
|
|
|
|
title,
|
|
|
|
args,
|
|
|
|
exec: true,
|
|
|
|
})
|
|
|
|
|
|
|
|
results.push(promise)
|
|
|
|
|
|
|
|
await sleep(1500)
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.allSettled(results)
|
|
|
|
}
|
|
|
|
|
2024-10-23 10:14:25 +02:00
|
|
|
async function control(serial, { command, exec = true, ...options } = {}) {
|
|
|
|
const currentShell = exec ? execShell : shell
|
|
|
|
|
|
|
|
const stringCommand = commandHelper.stringify(command)
|
|
|
|
|
|
|
|
return currentShell(
|
|
|
|
`--serial="${serial}" --no-video --no-audio ${stringCommand}`,
|
|
|
|
options,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-11-07 07:09:09 +01:00
|
|
|
export default (options = {}) => {
|
|
|
|
adbkit = options.adbkit
|
|
|
|
|
|
|
|
return {
|
|
|
|
shell,
|
|
|
|
execShell,
|
|
|
|
getEncoders,
|
|
|
|
mirror,
|
|
|
|
record,
|
|
|
|
mirrorGroup,
|
2024-10-23 10:14:25 +02:00
|
|
|
control,
|
2023-11-07 07:09:09 +01:00
|
|
|
}
|
|
|
|
}
|