Skip to content

Commit ab68f94

Browse files
committed
新增:直播功能开发
1 parent 2c7519c commit ab68f94

38 files changed

+1138
-91
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -432,11 +432,11 @@ npm run build
432432
<tr>
433433
<td>
434434
<img style="width:100%;"
435-
src="https://aigcpanel.com/app_manager/image/wechat" />
435+
src="https://modstart.com/code_dynamic/modstart_wx" />
436436
</td>
437437
<td>
438438
<img style="width:100%;"
439-
src="https://aigcpanel.com/app_manager/image/qq" />
439+
src="https://modstart.com/code_dynamic/modstart_qq" />
440440
</td>
441441
</tr>
442442
</tbody>

changelog.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## v0.11.0
1+
## v0.11.0 聚合声音合成,支持千种声音,已知问题修复
22

33
- 新增:云端聚合声音合成,支持千种声音合成
44
- 优化:模型启动后无HTTP地址不再显示服务信息

electron/aigcserver/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import {ServerMuseTalk} from "./server-MuseTalk";
33
import {ServerCosyvoice205b} from "./server-cosyvoice2-0.5b";
44
import {ServerCosyvoice2300mInstruct} from "./server-cosyvoice2-300m-instruct";
55
import {EasyServer} from "./EasyServer";
6+
import {ServerLive} from "./server-live";
67

78

