feat: 📸 Enhanced recording

This commit is contained in:
viarotel 2024-10-28 15:55:49 +08:00
parent 9555f58df5
commit 7f10161ad7
24 changed files with 291 additions and 216 deletions

91
.vscode/settings.json vendored
View File

@ -1,15 +1,67 @@
{ {
"eslint.codeAction.showDocumentation": {
"enable": true
},
"eslint.format.enable": true,
"prettier.enable": false,
"editor.formatOnSave": false, "editor.formatOnSave": false,
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit", "source.fixAll.eslint": "explicit",
"source.organizeImports": "never" "source.organizeImports": "never"
}, },
"eslint.format.enable": true, "eslint.rules.customizations": [
"eslint.codeAction.showDocumentation": { {
"enable": true "rule": "style/*",
}, "severity": "off",
"fixable": true
},
{
"rule": "format/*",
"severity": "off",
"fixable": true
},
{
"rule": "*-indent",
"severity": "off",
"fixable": true
},
{
"rule": "*-spacing",
"severity": "off",
"fixable": true
},
{
"rule": "*-spaces",
"severity": "off",
"fixable": true
},
{
"rule": "*-order",
"severity": "off",
"fixable": true
},
{
"rule": "*-dangle",
"severity": "off",
"fixable": true
},
{
"rule": "*-newline",
"severity": "off",
"fixable": true
},
{
"rule": "*quotes",
"severity": "off",
"fixable": true
},
{
"rule": "*semi",
"severity": "off",
"fixable": true
}
],
"eslint.validate": [ "eslint.validate": [
// "jsonc",
"javascript", "javascript",
"javascriptreact", "javascriptreact",
"typescript", "typescript",
@ -18,19 +70,25 @@
"html", "html",
"markdown", "markdown",
"json", "json",
"yaml" "jsonc",
"yaml",
"toml",
"xml",
"gql",
"graphql",
"astro",
"svelte",
"css",
"less",
"scss",
"pcss",
"postcss"
], ],
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"i18n-ally.localesPaths": ["src/locales/index.js", "src/locales/languages"],
"i18n-ally.sourceLanguage": "zh-CN", "i18n-ally.sourceLanguage": "zh-CN",
"i18n-ally.localesPaths": [
"src/locales/index.js",
"src/locales/languages"
],
"i18n-ally.keystyle": "nested", "i18n-ally.keystyle": "nested",
"i18n-ally.extract.ignored": [ "i18n-ally.extract.ignored": [
"Switch", "Switch",
@ -52,5 +110,4 @@
"cSpell.words": [ "cSpell.words": [
"bhsn" "bhsn"
], ],
"common-intellisense.ui": []
} }

View File

@ -84,15 +84,18 @@ Windows 及 Linux 端内部集成了 Gnirehtet 用于提供 PC 到安卓设
- 镜像 - 镜像
- 录制 - 录制
- OTG - 录制相机
- 摄像 - 录制音频
- 相机
- 灵活启动 - 灵活启动
- OTG
### 设备交互栏 ### 设备交互栏
- 切换键 - 切换键
- 主屏幕 - 主屏幕
- 返回键 - 返回键
- 关闭屏幕(实验性)
- 通知栏 - 通知栏
- 电源键 - 电源键
- 旋转屏幕 - 旋转屏幕
@ -312,6 +315,10 @@ Windows 及 Linux 端内部集成了 Gnirehtet 用于提供 PC 到安卓设
你需要自定义 `scrcpy` 以及 `adb` 的文件路径(确保具有可执行权限),如果用到反向供网则同样需要以同样方法配置 `gnirehtet` 你需要自定义 `scrcpy` 以及 `adb` 的文件路径(确保具有可执行权限),如果用到反向供网则同样需要以同样方法配置 `gnirehtet`
### Could not execute "adb start-server"
这可能是因为安装路径中包含中文或特殊字符导致的,请尝试更改安装路径。
## 获得帮助 ## 获得帮助
> 因为是开源项目 全靠爱发电 所以支持有限 更新节奏不固定 > 因为是开源项目 全靠爱发电 所以支持有限 更新节奏不固定

