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 appStore from '$electron/helpers/store.js'
|
||||
import { replaceIP, sleep } from '$renderer/utils/index.js'
|
||||
import commandHelper from '$renderer/utils/command/index.js'
|
||||
|
||||
let adbkit
|
||||
|
||||
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 ADB = appStore.get('common.adbPath') || adbPath
|
||||
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 ADB = appStore.get('common.adbPath') || adbPath
|
||||
|
||||
@ -71,7 +72,7 @@ const execShell = async (command) => {
|
||||
return res
|
||||
}
|
||||
|
||||
const getEncoders = async (serial) => {
|
||||
async function getEncoders(serial) {
|
||||
const res = await execShell(`--serial="${serial}" --list-encoders`)
|
||||
const stdout = res.stdout
|
||||
|
||||
@ -97,29 +98,26 @@ const getEncoders = async (serial) => {
|
||||
return value
|
||||
}
|
||||
|
||||
const mirror = async (
|
||||
async function mirror(
|
||||
serial,
|
||||
{ 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}`,
|
||||
options,
|
||||
)
|
||||
}
|
||||
|
||||
const record = async (
|
||||
serial,
|
||||
{ title, args = '', savePath, ...options } = {},
|
||||
) => {
|
||||
async function record(serial, { title, args = '', savePath, ...options } = {}) {
|
||||
return shell(
|
||||
`--serial="${serial}" --window-title="${title}" --record="${savePath}" ${args}`,
|
||||
options,
|
||||
)
|
||||
}
|
||||
|
||||
const mirrorGroup = async (serial, { openNum = 1, ...options } = {}) => {
|
||||
async function mirrorGroup(serial, { openNum = 1, ...options } = {}) {
|
||||
const overlayDisplay
|
||||
= appStore.get(`scrcpy.${replaceIP(serial)}.--display-overlay`)
|
||||
|| appStore.get('scrcpy.global.--display-overlay')
|
||||
@ -170,6 +168,17 @@ const mirrorGroup = async (serial, { openNum = 1, ...options } = {}) => {
|
||||
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 = {}) => {
|
||||
adbkit = options.adbkit
|
||||
|
||||
@ -180,5 +189,6 @@ export default (options = {}) => {
|
||||
mirror,
|
||||
record,
|
||||
mirrorGroup,
|
||||
control,
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ import {
|
||||
} from './helpers/index.js'
|
||||
import model from './model/index.js'
|
||||
|
||||
import command from '$/utils/command/index.js'
|
||||
|
||||
const { adbPath, scrcpyPath, gnirehtetPath } = window.electron?.configs || {}
|
||||
|
||||
export const usePreferenceStore = defineStore({
|
||||
@ -130,7 +132,7 @@ export const usePreferenceStore = defineStore({
|
||||
return value
|
||||
},
|
||||
|
||||
getScrcpyArgs(
|
||||
scrcpyParameter(
|
||||
scope = this.deviceScope,
|
||||
{ isRecord = false, isCamera = false, isOtg = false, excludes = [] } = {},
|
||||
) {
|
||||
@ -140,55 +142,31 @@ export const usePreferenceStore = defineStore({
|
||||
return ''
|
||||
}
|
||||
|
||||
const valueList = Object.entries(data).reduce((arr, [key, value]) => {
|
||||
if (!value && typeof value !== 'number') {
|
||||
return arr
|
||||
const params = Object.entries(data).reduce((obj, [key, value]) => {
|
||||
const shouldExclude
|
||||
= (!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)) {
|
||||
return arr
|
||||
}
|
||||
obj[key] = value
|
||||
|
||||
if (!isRecord) {
|
||||
if (this.recordKeys.includes(key)) {
|
||||
return arr
|
||||
}
|
||||
}
|
||||
return obj
|
||||
}, {})
|
||||
|
||||
if (!isCamera) {
|
||||
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
|
||||
}, [])
|
||||
let value = command.stringify(params)
|
||||
|
||||
if (data.scrcpyAppend) {
|
||||
valueList.push(...data.scrcpyAppend.split(' '))
|
||||
value += ` ${data.scrcpyAppend}`
|
||||
}
|
||||
|
||||
const value = valueList.join(' ')
|
||||
|
||||
// console.log('value', value)
|
||||
|
||||
return value
|
||||
},
|
||||
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