@oxiaom/adoremix 1.0.41 → 1.0.43

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxiaom/adoremix",
3
- "version": "1.0.41",
3
+ "version": "1.0.43",
4
4
  "description": "AdoreMix broadcast server - cross-platform installer, runner and service manager",
5
5
  "bin": {
6
6
  "adoremix": "./bin/adoremix.js"
package/src/cli.js CHANGED
@@ -465,6 +465,51 @@ function buildProgram() {
465
465
  try { process.exitCode = mod.status(); } catch (e) { logger.error(e.message); process.exitCode = 1; }
466
466
  });
467
467
 
468
+ const audio8 = program
469
+ .command('audio8')
470
+ .description('Audio8 TTS(本地自部署流式 TTS,自动安装到远程 Linux)');
471
+
472
+ audio8
473
+ .command('install')
474
+ .description('在本机(Linux)自动安装 Audio8 TTS + systemd 服务(端口 8024),改本地 config.ini;Windows 不支持')
475
+ .option('--workdir <path>', 'AdoreMix 工作目录(写入 config.ini 用)')
476
+ .action(async (opts) => {
477
+ const mod = require('../tts/providers/audio8');
478
+ try {
479
+ process.exitCode = await mod.install(opts);
480
+ } catch (e) {
481
+ logger.error(e.message);
482
+ process.exitCode = 1;
483
+ }
484
+ });
485
+
486
+ audio8
487
+ .command('uninstall')
488
+ .description('卸载本机的 Audio8 TTS 服务')
489
+ .action(async (opts) => {
490
+ const mod = require('../tts/providers/audio8');
491
+ try {
492
+ process.exitCode = await mod.uninstall(opts);
493
+ } catch (e) {
494
+ logger.error(e.message);
495
+ process.exitCode = 1;
496
+ }
497
+ });
498
+
499
+ audio8
500
+ .command('status')
501
+ .description('检查本机 Audio8 TTS 服务健康(GET /v1/models)')
502
+ .option('--url <url>', '自定义 base_url,默认 http://127.0.0.1:8024')
503
+ .action(async (opts) => {
504
+ const mod = require('../tts/providers/audio8');
505
+ try {
506
+ process.exitCode = await mod.status(opts);
507
+ } catch (e) {
508
+ logger.error(e.message);
509
+ process.exitCode = 1;
510
+ }
511
+ });
512
+
468
513
  const cfgmgr = program
469
514
  .command('config-manager')
470
515
  .description('设备配置管理 UI(配置 IP / 修改 config.ini / 查看日志),端口 9877,开机自启');
@@ -104,7 +104,7 @@ module.exports = {
104
104
  xf_voice_override: '',
105
105
  minimax_token: '',
106
106
  edge_voice_override: '',
107
- audio8_base_url: 'http://127.0.0.1:7860',
107
+ audio8_base_url: 'http://127.0.0.1:8024',
108
108
  audio8_voice_override: ''
109
109
  }
110
110
  };
@@ -6,8 +6,11 @@
6
6
  * 支持流式 MP3 输出(设备边收边播,匹配现有 quemp3 推送管道)
7
7
  *
8
8
  * 服务部署参考(Audio8-AI/Audio8_TTS):
9
- * pip install audio8-tts(或依官方文档)
10
- * python -m audio8_tts.server # 默认监听 http://0.0.0.0:7860
9
+ * git clone https://github.com/Audio8-AI/Audio8_TTS.git ~/audio8
10
+ * cd ~/audio8/onnx_runtime && python3 -m venv .venv && . .venv/bin/activate
11
+ * pip install -r requirements.txt # torch/transformers/onnxruntime 等
12
+ * bash start_server.sh # 默认监听 http://0.0.0.0:8024
13
+ * 首次启动自动从 HuggingFace 下载 ~572MB 模型(0.1B-INT8)
11
14
  *
12
15
  * 凭证:不需要 API Key(本地服务)
