@oxiaom/adoremix 1.0.18 → 1.0.19
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/bin/adoremix.js +6 -3
- package/package.json +4 -2
- package/src/doctor.js +94 -12
- package/src/paths.js +4 -2
- package/src/tts-deps.js +42 -1
- package/templates/adoremix.service.tmpl +6 -2
package/bin/adoremix.js
CHANGED
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const cli = require('../src/cli');
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
// 注意:不能在 run() resolve 后强制 process.exit —— 前台模式 `adoremix start`
|
|
6
|
+
// spawn 出的二进制是 active handle,node 会自然挂起等待它运行(Ctrl+C 退出)。
|
|
7
|
+
// 无脑 process.exit 会把 node 杀掉,二进制成孤儿后收 SIGHUP 被终止,表现为"启动即退"。
|
|
8
|
+
// 普通命令(version/install/config 等)action 完成后事件循环空了会自然退出,
|
|
9
|
+
// 退出码由各命令自己设 process.exitCode(doctor/doctor --fix 等已设)。
|
|
10
|
+
cli.run(process.argv.slice(2)).catch((err) => {
|
|
8
11
|
const logger = require('../src/logger');
|
|
9
12
|
logger.error('未捕获的错误:', err);
|
|
10
13
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxiaom/adoremix",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.19",
|
|
4
4
|
"description": "AdoreMix broadcast server - cross-platform installer, runner and service manager",
|
|
5
5
|
"bin": {
|
|
6
6
|
"adoremix": "./bin/adoremix.js"
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
"@oxiaom/adoremix-win32-x64": "^1.0.0",
|
|
15
15
|
"@oxiaom/adoremix-linux-x64": "^1.0.0",
|
|
16
16
|
"@oxiaom/adoremix-linux-arm64": "^1.0.0",
|
|
17
|
-
"@oxiaom/adoremix-linux-arm": "^1.0.0"
|
|
17
|
+
"@oxiaom/adoremix-linux-arm": "^1.0.0",
|
|
18
|
+
"@oxiaom/adoremix-darwin-x64": "^1.0.0",
|
|
19
|
+
"@oxiaom/adoremix-darwin-arm64": "^1.0.0"
|
|
18
20
|
},
|
|
19
21
|
"dependencies": {
|
|
20
22
|
"commander": "^12.1.0",
|
package/src/doctor.js
CHANGED
|
@@ -26,6 +26,57 @@ const LIB_TO_PKG = {
|
|
|
26
26
|
'libkrb5.so.3': 'libkrb5-3'
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
+
// ICU 70 兜底:
|
|
30
|
+
// AdoreMix 二进制在 Ubuntu 22.04(ICU 70)编译,链接 libicui18n.so.70 / libicuuc.so.70 / libicudata.so.70。
|
|
31
|
+
// Debian 12 / 树莓派 OS / Armbian 等 apt 源里只有 libicu72,没有 libicu70,跨发行版运行会缺库。
|
|
32
|
+
// 这三个 .so 都由同一个包 libicu70 提供,从 Ubuntu 官方源下载对应架构 .deb 装上即可(与系统 libicu72 共存,不冲突)。
|
|
33
|
+
const ICU70_LIBS = ['libicui18n.so.70', 'libicuuc.so.70', 'libicudata.so.70'];
|
|
34
|
+
const ICU70_DEB_SOURCES = {
|
|
35
|
+
// node arch → [deb arch, Ubuntu 源根]
|
|
36
|
+
arm64: ['arm64', 'http://ports.ubuntu.com/ubuntu-ports'], // 非 amd64 架构走 ports
|
|
37
|
+
arm: ['armhf', 'http://ports.ubuntu.com/ubuntu-ports'], // armv7 / armhf(树莓派 32 位、老款盒子)
|
|
38
|
+
x64: ['amd64', 'http://archive.ubuntu.com/ubuntu']
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function getIcu70DebInfo() {
|
|
42
|
+
const src = ICU70_DEB_SOURCES[process.arch];
|
|
43
|
+
if (!src) return null;
|
|
44
|
+
const [debArch, base] = src;
|
|
45
|
+
return {
|
|
46
|
+
debArch,
|
|
47
|
+
url: `${base}/pool/main/i/icu/libicu70_70.1-2_${debArch}.deb`,
|
|
48
|
+
tmpPath: '/tmp/adoremix-libicu70.deb'
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function tryFixIcu70() {
|
|
53
|
+
const info = getIcu70DebInfo();
|
|
54
|
+
if (!info) {
|
|
55
|
+
logger.warn(` ICU 70 兜底暂不支持架构 ${process.arch},请手动安装 libicu70`);
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
logger.info(`==> ICU 70 兜底:从 Ubuntu 官方源下载 libicu70 (${info.debArch})`);
|
|
59
|
+
logger.log(` ${info.url}`);
|
|
60
|
+
try {
|
|
61
|
+
execSync(`wget -q -O ${info.tmpPath} '${info.url}'`, { stdio: 'inherit' });
|
|
62
|
+
const size = fs.statSync(info.tmpPath).size;
|
|
63
|
+
if (size < 100000) {
|
|
64
|
+
logger.error(`下载失败(文件 ${size} 字节,可能 URL 失效或网络不通)`);
|
|
65
|
+
try { fs.unlinkSync(info.tmpPath); } catch (e) {}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
logger.info(`==> dpkg -i libicu70_70.1-2_${info.debArch}.deb (${(size / 1024 / 1024).toFixed(1)} MB)`);
|
|
69
|
+
execSync(`dpkg -i ${info.tmpPath}`, { stdio: 'inherit' });
|
|
70
|
+
try { fs.unlinkSync(info.tmpPath); } catch (e) {}
|
|
71
|
+
return true;
|
|
72
|
+
} catch (e) {
|
|
73
|
+
logger.error(`ICU 70 安装失败:${(e.message || '').split('\n')[0]}`);
|
|
74
|
+
logger.warn(` 可能需要 root,手动执行:`);
|
|
75
|
+
logger.warn(` wget -O /tmp/libicu70.deb '${info.url}' && sudo dpkg -i /tmp/libicu70.deb`);
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
29
80
|
function runDoctor(workdir, opts) {
|
|
30
81
|
opts = opts || {};
|
|
31
82
|
logger.log('=== AdoreMix 健康检查 ===');
|
|
@@ -93,38 +144,69 @@ function runDoctor(workdir, opts) {
|
|
|
93
144
|
logger.ok(`✓ 动态库依赖完整`);
|
|
94
145
|
} else {
|
|
95
146
|
logger.error(`❌ 缺动态库:${missing.join(', ')}`);
|
|
147
|
+
// 分类:apt 可装 / ICU 70 跨发行版兜底 / 完全未知
|
|
96
148
|
const pkgs = [];
|
|
149
|
+
const icu70 = [];
|
|
97
150
|
const unknown = [];
|
|
98
151
|
for (const lib of missing) {
|
|
99
|
-
|
|
100
|
-
if (
|
|
152
|
+
if (ICU70_LIBS.includes(lib)) icu70.push(lib);
|
|
153
|
+
else if (LIB_TO_PKG[lib]) pkgs.push(LIB_TO_PKG[lib]);
|
|
101
154
|
else unknown.push(lib);
|
|
102
155
|
}
|
|
103
156
|
const allPkgs = [...new Set(pkgs)];
|
|
157
|
+
|
|
158
|
+
// 1) apt 可装的库
|
|
159
|
+
let aptSolved = allPkgs.length === 0;
|
|
104
160
|
if (allPkgs.length > 0) {
|
|
105
161
|
const cmd = `apt-get install -y ${allPkgs.join(' ')}`;
|
|
106
162
|
logger.log(` 建议命令(root 用户):${cmd}`);
|
|
107
|
-
if (unknown.length > 0) {
|
|
108
|
-
logger.warn(` 未知库(无 apt 映射):${unknown.join(', ')}`);
|
|
109
|
-
}
|
|
110
|
-
issues.push({
|
|
111
|
-
type: 'libs',
|
|
112
|
-
severity: 'error',
|
|
113
|
-
msg: missing.join(', '),
|
|
114
|
-
fixCmd: cmd,
|
|
115
|
-
pkgs: allPkgs
|
|
116
|
-
});
|
|
117
163
|
if (opts.fix) {
|
|
118
164
|
logger.info(`==> 自动修复:执行 ${cmd}`);
|
|
119
165
|
try {
|
|
120
166
|
execSync(cmd, { stdio: 'inherit', cwd: workdir });
|
|
121
167
|
logger.ok('✓ 缺库已安装');
|
|
168
|
+
aptSolved = true;
|
|
122
169
|
} catch (e) {
|
|
123
170
|
logger.error(`修复失败:${e.message}`);
|
|
124
171
|
logger.warn('可能需要 sudo:手动执行 sudo ' + cmd);
|
|
125
172
|
}
|
|
126
173
|
}
|
|
127
174
|
}
|
|
175
|
+
|
|
176
|
+
// 2) ICU 70 跨发行版兜底(Debian 12 / 树莓派 OS / Armbian 等无 libicu70)
|
|
177
|
+
let icuSolved = icu70.length === 0;
|
|
178
|
+
if (icu70.length > 0) {
|
|
179
|
+
const icuMsg = `ICU 70 缺失(${icu70.join(', ')})— 二进制在 Ubuntu 22.04 编译,Debian 12 等需补 libicu70`;
|
|
180
|
+
if (opts.fix) {
|
|
181
|
+
if (tryFixIcu70()) {
|
|
182
|
+
logger.ok('✓ libicu70 已安装(ICU 70 兜底)');
|
|
183
|
+
icuSolved = true;
|
|
184
|
+
} else {
|
|
185
|
+
issues.push({ type: 'icu70', severity: 'error', msg: icuMsg });
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
logger.warn(` ${icuMsg}`);
|
|
189
|
+
logger.log(` 自动修复:adoremix doctor --fix(从 Ubuntu 官方源下载 libicu70 .deb)`);
|
|
190
|
+
issues.push({ type: 'icu70', severity: 'error', msg: icuMsg });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 3) 未解决的记 issue
|
|
195
|
+
if (!aptSolved) {
|
|
196
|
+
issues.push({
|
|
197
|
+
type: 'libs',
|
|
198
|
+
severity: 'error',
|
|
199
|
+
msg: missing.join(', '),
|
|
200
|
+
fixCmd: `apt-get install -y ${allPkgs.join(' ')}`,
|
|
201
|
+
pkgs: allPkgs
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// 4) 完全未知的库
|
|
206
|
+
if (unknown.length > 0) {
|
|
207
|
+
logger.warn(` 未知库(无 apt 映射,需手动排查):${unknown.join(', ')}`);
|
|
208
|
+
issues.push({ type: 'libs-unknown', severity: 'error', msg: unknown.join(', ') });
|
|
209
|
+
}
|
|
128
210
|
}
|
|
129
211
|
} catch (e) {
|
|
130
212
|
logger.warn(`⚠ ldd 检查失败(非 Linux 或权限问题):${e.message.split('\n')[0]}`);
|
package/src/paths.js
CHANGED
|
@@ -9,7 +9,9 @@ const PLATFORM_PKG = {
|
|
|
9
9
|
'win32_x64': '@oxiaom/adoremix-win32-x64',
|
|
10
10
|
'linux_x64': '@oxiaom/adoremix-linux-x64',
|
|
11
11
|
'linux_arm64': '@oxiaom/adoremix-linux-arm64',
|
|
12
|
-
'linux_arm': '@oxiaom/adoremix-linux-arm'
|
|
12
|
+
'linux_arm': '@oxiaom/adoremix-linux-arm',
|
|
13
|
+
'darwin_x64': '@oxiaom/adoremix-darwin-x64',
|
|
14
|
+
'darwin_arm64': '@oxiaom/adoremix-darwin-arm64'
|
|
13
15
|
}[PLATFORM_KEY];
|
|
14
16
|
|
|
15
17
|
let _native = null;
|
|
@@ -19,7 +21,7 @@ function loadNative() {
|
|
|
19
21
|
if (_native) return _native;
|
|
20
22
|
if (_loadError) throw _loadError;
|
|
21
23
|
if (!PLATFORM_PKG) {
|
|
22
|
-
_loadError = new Error(`不支持的平台:${PLATFORM_KEY}。当前支持 win32-x64 / linux-x64 / linux-arm64 / linux-arm。`);
|
|
24
|
+
_loadError = new Error(`不支持的平台:${PLATFORM_KEY}。当前支持 win32-x64 / linux-x64 / linux-arm64 / linux-arm / darwin-x64 / darwin-arm64。`);
|
|
23
25
|
throw _loadError;
|
|
24
26
|
}
|
|
25
27
|
try {
|
package/src/tts-deps.js
CHANGED
|
@@ -76,6 +76,27 @@ function pythonHasModule(pyCmd, modName) {
|
|
|
76
76
|
} catch (e) { return false; }
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
// python3 自带 pip 吗(Debian/Armbian 精简系统默认不带,要 apt install python3-pip)
|
|
80
|
+
function pythonHasPip(pyCmd) {
|
|
81
|
+
if (!pyCmd) return false;
|
|
82
|
+
try {
|
|
83
|
+
execFileSync(pyCmd, ['-m', 'pip', '--version'], { stdio: 'pipe' });
|
|
84
|
+
return true;
|
|
85
|
+
} catch (e) { return false; }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 是否 PEP 668 externally-managed(Debian 12 / Ubuntu 23.04+ 默认禁止往系统 Python 装 pip 包)
|
|
89
|
+
// 这种环境 pip install 会报 externally-managed-environment,需加 --break-system-packages
|
|
90
|
+
function isExternallyManaged(pyCmd) {
|
|
91
|
+
if (!pyCmd) return false;
|
|
92
|
+
try {
|
|
93
|
+
const out = execFileSync(pyCmd, ['-c',
|
|
94
|
+
'import sysconfig,os;print(os.path.exists(os.path.join(sysconfig.get_path("stdlib"),"EXTERNALLY-MANAGED")))'
|
|
95
|
+
], { stdio: 'pipe', encoding: 'utf8' });
|
|
96
|
+
return String(out).trim() === 'True';
|
|
97
|
+
} catch (e) { return false; }
|
|
98
|
+
}
|
|
99
|
+
|
|
79
100
|
function nodeModuleInstalled(workdir, modName) {
|
|
80
101
|
try {
|
|
81
102
|
require.resolve(modName, { paths: [workdir] });
|
|
@@ -220,7 +241,27 @@ function fixDeps(workdir, issues, opts) {
|
|
|
220
241
|
});
|
|
221
242
|
} else if (issue.fixType === 'pip') {
|
|
222
243
|
const py = issue.fixArgs.pyCmd || findPythonCmd() || 'python3';
|
|
223
|
-
|
|
244
|
+
// 1. 确保 pip 可用(Debian/Armbian 精简系统 python3 自带但 pip 缺失)
|
|
245
|
+
if (!pythonHasPip(py)) {
|
|
246
|
+
const pkgMgr = detectAptLike();
|
|
247
|
+
console.log(` pip 未安装,先补 python3-pip ...`);
|
|
248
|
+
if (pkgMgr) {
|
|
249
|
+
execSync(`${sudo}${pkgMgr} install -y python3-pip`, { stdio: 'inherit' });
|
|
250
|
+
} else {
|
|
251
|
+
// 退路:用 Python 自带的 ensurepip 引导
|
|
252
|
+
execSync(`${sudo}${py} -m ensurepip --upgrade`, { stdio: 'inherit' });
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// 2. PEP 668:Debian 12 / Ubuntu 23.04+ 禁止往系统 Python 装包,需 --break-system-packages
|
|
256
|
+
const breakFlag = isExternallyManaged(py) ? ' --break-system-packages' : '';
|
|
257
|
+
if (breakFlag) console.log(' 检测到 externally-managed-environment,加 --break-system-packages');
|
|
258
|
+
// 3. PyPI 镜像:默认用清华(国内默认 PyPI 慢,常因下载损坏导致 hash 校验失败)。
|
|
259
|
+
// 国外用户可设环境变量 ADOREMIX_PIP_INDEX 覆盖,留空则用官方源。
|
|
260
|
+
const indexUrl = process.env.ADOREMIX_PIP_INDEX !== undefined
|
|
261
|
+
? process.env.ADOREMIX_PIP_INDEX
|
|
262
|
+
: 'https://pypi.tuna.tsinghua.edu.cn/simple';
|
|
263
|
+
const indexFlag = indexUrl ? ` -i ${indexUrl}` : '';
|
|
264
|
+
execSync(`${sudo}${py} -m pip install${breakFlag}${indexFlag} ${issue.fixArgs.pkg}`, { stdio: 'inherit' });
|
|
224
265
|
} else if (issue.fixType === 'system') {
|
|
225
266
|
if (!issue.fixArgs.pkgMgr) throw new Error('包管理器未识别');
|
|
226
267
|
execSync(`${sudo}${issue.fixArgs.pkgMgr} install -y ${issue.fixArgs.aptPkg}`, { stdio: 'inherit' });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[Unit]
|
|
2
2
|
Description=AdoreMix Broadcast Server
|
|
3
|
-
Documentation=https://github.com/
|
|
3
|
+
Documentation=https://github.com/oxiaom/adoremix-npm
|
|
4
4
|
After=network.target mysqld.service redis.service
|
|
5
5
|
|
|
6
6
|
[Service]
|
|
@@ -8,7 +8,11 @@ Type=simple
|
|
|
8
8
|
User=__USER__
|
|
9
9
|
Group=__GROUP__
|
|
10
10
|
WorkingDirectory=__WORKDIR__
|
|
11
|
-
|
|
11
|
+
# 用前台模式(不加 --daemon):node 持续运行作为 systemd 主进程,
|
|
12
|
+
# systemd 才能正确跟踪/重启/停止服务。
|
|
13
|
+
# daemon 模式 node 会 unref 后退出,systemd 默认 KillMode=control-group
|
|
14
|
+
# 会把整个 cgroup(含刚 spawn 的二进制)一起杀掉,服务起不来。
|
|
15
|
+
ExecStart=__NODE__ __CLI__ start --workdir __WORKDIR__
|
|
12
16
|
Restart=on-failure
|
|
13
17
|
RestartSec=5
|
|
14
18
|
StandardOutput=append:__WORKDIR__/logs/svc.log
|