@linsxyx/kamusic 0.1.0 → 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 +3 -13
- package/bin/kamusic.js +204 -35
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -5,22 +5,12 @@
|
|
|
5
5
|
## 安装与启动
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
#
|
|
8
|
+
# 全局安装
|
|
9
9
|
npm install -g @linsxyx/kamusic
|
|
10
10
|
|
|
11
11
|
# 启动播放器
|
|
12
12
|
kamusic
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
或者免安装直接运行:
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
# 检查并更新到最新版本
|
|
15
|
+
kamusic update
|
|
19
16
|
```
|
|
20
|
-
|
|
21
|
-
## 特性
|
|
22
|
-
|
|
23
|
-
- **多协议终端高清图片**:自动检测并支持 Kitty Graphics Protocol、iTerm2 (`OSC 1337`)、Sixel 与 UTF-8 半块字符(`▀`),终端内直接渲染登录二维码、专辑封面与歌单封面。
|
|
24
|
-
- **全功能音频播放与音效**:支持每日推荐、私人 FM、发现歌单、排行榜、搜索、我的歌单、KRC 逐字高亮歌词、频谱可视化、10 段均衡器与智能无缝过渡。
|
|
25
|
-
- **鼠标与键盘双交互**:支持键盘快捷键与终端鼠标点击/双击/滚轮交互。
|
|
26
|
-
- **独立安全会话**:与桌面端分离存储(`KgTestSession.json`),Windows 下采用系统 DPAPI 加密保护登录态。
|
package/bin/kamusic.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// kamusic CLI
|
|
3
|
+
// kamusic CLI 启动入口:支持 `kamusic update` / `kamusic -v`,并根据当前 OS 与 CPU 架构定位对应平台子包中的 NativeAOT 可执行文件(支持 TUI 内触发退出码 42 自动更新并重启)。
|
|
4
4
|
|
|
5
5
|
const fs = require("node:fs");
|
|
6
|
+
const os = require("node:os");
|
|
6
7
|
const path = require("node:path");
|
|
7
|
-
const
|
|
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
|
+
];
|
|
8
18
|
|
|
9
19
|
const PLATFORM_PACKAGES = {
|
|
10
20
|
"win32-x64": {
|
|
@@ -39,7 +49,123 @@ const PLATFORM_PACKAGES = {
|
|
|
39
49
|
},
|
|
40
50
|
};
|
|
41
51
|
|
|
42
|
-
|
|
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
|
+
|
|
43
169
|
function resolveBinaryPath() {
|
|
44
170
|
const key = `${process.platform}-${process.arch}`;
|
|
45
171
|
const target = PLATFORM_PACKAGES[key];
|
|
@@ -70,46 +196,89 @@ function resolveBinaryPath() {
|
|
|
70
196
|
}
|
|
71
197
|
|
|
72
198
|
console.error(
|
|
73
|
-
`[kamusic] 未找到当前平台 (${key}) 的原生可执行文件,请确认可选依赖包 "${target.pkg}"
|
|
199
|
+
`[kamusic] 未找到当前平台 (${key}) 的原生可执行文件,请确认可选依赖包 "${target.pkg}" 已安装(可执行 kamusic update --force 修复)。`
|
|
74
200
|
);
|
|
75
201
|
process.exit(1);
|
|
76
202
|
}
|
|
77
203
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
+
}
|
|
84
241
|
}
|
|
85
|
-
}
|
|
86
242
|
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
+
}
|
|
99
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;
|
|
100
279
|
}
|
|
280
|
+
process.exit(code ?? 0);
|
|
101
281
|
});
|
|
102
282
|
}
|
|
103
283
|
|
|
104
|
-
|
|
105
|
-
console.error(`[kamusic] 启动失败: ${err.message}`);
|
|
106
|
-
process.exit(1);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
child.on("exit", (code, signal) => {
|
|
110
|
-
if (signal) {
|
|
111
|
-
process.kill(process.pid, signal);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
process.exit(code ?? 0);
|
|
115
|
-
});
|
|
284
|
+
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linsxyx/kamusic",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "KA Music 终端 TUI 播放器(NativeAOT 跨平台命令行版,支持终端专辑封面、KRC 逐字歌词、频谱与酷狗账号登录)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
35
|
"optionalDependencies": {
|
|
36
|
-
"@linsxyx/kamusic-
|
|
37
|
-
"@linsxyx/kamusic-
|
|
38
|
-
"@linsxyx/kamusic-linux-
|
|
39
|
-
"@linsxyx/kamusic-linux-
|
|
40
|
-
"@linsxyx/kamusic-
|
|
41
|
-
"@linsxyx/kamusic-
|
|
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
42
|
}
|
|
43
43
|
}
|