@linsxyx/kamusic 0.0.0-stage → 0.1.1
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/README.md +15 -2
- package/bin/kamusic.js +284 -0
- package/package.json +41 -4
package/README.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @linsxyx/kamusic (KA Music CLI)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
最好用、最轻量的酷狗音乐概念版终端 TUI 播放器(NativeAOT 原生编译,跨 Windows / macOS / Linux 全平台)。
|
|
4
|
+
|
|
5
|
+
## 安装与启动
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# 全局安装
|
|
9
|
+
npm install -g @linsxyx/kamusic
|
|
10
|
+
|
|
11
|
+
# 启动播放器
|
|
12
|
+
kamusic
|
|
13
|
+
|
|
14
|
+
# 检查并更新到最新版本
|
|
15
|
+
kamusic update
|
|
16
|
+
```
|
package/bin/kamusic.js
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// kamusic CLI 启动入口:支持 `kamusic update` / `kamusic -v`,并根据当前 OS 与 CPU 架构定位对应平台子包中的 NativeAOT 可执行文件(支持 TUI 内触发退出码 42 自动更新并重启)。
|
|
4
|
+
|
|
5
|
+
const fs = require("node:fs");
|
|
6
|
+
const os = require("node:os");
|
|
7
|
+
const path = require("node:path");
|
|
8
|
+
const https = require("node:https");
|
|
9
|
+
const { spawn, spawnSync } = require("node:child_process");
|
|
10
|
+
|
|
11
|
+
const MAIN_PKG_NAME = "@linsxyx/kamusic";
|
|
12
|
+
const UPDATE_EXIT_CODE = 42;
|
|
13
|
+
|
|
14
|
+
const REGISTRY_URLS = [
|
|
15
|
+
"https://registry.npmjs.org/@linsxyx%2fkamusic/latest",
|
|
16
|
+
"https://registry.npmmirror.com/@linsxyx/kamusic/latest",
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const PLATFORM_PACKAGES = {
|
|
20
|
+
"win32-x64": {
|
|
21
|
+
pkg: "@linsxyx/kamusic-win32-x64",
|
|
22
|
+
dir: "kamusic-win32-x64",
|
|
23
|
+
bin: "KaMusic.Cli.exe",
|
|
24
|
+
},
|
|
25
|
+
"win32-arm64": {
|
|
26
|
+
pkg: "@linsxyx/kamusic-win32-arm64",
|
|
27
|
+
dir: "kamusic-win32-arm64",
|
|
28
|
+
bin: "KaMusic.Cli.exe",
|
|
29
|
+
},
|
|
30
|
+
"linux-x64": {
|
|
31
|
+
pkg: "@linsxyx/kamusic-linux-x64",
|
|
32
|
+
dir: "kamusic-linux-x64",
|
|
33
|
+
bin: "KaMusic.Cli",
|
|
34
|
+
},
|
|
35
|
+
"linux-arm64": {
|
|
36
|
+
pkg: "@linsxyx/kamusic-linux-arm64",
|
|
37
|
+
dir: "kamusic-linux-arm64",
|
|
38
|
+
bin: "KaMusic.Cli",
|
|
39
|
+
},
|
|
40
|
+
"darwin-arm64": {
|
|
41
|
+
pkg: "@linsxyx/kamusic-darwin-arm64",
|
|
42
|
+
dir: "kamusic-darwin-arm64",
|
|
43
|
+
bin: "KaMusic.Cli",
|
|
44
|
+
},
|
|
45
|
+
"darwin-x64": {
|
|
46
|
+
pkg: "@linsxyx/kamusic-darwin-x64",
|
|
47
|
+
dir: "kamusic-darwin-x64",
|
|
48
|
+
bin: "KaMusic.Cli",
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
function getCurrentVersion() {
|
|
53
|
+
try {
|
|
54
|
+
const pkgPath = path.resolve(__dirname, "..", "package.json");
|
|
55
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
56
|
+
return pkg.version || "0.1.0";
|
|
57
|
+
} catch {
|
|
58
|
+
return "0.1.0";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function compareSemVer(a, b) {
|
|
63
|
+
const pa = String(a).replace(/^v/i, "").split("-")[0].split(".");
|
|
64
|
+
const pb = String(b).replace(/^v/i, "").split("-")[0].split(".");
|
|
65
|
+
const len = Math.max(pa.length, pb.length);
|
|
66
|
+
for (let i = 0; i < len; i++) {
|
|
67
|
+
const va = Number.parseInt(pa[i] || "0", 10) || 0;
|
|
68
|
+
const vb = Number.parseInt(pb[i] || "0", 10) || 0;
|
|
69
|
+
if (va !== vb) return va > vb ? 1 : -1;
|
|
70
|
+
}
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function fetchJson(url, timeoutMs = 5000) {
|
|
75
|
+
return new Promise((resolve, reject) => {
|
|
76
|
+
const req = https.get(
|
|
77
|
+
url,
|
|
78
|
+
{ headers: { "User-Agent": "kamusic-cli-updater/1.0" }, timeout: timeoutMs },
|
|
79
|
+
(res) => {
|
|
80
|
+
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
81
|
+
fetchJson(res.headers.location, timeoutMs).then(resolve, reject);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (res.statusCode !== 200) {
|
|
85
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
let body = "";
|
|
89
|
+
res.setEncoding("utf8");
|
|
90
|
+
res.on("data", (chunk) => (body += chunk));
|
|
91
|
+
res.on("end", () => {
|
|
92
|
+
try {
|
|
93
|
+
resolve(JSON.parse(body));
|
|
94
|
+
} catch (e) {
|
|
95
|
+
reject(e);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
req.on("error", reject);
|
|
101
|
+
req.on("timeout", () => {
|
|
102
|
+
req.destroy(new Error("timeout"));
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function fetchLatestVersion() {
|
|
108
|
+
for (const url of REGISTRY_URLS) {
|
|
109
|
+
try {
|
|
110
|
+
const data = await fetchJson(url);
|
|
111
|
+
if (data && typeof data.version === "string" && data.version.trim()) {
|
|
112
|
+
return data.version.trim();
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
// try next mirror
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function installGlobalPackage(targetSpec) {
|
|
122
|
+
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
123
|
+
const baseArgs = ["install", "-g", targetSpec, "--prefer-online"];
|
|
124
|
+
|
|
125
|
+
let res = spawnSync(npmCmd, baseArgs, { stdio: "inherit", shell: process.platform === "win32" });
|
|
126
|
+
if (res.status === 0) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
console.log("[kamusic] 检测到默认 npm 缓存可能存在权限冲突,正在使用独立临时缓存重试...");
|
|
131
|
+
const tempCache = path.join(os.tmpdir(), "kamusic-npm-cache");
|
|
132
|
+
res = spawnSync(npmCmd, [...baseArgs, "--cache", tempCache], {
|
|
133
|
+
stdio: "inherit",
|
|
134
|
+
shell: process.platform === "win32",
|
|
135
|
+
});
|
|
136
|
+
return res.status === 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function handleUpdateCommand({ force = false, restartAfter = false } = {}) {
|
|
140
|
+
const current = getCurrentVersion();
|
|
141
|
+
console.log(`[kamusic] 当前版本: v${current},正在检查 npm (${MAIN_PKG_NAME}) 最新版本...`);
|
|
142
|
+
const latest = await fetchLatestVersion();
|
|
143
|
+
if (!latest) {
|
|
144
|
+
console.error("[kamusic] 无法连接 npm 源获取最新版本,请检查网络连接。");
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!force && compareSemVer(latest, current) <= 0) {
|
|
149
|
+
console.log(`[kamusic] 当前已是最新版本 (v${current})。如需强制重装可执行: kamusic update --force`);
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
console.log(`[kamusic] 发现新版本 v${latest},开始更新 ${MAIN_PKG_NAME}@${latest} ...`);
|
|
154
|
+
const ok = installGlobalPackage(`${MAIN_PKG_NAME}@${latest}`);
|
|
155
|
+
if (!ok) {
|
|
156
|
+
console.error(`[kamusic] 更新失败,可手动执行: npm i -g ${MAIN_PKG_NAME}@latest`);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
console.log(`[kamusic] ✓ 已成功更新至 v${latest}!`);
|
|
161
|
+
if (restartAfter) {
|
|
162
|
+
console.log("[kamusic] 正在重新启动播放器...");
|
|
163
|
+
const res = spawnSync("kamusic", [], { stdio: "inherit", shell: process.platform === "win32" });
|
|
164
|
+
process.exit(res.status ?? 0);
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function resolveBinaryPath() {
|
|
170
|
+
const key = `${process.platform}-${process.arch}`;
|
|
171
|
+
const target = PLATFORM_PACKAGES[key];
|
|
172
|
+
if (!target) {
|
|
173
|
+
console.error(
|
|
174
|
+
`[kamusic] 当前平台暂不支持: ${key}(支持平台: ${Object.keys(PLATFORM_PACKAGES).join(", ")})`
|
|
175
|
+
);
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const candidates = [];
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
const pkgJsonPath = require.resolve(`${target.pkg}/package.json`);
|
|
183
|
+
candidates.push(path.join(path.dirname(pkgJsonPath), "vendor", target.bin));
|
|
184
|
+
} catch {
|
|
185
|
+
// fallback to workspace sibling directory
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
candidates.push(
|
|
189
|
+
path.resolve(__dirname, "..", "..", target.dir, "vendor", target.bin)
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
for (const candidate of candidates) {
|
|
193
|
+
if (fs.existsSync(candidate)) {
|
|
194
|
+
return candidate;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
console.error(
|
|
199
|
+
`[kamusic] 未找到当前平台 (${key}) 的原生可执行文件,请确认可选依赖包 "${target.pkg}" 已安装(可执行 kamusic update --force 修复)。`
|
|
200
|
+
);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function main() {
|
|
205
|
+
const args = process.argv.slice(2);
|
|
206
|
+
const first = args[0];
|
|
207
|
+
|
|
208
|
+
if (first === "-v" || first === "--version" || first === "version") {
|
|
209
|
+
console.log(`${MAIN_PKG_NAME} v${getCurrentVersion()} (${process.platform}-${process.arch})`);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (first === "update" || first === "upgrade" || first === "-u" || first === "--update") {
|
|
214
|
+
const force = args.includes("--force") || args.includes("-f");
|
|
215
|
+
await handleUpdateCommand({ force, restartAfter: false });
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (first === "check" || first === "--check-update") {
|
|
220
|
+
const current = getCurrentVersion();
|
|
221
|
+
const latest = await fetchLatestVersion();
|
|
222
|
+
if (!latest) {
|
|
223
|
+
console.error("[kamusic] 检查更新失败:无法连接 npm 源。");
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
if (compareSemVer(latest, current) > 0) {
|
|
227
|
+
console.log(`[kamusic] 发现新版本: v${current} -> v${latest}(运行 \`kamusic update\` 即可更新)`);
|
|
228
|
+
} else {
|
|
229
|
+
console.log(`[kamusic] 当前已是最新版本: v${current}`);
|
|
230
|
+
}
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const binPath = resolveBinaryPath();
|
|
235
|
+
if (process.platform !== "win32") {
|
|
236
|
+
try {
|
|
237
|
+
fs.chmodSync(binPath, 0o755);
|
|
238
|
+
} catch {
|
|
239
|
+
// ignore chmod errors on read-only mounts
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const currentVersion = getCurrentVersion();
|
|
244
|
+
const child = spawn(binPath, args, {
|
|
245
|
+
stdio: "inherit",
|
|
246
|
+
windowsHide: false,
|
|
247
|
+
env: {
|
|
248
|
+
...process.env,
|
|
249
|
+
KAMUSIC_NPM_WRAPPER: "1",
|
|
250
|
+
KAMUSIC_VERSION: currentVersion,
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
255
|
+
process.on(sig, () => {
|
|
256
|
+
if (!child.killed) {
|
|
257
|
+
try {
|
|
258
|
+
child.kill(sig);
|
|
259
|
+
} catch {
|
|
260
|
+
// ignore
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
child.on("error", (err) => {
|
|
267
|
+
console.error(`[kamusic] 启动失败: ${err.message}`);
|
|
268
|
+
process.exit(1);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
child.on("exit", async (code, signal) => {
|
|
272
|
+
if (signal) {
|
|
273
|
+
process.kill(process.pid, signal);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (code === UPDATE_EXIT_CODE) {
|
|
277
|
+
await handleUpdateCommand({ force: false, restartAfter: true });
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
process.exit(code ?? 0);
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linsxyx/kamusic",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "KA Music 终端 TUI 播放器(NativeAOT 跨平台命令行版,支持终端专辑封面、KRC 逐字歌词、频谱与酷狗账号登录)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/Linsxyx/KugouMusic.NET.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/Linsxyx/KugouMusic.NET",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/Linsxyx/KugouMusic.NET/issues"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"kamusic",
|
|
16
|
+
"kugou",
|
|
17
|
+
"music",
|
|
18
|
+
"player",
|
|
19
|
+
"tui",
|
|
20
|
+
"cli",
|
|
21
|
+
"terminal"
|
|
22
|
+
],
|
|
23
|
+
"bin": {
|
|
24
|
+
"kamusic": "bin/kamusic.js"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"bin/"
|
|
28
|
+
],
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=16.0.0"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"optionalDependencies": {
|
|
36
|
+
"@linsxyx/kamusic-win32-x64": "0.1.1",
|
|
37
|
+
"@linsxyx/kamusic-win32-arm64": "0.1.1",
|
|
38
|
+
"@linsxyx/kamusic-linux-x64": "0.1.1",
|
|
39
|
+
"@linsxyx/kamusic-linux-arm64": "0.1.1",
|
|
40
|
+
"@linsxyx/kamusic-darwin-arm64": "0.1.1",
|
|
41
|
+
"@linsxyx/kamusic-darwin-x64": "0.1.1"
|
|
42
|
+
}
|
|
43
|
+
}
|