perf: Support batch screenshot and other performance optimization

This commit is contained in:
viarotel 2024-07-11 17:18:27 +08:00
parent 49ae54166a
commit db9e3e791e
22 changed files with 123 additions and 121 deletions

View File

@ -49,5 +49,6 @@
],
"cSpell.words": [
"bhsn"
]
],
"common-intellisense.ui": []
}

View File

@ -1,5 +1,24 @@
# Changelog
## [1.20.1](https://github.com/viarotel-org/escrcpy/compare/v1.20.0...v1.20.1) (2024-07-04)
### Bug Fixes
* 🐛 Fix batch text spelling errors ([062c689](https://github.com/viarotel-org/escrcpy/commit/062c689755df5bcc5f8e38605c7f101762d7ada0))
## [1.20.0](https://github.com/viarotel-org/escrcpy/compare/v1.19.4...v1.20.0) (2024-07-04)
### Features
* ✨ Add batch installation application function ([37ce245](https://github.com/viarotel-org/escrcpy/commit/37ce2457bce9a1b661c6db7162023f53268833f5))
### Performance Improvements
* 🚀 Add mouse binding options ([7ee4ba4](https://github.com/viarotel-org/escrcpy/commit/7ee4ba4f2b177e6dbfce85036425b51bfa35ecff))
## [1.19.4](https://github.com/viarotel-org/escrcpy/compare/v1.19.3...v1.19.4) (2024-07-02)

View File

@ -203,7 +203,7 @@ Windows 及 Linux 端内部集成了 Gnirehtet 用于提供 PC 到安卓设
15. 支持批量连接历史设备功能 ✅
16. 支持使用内置终端执行自定义命令 ✅
17. 支持设备自动执行镜像 ✅
18. 添加批量安装应用功能 ✅
18. 支持常用批量功能 ✅
19. 支持更多批量处理功能 🚧
20. 支持对设备进行分组 🚧
21. 添加文件传输助手功能 🚧

View File

@ -201,7 +201,7 @@ Refer to [scrcpy/doc/shortcuts](https://github.com/Genymobile/scrcpy/blob/master
15. Support bulk connecting to historical devices ✅
16. Support to use built-in terminals to execute custom commands ✅
17. Supports automatic execution of mirror on devices ✅
18. Add batch installation application function ✅
18. Support common batch processing function ✅
19. Support more batch processing functions 🚧
20. Support the device to group 🚧
21. Add file transmission assistant function 🚧

32
components.d.ts vendored
View File

@ -7,21 +7,11 @@ export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
About: typeof import('./src/components/About/index.vue')['default']
AppInstall: typeof import('./src/components/Device/components/BatchActions/AppInstall/index.vue')['default']
AppSearch: typeof import('./src/components/AppSearch/index.vue')['default']
AudioCodecSelect: typeof import('./src/components/Preference/components/AudioCodecSelect/index.vue')['default']
BatchActions: typeof import('./src/components/Device/components/BatchActions/index.vue')['default']
Camera: typeof import('./src/components/Device/components/MoreDropdown/components/Camera/index.vue')['default']
ControlBar: typeof import('./src/components/Device/components/ControlBar/index.vue')['default']
Device: typeof import('./src/components/Device/index.vue')['default']
DisplaySelect: typeof import('./src/components/Preference/components/DisplaySelect/index.vue')['default']
ElAutocomplete: typeof import('element-plus/es')['ElAutocomplete']
ElButton: typeof import('element-plus/es')['ElButton']
ElCol: typeof import('element-plus/es')['ElCol']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElCollapseTransition: typeof import('element-plus/es')['ElCollapseTransition']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
@ -44,28 +34,6 @@ declare module 'vue' {
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
FileManage: typeof import('./src/components/Device/components/ControlBar/FileManage/index.vue')['default']
Gnirehtet: typeof import('./src/components/Device/components/ControlBar/Gnirehtet/index.vue')['default']
KeyboardInjectSelect: typeof import('./src/components/Preference/components/KeyboardInjectSelect/index.vue')['default']
LanguageSelect: typeof import('./src/components/Preference/components/LanguageSelect/index.vue')['default']
LoadingIcon: typeof import('./src/components/Device/components/LoadingIcon/index.vue')['default']
MirrorAction: typeof import('./src/components/Device/components/MirrorAction/index.vue')['default']
MirrorGroup: typeof import('./src/components/Device/components/ControlBar/MirrorGroup/index.vue')['default']
MoreDropdown: typeof import('./src/components/Device/components/MoreDropdown/index.vue')['default']
Otg: typeof import('./src/components/Device/components/MoreDropdown/components/Otg/index.vue')['default']
PairDialog: typeof import('./src/components/Device/components/Wireless/PairDialog/index.vue')['default']
PathInput: typeof import('./src/components/Preference/components/PathInput/index.vue')['default']
Preference: typeof import('./src/components/Preference/index.vue')['default']
Record: typeof import('./src/components/Device/components/MoreDropdown/components/Record/index.vue')['default']
Remark: typeof import('./src/components/Device/components/Remark/index.vue')['default']
Rotation: typeof import('./src/components/Device/components/ControlBar/Rotation/index.vue')['default']
Screenshot: typeof import('./src/components/Device/components/ControlBar/Screenshot/index.vue')['default']
TerminalAction: typeof import('./src/components/Device/components/TerminalAction/index.vue')['default']
TerminalDialog: typeof import('./src/components/Device/components/TerminalAction/components/TerminalDialog/index.vue')['default']
VideoCodecSelect: typeof import('./src/components/Preference/components/VideoCodecSelect/index.vue')['default']
Volume: typeof import('./src/components/Device/components/ControlBar/Volume/index.vue')['default']
Wireless: typeof import('./src/components/Device/components/Wireless/index.vue')['default']
WirelessAction: typeof import('./src/components/Device/components/WirelessAction/index.vue')['default']
}
export interface ComponentCustomProperties {
vLoading: typeof import('element-plus/es')['ElLoadingDirective']

View File

@ -27,8 +27,6 @@ log.initialize({ preload: true })
const debug = !!appStore.get('common.debug')
log.info('Debug Status:', debug)
if (!debug) {
log.warn(
'Debug Tips:',

View File

@ -1,7 +1,7 @@
{
"name": "escrcpy",
"type": "module",
"version": "1.19.4",
"version": "1.20.1",
"private": true,
"description": "Scrcpy Powered by Electron",
"author": "viarotel",

View File

@ -33,7 +33,7 @@ import { ElMessageBox } from 'element-plus'
import Device from './components/Device/index.vue'
import Preference from './components/Preference/index.vue'
import About from './components/About/index.vue'
import AppSearch from './components/AppSearch/index.vue'
import AppSearch from './components/Search/index.vue'
import { useThemeStore } from '$/store/theme/index.js'
import { usePreferenceStore } from '$/store/preference/index.js'

View File

@ -1,17 +1,17 @@
<template>
<div class="" @click="handleClick">
<slot />
<AppInstallProxy ref="appInstallProxyRef" />
<ApplicationProxy ref="applicationProxyRef" />
</div>
</template>
<script>
import AppInstallProxy from '$/components/Device/components/ControlBar/AppInstall/index.vue'
import ApplicationProxy from '$/components/Device/components/ControlBar/Application/index.vue'
import { sleep } from '$/utils'
export default {
components: {
AppInstallProxy,
ApplicationProxy,
},
props: {
devices: {
@ -23,7 +23,7 @@ export default {
async handleClick() {
for (let index = 0; index < this.devices.length; index++) {
const item = this.devices[index]
await this.$refs.appInstallProxyRef.handleInstall(item)
await this.$refs.applicationProxyRef.invoke(item)
await sleep(2 * 1000)
}
},

View File

@ -0,0 +1,34 @@
<template>
<div class="" @click="handleClick">
<slot />
<ScreenshotProxy ref="screenshotProxyRef" />
</div>
</template>
<script>
import ScreenshotProxy from '$/components/Device/components/ControlBar/Screenshot/index.vue'
import { sleep } from '$/utils'
export default {
components: {
ScreenshotProxy,
},
props: {
devices: {
type: Array,
default: () => [],
},
},
methods: {
async handleClick() {
for (let index = 0; index < this.devices.length; index++) {
const item = this.devices[index]
await this.$refs.screenshotProxyRef.invoke(item)
await sleep(2 * 1000)
}
},
},
}
</script>
<style></style>

View File

@ -1,5 +1,5 @@
<template>
<div class="flex items-center">
<div class="flex items-center space-x-2">
<component
:is="item.component || 'div'"
v-for="(item, index) in actionModel"
@ -31,7 +31,7 @@
<component :is="item.elIcon" />
</el-icon>
</template>
{{ $t('common.batch') }}{{ $t(item.label) }}
{{ $t('common.batch') }}-{{ $t(item.label) }}
</el-button>
</template>
</component>
@ -39,11 +39,13 @@
</template>
<script>
import AppInstall from './AppInstall/index.vue'
import Application from './Application/index.vue'
import Screenshot from './Screenshot/index.vue'
export default {
components: {
AppInstall,
Application,
Screenshot,
},
props: {
devices: {
@ -54,10 +56,15 @@ export default {
data() {
return {
actionModel: [
{
label: 'device.control.capture',
elIcon: 'Crop',
component: 'Screenshot',
},
{
label: 'device.control.install',
svgIcon: 'install',
component: 'AppInstall',
component: 'Application',
},
],
}

View File

@ -5,8 +5,6 @@
</template>
<script>
import LoadingIcon from '$/components/Device/components/LoadingIcon/index.vue'
export default {
props: {
device: {
@ -18,6 +16,9 @@ export default {
return {}
},
methods: {
invoke(...args) {
return this.handleInstall(...args)
},
preferenceData(...args) {
return this.$store.preference.getData(...args)
},
@ -46,13 +47,11 @@ export default {
return false
}
const messageEl = this.$message({
message: this.$t('device.control.install.progress', {
const messageEl = this.$message.loading(
this.$t('device.control.install.progress', {
deviceName: this.$store.device.getLabel(device),
}),
icon: LoadingIcon,
duration: 0,
})
)
let failCount = 0

View File

@ -1,12 +1,10 @@
<template>
<div class="" @click="handleScreenCap(device)">
<div class="" @click="handleCapture(device)">
<slot />
</div>
</template>
<script>
import LoadingIcon from '$/components/Device/components/LoadingIcon/index.vue'
export default {
props: {
device: {
@ -18,21 +16,22 @@ export default {
return {}
},
methods: {
invoke(...args) {
return this.handleCapture(...args)
},
preferenceData(...args) {
return this.$store.preference.getData(...args)
},
async handleScreenCap(device) {
const messageEl = this.$message({
message: this.$t('device.control.capture.progress', {
async handleCapture(device) {
const messageEl = this.$message.loading(
this.$t('device.control.capture.progress', {
deviceName: this.$store.device.getLabel(device),
}),
icon: LoadingIcon,
duration: 0,
})
)
const fileName = this.$store.device.getLabel(
device,
({ time }) => `screenshot-${time}.png`,
({ time }) => `screenshot-${time}.jpg`,
)
const deviceConfig = this.preferenceData(device.id)
@ -40,7 +39,7 @@ export default {
try {
await this.$adb.screencap(device.id, { savePath })
this.handleScreencapSuccess(savePath)
await this.handleSuccess(savePath)
}
catch (error) {
if (error.message) {
@ -50,29 +49,12 @@ export default {
messageEl.close()
},
async handleScreencapSuccess(savePath) {
try {
await this.$confirm(
this.$t('device.control.capture.success.message'),
this.$t('device.control.capture.success.message.title'),
{
confirmButtonText: this.$t('common.confirm'),
cancelButtonText: this.$t('common.cancel'),
closeOnClickModal: false,
type: 'success',
},
)
await this.$electron.ipcRenderer.invoke(
'show-item-in-folder',
savePath,
)
}
catch (error) {
if (error.message) {
this.$message.warning(error.message)
}
}
async handleSuccess(savePath) {
return this.$message.success(
`${this.$t(
'device.control.capture.success.message.title',
)}: ${savePath}`,
)
},
},
}

View File

@ -66,7 +66,7 @@
<script>
import Screenshot from './Screenshot/index.vue'
import AppInstall from './AppInstall/index.vue'
import Application from './Application/index.vue'
import Gnirehtet from './Gnirehtet/index.vue'
import MirrorGroup from './MirrorGroup/index.vue'
import Rotation from './Rotation/index.vue'
@ -76,7 +76,7 @@ import FileManage from './FileManage/index.vue'
export default {
components: {
Screenshot,
AppInstall,
Application,
Gnirehtet,
MirrorGroup,
Rotation,
@ -142,7 +142,7 @@ export default {
{
label: 'device.control.install',
svgIcon: 'install',
component: 'AppInstall',
component: 'Application',
},
{
label: 'device.control.file.name',

View File

@ -51,7 +51,7 @@ export default {
await recording
this.onRecordSuccess(savePath)
await this.handleSuccess(savePath)
}
catch (error) {
console.error('record.args', args)
@ -79,24 +79,10 @@ export default {
return value
},
async onRecordSuccess(savePath) {
try {
await this.$confirm(
this.$t('device.record.success.message'),
this.$t('device.record.success.title'),
{
confirmButtonText: this.$t('common.confirm'),
cancelButtonText: this.$t('common.cancel'),
closeOnClickModal: false,
type: 'success',
},
)
await this.$electron.ipcRenderer.invoke('show-item-in-folder', savePath)
}
catch (error) {
console.warn(error)
}
async handleSuccess(savePath) {
return this.$message.success(
`${this.$t('device.record.success.title')}: ${savePath}`,
)
},
},
}

View File

@ -1,6 +1,6 @@
<template>
<div class="h-full flex flex-col">
<div class="flex items-center flex-none space-x-2 pt-1">
<div class="flex items-center flex-none space-x-2 py-1 overflow-x-auto">
<Wireless ref="wireless" :reload="getDeviceData" />
<div class="w-px h-7 !ml-4 !mr-2 bg-gray-200"></div>
@ -33,7 +33,7 @@
<BatchActions
class="overflow-hidden transition-all"
:class="isMultipleRow ? 'h-12 opacity-100 mt-4' : 'h-0 opacity-0 mt-0'"
:class="isMultipleRow ? 'h-12 opacity-100 mt-3' : 'h-0 opacity-0 mt-0'"
:devices="selectionRows"
/>
@ -68,6 +68,7 @@
sortable
show-overflow-tooltip
align="left"
min-width="200"
>
<template #default="{ row }">
<div class="flex items-center">
@ -96,7 +97,7 @@
<el-table-column
v-slot="{ row, $index }"
:label="$t('device.control.name')"
width="450"
width="400"
align="left"
>
<MirrorAction

View File

@ -208,7 +208,6 @@ import DisplaySelect from './components/DisplaySelect/index.vue'
import KeyboardInjectSelect from './components/KeyboardInjectSelect/index.vue'
import { usePreferenceStore } from '$/store/index.js'
import LoadingIcon from '$/components/Device/components/LoadingIcon/index.vue'
export default {
components: {
@ -349,11 +348,7 @@ export default {
},
async handleExport() {
const messageEl = this.$message({
message: this.$t('preferences.config.export.message'),
icon: LoadingIcon,
duration: 0,
})
const messageEl = this.$message.loading(this.$t('preferences.config.export.message'))
try {
await this.$electron.ipcRenderer.invoke('show-save-dialog', {

View File

@ -16,6 +16,7 @@ export default () => {
}),
useAutoComponents({
resolvers,
dirs: 'none',
}),
]
}

View File

@ -6,7 +6,7 @@
<script>
export default {
name: 'LoadingIcon',
name: 'EleIconLoading',
}
</script>

View File

@ -8,12 +8,22 @@ import 'element-plus/theme-chalk/el-message-box.css'
import 'element-plus/theme-chalk/dark/css-vars.css'
import './restyle.css'
import EleIconLoading from './components/EleIconLoading/index.vue'
export default {
install(app) {
for (const [key, component] of Object.entries(ElementPlusIcons)) {
app.component(key, component)
}
ElMessage.loading = (message, options = {}) =>
ElMessage({
duration: 0,
...options,
message,
icon: EleIconLoading,
})
app.use(ElMessage)
app.use(ElMessageBox)
app.use(ElLoading)

View File

@ -7,6 +7,7 @@ html {
@screen sm {
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {