Skip to content

Commit 8425077

Browse files
committed
新增:视频合成增加本地文件选择,便于已有音频文件合成
1 parent 922ca96 commit 8425077

File tree

11 files changed

+122
-15
lines changed

11 files changed

+122
-15
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## v0.7.0
22

3+
- 新增:视频合成增加本地文件选择,便于已有音频文件合成
34
- 修复:GPU识别显示优化,支持多GPU显示序号和内存大小
45
- 修复:文件重命名跨设备异常问题
56
- 优化:界面显示优化,部分界面显示错位问题

electron/mapi/db/migration.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ const versions = [
8585
await db.execute(`ALTER TABLE data_video_gen ADD COLUMN result TEXT`);
8686
}
8787
},
88+
{
89+
version:3,
90+
up: async (db: DB) => {
91+
await db.execute(`ALTER TABLE data_video_gen ADD COLUMN soundCustomFile TEXT`);
92+
}
93+
},
8894
]
8995

9096
export default {

electron/mapi/file/index.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import fs from "node:fs";
44
import {StrUtil, TimeUtil} from "../../lib/util";
55
import Apps from "../app";
66
import {Readable} from "node:stream";
7-
import {isWin} from "../../lib/env";
87

98
const nodePath = path
109

@@ -355,6 +354,47 @@ const copy = async (pathOld: string, pathNew: string, option?: { isFullPath?: bo
355354
fs.copyFileSync(fullPathOld, fullPathNew)
356355
}
357356

357+
const hubCreate = async (ext: string = 'bin') => {
358+
return path.join(
359+
'hub',
360+
TimeUtil.replacePattern('{year}'),
361+
TimeUtil.replacePattern('{month}'),
362+
TimeUtil.replacePattern('{day}'),
363+
TimeUtil.replacePattern('{hour}'),
364+
[
365+
TimeUtil.replacePattern('{minute}'),
366+
TimeUtil.replacePattern('{second}'),
367+
StrUtil.randomString(10),
368+
].join('_') + `.${ext}`
369+
)
370+
}
371+
372+
const hubSave = async (file: string, option?: {
373+
isFullPath?: boolean,
374+
returnFullPath?: boolean,
375+
}) => {
376+
option = Object.assign({
377+
isFullPath: false,
378+
returnFullPath: false,
379+
}, option)
380+
let fp = file
381+
if (!option.isFullPath) {
382+
fp = await fullPath(file)
383+
}
384+
if (!fs.existsSync(fp)) {
385+
throw `FileNotFound ${fp}`
386+
}
387+
const fileExt = ext(fp)
388+
const hubFile = await hubCreate(fileExt)
389+
await copy(fp, path.join(root(), hubFile), {
390+
isFullPath: true,
391+
})
392+
if (option.returnFullPath) {
393+
return path.join(root(), hubFile)
394+
}
395+
return hubFile
396+
}
397+
358398
const tempRoot = async () => {
359399
await waitAppEnvReady()
360400
const tempDir = path.join(AppEnv.userData, 'temp')
@@ -594,6 +634,7 @@ export const FileIndex = {
594634
appendText,
595635
download,
596636
ext,
637+
hubSave,
597638
}
598639

599640
export default FileIndex

src/components/Video/VideoGenCreate.vue

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const formData = ref({
2626
soundType: 'soundTts',
2727
soundTtsId: 0,
2828
soundCloneId: 0,
29+
soundCustomFile: '',
2930
param: {},
3031
});
3132
const formDataParam = ref([])
@@ -81,6 +82,7 @@ const doSubmit = async () => {
8182
}
8283
let soundTtsRecord: SoundTtsRecord | null = null
8384
let soundCloneRecord: SoundCloneRecord | null = null
85+
let soundCustomFile: string | null = null
8486
if (formData.value.soundType === 'soundTts') {
8587
if (!formData.value.soundTtsId) {
8688
Dialog.tipError(t('请选择声音'))
@@ -101,6 +103,16 @@ const doSubmit = async () => {
101103
Dialog.tipError(t('请选择声音'))
102104
return
103105
}
106+
} else if (formData.value.soundType === 'soundCustom') {
107+
soundCustomFile = formData.value.soundCustomFile
108+
if (!soundCustomFile) {
109+
Dialog.tipError(t('请选择声音'))
110+
return
111+
}
112+
soundCustomFile = await window.$mapi.file.hubSave(soundCustomFile, {
113+
isFullPath: true,
114+
returnFullPath: true,
115+
})
104116
}
105117
if (!formData.value.videoTemplateId) {
106118
Dialog.tipError(t('请选择视频'))
@@ -131,6 +143,7 @@ const doSubmit = async () => {
131143
soundTtsText: soundTtsRecord ? soundTtsRecord.text : '',
132144
soundCloneId: formData.value.soundCloneId,
133145
soundCloneText: soundCloneRecord ? soundCloneRecord.text : '',
146+
soundCustomFile: soundCustomFile || '',
134147
param: formData.value.param,
135148
}
136149
if (!await PermissionService.checkForTask('VideoGen', record)) {
@@ -147,6 +160,22 @@ const refresh = async (type: 'videoTemplate') => {
147160
}
148161
}
149162
163+
const doSoundCustomSelect = async () => {
164+
const path = await window.$mapi.file.openFile({
165+
filters: [
166+
{name: '*.wav', extensions: ['wav']},
167+
],
168+
})
169+
if (!path) {
170+
return
171+
}
172+
formData.value.soundCustomFile = path
173+
}
174+
175+
const fileName = (fullPath: string) => {
176+
return fullPath.replace(/\\/g, '/').split('/').pop() || ''
177+
}
178+
150179
const emit = defineEmits({
151180
submitted: () => true
152181
})
@@ -187,13 +216,12 @@ defineExpose({
187216
<i class="iconfont icon-sound-clone"></i>
188217
{{ $t('声音克隆') }}
189218
</a-radio>
219+
<a-radio value="soundCustom">
220+
<icon-file/>
221+
{{ $t('本地文件') }}
222+
</a-radio>
190223
</a-radio-group>
191224
</div>
192-
<div class="mr-1" v-if="formData.soundType==='soundTts'">
193-
<a-tooltip :content="$t('声音合成')">
194-
<i class="iconfont icon-sound-generate"></i>
195-
</a-tooltip>
196-
</div>
197225
<div class="mr-3 w-56 flex-shrink-0" v-if="formData.soundType==='soundTts'">
198226
<a-select v-model="formData.soundTtsId">
199227
<a-option :value="0">{{ $t('请选择') }}</a-option>
@@ -204,11 +232,6 @@ defineExpose({
204232
</a-option>
205233
</a-select>
206234
</div>
207-
<div class="mr-1" v-if="formData.soundType==='soundClone'">
208-
<a-tooltip :content="$t('声音克隆')">
209-
<i class="iconfont icon-sound-clone"></i>
210-
</a-tooltip>
211-
</div>
212235
<div class="mr-3 w-56 flex-shrink-0" v-if="formData.soundType==='soundClone'">
213236
<a-select v-model="formData.soundCloneId">
214237
<a-option :value="0">{{ $t('请选择') }}</a-option>
@@ -219,6 +242,14 @@ defineExpose({
219242
</a-option>
220243
</a-select>
221244
</div>
245+
<div class="mr-3 w-56 flex-shrink-0" v-if="formData.soundType==='soundCustom'">
246+
<a-button @click="doSoundCustomSelect">
247+
<div v-if="formData.soundCustomFile">
248+
{{ fileName(formData.soundCustomFile) }}
249+
</div>
250+
<div v-else>{{ $t('选择本地文件') }}</div>
251+
</a-button>
252+
</div>
222253
</div>
223254
<div class="flex items-center h-12">
224255
<div class="mr-1">
@@ -245,7 +276,7 @@ defineExpose({
245276
<a-button class="mr-2" type="primary" @click="doSubmit">
246277
{{ $t('开始生成视频') }}
247278
</a-button>
248-
<ServerContentInfoAction :config="modelConfig as any" func="videoGen" />
279+
<ServerContentInfoAction :config="modelConfig as any" func="videoGen"/>
249280
</div>
250281
</div>
251282
</template>

src/declarations/type.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ declare interface Window {
151151
openDirectory: (options: {} = {}) => Promise<any>,
152152
openSave: (options: {} = {}) => Promise<any>,
153153
openPath: (path: string, options: {} = {}) => Promise<void>,
154+
hubSave: (file: string, option?: { isFullPath?: boolean, returnFullPath?: boolean, }) => Promise<string>,
154155
},
155156
updater: {
156157
checkForUpdate: () => Promise<ApiResult<any>>,

src/lang/en-US.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
"041b5a9a": "GPU Mode",
9393
"04f71f68": "Server",
9494
"075e53bc": "Cloud model",
95+
"08a62bfe": "Please select video",
9596
"08ac1686": "Please select sound",
9697
"09e64baa": "Started at {time}",
9798
"0b2004f2": "Are you sure you want to exit the software?",
@@ -152,6 +153,7 @@
152153
"301d8708": "Service Port",
153154
"302416b0": "No Logs Available",
154155
"3034d991": "Coming soon",
156+
"303a1ed3": "Local file",
155157
"303ab60e": "Local Model",
156158
"31450307": "Check for Updates",
157159
"3188fe53": "New version available",
@@ -230,6 +232,7 @@
230232
"65bc9692": "Select a voice file",
231233
"6a7ee8a1": "Use CPU",
232234
"6b5abd5b": "Please upgrade to the pro version",
235+
"6b41f2d3": "Select local file",
233236
"6c9b6559": "Select Model File",
234237
"6e81e205": "Auto-detect updates",
235238
"6fdfdfff": "Add cloud model service",

src/lang/source.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
"未检测到录音设备": "0cc262e2",
116116
"未登录": "0191fce4",
117117
"本产品为开源软件,遵循 AGPL-3.0 license 协议。": "1bb341a5",
118+
"本地文件": "303a1ed3",
118119
"本地模型": "303ab60e",
119120
"本地模型目录": "0c7430b5",
120121
"标识": "000d249f",
@@ -196,6 +197,7 @@
196197
"请选择": "021f3e37",
197198
"请选择声音": "08ac1686",
198199
"请选择声音角色": "720a177b",
200+
"请选择视频": "08a62bfe",
199201
"跟随系统": "42c281d4",
200202
"跨语种": "02261f68",
201203
"软件不满足模型版本要求": "218fecc1",
@@ -210,6 +212,7 @@
210212
"适配": "0012018b",
211213
"选择声音文件": "65bc9692",
212214
"选择声音角色": "65baa0c4",
215+
"选择本地文件": "6b41f2d3",
213216
"选择本地模型": "26d1d59f",
214217
"选择模型": "42fa058a",
215218
"选择视频文件": "7bf1ff1a",

src/lang/zh-CN.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
"041b5a9a": "GPU模式",
9393
"04f71f68": "服务",
9494
"075e53bc": "云端模型服务",
95+
"08a62bfe": "请选择视频",
9596
"08ac1686": "请选择声音",
9697
"09e64baa": "已启动 {time}",
9798
"0b2004f2": "确定退出软件?",
@@ -149,6 +150,7 @@
149150
"301d8708": "服务端口",
150151
"302416b0": "暂无日志",
151152
"3034d991": "敬请期待",
153+
"303a1ed3": "本地文件",
152154
"303ab60e": "本地模型",
153155
"31450307": "检测更新",
154156
"3188fe53": "新版本可用",
@@ -225,6 +227,7 @@
225227
"65baa0c4": "选择声音角色",
226228
"65bc9692": "选择声音文件",
227229
"6a7ee8a1": "使用CPU",
230+
"6b41f2d3": "选择本地文件",
228231
"6b5abd5b": "请升级Pro版使用",
229232
"6e81e205": "自动检测更新",
230233
"6fdfdfff": "添加云端模型服务",

src/pages/Video/VideoGen.vue

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import VideoGenActionDelete from "../../components/Video/VideoGenActionDelete.vu
1111
import VideoPlayer from "../../components/common/VideoPlayer.vue";
1212
import VideoDuration from "../../components/Video/VideoDuration.vue";
1313
import ServerTaskResultParam from "../../components/Server/ServerTaskResultParam.vue";
14+
import AudioPlayer from "../../components/common/AudioPlayer.vue";
1415
1516
const videoTemplateDialog = ref<InstanceType<typeof VideoTemplateDialog> | null>(null)
1617
const videoGenCreate = ref<InstanceType<typeof VideoGenCreate> | null>(null)
@@ -118,10 +119,19 @@ onBeforeUnmount(() => {
118119
{{ r.soundCloneText }}
119120
</div>
120121
</div>
122+
<div v-if="r.soundType==='soundCustom'" class="flex items-start">
123+
<div class="bg-gray-100 px-3 py-1 leading-6 rounded mr-2">
124+
<icon-file/>
125+
{{ $t('本地文件') }}
126+
</div>
127+
<div class="flex-grow">
128+
<AudioPlayer :url="`file://${r.soundCustomFile}`" />
129+
</div>
130+
</div>
121131
</div>
122132
<div class="flex-shrink-0 ml-8">
123-
<div class="p-2 rounded shadow bg-gray-300" v-if="r.resultMp4">
124-
<div class="w-48 h-48" v-if="r.resultMp4">
133+
<div class="p-2 rounded shadow bg-gray-300" v-if="1||r.resultMp4">
134+
<div class="w-48 h-48" v-if="1||r.resultMp4">
125135
<VideoPlayer :url="'file://'+r.resultMp4"/>
126136
</div>
127137
</div>

src/service/VideoGenService.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export type VideoGenRecord = {
1717
soundTtsText: string;
1818
soundCloneId: number;
1919
soundCloneText: string;
20+
soundCustomFile: string;
2021

2122
param?: any;
2223

@@ -97,7 +98,7 @@ export const VideoGenService = {
9798
const fields = [
9899
'serverName', 'serverTitle', 'serverVersion',
99100
'videoTemplateId', 'videoTemplateName',
100-
'soundType', 'soundTtsId', 'soundTtsText', 'soundCloneId', 'soundCloneText',
101+
'soundType', 'soundTtsId', 'soundTtsText', 'soundCloneId', 'soundCloneText', 'soundCustomFile',
101102
'param',
102103
'status', 'statusMsg', 'startTime', 'endTime',
103104
]
@@ -132,6 +133,11 @@ export const VideoGenService = {
132133
const resultMp4Abs = window.$mapi.file.absolutePath(record.resultMp4)
133134
await window.$mapi.file.deletes(resultMp4Abs)
134135
}
136+
if (record.soundCustomFile) {
137+
await window.$mapi.file.deletes(record.soundCustomFile, {
138+
isFullPath: true
139+
})
140+
}
135141
await window.$mapi.db.delete(`DELETE
136142
FROM ${this.tableName()}
137143
WHERE id = ?`, [record.id])

0 commit comments

Comments
 (0)