13
16
  * 依赖:lame(mp3 转码),ffmpeg(HTTP 流 → mp3)
@@ -39,7 +42,7 @@ const VOICES = {
39
42
  default: 'zh-CN-XiaoxiaoNeural'
40
43
  };
41
44
 
42
- const DEFAULT_BASE_URL = 'http://127.0.0.1:7860';
45
+ const DEFAULT_BASE_URL = 'http://127.0.0.1:8024';
43
46
  const DEFAULT_TIMEOUT_MS = 30000;
44
47
 
45
48
  function checkDeps() {
@@ -105,7 +108,7 @@ function synthesize({ text, voice, volume, speed, outFile }, creds) {
105
108
  // OpenAI 兼容调用:POST /v1/audio/speech,body=json,response=audio/mpeg 流
106
109
  let urlObj;
107
110
  try { urlObj = new URL(baseUrl); } catch (e) {
108
- return reject(new Error(`Audio8 服务地址无效: ${baseUrl}(请在 config.ini [TTS] audio8_base_url 配置,例 http://127.0.0.1:7860)`));
111
+ return reject(new Error(`Audio8 服务地址无效: ${baseUrl}(请在 config.ini [TTS] audio8_base_url 配置,例 http://127.0.0.1:8024)`));
109
112
  }
110
113
  urlObj.pathname = '/v1/audio/speech';
111
114
  const postData = JSON.stringify({
@@ -154,4 +157,236 @@ function synthesize({ text, voice, volume, speed, outFile }, creds) {
154
157
  });
155
158
  }
156
159
 
157
- module.exports = { synthesize, checkDeps, VOICES, DEFAULT_BASE_URL };
160
+ // ===== audio8 install / uninstall / status(类似 nginx install 的可选组件)=====
161
+ // **本机部署**:在当前 adoremix 进程所在机器上直接装(不是 SSH)。
162
+ // 各机器跑 `adoremix audio8 install` 就在自己身上起 Audio8 TTS 服务。
163
+ // 仅 Linux 可装(依赖 Python3 + venv + systemd),Windows/macOS 直接提示不支持。
164
+ const AUDIO8_DEFAULT_PORT = 8024;
165
+ const AUDIO8_REPO = 'https://github.com/Audio8-AI/Audio8_TTS.git';
166
+ const AUDIO8_DEPLOY_DIR = '~/audio8';
167
+ const AUDIO8_VENV_DIR = '~/audio8/onnx_runtime/.venv';
168
+ const AUDIO8_REQUIREMENTS = 'onnx_runtime/requirements.txt';
169
+ const AUDIO8_SYSTEMD_NAME = 'audio8-tts';
170
+ const AUDIO8_SYSTEMD_PATH = '/etc/systemd/system/audio8-tts.service';
171
+ const AUDIO8_HEALTH_URL = `http://127.0.0.1:${AUDIO8_DEFAULT_PORT}/v1/models`;
172
+
173
+ // 包管理器嗅探(apt/dnf/yum/apk/pacman)
174
+ function detectPkgMgr() {
175
+ try {
176
+ if (execFileSync('which apt-get', { stdio: 'pipe' }) && true) return 'apt';
177
+ } catch (e) {}
178
+ try {
179
+ if (execFileSync('which dnf', { stdio: 'pipe' }) && true) return 'dnf';
180
+ } catch (e) {}
181
+ try {
182
+ if (execFileSync('which yum', { stdio: 'pipe' }) && true) return 'yum';
183
+ } catch (e) {}
184
+ try {
185
+ if (execFileSync('which apk', { stdio: 'pipe' }) && true) return 'apk';
186
+ } catch (e) {}
187
+ try {
188
+ if (execFileSync('which pacman', { stdio: 'pipe' }) && true) return 'pacman';
189
+ } catch (e) {}
190
+ return null;
191
+ }
192
+
193
+ function systemInstallCmd(pkgMgr, pkgs) {
194
+ // pkgs: { name: 'pkg-name' } 数组
195
+ const list = pkgs.map(p => p.name).join(' ');
196
+ switch (pkgMgr) {
197
+ case 'apt': return `apt-get update && apt-get install -y ${list}`;
198
+ case 'dnf':
199
+ case 'yum': return `${pkgMgr} install -y ${list}`;
200
+ case 'apk': return `apk add ${list}`;
201
+ case 'pacman': return `pacman -Sy --noconfirm ${list}`;
202
+ default: return null;
203
+ }
204
+ }
205
+
206
+ function buildSystemdUnit() {
207
+ return `[Unit]
208
+ Description=Audio8 TTS (AdoreMix)
209
+ After=network.target
210
+
211
+ [Service]
212
+ Type=simple
213
+ User=root
214
+ WorkingDirectory=${AUDIO8_DEPLOY_DIR}/onnx_runtime
215
+ Environment=PATH=${AUDIO8_VENV_DIR}/bin:/usr/bin:/bin
216
+ ExecStart=${AUDIO8_VENV_DIR}/bin/python -m arktts_runtime.service --model-dir ./model --voices-dir ./model/voices --port ${AUDIO8_DEFAULT_PORT}
217
+ Restart=on-failure
218
+ RestartSec=5
219
+
220
+ [Install]
221
+ WantedBy=multi-user.target
222
+ `;
223
+ }
224
+
225
+ function install(opts) {
226
+ opts = opts || {};
227
+ // 平台检查
228
+ if (process.platform !== 'linux') {
229
+ logger.error(`Audio8 TTS 仅支持 Linux(当前 ${process.platform},Windows/macOS 上 ffmpeg/venv/systemd 不可用)`);
230
+ logger.log(`如需在 Windows 本机用 Audio8,请用 WSL2 或部署到 Linux 主机后通过 config.ini audio8_base_url 远程调用。`);
231
+ return 1;
232
+ }
233
+
234
+ // 探测包管理器
235
+ const pkgMgr = detectPkgMgr();
236
+ if (!pkgMgr) {
237
+ logger.error('未识别到包管理器(apt/dnf/yum/apk/pacman 均未找到)。Audio8 install 仅支持这些主流 Linux 发行版。');
238
+ return 1;
239
+ }
240
+ logger.ok(`检测到包管理器: ${pkgMgr}`);
241
+
242
+ // 包映射(按发行版提供 ffmpeg/git/python3)
243
+ // ffmpeg 在 RHEL 系官方源没有,要先装 epel-release
244
+ let sysPkgs = [
245
+ { name: 'git' },
246
+ { name: 'python3' },
247
+ { name: 'python3-pip' }, // Debian/Ubuntu
248
+ { name: 'python3-venv' },
249
+ { name: 'ffmpeg' }
250
+ ];
251
+ if (pkgMgr === 'dnf' || pkgMgr === 'yum') {
252
+ sysPkgs = [
253
+ { name: 'git' },
254
+ { name: 'python3' },
255
+ { name: 'python3-pip' },
256
+ { name: 'python3-virtualenv' }, // RHEL 系包名不同
257
+ { name: 'epel-release' }, // ffmpeg 在 EPEL
258
+ { name: 'ffmpeg' }
259
+ ];
260
+ }
261
+ if (pkgMgr === 'apk') {
262
+ sysPkgs = [
263
+ { name: 'git' },
264
+ { name: 'python3' },
265
+ { name: 'py3-pip' },
266
+ { name: 'ffmpeg' }
267
+ ];
268
+ }
269
+
270
+ try {
271
+ logger.log('=== 安装系统依赖 ===');
272
+ execSync(systemInstallCmd(pkgMgr, sysPkgs), { stdio: 'inherit' });
273
+ if (pkgMgr === 'dnf' || pkgMgr === 'yum') {
274
+ // EPEL 装好后再装 ffmpeg
275
+ try { execSync(`${pkgMgr} install -y ffmpeg --enablerepo=epel`, { stdio: 'inherit' }); } catch (e) { logger.warn('ffmpeg 安装失败(EPEL 装好后重试): ' + e.message); }
276
+ }
277
+ } catch (e) {
278
+ logger.error('系统依赖安装失败: ' + e.message.slice(0, 200));
279
+ return 1;
280
+ }
281
+
282
+ // git clone
283
+ try {
284
+ logger.log('=== 克隆 Audio8_TTS 仓库 ===');
285
+ execSync(`mkdir -p ~/audio8 && if [ ! -d ~/audio8/.git ]; then git clone ${AUDIO8_REPO} ~/audio8_temp && mv ~/audio8_temp/* ~/audio8_temp/.[!.]* ~/audio8/ 2>/dev/null; rm -rf ~/audio8_temp; else cd ~/audio8 && git pull; fi`, { stdio: 'inherit', shell: '/bin/bash' });
286
+ } catch (e) {
287
+ logger.error('克隆失败: ' + e.message.slice(0, 200));
288
+ return 1;
289
+ }
290
+
291
+ // venv + pip install
292
+ try {
293
+ logger.log('=== 创建 venv 并安装 Python 依赖(首次需 5-10 分钟下载 torch 等)===');
294
+ execSync('python3 -m venv ~/.audio8_venv', { stdio: 'inherit', shell: '/bin/bash' });
295
+ execSync('source ~/.audio8_venv/bin/activate && pip install -U pip --quiet && pip install -r ~/audio8/onnx_runtime/requirements.txt --quiet', { stdio: 'inherit', shell: '/bin/bash' });
296
+ } catch (e) {
297
+ logger.error('Python 依赖安装失败: ' + e.message.slice(0, 200));
298
+ return 1;
299
+ }
300
+
301
+ // 写 systemd 单元
302
+ try {
303
+ fs.writeFileSync(AUDIO8_SYSTEMD_PATH, buildSystemdUnit(), 'utf8');
304
+ execSync('systemctl daemon-reload', { stdio: 'inherit', shell: '/bin/bash' });
305
+ execSync(`systemctl enable ${AUDIO8_SYSTEMD_NAME}`, { stdio: 'inherit', shell: '/bin/bash' });
306
+ execSync(`systemctl restart ${AUDIO8_SYSTEMD_NAME}`, { stdio: 'inherit', shell: '/bin/bash' });
307
+ } catch (e) {
308
+ logger.error('systemd 配置失败(无 systemd 容器?手动启动: ~/audio8_venv/bin/python -m arktts_runtime.service): ' + e.message.slice(0, 200));
309
+ return 1;
310
+ }
311
+
312
+ // 等服务健康(含下载 572MB 模型,冷启动最多 60s)
313
+ logger.log('=== 等待服务健康(首次启动要下载 ~572MB 模型,最多 60 秒)===');
314
+ let ok = false;
315
+ for (let i = 1; i <= 30; i++) {
316
+ try {
317
+ execSync(`curl -sf ${AUDIO8_HEALTH_URL} >/dev/null 2>&1`);
318
+ logger.ok(`Audio8 服务在 ${i*2} 秒后就绪: ${AUDIO8_HEALTH_URL}`);
319
+ ok = true;
320
+ break;
321
+ } catch (e) {}
322
+ execSync('sleep 2', { stdio: 'pipe', shell: '/bin/bash' });
323
+ }
324
+ if (!ok) {
325
+ logger.warn('60 秒内未就绪,请手动 systemctl status ' + AUDIO8_SYSTEMD_NAME);
326
+ logger.log('也可直接前台启动测试: source ~/.audio8_venv/bin/activate && python -m arktts_runtime.service --model-dir ~/audio8/model --voices-dir ~/audio8/model/voices --port ' + AUDIO8_DEFAULT_PORT);
327
+ }
328
+
329
+ // 改本地 config.ini
330
+ try {
331
+ const cfg = require(path.join(__dirname, '..', 'src', 'config'));
332
+ const wd = opts.workdir || process.env.ADOREMIX_WORKDIR || path.join(os.homedir(), '.local', 'share', 'adoremix');
333
+ cfg.setConfigValue(wd, 'TTS.provider', 'audio8');
334
+ cfg.setConfigValue(wd, 'TTS.audio8_base_url', `http://127.0.0.1:${AUDIO8_DEFAULT_PORT}`);
335
+ logger.ok(`config.ini 已更新: provider=audio8, audio8_base_url=http://127.0.0.1:${AUDIO8_DEFAULT_PORT}`);
336
+ } catch (e) {
337
+ logger.warn('config.ini 更新失败: ' + e.message);
338
+ }
339
+
340
+ logger.ok(`Audio8 TTS 本机安装完成 → http://127.0.0.1:${AUDIO8_DEFAULT_PORT}`);
341
+ return 0;
342
+ }
343
+
344
+ function uninstall(opts) {
345
+ if (process.platform !== 'linux') {
346
+ logger.error('audio8 uninstall 仅 Linux 支持');
347
+ return 1;
348
+ }
349
+ try {
350
+ execSync(`systemctl --now disable ${AUDIO8_SYSTEMD_NAME} 2>/dev/null; true`, { stdio: 'pipe', shell: '/bin/bash' });
351
+ execSync(`systemctl stop ${AUDIO8_SYSTEMD_NAME} 2>/dev/null; true`, { stdio: 'pipe', shell: '/bin/bash' });
352
+ execSync(`rm -f ${AUDIO8_SYSTEMD_PATH} && systemctl daemon-reload`, { stdio: 'pipe', shell: '/bin/bash' });
353
+ execSync(`rm -rf ~/audio8 ~/audio8_venv`, { stdio: 'pipe', shell: '/bin/bash' });
354
+ execSync(`rm -rf ~/.cache/huggingface/hub/models--Audio8*`, { stdio: 'pipe', shell: '/bin/bash' });
355
+ } catch (e) {
356
+ logger.warn('uninstall 清理部分出错: ' + e.message);
357
+ return 1;
358
+ }
359
+ logger.ok('Audio8 TTS 已卸载');
360
+ return 0;
361
+ }
362
+
363
+ async function status(opts) {
364
+ opts = opts || {};
365
+ const baseUrl = opts.url || process.env.ADOREMIX_AUDIO8_BASE_URL || `http://127.0.0.1:${AUDIO8_DEFAULT_PORT}`;
366
+ try {
367
+ const urlObj = new URL(`${baseUrl}/v1/models`);
368
+ const lib = urlObj.protocol === 'https:' ? require('https') : require('http');
369
+ await new Promise(resolve => {
370
+ const req = lib.request({ hostname: urlObj.hostname, port: urlObj.port, path: urlObj.pathname, method: 'GET', timeout: 5000 }, res => {
371
+ if (res.statusCode === 200) {
372
+ logger.ok(`Audio8 服务健康: ${baseUrl}/v1/models 返回 ${res.statusCode}`);
373
+ res.resume();
374
+ resolve(0);
375
+ } else {
376
+ logger.warn(`Audio8 服务异常: ${baseUrl} 返回 ${res.statusCode}`);
377
+ res.resume();
378
+ resolve(1);
379
+ }
380
+ });
381
+ req.on('error', err => { logger.warn(`Audio8 服务不可达: ${err.message}`); resolve(1); });
382
+ req.on('timeout', () => { req.destroy(); logger.warn(`Audio8 服务超时: ${baseUrl}`); resolve(1); });
383
+ req.end();
384
+ });
385
+ return 0;
386
+ } catch (e) {
387
+ logger.warn(`Audio8 status 检查失败: ${e.message}`);
388
+ return 1;
389
+ }
390
+ }
391
+
392
+ module.exports = { synthesize, checkDeps, VOICES, DEFAULT_BASE_URL, install, uninstall, status };