@oxiaom/adoremix 1.0.21 → 1.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/cli.js +24 -0
- package/tts/providers/minimax.js +4 -0
- package/tts/providers/xf.js +5 -2
- package/tts/voice-mapping.js +26 -14
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -256,12 +256,34 @@ function buildProgram() {
|
|
|
256
256
|
daemon: opts.daemon === true
|
|
257
257
|
});
|
|
258
258
|
|
|
259
|
+
// systemd 管理本服务时,start/stop/restart 委托 systemctl(避免手动 spawn/kill 脱离
|
|
260
|
+
// systemd 管理——尤其 restart 的 stop+手动start 会让服务脱离 systemd)。
|
|
261
|
+
// status 不委托(pidfile 检查已与 systemctl 一致)。仅 Linux + 服务 active 时生效。
|
|
262
|
+
function systemdDelegate(cmd) {
|
|
263
|
+
if (process.platform !== 'linux') return false;
|
|
264
|
+
const { execSync } = require('child_process');
|
|
265
|
+
let active;
|
|
266
|
+
try {
|
|
267
|
+
active = execSync('systemctl is-active adoremix 2>/dev/null', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
268
|
+
} catch (e) { return false; }
|
|
269
|
+
if (active !== 'active') return false;
|
|
270
|
+
logger.info(`检测到 systemd 管理,执行 systemctl ${cmd} adoremix`);
|
|
271
|
+
try {
|
|
272
|
+
execSync(`systemctl ${cmd} adoremix`, { stdio: 'inherit' });
|
|
273
|
+
return true;
|
|
274
|
+
} catch (e) {
|
|
275
|
+
logger.error(`systemctl ${cmd} 失败:${(e.message || '').split('\n')[0]}(可能需要 sudo,或用 systemctl --user)`);
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
259
280
|
program
|
|
260
281
|
.command('start')
|
|
261
282
|
.description('启动 AdoreMix(默认前台,加 --daemon 后台)')
|
|
262
283
|
.option('--workdir <path>')
|
|
263
284
|
.option('--daemon', '后台运行(nohup 风格)')
|
|
264
285
|
.action((opts) => {
|
|
286
|
+
if (systemdDelegate('start')) return;
|
|
265
287
|
try {
|
|
266
288
|
if (opts.daemon) runner.startDaemon(runnerOpts(opts));
|
|
267
289
|
else runner.startForeground(runnerOpts(opts));
|
|
@@ -277,6 +299,7 @@ function buildProgram() {
|
|
|
277
299
|
.option('--workdir <path>')
|
|
278
300
|
.option('--timeout <ms>', 'SIGTERM 超时后 SIGKILL 的毫秒数', parseInt)
|
|
279
301
|
.action(async (opts) => {
|
|
302
|
+
if (systemdDelegate('stop')) return;
|
|
280
303
|
process.exitCode = await runner.stop(runnerOpts(opts));
|
|
281
304
|
});
|
|
282
305
|
|
|
@@ -294,6 +317,7 @@ function buildProgram() {
|
|
|
294
317
|
.option('--workdir <path>')
|
|
295
318
|
.option('--daemon', '后台模式重启')
|
|
296
319
|
.action(async (opts) => {
|
|
320
|
+
if (systemdDelegate('restart')) return;
|
|
297
321
|
const r = await runner.restart(runnerOpts(opts));
|
|
298
322
|
if (r && r.alreadyRunning === false && r.pid) {
|
|
299
323
|
// ok
|
package/tts/providers/minimax.js
CHANGED
|
@@ -35,6 +35,10 @@ const VOICES = {
|
|
|
35
35
|
yunhao: 'male-qn-badao',
|
|
36
36
|
yunxiang: 'male-qn-jingying',
|
|
37
37
|
yunyang: 'male-qn-qingse',
|
|
38
|
+
// 讯飞原生发音人(Qt/WebUI 实际传)→ 就近映射到 minimax voice_id
|
|
39
|
+
aisjiuxu: 'male-qn-jingying', // 许久(男)
|
|
40
|
+
aisxping: 'female-tianmei', // 小萍(女)
|
|
41
|
+
aisjinger: 'female-shaonv', // 小婧(女)
|
|
38
42
|
// minimax 自有音色(直接透传)
|
|
39
43
|
'male-qn-qingse': 'male-qn-qingse',
|
|
40
44
|
'male-qn-daxuesheng': 'male-qn-daxuesheng',
|
package/tts/providers/xf.js
CHANGED
|
@@ -20,7 +20,9 @@ const VOICES = {
|
|
|
20
20
|
xiaohan: 'xiaohan', xiaomeng: 'xiaomeng', xiaomo: 'xiaomo',
|
|
21
21
|
xiaoqiu: 'xiaoqiu', xiaorui: 'xiaorui', xiaoshuang: 'xiaoshuang',
|
|
22
22
|
xiaoxuan: 'xiaoxuan', xiaoyan: 'xiaoyan', xiaoyou: 'xiaoyou',
|
|
23
|
-
yunfeng: 'yunfeng', yunhao: 'yunhao', yunxiang: 'yunxiang', yunyang: 'yunyang'
|
|
23
|
+
yunfeng: 'yunfeng', yunhao: 'yunhao', yunxiang: 'yunxiang', yunyang: 'yunyang',
|
|
24
|
+
// 讯飞原生发音人(WebUI 实际传,讯飞 API 直接认,自身透传)
|
|
25
|
+
aisjiuxu: 'aisjiuxu', aisxping: 'aisxping', aisjinger: 'aisjinger'
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
function synthesize({ text, voice, volume, speed, outFile }, creds) {
|
|
@@ -32,7 +34,8 @@ function synthesize({ text, voice, volume, speed, outFile }, creds) {
|
|
|
32
34
|
return reject(new Error('讯飞 TTS 缺凭证:xf_appid / xf_apiSecret / xf_apiKey'));
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
// 标准短名走 VOICES 映射;讯飞原生 vcn(如 aisjiuxu 许久)直接透传(讯飞接受任意 vcn)
|
|
38
|
+
const vcn = VOICES[voice] || voice || creds.xf_voice_override || 'xiaoxiao';
|
|
36
39
|
const host = 'tts-api.xfyun.cn';
|
|
37
40
|
const hostUrl = `wss://${host}/v2/tts`;
|
|
38
41
|
|
package/tts/voice-mapping.js
CHANGED
|
@@ -1,21 +1,36 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* 统一 voice
|
|
4
|
+
* 统一 voice 表
|
|
5
5
|
*
|
|
6
|
-
* Qt 调 tts.js
|
|
7
|
-
* 每个 provider 在自己的
|
|
6
|
+
* Qt/WebUI 调 tts.js 时传讯飞发音人 ID(WebUI 音色选项:小燕/许久/小萍/小婧)。
|
|
7
|
+
* 每个 provider 在自己的 VOICES 字典里把发音人映射到平台真实 voice_id:
|
|
8
|
+
* - xf: 讯飞原生 vcn 直接透传(讯飞接受任意 vcn)
|
|
9
|
+
* - minimax: 讯飞名 → minimax voice_id
|
|
10
|
+
* - edge: 讯飞名 → edge-tts voice(edge 实际只有 8 个 zh-CN voice,就近映射 + fallback 兜底)
|
|
8
11
|
*
|
|
9
|
-
*
|
|
12
|
+
* STANDARD_VOICES 是 Qt 实际传的(adoremix tts voices 命令展示这些);
|
|
13
|
+
* EXTENDED_VOICES 是 edge 风格扩展短名(Qt 不传,但各 provider VOICES 兼容映射,
|
|
14
|
+
* 用户在 config.ini 手动配 voice 或 xf_voice_override 时可用)。
|
|
10
15
|
*/
|
|
11
16
|
|
|
12
|
-
//
|
|
17
|
+
// Qt/WebUI 实际传的讯飞发音人(WebUI 音色下拉选项)
|
|
13
18
|
const STANDARD_VOICES = [
|
|
14
|
-
{ short: '
|
|
15
|
-
{ short: '
|
|
16
|
-
{ short: '
|
|
17
|
-
{ short: '
|
|
18
|
-
|
|
19
|
+
{ short: 'xiaoyan', desc: '小燕 - 女声(讯飞,WebUI 默认)' },
|
|
20
|
+
{ short: 'aisjiuxu', desc: '许久 - 男声(讯飞)' },
|
|
21
|
+
{ short: 'aisxping', desc: '小萍 - 女声(讯飞)' },
|
|
22
|
+
{ short: 'aisjinger', desc: '小婧 - 女声(讯飞)' }
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const DEFAULT_VOICE = 'xiaoyan';
|
|
26
|
+
|
|
27
|
+
// 扩展短名(edge 风格,Qt 不直接传;provider VOICES 兼容映射,手动配置可用)
|
|
28
|
+
const EXTENDED_VOICES = [
|
|
29
|
+
{ short: 'xiaoxiao', desc: '晓晓 - 女声,自然亲切(edge)' },
|
|
30
|
+
{ short: 'yunxi', desc: '云希 - 男声,年轻活力(edge)' },
|
|
31
|
+
{ short: 'yunjian', desc: '云健 - 男声,成熟稳重(edge)' },
|
|
32
|
+
{ short: 'xiaoyi', desc: '晓伊 - 女声,温柔甜美(edge)' },
|
|
33
|
+
{ short: 'yunxia', desc: '云夏 - 男童声(edge)' },
|
|
19
34
|
{ short: 'xiaochen', desc: '晓辰 - 女声,新闻播报' },
|
|
20
35
|
{ short: 'xiaohan', desc: '晓涵 - 女声,温暖' },
|
|
21
36
|
{ short: 'xiaomeng', desc: '晓梦 - 女声,活泼' },
|
|
@@ -24,7 +39,6 @@ const STANDARD_VOICES = [
|
|
|
24
39
|
{ short: 'xiaorui', desc: '晓睿 - 女童声' },
|
|
25
40
|
{ short: 'xiaoshuang', desc: '晓双 - 女童声' },
|
|
26
41
|
{ short: 'xiaoxuan', desc: '晓萱 - 女声' },
|
|
27
|
-
{ short: 'xiaoyan', desc: '晓妍 - 女声' },
|
|
28
42
|
{ short: 'xiaoyou', desc: '悠悠 - 女童声' },
|
|
29
43
|
{ short: 'yunfeng', desc: '云枫 - 男声' },
|
|
30
44
|
{ short: 'yunhao', desc: '云皓 - 男声' },
|
|
@@ -32,6 +46,4 @@ const STANDARD_VOICES = [
|
|
|
32
46
|
{ short: 'yunyang', desc: '云扬 - 男声' }
|
|
33
47
|
];
|
|
34
48
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
module.exports = { STANDARD_VOICES, DEFAULT_VOICE };
|
|
49
|
+
module.exports = { STANDARD_VOICES, DEFAULT_VOICE, EXTENDED_VOICES };
|