escrcpy/electron/exposes/scrcpy/index.js

229 lines
5.1 KiB
JavaScript

import { exec as _exec, spawn } from 'node:child_process'
import util from 'node:util'
import { adbPath, scrcpyPath } from '$electron/configs/index.js'
import appStore from '$electron/helpers/store.js'
import { sleep } from '$renderer/utils/index.js'
import commandHelper from '$renderer/utils/command/index.js'
import { getDisplayOverlay, parseScrcpyAppList, parseScrcpyCodecList } from './helper.js'
let adbkit
const exec = util.promisify(_exec)
async function shell(command, { stdout, stderr, signal, ...options } = {}) {
const spawnPath = appStore.get('common.scrcpyPath') || scrcpyPath
const ADB = appStore.get('common.adbPath') || adbPath
const args = command.split(' ')
const scrcpyProcess = spawn(`"${spawnPath}"`, args, {
env: { ...process.env, ADB },
shell: true,
encoding: 'utf8',
...options,
})
const stderrList = []
return new Promise((resolve, reject) => {
scrcpyProcess.stdout.on('data', (data) => {
const stringData = data.toString()
if (stdout) {
stdout(stringData, scrcpyProcess)
}
const matchList = stringData.match(signal)
if (matchList) {
resolve(matchList, stringData, scrcpyProcess)
}
})
scrcpyProcess.stderr.on('data', (data) => {
const stringData = data.toString()
stderrList.push(stringData)
console.error('scrcpyProcess.stderr.data:', stringData)
if (stderr) {
stderr(stringData, scrcpyProcess)
}
})
scrcpyProcess.on('close', (code) => {
if (code === 0) {
resolve()
}
else {
reject(
new Error(stderrList.join(',') || `Command failed with code ${code}`),
)
}
})
scrcpyProcess.on('error', (err) => {
reject(err)
})
})
}
async function execShell(command) {
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
}
async function getEncoders(serial) {
const res = await execShell(`--serial="${serial}" --list-encoders`)
const stdout = res.stdout
const value = parseScrcpyCodecList(stdout)
console.log('value', value)
return value
}
async function mirror(
serial,
{ title, args = '', exec = false, ...options } = {},
) {
const currentShell = exec ? execShell : shell
return currentShell(
`--serial="${serial}" --window-title="${title}" ${args}`,
options,
)
}
async function record(serial, { title, args = '', savePath, ...options } = {}) {
return shell(
`--serial="${serial}" --window-title="${title}" --record="${savePath}" ${args}`,
options,
)
}
async function mirrorGroup(serial, { openNum = 1, ...options } = {}) {
const displayOverlay = getDisplayOverlay(serial)
const command = `settings put global overlay_display_devices "${[
...Array.from({ length: openNum }).keys(),
]
.map(() => displayOverlay)
.join(';')}"`
await adbkit.deviceShell(serial, command)
await sleep()
const displayList = await adbkit.display(serial)
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)
}
async function helper(
serial,
command = '',
{ hiddenWindow = false, ...options } = {},
) {
const stringCommand = commandHelper.stringify(command)
return execShell(
`--serial="${serial}" --window-title="EscrcpyHelper" ${
hiddenWindow ? '--window-x=-300 --window-y=-300' : ''
} --no-video --no-audio --mouse=disabled ${stringCommand}`,
options,
)
}
async function getAppList(serial) {
const res = await execShell(`--serial="${serial}" --list-apps`)
const stdout = res.stdout
const value = parseScrcpyAppList(stdout)
return value
}
async function startApp(serial, args = {}) {
let { commands, packageName, ...options } = args
const displayOverlay = getDisplayOverlay(serial)
commands += ` --new-display`
if (displayOverlay) {
commands += `=${displayOverlay}`
}
if (packageName) {
commands += ` --start-app=${packageName}`
}
const res = await mirror(serial, { ...options, args: commands, signal: /display id: (\d+)/i })
const displayId = res?.[1]
if (!displayId) {
throw new Error('The display ID was not obtained.')
}
return displayId
}
export default (options = {}) => {
adbkit = options.adbkit
return {
shell,
execShell,
getEncoders,
mirror,
record,
mirrorGroup,
helper,
getAppList,
startApp,
}
}