mirror of
https://github.com/viarotel-org/escrcpy.git
synced 2024-11-30 18:24:27 +01:00
perf: ⚡️ Optimize scrcpy parameter conversion performance
This commit is contained in:
parent
ca79e1b57d
commit
18dcd24e65
@ -3,12 +3,13 @@ import util from 'node:util'
|
|||||||
import { adbPath, scrcpyPath } from '$electron/configs/index.js'
|
import { adbPath, scrcpyPath } from '$electron/configs/index.js'
|
||||||
import appStore from '$electron/helpers/store.js'
|
import appStore from '$electron/helpers/store.js'
|
||||||
import { replaceIP, sleep } from '$renderer/utils/index.js'
|
import { replaceIP, sleep } from '$renderer/utils/index.js'
|
||||||
|
import commandHelper from '$renderer/utils/command/index.js'
|
||||||
|
|
||||||
let adbkit
|
let adbkit
|
||||||
|
|
||||||
const exec = util.promisify(_exec)
|
const exec = util.promisify(_exec)
|
||||||
|
|
||||||
const shell = async (command, { stdout, stderr } = {}) => {
|
async function shell(command, { stdout, stderr } = {}) {
|
||||||
const spawnPath = appStore.get('common.scrcpyPath') || scrcpyPath
|
const spawnPath = appStore.get('common.scrcpyPath') || scrcpyPath
|
||||||
const ADB = appStore.get('common.adbPath') || adbPath
|
const ADB = appStore.get('common.adbPath') || adbPath
|
||||||
const args = command.split(' ')
|
const args = command.split(' ')
|
||||||
@ -58,7 +59,7 @@ const shell = async (command, { stdout, stderr } = {}) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const execShell = async (command) => {
|
async function execShell(command) {
|
||||||
const spawnPath = appStore.get('common.scrcpyPath') || scrcpyPath
|
const spawnPath = appStore.get('common.scrcpyPath') || scrcpyPath
|
||||||
const ADB = appStore.get('common.adbPath') || adbPath
|
const ADB = appStore.get('common.adbPath') || adbPath
|
||||||
|
|
||||||
@ -71,7 +72,7 @@ const execShell = async (command) => {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
const getEncoders = async (serial) => {
|
async function getEncoders(serial) {
|
||||||
const res = await execShell(`--serial="${serial}" --list-encoders`)
|
const res = await execShell(`--serial="${serial}" --list-encoders`)
|
||||||
const stdout = res.stdout
|
const stdout = res.stdout
|
||||||
|
|
||||||
@ -97,29 +98,26 @@ const getEncoders = async (serial) => {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
const mirror = async (
|
async function mirror(
|
||||||
serial,
|
serial,
|
||||||
{ title, args = '', exec = false, ...options } = {},
|
{ title, args = '', exec = false, ...options } = {},
|
||||||
) => {
|
) {
|
||||||
const mirrorShell = exec ? execShell : shell
|
const currentShell = exec ? execShell : shell
|
||||||
|
|
||||||
return mirrorShell(
|
return currentShell(
|
||||||
`--serial="${serial}" --window-title="${title}" ${args}`,
|
`--serial="${serial}" --window-title="${title}" ${args}`,
|
||||||
options,
|
options,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const record = async (
|
async function record(serial, { title, args = '', savePath, ...options } = {}) {
|
||||||
serial,
|
|
||||||
{ title, args = '', savePath, ...options } = {},
|
|
||||||
) => {
|
|
||||||
return shell(
|
return shell(
|
||||||
`--serial="${serial}" --window-title="${title}" --record="${savePath}" ${args}`,
|
`--serial="${serial}" --window-title="${title}" --record="${savePath}" ${args}`,
|
||||||
options,
|
options,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const mirrorGroup = async (serial, { openNum = 1, ...options } = {}) => {
|
async function mirrorGroup(serial, { openNum = 1, ...options } = {}) {
|
||||||
const overlayDisplay
|
const overlayDisplay
|
||||||
= appStore.get(`scrcpy.${replaceIP(serial)}.--display-overlay`)
|
= appStore.get(`scrcpy.${replaceIP(serial)}.--display-overlay`)
|
||||||
|| appStore.get('scrcpy.global.--display-overlay')
|
|| appStore.get('scrcpy.global.--display-overlay')
|
||||||
@ -170,6 +168,17 @@ const mirrorGroup = async (serial, { openNum = 1, ...options } = {}) => {
|
|||||||
return Promise.allSettled(results)
|
return Promise.allSettled(results)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default (options = {}) => {
|
export default (options = {}) => {
|
||||||
adbkit = options.adbkit
|
adbkit = options.adbkit
|
||||||
|
|
||||||
@ -180,5 +189,6 @@ export default (options = {}) => {
|
|||||||
mirror,
|
mirror,
|
||||||
record,
|
record,
|
||||||
mirrorGroup,
|
mirrorGroup,
|
||||||
|
control,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ import {
|
|||||||
} from './helpers/index.js'
|
} from './helpers/index.js'
|
||||||
import model from './model/index.js'
|
import model from './model/index.js'
|
||||||
|
|
||||||
|
import command from '$/utils/command/index.js'
|
||||||
|
|
||||||
const { adbPath, scrcpyPath, gnirehtetPath } = window.electron?.configs || {}
|
const { adbPath, scrcpyPath, gnirehtetPath } = window.electron?.configs || {}
|
||||||
|
|
||||||
export const usePreferenceStore = defineStore({
|
export const usePreferenceStore = defineStore({
|
||||||
@ -130,7 +132,7 @@ export const usePreferenceStore = defineStore({
|
|||||||
return value
|
return value
|
||||||
},
|
},
|
||||||
|
|
||||||
getScrcpyArgs(
|
scrcpyParameter(
|
||||||
scope = this.deviceScope,
|
scope = this.deviceScope,
|
||||||
{ isRecord = false, isCamera = false, isOtg = false, excludes = [] } = {},
|
{ isRecord = false, isCamera = false, isOtg = false, excludes = [] } = {},
|
||||||
) {
|
) {
|
||||||
@ -140,55 +142,31 @@ export const usePreferenceStore = defineStore({
|
|||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const valueList = Object.entries(data).reduce((arr, [key, value]) => {
|
const params = Object.entries(data).reduce((obj, [key, value]) => {
|
||||||
if (!value && typeof value !== 'number') {
|
const shouldExclude
|
||||||
return arr
|
= (!value && typeof value !== 'number')
|
||||||
|
|| this.excludeKeys.includes(key)
|
||||||
|
|| (!isRecord && this.recordKeys.includes(key))
|
||||||
|
|| (!isCamera && this.cameraKeys.includes(key))
|
||||||
|
|| (!isOtg && this.otgKeys.includes(key))
|
||||||
|
|| excludes.includes(key)
|
||||||
|
|| excludes.includes(`${key}=${value}`)
|
||||||
|
|
||||||
|
if (shouldExclude) {
|
||||||
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.excludeKeys.includes(key)) {
|
obj[key] = value
|
||||||
return arr
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isRecord) {
|
return obj
|
||||||
if (this.recordKeys.includes(key)) {
|
}, {})
|
||||||
return arr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isCamera) {
|
let value = command.stringify(params)
|
||||||
if (this.cameraKeys.includes(key)) {
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isOtg) {
|
|
||||||
if (this.otgKeys.includes(key)) {
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (excludes.includes(key) || excludes.includes(`${key}=${value}`)) {
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof value === 'boolean') {
|
|
||||||
arr.push(key)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
arr.push(`${key}="${value}"`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return arr
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
if (data.scrcpyAppend) {
|
if (data.scrcpyAppend) {
|
||||||
valueList.push(...data.scrcpyAppend.split(' '))
|
value += ` ${data.scrcpyAppend}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const value = valueList.join(' ')
|
|
||||||
|
|
||||||
// console.log('value', value)
|
|
||||||
|
|
||||||
return value
|
return value
|
||||||
},
|
},
|
||||||
getModel(path) {
|
getModel(path) {
|
||||||
|
109
src/utils/command/index.js
Normal file
109
src/utils/command/index.js
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/**
|
||||||
|
* Convert object parameters to command line arguments string
|
||||||
|
* @param {Object} options - The options object containing parameters
|
||||||
|
* @returns {string} The formatted command line arguments string
|
||||||
|
*/
|
||||||
|
function stringify(options) {
|
||||||
|
if (!options || typeof options !== 'object' || Array.isArray(options)) {
|
||||||
|
console.warn('Options must be a plain object')
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = []
|
||||||
|
|
||||||
|
// Helper function to format parameter names
|
||||||
|
const formatParamName = (name) => {
|
||||||
|
// 验证参数名称的合法性
|
||||||
|
if (typeof name !== 'string' || !name.length) {
|
||||||
|
throw new TypeError('Parameter name must be a non-empty string')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.startsWith('-')) {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
return name.length === 1
|
||||||
|
? `-${name}`
|
||||||
|
: `--${name.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to format values based on their types
|
||||||
|
const formatValue = (value) => {
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
throw new TypeError('Value cannot be null or undefined')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
// 处理空字符串
|
||||||
|
if (!value.length) {
|
||||||
|
return '""'
|
||||||
|
}
|
||||||
|
// 转义引号并在需要时添加引号
|
||||||
|
const needsQuotes = /[\s"']/.test(value)
|
||||||
|
return needsQuotes ? `"${value.replace(/"/g, '\\"')}"` : value
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
if (!Number.isFinite(value)) {
|
||||||
|
throw new TypeError('Number values must be finite')
|
||||||
|
}
|
||||||
|
return value.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'boolean') {
|
||||||
|
return '' // 布尔值不需要返回值
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return formatValue(value.join(','))
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new TypeError(`Unsupported value type: ${typeof value}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process each option
|
||||||
|
for (const [key, value] of Object.entries(options)) {
|
||||||
|
// Skip null or undefined values
|
||||||
|
if ([null, undefined, false].includes(value)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const paramName = formatParamName(key)
|
||||||
|
|
||||||
|
// Handle boolean flags
|
||||||
|
if (typeof value === 'boolean') {
|
||||||
|
if (value) {
|
||||||
|
args.push(paramName)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle array values
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
if (value.length === 0) {
|
||||||
|
continue // 跳过空数组
|
||||||
|
}
|
||||||
|
value.forEach((item) => {
|
||||||
|
if (item !== null && item !== undefined) {
|
||||||
|
const formattedValue = formatValue(item)
|
||||||
|
if (formattedValue) {
|
||||||
|
args.push(`${paramName} ${formattedValue}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle regular key-value pairs
|
||||||
|
const formattedValue = formatValue(value)
|
||||||
|
if (formattedValue) {
|
||||||
|
args.push(`${paramName}=${formattedValue}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return args.join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
export { stringify }
|
||||||
|
|
||||||
|
export default { stringify }
|
Loading…
Reference in New Issue
Block a user