@oxiaom/adoremix 1.0.9 → 1.0.11

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.9",
3
+ "version": "1.0.11",
4
4
  "description": "AdoreMix broadcast server - cross-platform installer, runner and service manager",
5
5
  "bin": {
6
6
  "adoremix": "./bin/adoremix.js"
package/src/doctor.js CHANGED
@@ -162,6 +162,31 @@ function runDoctor(workdir, opts) {
162
162
  logger.log(`○ 服务未运行(adoremix start 启动)`);
163
163
  }
164
164
 
165
+ // 8. TTS provider 依赖检查
166
+ logger.log('');
167
+ logger.log('=== TTS Provider 检查 ===');
168
+ const cfg = require('./config');
169
+ const cfgObj = cfg.readConfig(workdir) || {};
170
+ const ttsDeps = require('./tts-deps');
171
+ const providerName = (cfgObj.TTS && cfgObj.TTS.provider) || 'xf';
172
+ logger.log(`当前 provider: ${providerName}`);
173
+ const ttsResult = ttsDeps.checkDeps(workdir, providerName, cfgObj);
174
+ if (ttsResult.issues.length === 0) {
175
+ logger.ok(`✓ ${providerName} provider 所有依赖就绪`);
176
+ } else {
177
+ for (const iss of ttsResult.issues) {
178
+ const mark = iss.severity === 'error' ? '❌' : '⚠';
179
+ logger.log(` ${mark} ${iss.msg}`);
180
+ if (iss.fixCmd) logger.log(` 修复:${iss.fixCmd}`);
181
+ if (iss.autoFixable) issues.push({ type: 'tts', severity: iss.severity, msg: iss.msg, fixCmd: iss.fixCmd });
182
+ }
183
+ if (opts.fix) {
184
+ logger.info('==> 自动修复 TTS 依赖...');
185
+ const r = ttsDeps.fixDeps(workdir, ttsResult.issues, { sudo: process.getuid && process.getuid() === 0 });
186
+ logger.ok(`✓ 修复 ${r.fixed} 项,失败 ${r.failed} 项`);
187
+ }
188
+ }
189
+
165
190
  return reportAndExit(issues, opts);
166
191
  }
167
192
 
package/src/install.js CHANGED
@@ -17,7 +17,8 @@ const NODE_HELPER_DEPS = {
17
17
  'mysql': '^2.18.1',
18
18
  'request': '^2.88.2',
19
19
  'sqlite': '^4.2.0',
20
- 'ws': '^8.13.0'
20
+ 'ws': '^8.13.0',
21
+ 'ini': '^4.1.3'
21
22
  };
22
23
 