View File

@ -82,15 +82,18 @@ Gnirehtet встроен в приложения для Windows и Linux, что
- Зеркалирование - Зеркалирование
- Запись - Запись
- OTG - Запись с камеры
- Запись аудио
- Камера - Камера
- Пользовательский - Гибкий запуск
- OTG
### Панель взаимодействия с устройством ### Панель взаимодействия с устройством
- Переключатель - Переключатель
- Домой - Домой
- Назад - Назад
- Выключение экрана (экспериментально)
- Уведомление - Уведомление
- Питание - Питание
- Поворот - Поворот
@ -311,6 +314,10 @@ Gnirehtet встроен в приложения для Windows и Linux, что
Вам нужно настроить пользовательские пути к файлам для `scrcpy` и `adb` (убедившись, что у них есть разрешения на выполнение). Если вы используете обратный тетеринг, аналогично настройте `gnirehtet`. Вам нужно настроить пользовательские пути к файлам для `scrcpy` и `adb` (убедившись, что у них есть разрешения на выполнение). Если вы используете обратный тетеринг, аналогично настройте `gnirehtet`.
### Could not execute "adb start-server"
Это может быть вызвано наличием китайских или специальных символов в пути установки. Попробуйте изменить путь установки.
## Получение помощи ## Получение помощи
> Поскольку это проект с открытым исходным кодом, полностью поддерживаемый пожертвованиями, поддержка ограничена, и обновления могут не выходить по фиксированному расписанию. > Поскольку это проект с открытым исходным кодом, полностью поддерживаемый пожертвованиями, поддержка ограничена, и обновления могут не выходить по фиксированному расписанию.

View File

@ -82,15 +82,18 @@ Refer to [scrcpy/doc/shortcuts](https://github.com/Genymobile/scrcpy/blob/master
- Mirror - Mirror
- Recording - Recording
- OTG - Recording Camera
- Recording Audio
- Camera - Camera
- Custom - Custom
- OTG
### Device Interaction Bar ### Device Interaction Bar
- Switch - Switch
- Home - Home
- Back - Back
- Turn off screen (experimental)
- Notification - Notification
- Power - Power
- Rotation - Rotation
@ -311,6 +314,10 @@ Please try `disabling audio forwarding` feature through the `preferences setting
You need to customize the file paths for `scrcpy` and `adb` (ensuring they have executable permissions). If using reverse tethering, configure `gnirehtet` similarly. You need to customize the file paths for `scrcpy` and `adb` (ensuring they have executable permissions). If using reverse tethering, configure `gnirehtet` similarly.
### Could not execute "adb start-server"
This might be due to Chinese or special characters in the installation path. Please try changing the installation path.
## Getting Help ## Getting Help
> As this is an open source project run entirely by donations, support is limited and updates may not be on a fixed schedule. > As this is an open source project run entirely by donations, support is limited and updates may not be on a fixed schedule.

View File

@ -63,7 +63,6 @@ import { i18n } from '$/locales/index.js'
import localeModel from '$/plugins/element-plus/locale.js' import localeModel from '$/plugins/element-plus/locale.js'
import { useDeviceStore, useThemeStore } from '$/store/index.js' import { useDeviceStore, useThemeStore } from '$/store/index.js'
import { ElMessage } from 'element-plus'
const themeStore = useThemeStore() const themeStore = useThemeStore()
const deviceStore = useDeviceStore() const deviceStore = useDeviceStore()

View File

@ -10,7 +10,6 @@ export function initControlWindow(mainWindow) {
const controlWindow = new BrowserWindow({ const controlWindow = new BrowserWindow({
icon: getLogoPath(), icon: getLogoPath(),
parent: mainWindow,
width: 700, width: 700,
minWidth: 700, minWidth: 700,
height: 28, height: 28,

View File

@ -27,7 +27,7 @@
"vue": "3.4.21" "vue": "3.4.21"
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "3.3.2", "@antfu/eslint-config": "3.8.0",
"@devicefarmer/adbkit": "3.2.6", "@devicefarmer/adbkit": "3.2.6",
"@electron-toolkit/preload": "3.0.1", "@electron-toolkit/preload": "3.0.1",
"@electron-toolkit/utils": "3.0.0", "@electron-toolkit/utils": "3.0.0",
@ -39,15 +39,15 @@
"@vitejs/plugin-vue": "5.0.4", "@vitejs/plugin-vue": "5.0.4",
"@vueuse/core": "10.9.0", "@vueuse/core": "10.9.0",
"dayjs": "1.11.11", "dayjs": "1.11.11",
"electron": "29.1.1", "electron": "33.0.2",
"electron-builder": "24.13.3", "electron-builder": "25.1.8",
"electron-context-menu": "4.0.4", "electron-context-menu": "4.0.4",
"electron-find-in-page": "1.0.8", "electron-find-in-page": "1.0.8",
"electron-log": "5.2.0", "electron-log": "5.2.0",
"electron-store": "9.0.0", "electron-store": "9.0.0",
"electron-updater": "6.1.8", "electron-updater": "6.3.9",
"element-plus": "2.8.2", "element-plus": "2.8.2",
"eslint": "9.10.0", "eslint": "9.13.0",
"fix-path": "4.0.0", "fix-path": "4.0.0",
"fs-extra": "11.2.0", "fs-extra": "11.2.0",
"husky": "9.0.11", "husky": "9.0.11",
@ -59,11 +59,11 @@
"rimraf": "^6.0.1", "rimraf": "^6.0.1",
"simple-git": "^3.27.0", "simple-git": "^3.27.0",
"unocss": "0.62.3", "unocss": "0.62.3",
"unplugin-auto-import": "0.18.2", "unplugin-auto-import": "0.18.3",
"unplugin-vue-components": "0.27.4", "unplugin-vue-components": "0.27.4",
"vite": "5.1.5", "vite": "5.1.5",
"vite-plugin-electron": "0.28.7", "vite-plugin-electron": "0.28.8",
"vite-plugin-electron-renderer": "0.14.5", "vite-plugin-electron-renderer": "0.14.6",
"vite-svg-loader": "5.1.0", "vite-svg-loader": "5.1.0",
"vue-command": "35.2.1", "vue-command": "35.2.1",
"vue-i18n": "9.13.1", "vue-i18n": "9.13.1",

View File

@ -6,6 +6,7 @@
import { sleep } from '$/utils' import { sleep } from '$/utils'
export default { export default {
inheritAttrs: false,
props: { props: {
row: { row: {
type: Object, type: Object,

View File

@ -14,6 +14,7 @@ export default {
components: { components: {
DeployDialog, DeployDialog,
}, },
inheritAttrs: false,
props: { props: {
row: { row: {
type: Object, type: Object,

View File

@ -6,6 +6,7 @@
import { sleep } from '$/utils' import { sleep } from '$/utils'
export default { export default {
inheritAttrs: false,
props: { props: {
row: { row: {
type: Object, type: Object,

View File

@ -6,8 +6,31 @@
import { sleep } from '$/utils' import { sleep } from '$/utils'
import { openFloatControl } from '$/utils/device/index.js' import { openFloatControl } from '$/utils/device/index.js'
const recordModel = {
default: {
excludes: '',
command: '',
extname: config => config['--record-format'] || 'mp4',
},
audio: {
excludes: ['--video-source', '--no-audio', '--mouse'],
commands: ['--no-video', '--mouse=disabled'],
extname: config => config['--audio-record-format'] || 'opus',
},
camera: {
excludes: ['--video-source'],
commands: ['--video-source=camera'],
extname: config => config['--record-format'] || 'mp4',
},
}
export default { export default {
inheritAttrs: false,
props: { props: {
recordType: {
type: String,
default: 'default',
},
row: { row: {
type: Object, type: Object,
default: () => ({}), default: () => ({}),
@ -22,6 +45,11 @@ export default {
loading: false, loading: false,
} }
}, },
computed: {
activeModel() {
return recordModel[this.recordType]
},
},
methods: { methods: {
async handleClick() { async handleClick() {
const row = this.row const row = this.row
@ -32,25 +60,36 @@ export default {
const savePath = this.getRecordPath(row) const savePath = this.getRecordPath(row)
const args = this.$store.preference.scrcpyParameter(row.id, { let args = this.$store.preference.scrcpyParameter(row.id, {
isRecord: true, isRecord: ['default', 'audio'].includes(this.recordType),
excludes: ['--otg', '--mouse=aoa', '--keyboard=aoa', '--show-touches'], isCamera: ['camera'].includes(this.recordType),
excludes: [
'--otg',
'--mouse=aoa',
'--keyboard=aoa',
'--show-touches',
...this.activeModel.excludes,
],
}) })
args += ` ${this.activeModel.commands.join(' ')}`
console.log('args', args)
try { try {
const recording = this.$scrcpy.record(row.id, { const recording = this.$scrcpy.record(row.id, {
title: this.$store.device.getLabel(row, 'recording'), title: this.$store.device.getLabel(row, 'recording'),
savePath, savePath,
args, args,
stdout: this.onStdout,
stderr: this.onStderr,
}) })
await sleep(1 * 1000) await sleep(1 * 1000)
this.loading = false this.loading = false
openFloatControl(toRaw(this.row)) if (['default'].includes(this.$props.type)) {
openFloatControl(toRaw(this.row))
}
await recording await recording
@ -65,20 +104,21 @@ export default {
} }
} }
}, },
onStdout() {},
onStderr() {},
getRecordPath(row) { getRecordPath(row) {
const config = this.$store.preference.getData(row.id) const deviceConfig = this.$store.preference.getData(this.row.id)
const basePath = config.savePath
const extension = config['--record-format'] || 'mp4' const savePath = deviceConfig.savePath
const extension = this.activeModel.extname(deviceConfig)
const fileName = this.$store.device.getLabel( const fileName = this.$store.device.getLabel(
row, row,
({ time }) => `record-${time}.${extension}`, ({ time }) => `record-${time}.${extension}`,
) )
const joinValue = this.$path.join(basePath, fileName) const filePath = this.$path.join(savePath, fileName)
const value = this.$path.normalize(joinValue)
const value = this.$path.normalize(filePath)
return value return value
}, },

View File

@ -1,96 +0,0 @@
<template>
<slot :loading="loading" :trigger="handleClick" />
</template>
<script>
import { sleep } from '$/utils'
import { openFloatControl } from '$/utils/device/index.js'
export default {
props: {
row: {
type: Object,
default: () => ({}),
},
toggleRowExpansion: {
type: Function,
default: () => () => false,
},
},
data() {
return {
loading: false,
}
},
methods: {
async handleClick() {
const row = this.row
this.loading = true
this.toggleRowExpansion(row, true)
const savePath = this.getRecordPath(row)
let args = this.$store.preference.scrcpyParameter(row.id, {
isRecord: true,
excludes: ['--otg', '--mouse=aoa', '--keyboard=aoa', '--video-source', '--show-touches'],
})
args += ' --video-source=camera'
try {
const recording = this.$scrcpy.record(row.id, {
title: this.$store.device.getLabel(row, 'recording'),
savePath,
args,
stdout: this.onStdout,
stderr: this.onStderr,
})
await sleep(1 * 1000)
this.loading = false
openFloatControl(toRaw(this.row))
await recording
await this.handleSuccess(savePath)
}
catch (error) {
console.error('record.args', args)
console.error('record.error', error)
if (error.message) {
this.$message.warning(error.message)
}
}
},
onStdout() {},
onStderr() {},
getRecordPath(row) {
const config = this.$store.preference.getData(row.id)
const basePath = config.savePath
const extension = config['--record-format'] || 'mp4'
const fileName = this.$store.device.getLabel(
row,
({ time }) => `record-${time}.${extension}`,
)
const joinValue = this.$path.join(basePath, fileName)
const value = this.$path.normalize(joinValue)
return value
},
async handleSuccess(savePath) {
return this.$message.success(
`${this.$t('device.record.success.title')}: ${savePath}`,
)
},
},
}
</script>
<style></style>

View File

@ -17,6 +17,7 @@
:key="index" :key="index"
v-bind="{ v-bind="{
...$props, ...$props,
...(item.props || {}),
}" }"
v-slot="{ loading, trigger }" v-slot="{ loading, trigger }"
> >
@ -40,7 +41,6 @@
<script> <script>
import Record from './components/Record/index.vue' import Record from './components/Record/index.vue'
import Camera from './components/Camera/index.vue' import Camera from './components/Camera/index.vue'
import RecordCamera from './components/RecordCamera/index.vue'
import Otg from './components/Otg/index.vue' import Otg from './components/Otg/index.vue'
import Custom from './components/Custom/index.vue' import Custom from './components/Custom/index.vue'
@ -48,7 +48,6 @@ export default {
components: { components: {
Record, Record,
Camera, Camera,
RecordCamera,
Otg, Otg,
Custom, Custom,
}, },
@ -66,12 +65,22 @@ export default {
component: 'Record', component: 'Record',
}, },
{ {
label: 'device.actions.more.camera.name', label: 'device.actions.more.recordCamera.name',
component: 'Camera', component: 'Record',
props: {
recordType: 'camera',
},
}, },
{ {
label: 'device.actions.more.recordCamera.name', label: 'device.actions.more.recordAudio.name',
component: 'RecordCamera', component: 'Record',
props: {
recordType: 'audio',
},
},
{
label: 'device.actions.more.camera.name',
component: 'Camera',
}, },
{ {
label: 'device.actions.more.otg.name', label: 'device.actions.more.otg.name',

View File

@ -253,21 +253,21 @@ export default {
await this.$confirm( await this.$confirm(
`<div class="pt-4 pl-4"> `<div class="pt-4 pl-4">
<div class="text-sm text-red-500 pb-4">${this.$t( <div class="text-sm text-red-500 pb-4">${this.$t(
'device.wireless.connect.error.detail', 'device.wireless.connect.error.detail',
)}${message}</div> )}${message}</div>
<div>${this.$t('device.wireless.connect.error.reasons[0]')}</div> <div>${this.$t('device.wireless.connect.error.reasons[0]')}</div>
<div>1. ${this.$t( <div>1. ${this.$t(
'device.wireless.connect.error.reasons[1]', 'device.wireless.connect.error.reasons[1]',
)} </div> )} </div>
<div>2. ${this.$t( <div>2. ${this.$t(
'device.wireless.connect.error.reasons[2]', 'device.wireless.connect.error.reasons[2]',
)} </div> )} </div>
<div>3. ${this.$t( <div>3. ${this.$t(
'device.wireless.connect.error.reasons[3]', 'device.wireless.connect.error.reasons[3]',
)} </div> )} </div>
<div>4. ${this.$t( <div>4. ${this.$t(
'device.wireless.connect.error.reasons[4]', 'device.wireless.connect.error.reasons[4]',
)} </div> )} </div>
</div>`, </div>`,
this.$t('device.wireless.connect.error.title'), this.$t('device.wireless.connect.error.title'),
{ {

View File

@ -1,6 +1,6 @@
<template> <template>
<el-select <el-select
v-bind="{ ...(data.props || {}) }" v-bind="{ clearable: true, ...(data.props || {}) }"
v-model="selectValue" v-model="selectValue"
class="!w-full" class="!w-full"
> >

View File

@ -1,6 +1,7 @@
<template> <template>
<el-select <el-select
v-bind="{ v-bind="{
clearable: true,
...(data.props || {}), ...(data.props || {}),
}" }"
v-model="selectValue" v-model="selectValue"

View File

@ -1,6 +1,6 @@
<template> <template>
<el-select <el-select
v-bind="{ ...(data.props || {}) }" v-bind="{ clearable: true, ...(data.props || {}) }"
v-model="selectValue" v-model="selectValue"
class="!w-full" class="!w-full"
> >

View File

@ -1,5 +1,5 @@
<template> <template>
<el-form ref="elForm" :model="preferenceData" label-width="225px" class=""> <el-form ref="elForm" :model="preferenceData" label-width="250px" class="">
<el-collapse <el-collapse
v-model="collapseValue" v-model="collapseValue"
v-bind="{ v-bind="{
@ -31,61 +31,52 @@
</div> </div>
</div> </div>
</template> </template>
<div class="pt-4"> <div class="pr-8 pt-4">
<el-form <el-row :gutter="20">
ref="elForm" <el-col
:model="preferenceData" v-for="(item_1, name_1) of subModel(item)"
label-width="250px" :key="name_1"
class="pr-8 pt-4" :span="item_1.span || 12"
> :offset="item_1.offset || 0"
<el-row :gutter="20"> >
<el-col <el-form-item :label="$t(item_1.label)" :prop="item_1.field">
v-for="(item_1, name_1) of subModel(item)" <template #label>
:key="name_1" <div class="flex items-center">
:span="item_1.span || 12" <el-tooltip
:offset="item_1.offset || 0" v-if="item_1.tips"
> popper-class="max-w-96"
<el-form-item :label="$t(item_1.label)" :prop="item_1.field"> effect="dark"
<template #label> :content="$t(item_1.tips)"
<div class="flex items-center"> placement="bottom"
<el-tooltip >
v-if="item_1.tips" <el-link
popper-class="max-w-96" class="mr-1 !text-base"
effect="dark" icon="InfoFilled"
:content="$t(item_1.tips)" type="warning"
placement="bottom" :underline="false"
> >
<el-link </el-link>
class="mr-1 !text-base" </el-tooltip>
icon="InfoFilled" <div class="truncate max-w-56" :title="$t(item_1.label)">
type="warning" {{ $t(item_1.label) }}
:underline="false"
>
</el-link>
</el-tooltip>
<div class="truncate max-w-56" :title="$t(item_1.label)">
{{
$t(item_1.label)
}}
</div>
</div> </div>
</template> </div>
</template>
<component <component
:is="inputModel[item_1.type]" :is="inputModel[item_1.type]"
v-model="preferenceData[item_1.field]" v-model="preferenceData[item_1.field]"
v-bind="{ v-bind="{
preferenceData, preferenceData,
deviceScope, deviceScope,
title: $t(item_1.placeholder), title: $t(item_1.placeholder),
placeholder: $t(item_1.placeholder), placeholder: $t(item_1.placeholder),
data: item_1, data: item_1,
}" }"
></component> ></component>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
</el-form>
</div> </div>
</el-collapse-item> </el-collapse-item>
</el-collapse> </el-collapse>
@ -93,9 +84,12 @@
</template> </template>
<script setup> <script setup>
import { i18n } from '$/locales/index.js'
import { usePreferenceStore } from '$/store/index.js' import { usePreferenceStore } from '$/store/index.js'
import { omit } from 'lodash-es' import { omit } from 'lodash-es'
import { computed } from 'vue'
import { inputModel } from './components/index.js' import { inputModel } from './components/index.js'
@ -114,6 +108,8 @@ const props = defineProps({
}, },
}) })
const locale = computed(() => i18n.global.locale.value)
const preferenceData = defineModel('modelValue', { const preferenceData = defineModel('modelValue', {
type: Object, type: Object,
default: () => ({}), default: () => ({}),

View File

@ -129,6 +129,7 @@
"device.actions.more.record.name": "Start Recording", "device.actions.more.record.name": "Start Recording",
"device.actions.more.camera.name": "Startup Camera", "device.actions.more.camera.name": "Startup Camera",
"device.actions.more.recordCamera.name": "Record Camera", "device.actions.more.recordCamera.name": "Record Camera",
"device.actions.more.recordAudio.name": "Record Audio",
"device.actions.more.otg.name": "Startup OTG", "device.actions.more.otg.name": "Startup OTG",
"device.actions.more.custom.name": "Custom Startup", "device.actions.more.custom.name": "Custom Startup",
@ -317,9 +318,11 @@
"preferences.record.name": "Recording", "preferences.record.name": "Recording",
"preferences.record.format.name": "Format", "preferences.record.format.name": "Format",
"preferences.record.format.placeholder": "mp4",
"preferences.record.format.audio.name": "Audio Format",
"preferences.record.format.audio.placeholder": "opus",
"preferences.record.time-limit.name": "Recording Time Limit", "preferences.record.time-limit.name": "Recording Time Limit",
"preferences.record.time-limit.placeholder": "No time limit", "preferences.record.time-limit.placeholder": "No time limit",
"preferences.record.format.placeholder": "mp4",
"preferences.record.lock-video-orientation.name": "Video Direction", "preferences.record.lock-video-orientation.name": "Video Direction",
"preferences.record.lock-video-orientation.placeholder": "Device Orientation", "preferences.record.lock-video-orientation.placeholder": "Device Orientation",
"preferences.record.no-video-playback.name": "Disable Video Playback", "preferences.record.no-video-playback.name": "Disable Video Playback",

View File

@ -129,6 +129,7 @@
"device.actions.more.record.name": "Начать запись", "device.actions.more.record.name": "Начать запись",
"device.actions.more.camera.name": "Запустить камеры", "device.actions.more.camera.name": "Запустить камеры",
"device.actions.more.recordCamera.name": "Запись камеры", "device.actions.more.recordCamera.name": "Запись камеры",
"device.actions.more.recordAudio.name": "Записать аудио",
"device.actions.more.otg.name": "Запустить OTG", "device.actions.more.otg.name": "Запустить OTG",
"device.actions.more.custom.name": "Пользовательский запуск", "device.actions.more.custom.name": "Пользовательский запуск",
@ -317,9 +318,11 @@
"preferences.record.name": "Запись", "preferences.record.name": "Запись",
"preferences.record.format.name": "Формат", "preferences.record.format.name": "Формат",
"preferences.record.format.placeholder": "mp4",
"preferences.record.format.audio.name": "Аудио формат",
"preferences.record.format.audio.placeholder": "opus",
"preferences.record.time-limit.name": "Ограничение времени записи", "preferences.record.time-limit.name": "Ограничение времени записи",
"preferences.record.time-limit.placeholder": "Без ограничения времени", "preferences.record.time-limit.placeholder": "Без ограничения времени",
"preferences.record.format.placeholder": "mp4",
"preferences.record.lock-video-orientation.name": "Ориентация видео", "preferences.record.lock-video-orientation.name": "Ориентация видео",
"preferences.record.lock-video-orientation.placeholder": "Ориентация устройства", "preferences.record.lock-video-orientation.placeholder": "Ориентация устройства",
"preferences.record.no-video-playback.name": "Отключить воспроизведение видео", "preferences.record.no-video-playback.name": "Отключить воспроизведение видео",

View File

@ -129,6 +129,7 @@
"device.actions.more.record.name": "开始录制", "device.actions.more.record.name": "开始录制",
"device.actions.more.camera.name": "启动相机", "device.actions.more.camera.name": "启动相机",
"device.actions.more.recordCamera.name": "录制相机", "device.actions.more.recordCamera.name": "录制相机",
"device.actions.more.recordAudio.name": "录制音频",
"device.actions.more.otg.name": "启动OTG", "device.actions.more.otg.name": "启动OTG",
"device.actions.more.custom.name": "灵活启动", "device.actions.more.custom.name": "灵活启动",
@ -316,8 +317,10 @@
"preferences.window.position.y.placeholder": "相对于桌面中心", "preferences.window.position.y.placeholder": "相对于桌面中心",
"preferences.record.name": "音视频录制", "preferences.record.name": "音视频录制",
"preferences.record.format.name": "录制视频格式", "preferences.record.format.name": "视频格式",
"preferences.record.format.placeholder": "mp4", "preferences.record.format.placeholder": "mp4",
"preferences.record.format.audio.name": "音频格式",
"preferences.record.format.audio.placeholder": "opus",
"preferences.record.time-limit.name": "录制时长", "preferences.record.time-limit.name": "录制时长",
"preferences.record.time-limit.placeholder": "不限时长", "preferences.record.time-limit.placeholder": "不限时长",
"preferences.record.lock-video-orientation.name": "录制视频方向", "preferences.record.lock-video-orientation.name": "录制视频方向",

View File

@ -129,6 +129,7 @@
"device.actions.more.record.name": "開始錄製", "device.actions.more.record.name": "開始錄製",
"device.actions.more.camera.name": "啟動鏡頭", "device.actions.more.camera.name": "啟動鏡頭",
"device.actions.more.recordCamera.name": "錄製鏡頭", "device.actions.more.recordCamera.name": "錄製鏡頭",
"device.actions.more.recordAudio.name": "錄製音訊",
"device.actions.more.otg.name": "啟動 OTG", "device.actions.more.otg.name": "啟動 OTG",
"device.actions.more.custom.name": "靈活啟動", "device.actions.more.custom.name": "靈活啟動",
@ -316,8 +317,10 @@
"preferences.window.position.y.placeholder": "相對於桌面中心", "preferences.window.position.y.placeholder": "相對於桌面中心",
"preferences.record.name": "影片錄製", "preferences.record.name": "影片錄製",
"preferences.record.format.name": "錄製影片格式", "preferences.record.format.name": "影片格式",
"preferences.record.format.placeholder": "mp4", "preferences.record.format.placeholder": "mp4",
"preferences.record.format.audio.name": "音訊格式",
"preferences.record.format.audio.placeholder": "opus",
"preferences.record.time-limit.name": "錄製時長", "preferences.record.time-limit.name": "錄製時長",
"preferences.record.time-limit.placeholder": "不限時長", "preferences.record.time-limit.placeholder": "不限時長",
"preferences.record.lock-video-orientation.name": "錄製影片方向", "preferences.record.lock-video-orientation.name": "錄製影片方向",

View File

@ -46,6 +46,7 @@ export const usePreferenceStore = defineStore({
'--video-code', '--video-code',
'--audio-code', '--audio-code',
'--keyboard-inject', '--keyboard-inject',
'--audio-record-format',
...getOtherFields('scrcpy'), ...getOtherFields('scrcpy'),
], ],
recordKeys, recordKeys,

View File

@ -19,6 +19,39 @@ export default {
}, },
], ],
}, },
audioRecordFormat: {
label: 'preferences.record.format.audio.name',
field: '--audio-record-format',
type: 'Select',
value: void 0,
placeholder: 'preferences.record.format.audio.placeholder',
options: [
{
label: 'opus',
value: 'opus',
},
{
label: 'wav',
value: 'wav',
},
{
label: 'mka',
value: 'mka',
},
{
label: 'flac',
value: 'flac',
},
{
label: 'aac',
value: 'aac',
},
{
label: 'm4a',
value: 'm4a',
},
],
},
lockVideoOrientation: { lockVideoOrientation: {
label: 'preferences.record.lock-video-orientation.name', label: 'preferences.record.lock-video-orientation.name',
field: '--lock-video-orientation', field: '--lock-video-orientation',