89
export const AigcServer = {
910
'EasyServer': EasyServer,
11+
'server-live': ServerLive,
1012
'server-cosyvoice': ServerCosyvoice,
1113
'server-MuseTalk': ServerMuseTalk,
1214
'server-cosyvoice2-0.5b': ServerCosyvoice205b,

electron/aigcserver/server-live.ts

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import {SendType, ServerApiType, ServerContext, ServerInfo} from "../mapi/server/type";
2+
3+
const serverRuntime = {
4+
port: 0,
5+
}
6+
7+
let shellController = null
8+
let isRunning = false
9+
10+
export const ServerLive: ServerContext = {
11+
ServerApi: null as ServerApiType | null,
12+
ServerInfo: null as ServerInfo | null,
13+
url() {
14+
return `http://localhost:${serverRuntime.port}/`
15+
},
16+
send(type: SendType, data: any) {
17+
this.ServerApi.event.sendChannel(this.ServerInfo.eventChannelName, {type, data})
18+
},
19+
20+
async _client() {
21+
return await this.ServerApi.GradioClient.connect(this.url());
22+
},
23+
async init() {
24+
},
25+
async start() {
26+
// console.log('this.ServerApi.app.availablePort(50617)', await this.ServerApi.app.availablePort(50617))
27+
this.send('starting', this.ServerInfo)
28+
let command = []
29+
if (this.ServerInfo.setting?.['port']) {
30+
serverRuntime.port = this.ServerInfo.setting.port
31+
} else if (!serverRuntime.port || !await this.ServerApi.app.isPortAvailable(serverRuntime.port)) {
32+
serverRuntime.port = await this.ServerApi.app.availablePort(50617)
33+
}
34+
const env = await this.ServerApi.env()
35+
command.push(`"${this.ServerInfo.localPath}/launcher"`)
36+
command.push(`--env=DEBUG=true`)
37+
const dep = process.platform === 'win32' ? ';' : ':'
38+
env['PATH'] = process.env['PATH'] || ''
39+
env['PATH'] = `${this.ServerInfo.localPath}/binary${dep}${env['PATH']}`
40+
env['AIGCPANEL_SERVER_PORT'] = `${serverRuntime.port}`
41+
env['AIGCPANEL_SERVER_PLACEHOLDER_CONFIG'] = './_aigcpanel/build-config-live.json'
42+
// console.log('command', JSON.stringify(command))
43+
shellController = await this.ServerApi.app.spawnShell(command, {
44+
stdout: (data) => {
45+
this.ServerApi.file.appendText(this.ServerInfo.logFile, data)
46+
},
47+
stderr: (data) => {
48+
this.ServerApi.file.appendText(this.ServerInfo.logFile, data)
49+
},
50+
success: (data) => {
51+
this.send('success', this.ServerInfo)
52+
},
53+
error: (data, code) => {
54+
this.ServerApi.file.appendText(this.ServerInfo.logFile, data)
55+
this.send('error', this.ServerInfo)
56+
},
57+
env,
58+
cwd: this.ServerInfo.localPath,
59+
})
60+
},
61+
async ping() {
62+
try {
63+
const res = await this.ServerApi.request(`${this.url()}ping`)
64+
return true
65+
} catch (e) {
66+
}
67+
return false
68+
},
69+
async stop() {
70+
this.send('stopping', this.ServerInfo)
71+
try {
72+
shellController.stop()
73+
shellController = null
74+
} catch (e) {
75+
console.log('stop error', e)
76+
}
77+
this.send('stopped', this.ServerInfo)
78+
},
79+
async cancel() {
80+
await this.ServerApi.launcherCancel(this)
81+
},
82+
async config() {
83+
return {
84+
code: 0,
85+
msg: "ok",
86+
data: {
87+
httpUrl: shellController ? this.url() : null,
88+
content: ``,
89+
functions: {
90+
live: {
91+
content: ``,
92+
param: [],
93+
},
94+
}
95+
}
96+
}
97+
},
98+
async apiRequest(data: {
99+
url: string,
100+
param: any,
101+
}, option: any) {
102+
serverRuntime.port = 60617
103+
// console.log('apiRequest', {url: this.url(), data, option})
104+
const {url, param} = data
105+
return this.ServerApi.request(`${this.url()}${url}`, param, {
106+
method: 'POST',
107+
})
108+
},
109+
}

electron/mapi/file/index.ts

+30
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ 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";
78

89
const nodePath = path
910

@@ -354,6 +355,15 @@ const copy = async (pathOld: string, pathNew: string, option?: { isFullPath?: bo
354355
fs.copyFileSync(fullPathOld, fullPathNew)
355356
}
356357

358+
const hubRoot = async () => {
359+
await waitAppEnvReady()
360+
const hubDir = path.join(root(), 'hub')
361+
if (!fs.existsSync(hubDir)) {
362+
fs.mkdirSync(hubDir, {recursive: true})
363+
}
364+
return hubDir
365+
}
366+
357367
const hubCreate = async (ext: string = 'bin') => {
358368
return path.join(
359369
'hub',
@@ -373,11 +383,13 @@ const hubSave = async (file: string, option?: {
373383
ext?: string,
374384
isFullPath?: boolean,
375385
returnFullPath?: boolean,
386+
ignoreWhenInHub?: boolean,
376387
}) => {
377388
option = Object.assign({
378389
ext: null,
379390
isFullPath: false,
380391
returnFullPath: false,
392+
ignoreWhenInHub: false,
381393
}, option)
382394
let fp = file
383395
if (!option.isFullPath) {
@@ -389,6 +401,12 @@ const hubSave = async (file: string, option?: {
389401
if (!option.ext) {
390402
option.ext = ext(fp)
391403
}
404+
if (option.ignoreWhenInHub) {
405+
const hubRoot_ = await hubRoot()
406+
if (inDir(fp, hubRoot_)) {
407+
return fp
408+
}
409+
}
392410
const hubFile = await hubCreate(option.ext)
393411
await copy(fp, path.join(root(), hubFile), {
394412
isFullPath: true,
@@ -633,6 +651,18 @@ const textToName = (text: string, ext: string = '', maxLimit: number = 100) => {
633651
return `${text}.${ext}`
634652
}
635653

654+
const inDir = (path: string, dir: string) => {
655+
if (!path || !dir) {
656+
return false
657+
}
658+
path = path.replace(/\\/g, '/')
659+
dir = dir.replace(/\\/g, '/')
660+
if (path === dir) {
661+
return true
662+
}
663+
return path.startsWith(dir)
664+
}
665+
636666
export const FileIndex = {
637667
fullPath,
638668
absolutePath,

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
"electron-context-menu": "^4.0.4",
7272
"ffmpeg-static": "^5.2.0",
7373
"fix-path": "^4.0.0",
74+
"hls.js": "^1.6.2",
7475
"iconv": "^3.0.1",
7576
"iconv-lite": "^0.6.3",
7677
"js-base64": "^3.7.7",

src/assets/image/upgrade.svg

+1
Loading

src/components/PageNav.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ const doUser = async () => {
8181
</a>
8282
<a class="page-nav-item block text-center py-3"
8383
:class="activeTab==='live'?'active':''"
84-
v-if="0"
8584
@click="$router.push('/live')"
85+
v-if="0"
8686
href="javascript:;">
8787
<div>
8888
<icon-live-broadcast class="text-xl"/>

src/components/Server/ServerAddDialog.vue

+19-3
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,13 @@ const show = () => {
4040
}
4141
4242
const platformInfo = computed(() => {
43-
return `${modelInfo.value.platformName} ${modelInfo.value.platformArch}`
43+
const platformNameMap = {
44+
'win': 'Windows',
45+
'osx': 'macOS',
46+
'linux': 'Linux',
47+
}
48+
const platformName = platformNameMap[modelInfo.value.platformName] || modelInfo.value.platformName
49+
return `${platformName} ${modelInfo.value.platformArch}`
4450
})
4551
const functionLabels = computed(() => {
4652
return functionToLabels(modelInfo.value.functions)
@@ -160,7 +166,17 @@ const doSelectLocalDir = async () => {
160166
localPath: serverPath,
161167
name: modelInfo.value.name,
162168
} as any)
163-
logStatus.value = modelInfo.value.isSupport ? '' : t('模型不支持')
169+
if (modelInfo.value.isSupport) {
170+
logStatus.value = ''
171+
} else {
172+
logStatus.value = t('模型不支持')
173+
if (modelInfo.value.platformName !== window.$mapi.app.platformName()) {
174+
logStatus.value += `(${t('平台不匹配')})`
175+
}
176+
if (modelInfo.value.platformArch !== window.$mapi.app.platformArch()) {
177+
logStatus.value += `(${t('芯片架构不匹配')})`
178+
}
179+
}
164180
} catch (e) {
165181
console.log('ServerImportLocalDialog.doSelectLocalDir.error', e)
166182
Dialog.tipError(t('模型目录识别失败,请选择正确的模型目录'))
@@ -291,7 +307,7 @@ const emit = defineEmits({
291307
{{ $t('重新选择') }}
292308
</a-button>
293309
</div>
294-
<div class="flex-grow pl-3 text-sm truncate">
310+
<div class="flex-grow pl-3 text-sm truncate text-red-600">
295311
{{ logStatus }}
296312
</div>
297313
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<script setup lang="ts">
2+
import {Dialog} from "../../lib/dialog";
3+
import {t} from "../../lang";
4+
import {computed} from "vue";
5+
import {sleep} from "../../lib/util";
6+
import {StorageRecord, StorageService} from "../../service/StorageService";
7+
8+
const props = defineProps<{
9+
records: StorageRecord[],
10+
}>()
11+
12+
const canDelete = computed(() => {
13+
return props.records.length > 0
14+
})
15+
16+
const emit = defineEmits({
17+
update: () => true
18+
})
19+
20+
const doDelete = async () => {
21+
await Dialog.confirm(t('确定删除 {count} 条记录?', {count: props.records.length}))
22+
Dialog.loadingOn(t('正在删除'))
23+
await sleep(500)
24+
for (const r of props.records) {
25+
await StorageService.delete(r)
26+
}
27+
Dialog.loadingOff()
28+
emit('update')
29+
}
30+
</script>
31+
32+
<template>
33+
<a-tooltip :content="$t('删除')">
34+
<a-button class="mr-2"
35+
:disabled="!canDelete"
36+
@click="doDelete()">
37+
<template #icon>
38+
<icon-delete/>
39+
</template>
40+
</a-button>
41+
</a-tooltip>
42+
</template>

0 commit comments

Comments
 (0)