23
24
  function isWorkdirInitialized(workdir) {
@@ -0,0 +1,253 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * TTS provider 依赖检查(最硬核版)
5
+ *
6
+ * 检查每个 provider 需要的所有东西:
7
+ * xf: crypto-js, ws, log4node (Node 包) + appid/apiSecret/apiKey (凭证)
8
+ * minimax: token (凭证) - Node 内置 https,无外部依赖
9
+ * edge: python3, edge-tts (pip 包), ffmpeg (系统) - 完全免费但依赖多
10
+ *
11
+ * 用法:
12
+ * const issues = checkDeps(workdir);
13
+ * // issues: [{severity, msg, fixCmd, autoFixable}]
14
+ * if (opts.fix) fixDeps(workdir, issues);
15
+ */
16
+
17
+ const { execSync, execFileSync } = require('child_process');
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+
21
+ const PROVIDER_DEPENDENCIES = {
22
+ xf: {
23
+ nodeModules: ['crypto-js', 'ws', 'log4node'],
24
+ creds: [
25
+ { key: 'TTS.xf_appid', name: 'appid' },
26
+ { key: 'TTS.xf_apiSecret', name: 'apiSecret' },
27
+ { key: 'TTS.xf_apiKey', name: 'apiKey' }
28
+ ],
29
+ systemPkgs: [],
30
+ pipPkgs: [],
31
+ executables: []
32
+ },
33
+ minimax: {
34
+ nodeModules: [], // 用 Node 内置 https
35
+ creds: [{ key: 'TTS.minimax_token', name: 'token' }],
36
+ systemPkgs: [],
37
+ pipPkgs: [],
38
+ executables: []
39
+ },
40
+ edge: {
41
+ nodeModules: [],
42
+ creds: [],
43
+ systemPkgs: [
44
+ { name: 'python3', cmd: 'python3', altCmd: 'python' },
45
+ { name: 'ffmpeg', cmd: 'ffmpeg' }
46
+ ],
47
+ pipPkgs: [{ pkg: 'edge-tts', import: 'edge_tts' }],
48
+ executables: [{ name: 'ffmpeg', cmd: 'ffmpeg' }]
49
+ }
50
+ };
51
+
52
+ function which(cmd) {
53
+ try {
54
+ execSync(`command -v ${cmd} 2>/dev/null || which ${cmd} 2>/dev/null`, { stdio: 'pipe' });
55
+ return true;
56
+ } catch (e) { return false; }
57
+ }
58
+
59
+ function findPythonCmd() {
60
+ for (const c of ['python3', 'python']) {
61
+ if (which(c)) {
62
+ try {
63
+ const v = execSync(`${c} --version`, { stdio: 'pipe', encoding: 'utf8' });
64
+ if (/Python 3\./.test(v)) return c;
65
+ } catch (e) {}
66
+ }
67
+ }
68
+ return null;
69
+ }
70
+
71
+ function pythonHasModule(pyCmd, modName) {
72
+ if (!pyCmd) return false;
73
+ try {
74
+ execFileSync(pyCmd, ['-c', `import ${modName}`], { stdio: 'pipe' });
75
+ return true;
76
+ } catch (e) { return false; }
77
+ }
78
+
79
+ function nodeModuleInstalled(workdir, modName) {
80
+ try {
81
+ require.resolve(modName, { paths: [workdir] });
82
+ return true;
83
+ } catch (e) { return false; }
84
+ }
85
+
86
+ function getCredValue(cfg, dottedKey) {
87
+ const parts = dottedKey.split('.');
88
+ let cur = cfg;
89
+ for (const p of parts) {
90
+ if (cur == null || typeof cur !== 'object') return undefined;
91
+ cur = cur[p];
92
+ }
93
+ return cur;
94
+ }
95
+
96
+ function detectAptLike() {
97
+ if (which('apt-get')) return 'apt-get';
98
+ if (which('apt')) return 'apt';
99
+ if (which('yum')) return 'yum';
100
+ if (which('dnf')) return 'dnf';
101
+ if (which('apk')) return 'apk';
102
+ return null;
103
+ }
104
+
105
+ /**
106
+ * 检查指定 provider 的所有依赖
107
+ * @returns {issues: Array, provider: string}
108
+ */
109
+ function checkDeps(workdir, providerName, cfg) {
110
+ const issues = [];
111
+ providerName = providerName || (cfg && cfg.TTS && cfg.TTS.provider) || 'xf';
112
+ const deps = PROVIDER_DEPENDENCIES[providerName];
113
+ if (!deps) {
114
+ return { issues: [{ severity: 'error', msg: `未知 provider: ${providerName}`, autoFixable: false }], provider: providerName };
115
+ }
116
+
117
+ // 1. 凭证
118
+ for (const c of deps.creds) {
119
+ const val = getCredValue(cfg, c.key);
120
+ if (!val) {
121
+ issues.push({
122
+ severity: 'error',
123
+ category: 'cred',
124
+ msg: `${providerName} 缺凭证 ${c.name}(${c.key})`,
125
+ fixCmd: `adoremix config set ${c.key} <your_${c.name}>`,
126
+ autoFixable: false
127
+ });
128
+ }
129
+ }
130
+
131
+ // 2. Node 模块
132
+ for (const mod of deps.nodeModules) {
133
+ if (!nodeModuleInstalled(workdir, mod)) {
134
+ issues.push({
135
+ severity: 'error',
136
+ category: 'node_module',
137
+ msg: `Node 包未安装: ${mod}`,
138
+ fixCmd: `cd ${workdir} && npm install ${mod}`,
139
+ autoFixable: true,
140
+ fixType: 'npm',
141
+ fixArgs: { cwd: workdir, mod }
142
+ });
143
+ }
144
+ }
145
+
146
+ // 3. pip 包
147
+ const pyCmd = findPythonCmd();
148
+ for (const pip of deps.pipPkgs) {
149
+ if (!pythonHasModule(pyCmd, pip.import)) {
150
+ issues.push({
151
+ severity: 'error',
152
+ category: 'pip',
153
+ msg: `Python 包未安装: ${pip.pkg}`,
154
+ fixCmd: 'pip3 install ' + pip.pkg,
155
+ autoFixable: true,
156
+ fixType: 'pip',
157
+ fixArgs: { pkg: pip.pkg, pyCmd }
158
+ });
159
+ }
160
+ }
161
+
162
+ // 4. 系统可执行
163
+ for (const exe of deps.execubles || deps.systemPkgs) {
164
+ if (exe.cmd && !which(exe.cmd)) {
165
+ // 推断 apt 包名
166
+ let aptPkg = '';
167
+ if (exe.name === 'python3') aptPkg = 'python3 python3-pip';
168
+ else if (exe.name === 'ffmpeg') aptPkg = 'ffmpeg';
169
+ const pkgMgr = detectAptLike();
170
+ issues.push({
171
+ severity: 'error',
172
+ category: 'system',
173
+ msg: `系统命令未找到: ${exe.name}`,
174
+ fixCmd: aptPkg && pkgMgr ? `sudo ${pkgMgr} install -y ${aptPkg}` : '',
175
+ autoFixable: !!(aptPkg && pkgMgr),
176
+ fixType: 'system',
177
+ fixArgs: { pkgMgr, aptPkg }
178
+ });
179
+ }
180
+ }
181
+
182
+ // python3 特殊处理(如果 edge 需要 python3 但只装了 python)
183
+ if (providerName === 'edge' && !pyCmd) {
184
+ const pkgMgr = detectAptLike();
185
+ issues.push({
186
+ severity: 'error',
187
+ category: 'system',
188
+ msg: '需要 python3(未找到 Python 3.x)',
189
+ fixCmd: pkgMgr ? `sudo ${pkgMgr} install -y python3 python3-pip` : '',
190
+ autoFixable: !!pkgMgr,
191
+ fixType: 'system',
192
+ fixArgs: { pkgMgr, aptPkg: 'python3 python3-pip' }
193
+ });
194
+ }
195
+
196
+ return { issues, provider: providerName };
197
+ }
198
+
199
+ /**
200
+ * 自动修复(仅 autoFixable=true 的)
201
+ */
202
+ function fixDeps(workdir, issues, opts) {
203
+ opts = opts || {};
204
+ const sudo = opts.sudo ? 'sudo ' : '';
205
+ let fixed = 0;
206
+ let failed = 0;
207
+ for (const issue of issues) {
208
+ if (!issue.autoFixable) continue;
209
+ try {
210
+ if (issue.fixType === 'npm') {
211
+ execSync(`npm install ${issue.fixArgs.mod} --no-audit --no-fund`, {
212
+ cwd: issue.fixArgs.cwd,
213
+ stdio: 'inherit'
214
+ });
215
+ } else if (issue.fixType === 'pip') {
216
+ const py = issue.fixArgs.pyCmd || findPythonCmd() || 'python3';
217
+ execSync(`${sudo}${py} -m pip install ${issue.fixArgs.pkg}`, { stdio: 'inherit' });
218
+ } else if (issue.fixType === 'system') {
219
+ if (!issue.fixArgs.pkgMgr) throw new Error('包管理器未识别');
220
+ execSync(`${sudo}${issue.fixArgs.pkgMgr} install -y ${issue.fixArgs.aptPkg}`, { stdio: 'inherit' });
221
+ }
222
+ fixed++;
223
+ } catch (e) {
224
+ console.error(` ✗ 修复失败 [${issue.msg}]: ${e.message.split('\n')[0]}`);
225
+ failed++;
226
+ }
227
+ }
228
+ return { fixed, failed };
229
+ }
230
+
231
+ /**
232
+ * 列出所有 provider 的依赖(用于 adoremix tts deps 不带 provider 时)
233
+ */
234
+ function listAllProviders(workdir, cfg) {
235
+ const result = {};
236
+ for (const name of Object.keys(PROVIDER_DEPENDENCIES)) {
237
+ const r = checkDeps(workdir, name, cfg);
238
+ result[name] = {
239
+ issues: r.issues,
240
+ ok: r.issues.length === 0
241
+ };
242
+ }
243
+ return result;
244
+ }
245
+
246
+ module.exports = {
247
+ PROVIDER_DEPENDENCIES,
248
+ checkDeps,
249
+ fixDeps,
250
+ listAllProviders,
251
+ which,
252
+ findPythonCmd
253
+ };
package/tts/tts.js CHANGED
@@ -45,10 +45,12 @@ async function main() {
45
45
  // 读 config.ini 找 provider
46
46
  const cfgPath = path.join(process.cwd(), 'config.ini');
47
47
  let ttsSection = {};
48
+ let settings = {};
48
49
  if (fs.existsSync(cfgPath)) {
49
50
  try {
50
51
  const cfg = ini.parse(fs.readFileSync(cfgPath, 'utf-8'));
51
52
  ttsSection = cfg.TTS || {};
53
+ settings = cfg.Settings || {};
52
54
  } catch (e) { err(`config.ini 解析失败: ${e.message}`); }
53
55
  }
54
56
  const providerName = (ttsSection.provider || 'xf').toLowerCase();
@@ -64,10 +66,11 @@ async function main() {
64
66
  const outFile = path.join(ttyDir, mp3Name);
65
67
 
66
68
  // 凭证:优先 config.ini,命令行参数兼容旧版 xf
69
+ // 兼容 Qt 已有字段:Settings.ttsxfAPPID / ttsxfAPISecret / ttsxfAPIKey
67
70
  const creds = {
68
- xf_appid: ttsSection.xf_appid || appidArg || '',
69
- xf_apiSecret: ttsSection.xf_apiSecret || apiSecretArg || '',
70
- xf_apiKey: ttsSection.xf_apiKey || apiKeyArg || '',
71
+ xf_appid: ttsSection.xf_appid || settings.ttsxfAPPID || appidArg || '',
72
+ xf_apiSecret: ttsSection.xf_apiSecret || settings.ttsxfAPISecret || apiSecretArg || '',
73
+ xf_apiKey: ttsSection.xf_apiKey || settings.ttsxfAPIKey || apiKeyArg || '',
71
74
  xf_voice_override: ttsSection.xf_voice_override || '',
72
75
  minimax_token: ttsSection.minimax_token || '',
73
76
  edge_voice_override: ttsSection.edge_voice_override || ''