mihomo-cli 2.3.0 → 2.4.0
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/CHANGELOG.md +31 -0
- package/README.md +4 -2
- package/dist/index.js +215 -26
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.4.0] - 2026-05-02
|
|
4
|
+
|
|
5
|
+
### 新功能
|
|
6
|
+
|
|
7
|
+
- **sub best** - `sub best <id>` 一键添加聚合订阅(每小时自动更新、去重、测活)
|
|
8
|
+
- `best 1` 精选 29 组(仅测速源:FreeSubsCheck, shaoyouvip, dalazhi, getnode)
|
|
9
|
+
- `best 2` ACL4SSR 29 组(全部 7 个源)
|
|
10
|
+
- `best 3` freeSub 24 组
|
|
11
|
+
- **新增免费源** - yahr601, Auto-Sync, ssrsub, dalazhi, getnode
|
|
12
|
+
|
|
13
|
+
### 修复
|
|
14
|
+
|
|
15
|
+
- `setDefaultSubscription` 移到下载成功后再设置,避免下载失败留下无效默认订阅
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## [2.3.1] - 2026-05-02
|
|
20
|
+
|
|
21
|
+
### 新功能
|
|
22
|
+
|
|
23
|
+
- **合并订阅** - `sub add url1,url2 name` 支持逗号分隔多 URL,合并节点(按名去重),分组/规则取第一个源
|
|
24
|
+
- **sub free 0** - 特殊 ID `0` 自动合并免费源 #1 + #2(节点更多,配置相同)
|
|
25
|
+
|
|
26
|
+
### 改进
|
|
27
|
+
|
|
28
|
+
- 合并订阅在列表中显示 `[合并 N 源]` 标记
|
|
29
|
+
- `sub update` 自动识别合并订阅并重新下载合并
|
|
30
|
+
- URL 脱敏支持逗号分隔多 URL
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
3
34
|
## [2.3.0] - 2026-05-02
|
|
4
35
|
|
|
5
36
|
### 新功能
|
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
- 🧹 **节点测速清理** - `sub test` 测试连通性,`sub clean` 自动清理失败节点,启动时自动清理
|
|
11
11
|
- 📊 **免费订阅基准测试** - `bench` 命令一键测试 20 个内置免费订阅源质量排名
|
|
12
12
|
- 🆓 **快速添加免费订阅** - `sub free <id>` 一键添加内置免费订阅源
|
|
13
|
+
- 🏆 **聚合订阅** - `sub best <id>` 一键添加自动更新的聚合订阅(每小时更新、去重、测活)
|
|
13
14
|
- 📝 **覆写配置** - 在订阅基础上进行自定义覆写,支持强制覆盖、数组合并
|
|
14
15
|
- 🔄 **智能重启** - `sub use` 切换订阅、`ow on/off` 切换覆写后自动重启
|
|
15
16
|
- 🚀 **进程管理** - 启动/停止/切换模式,自动清理残留进程
|
|
@@ -98,8 +99,9 @@ mihomo ui yacd # YACD
|
|
|
98
99
|
| ----------------------------- | -------------------------------------- |
|
|
99
100
|
| `mihomo sub` | 列出所有订阅(含流量、到期时间) |
|
|
100
101
|
| `mihomo sub use <name>` | 切换当前订阅(支持模糊匹配,自动重启) |
|
|
101
|
-
| `mihomo sub add <url> [name]` |
|
|
102
|
-
| `mihomo sub free <id>` | 添加内置免费订阅(`sub free` 列出可用源)|
|
|
102
|
+
| `mihomo sub add <url> [name]` | 添加订阅并自动切换(支持逗号分隔多 URL 合并) |
|
|
103
|
+
| `mihomo sub free <id>` | 添加内置免费订阅(`0`=合并 #1+#2,`sub free` 列出可用源)|
|
|
104
|
+
| `mihomo sub best <id>` | 添加聚合订阅(`sub best` 列出可用源)|
|
|
103
105
|
| `mihomo sub update` | 更新所有订阅 |
|
|
104
106
|
| `mihomo sub update <name>` | 更新指定订阅(支持模糊匹配) |
|
|
105
107
|
| `mihomo sub remove <name>` | 删除订阅(支持模糊匹配) |
|
package/dist/index.js
CHANGED
|
@@ -2652,9 +2652,16 @@ var TUN_CONFIG = {
|
|
|
2652
2652
|
};
|
|
2653
2653
|
function getFreeSubscriptionSources() {
|
|
2654
2654
|
return [
|
|
2655
|
-
// 完整配置(
|
|
2655
|
+
// 完整配置(ACL4SSR 29 组)
|
|
2656
2656
|
{ name: "FreeSubsCheck", url: "https://gh-proxy.org/raw.githubusercontent.com/kooker/FreeSubsCheck/main/mihomo.yaml" },
|
|
2657
|
+
{ name: "yahr601", url: "https://gh-proxy.org/raw.githubusercontent.com/yahr601-prog/1/main/clash.yaml" },
|
|
2658
|
+
{ name: "Auto-Sync", url: "https://gh-proxy.org/raw.githubusercontent.com/walke2019/Auto-Sync/main/clash/GG/clash.yaml" },
|
|
2659
|
+
{ name: "ssrsub", url: "https://gh-proxy.org/raw.githubusercontent.com/ssrsub/ssr/master/clash.yaml" },
|
|
2660
|
+
// OpenAi → Ai平台
|
|
2657
2661
|
{ name: "shaoyouvip", url: "https://gh-proxy.org/raw.githubusercontent.com/shaoyouvip/free/main/mihomo.yaml" },
|
|
2662
|
+
{ name: "dalazhi", url: "https://gh-proxy.org/raw.githubusercontent.com/dalazhi/v2ray/main/data/mihomo.yaml" },
|
|
2663
|
+
{ name: "getnode", url: "https://gh-proxy.org/raw.githubusercontent.com/limitless-d/getnode/main/clash.yaml" },
|
|
2664
|
+
// 完整配置(24 组)
|
|
2658
2665
|
{ name: "freeSub", url: "https://gh-proxy.org/raw.githubusercontent.com/Ruk1ng001/freeSub/main/clash.yaml" },
|
|
2659
2666
|
// 完整配置(13 组)
|
|
2660
2667
|
{ name: "PuddinCat", url: "https://gh-proxy.org/raw.githubusercontent.com/PuddinCat/BestClash/refs/heads/main/proxies.yaml" },
|
|
@@ -2680,6 +2687,14 @@ function getFreeSubscriptionSources() {
|
|
|
2680
2687
|
{ name: "NoMoreWalls", url: "https://gh-proxy.org/raw.githubusercontent.com/peasoft/NoMoreWalls/master/list.meta.yml" }
|
|
2681
2688
|
];
|
|
2682
2689
|
}
|
|
2690
|
+
var GH_SUB = "https://gh-proxy.org/raw.githubusercontent.com/imaex/mihomo-free-sub/sub";
|
|
2691
|
+
function getBestSubscriptionSources() {
|
|
2692
|
+
return [
|
|
2693
|
+
{ name: "curated", url: `${GH_SUB}/curated.yaml`, description: "\u7CBE\u9009 29 \u7EC4\uFF08\u4EC5\u6D4B\u901F\u6E90\uFF09" },
|
|
2694
|
+
{ name: "acl4ssr", url: `${GH_SUB}/acl4ssr.yaml`, description: "ACL4SSR 29 \u7EC4\uFF08\u5168\u90E8\u6E90\uFF09" },
|
|
2695
|
+
{ name: "freesub", url: `${GH_SUB}/freesub.yaml`, description: "freeSub 24 \u7EC4" }
|
|
2696
|
+
];
|
|
2697
|
+
}
|
|
2683
2698
|
var BENCH_CONFIG = {
|
|
2684
2699
|
"allow-lan": false,
|
|
2685
2700
|
"external-controller": "127.0.0.1:19090",
|
|
@@ -2795,6 +2810,9 @@ function invalidateSettingsCache() {
|
|
|
2795
2810
|
}
|
|
2796
2811
|
function maskUrl(url) {
|
|
2797
2812
|
if (!url) return url;
|
|
2813
|
+
if (url.includes(",")) {
|
|
2814
|
+
return url.split(",").map((u) => maskUrl(u.trim())).join(", ");
|
|
2815
|
+
}
|
|
2798
2816
|
try {
|
|
2799
2817
|
const parsed = new URL(url);
|
|
2800
2818
|
const tokenKeys = ["token", "key", "secret", "pass", "password", "auth", "access_token", "api_key"];
|
|
@@ -4207,6 +4225,12 @@ function viewLogWithTail(logPath, options) {
|
|
|
4207
4225
|
var DEFAULT_UPDATE_INTERVAL_HOURS = 12;
|
|
4208
4226
|
var YAML_DUMP_OPTS = { indent: 2, lineWidth: -1, noCompatMode: true };
|
|
4209
4227
|
var HTTP_CLIENT = createHttpClient({ timeout: 6e4 });
|
|
4228
|
+
function isMultiUrl(url) {
|
|
4229
|
+
return url.includes(",");
|
|
4230
|
+
}
|
|
4231
|
+
function splitUrls(url) {
|
|
4232
|
+
return url.split(",").map((u) => u.trim()).filter(Boolean);
|
|
4233
|
+
}
|
|
4210
4234
|
function loadSubscriptionConfig(subName) {
|
|
4211
4235
|
const rawContent = readSubscriptionRawConfig(subName);
|
|
4212
4236
|
if (!rawContent) {
|
|
@@ -4343,6 +4367,71 @@ async function downloadSubscription(url, subName = "default") {
|
|
|
4343
4367
|
username
|
|
4344
4368
|
};
|
|
4345
4369
|
}
|
|
4370
|
+
async function downloadMergedSubscription(urls, subName) {
|
|
4371
|
+
const responses = await Promise.all(
|
|
4372
|
+
urls.map(async (url, index) => {
|
|
4373
|
+
try {
|
|
4374
|
+
const response = await HTTP_CLIENT.get(url, { responseType: "text" });
|
|
4375
|
+
return { url, index, response, error: null };
|
|
4376
|
+
} catch (e) {
|
|
4377
|
+
return { url, index, response: null, error: e };
|
|
4378
|
+
}
|
|
4379
|
+
})
|
|
4380
|
+
);
|
|
4381
|
+
for (const r of responses) {
|
|
4382
|
+
if (r.error) {
|
|
4383
|
+
const maskedUrl = maskUrl(r.url);
|
|
4384
|
+
throw new Error(`\u5408\u5E76\u8BA2\u9605\u7B2C ${r.index + 1} \u4E2A URL \u83B7\u53D6\u5931\u8D25: ${r.error.message}
|
|
4385
|
+
URL: ${maskedUrl}`);
|
|
4386
|
+
}
|
|
4387
|
+
}
|
|
4388
|
+
const parsed = responses.map((r, i) => {
|
|
4389
|
+
const content = r.response?.data;
|
|
4390
|
+
if (!content?.trim()) throw new Error(`\u5408\u5E76\u8BA2\u9605\u7B2C ${i + 1} \u4E2A URL \u5185\u5BB9\u4E3A\u7A7A`);
|
|
4391
|
+
return parseYamlOrJson(content, `\u5408\u5E76\u8BA2\u9605\u7B2C ${i + 1} \u4E2A`);
|
|
4392
|
+
});
|
|
4393
|
+
const base = parsed[0];
|
|
4394
|
+
const baseProxies = base.proxies || [];
|
|
4395
|
+
const seenNames = new Set(baseProxies.map((p) => p.name));
|
|
4396
|
+
for (let i = 1; i < parsed.length; i++) {
|
|
4397
|
+
const extraProxies = parsed[i].proxies || [];
|
|
4398
|
+
for (const proxy of extraProxies) {
|
|
4399
|
+
if (!seenNames.has(proxy.name)) {
|
|
4400
|
+
baseProxies.push(proxy);
|
|
4401
|
+
seenNames.add(proxy.name);
|
|
4402
|
+
}
|
|
4403
|
+
}
|
|
4404
|
+
}
|
|
4405
|
+
base.proxies = baseProxies;
|
|
4406
|
+
const mergedContent = jsYaml.dump(base, YAML_DUMP_OPTS);
|
|
4407
|
+
saveSubscriptionRawConfig(subName, mergedContent);
|
|
4408
|
+
const firstHeaders = responses[0].response?.headers;
|
|
4409
|
+
const userInfo = parseUserInfo(firstHeaders?.get("subscription-userinfo") ?? null);
|
|
4410
|
+
const updateIntervalHeader = firstHeaders?.get("profile-update-interval");
|
|
4411
|
+
const updateInterval = updateIntervalHeader ? parseInt(updateIntervalHeader, 10) : null;
|
|
4412
|
+
const webPageUrl = firstHeaders?.get("profile-web-page-url") || null;
|
|
4413
|
+
const username = parseUsernameFromContentDisposition(firstHeaders?.get("content-disposition") ?? null);
|
|
4414
|
+
const cacheData = { updated_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
4415
|
+
if (userInfo) {
|
|
4416
|
+
cacheData.upload = userInfo.upload;
|
|
4417
|
+
cacheData.download = userInfo.download;
|
|
4418
|
+
cacheData.total = userInfo.total;
|
|
4419
|
+
cacheData.expire = userInfo.expire;
|
|
4420
|
+
}
|
|
4421
|
+
if (updateInterval) cacheData.update_interval = updateInterval;
|
|
4422
|
+
if (webPageUrl) cacheData.web_page_url = webPageUrl;
|
|
4423
|
+
if (username) cacheData.username = username;
|
|
4424
|
+
saveSubscriptionCache(subName, cacheData);
|
|
4425
|
+
const proxyGroups = base["proxy-groups"];
|
|
4426
|
+
return {
|
|
4427
|
+
proxies: baseProxies.length,
|
|
4428
|
+
proxyGroups: proxyGroups ? proxyGroups.length : 0,
|
|
4429
|
+
userInfo,
|
|
4430
|
+
updateInterval,
|
|
4431
|
+
webPageUrl,
|
|
4432
|
+
username
|
|
4433
|
+
};
|
|
4434
|
+
}
|
|
4346
4435
|
function prepareConfigForStart(mode, subName = "default") {
|
|
4347
4436
|
const rawContent = readSubscriptionRawConfig(subName);
|
|
4348
4437
|
if (!rawContent) {
|
|
@@ -4368,7 +4457,12 @@ function needsAutoUpdate(sub) {
|
|
|
4368
4457
|
}
|
|
4369
4458
|
async function tryUpdateOne(sub) {
|
|
4370
4459
|
try {
|
|
4371
|
-
|
|
4460
|
+
let info;
|
|
4461
|
+
if (isMultiUrl(sub.url)) {
|
|
4462
|
+
info = await downloadMergedSubscription(splitUrls(sub.url), sub.name);
|
|
4463
|
+
} else {
|
|
4464
|
+
info = await downloadSubscription(sub.url, sub.name);
|
|
4465
|
+
}
|
|
4372
4466
|
return { name: sub.name, success: true, proxies: info.proxies, proxyGroups: info.proxyGroups };
|
|
4373
4467
|
} catch (e) {
|
|
4374
4468
|
return { name: sub.name, success: false, error: e.message };
|
|
@@ -4730,8 +4824,9 @@ async function printSubscriptionList(options) {
|
|
|
4730
4824
|
subs.forEach((s, i) => {
|
|
4731
4825
|
const time = formatDate(s.updated_at);
|
|
4732
4826
|
const defaultMark = activeSub && s.name === activeSub.name ? colors.green(" [\u4F7F\u7528\u4E2D]") : "";
|
|
4827
|
+
const mergeBadge = isMultiUrl(s.url) ? colors.cyan(` [\u5408\u5E76 ${splitUrls(s.url).length} \u6E90]`) : "";
|
|
4733
4828
|
const interval = s.update_interval || DEFAULT_UPDATE_INTERVAL_HOURS;
|
|
4734
|
-
console.log(` ${i + 1}. ${s.name}${defaultMark}`);
|
|
4829
|
+
console.log(` ${i + 1}. ${s.name}${defaultMark}${mergeBadge}`);
|
|
4735
4830
|
console.log(` ${colors.gray("\u66F4\u65B0: ")}${time} (\u95F4\u9694: ${interval}h)`);
|
|
4736
4831
|
if (s.username) {
|
|
4737
4832
|
console.log(` ${colors.gray("\u7528\u6237: ")}${s.username}`);
|
|
@@ -4764,14 +4859,40 @@ async function printSubscriptionList(options) {
|
|
|
4764
4859
|
console.log("\u6253\u5F00\u9875\u9762: mihomo sub web [name]");
|
|
4765
4860
|
console.log("");
|
|
4766
4861
|
}
|
|
4862
|
+
function printFreeSourceList() {
|
|
4863
|
+
const freeSources = getFreeSubscriptionSources();
|
|
4864
|
+
console.log(` 00 \u5408\u5E76 #1 + #2 (\u8282\u70B9\u66F4\u591A)`);
|
|
4865
|
+
for (let i = 0; i < freeSources.length; i++) {
|
|
4866
|
+
console.log(` ${String(i + 1).padStart(2, "0")} ${freeSources[i].name}`);
|
|
4867
|
+
}
|
|
4868
|
+
}
|
|
4767
4869
|
async function addFreeSubscription(freeId) {
|
|
4768
4870
|
const freeSources = getFreeSubscriptionSources();
|
|
4871
|
+
if (freeId === 0) {
|
|
4872
|
+
const sources = [freeSources[0], freeSources[1]];
|
|
4873
|
+
const urls = sources.map((s) => s.url);
|
|
4874
|
+
const mergedUrl = urls.join(",");
|
|
4875
|
+
const name2 = "free0";
|
|
4876
|
+
console.log(`\u6DFB\u52A0\u5408\u5E76\u514D\u8D39\u8BA2\u9605: ${name2} (${sources.map((s) => s.name).join(" + ")})`);
|
|
4877
|
+
try {
|
|
4878
|
+
addSubscription(mergedUrl, name2);
|
|
4879
|
+
const info = await downloadMergedSubscription(urls, name2);
|
|
4880
|
+
setDefaultSubscription(name2);
|
|
4881
|
+
const repoUrls = sources.map((s) => githubRepoUrl(s.url)).filter(Boolean);
|
|
4882
|
+
if (repoUrls.length > 0) saveSubscriptionCache(name2, { web_page_url: repoUrls.join(", ") });
|
|
4883
|
+
console.log(`\u5DF2\u6DFB\u52A0\u5E76\u5207\u6362\u5230 "${name2}" (${formatProxySummary(info)}, \u5408\u5E76 ${sources.length} \u6E90)`);
|
|
4884
|
+
} catch (e) {
|
|
4885
|
+
console.error(`\u6DFB\u52A0\u5931\u8D25: ${e.message}`);
|
|
4886
|
+
process.exit(1);
|
|
4887
|
+
}
|
|
4888
|
+
console.log("");
|
|
4889
|
+
await printSubscriptionList();
|
|
4890
|
+
return;
|
|
4891
|
+
}
|
|
4769
4892
|
if (freeId < 1 || freeId > freeSources.length) {
|
|
4770
|
-
console.error(`\u9519\u8BEF: \u514D\u8D39\u8BA2\u9605 ID \u8303\u56F4
|
|
4893
|
+
console.error(`\u9519\u8BEF: \u514D\u8D39\u8BA2\u9605 ID \u8303\u56F4 0-${freeSources.length}`);
|
|
4771
4894
|
console.log("\n\u53EF\u7528\u6E90:");
|
|
4772
|
-
|
|
4773
|
-
console.log(` ${String(i + 1).padStart(2, "0")} ${freeSources[i].name}`);
|
|
4774
|
-
}
|
|
4895
|
+
printFreeSourceList();
|
|
4775
4896
|
process.exit(1);
|
|
4776
4897
|
}
|
|
4777
4898
|
const source = freeSources[freeId - 1];
|
|
@@ -4779,8 +4900,8 @@ async function addFreeSubscription(freeId) {
|
|
|
4779
4900
|
console.log(`\u6DFB\u52A0\u514D\u8D39\u8BA2\u9605: ${name}`);
|
|
4780
4901
|
try {
|
|
4781
4902
|
addSubscription(source.url, name);
|
|
4782
|
-
setDefaultSubscription(name);
|
|
4783
4903
|
const info = await downloadSubscription(source.url, name);
|
|
4904
|
+
setDefaultSubscription(name);
|
|
4784
4905
|
const repoUrl = githubRepoUrl(source.url);
|
|
4785
4906
|
if (repoUrl) saveSubscriptionCache(name, { web_page_url: repoUrl });
|
|
4786
4907
|
console.log(`\u5DF2\u6DFB\u52A0\u5E76\u5207\u6362\u5230 "${name}" (${formatProxySummary(info)})`);
|
|
@@ -4791,6 +4912,36 @@ async function addFreeSubscription(freeId) {
|
|
|
4791
4912
|
console.log("");
|
|
4792
4913
|
await printSubscriptionList();
|
|
4793
4914
|
}
|
|
4915
|
+
function printBestSourceList() {
|
|
4916
|
+
const bestSources = getBestSubscriptionSources();
|
|
4917
|
+
for (let i = 0; i < bestSources.length; i++) {
|
|
4918
|
+
console.log(` ${i + 1} ${bestSources[i].name} \u2014 ${bestSources[i].description}`);
|
|
4919
|
+
}
|
|
4920
|
+
}
|
|
4921
|
+
async function addBestSubscription(bestId) {
|
|
4922
|
+
const bestSources = getBestSubscriptionSources();
|
|
4923
|
+
if (bestId < 1 || bestId > bestSources.length) {
|
|
4924
|
+
console.error(`\u9519\u8BEF: best \u8BA2\u9605 ID \u8303\u56F4 1-${bestSources.length}`);
|
|
4925
|
+
console.log("\n\u53EF\u7528\u6E90:");
|
|
4926
|
+
printBestSourceList();
|
|
4927
|
+
process.exit(1);
|
|
4928
|
+
}
|
|
4929
|
+
const source = bestSources[bestId - 1];
|
|
4930
|
+
const name = `best${bestId}`;
|
|
4931
|
+
console.log(`\u6DFB\u52A0 best \u8BA2\u9605: ${name} (${source.description})`);
|
|
4932
|
+
try {
|
|
4933
|
+
addSubscription(source.url, name);
|
|
4934
|
+
const info = await downloadSubscription(source.url, name);
|
|
4935
|
+
setDefaultSubscription(name);
|
|
4936
|
+
saveSubscriptionCache(name, { web_page_url: "https://github.com/imaex/mihomo-free-sub" });
|
|
4937
|
+
console.log(`\u5DF2\u6DFB\u52A0\u5E76\u5207\u6362\u5230 "${name}" (${formatProxySummary(info)})`);
|
|
4938
|
+
} catch (e) {
|
|
4939
|
+
console.error(`\u6DFB\u52A0\u5931\u8D25: ${e.message}`);
|
|
4940
|
+
process.exit(1);
|
|
4941
|
+
}
|
|
4942
|
+
console.log("");
|
|
4943
|
+
await printSubscriptionList();
|
|
4944
|
+
}
|
|
4794
4945
|
async function cmdSubscription(args) {
|
|
4795
4946
|
const action = args[1];
|
|
4796
4947
|
if (!action || action === "list") {
|
|
@@ -4799,18 +4950,26 @@ async function cmdSubscription(args) {
|
|
|
4799
4950
|
}
|
|
4800
4951
|
if (action === "free") {
|
|
4801
4952
|
const id = parseInt(args[2], 10);
|
|
4802
|
-
if (
|
|
4803
|
-
const freeSources = getFreeSubscriptionSources();
|
|
4953
|
+
if (Number.isNaN(id)) {
|
|
4804
4954
|
console.log("\u7528\u6CD5: mihomo sub free <id>\n");
|
|
4805
4955
|
console.log("\u53EF\u7528\u6E90:");
|
|
4806
|
-
|
|
4807
|
-
console.log(` ${String(i + 1).padStart(2, "0")} ${freeSources[i].name}`);
|
|
4808
|
-
}
|
|
4956
|
+
printFreeSourceList();
|
|
4809
4957
|
process.exit(1);
|
|
4810
4958
|
}
|
|
4811
4959
|
await addFreeSubscription(id);
|
|
4812
4960
|
return;
|
|
4813
4961
|
}
|
|
4962
|
+
if (action === "best") {
|
|
4963
|
+
const id = parseInt(args[2], 10);
|
|
4964
|
+
if (Number.isNaN(id)) {
|
|
4965
|
+
console.log("\u7528\u6CD5: mihomo sub best <id>\n");
|
|
4966
|
+
console.log("\u53EF\u7528\u6E90:");
|
|
4967
|
+
printBestSourceList();
|
|
4968
|
+
process.exit(1);
|
|
4969
|
+
}
|
|
4970
|
+
await addBestSubscription(id);
|
|
4971
|
+
return;
|
|
4972
|
+
}
|
|
4814
4973
|
if (action === "add") {
|
|
4815
4974
|
const freeId = parseIntArg(args, "--free", "--free", -1);
|
|
4816
4975
|
if (freeId > 0) {
|
|
@@ -4819,21 +4978,45 @@ async function cmdSubscription(args) {
|
|
|
4819
4978
|
}
|
|
4820
4979
|
const url = args[2];
|
|
4821
4980
|
const name = args[3] || "default";
|
|
4822
|
-
if (!url
|
|
4981
|
+
if (!url) {
|
|
4823
4982
|
console.error("\u9519\u8BEF: \u8BF7\u63D0\u4F9B\u6709\u6548\u7684\u8BA2\u9605 URL");
|
|
4824
4983
|
process.exit(1);
|
|
4825
4984
|
}
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4985
|
+
if (isMultiUrl(url)) {
|
|
4986
|
+
const urls = splitUrls(url);
|
|
4987
|
+
for (const u of urls) {
|
|
4988
|
+
if (!u.startsWith("http")) {
|
|
4989
|
+
console.error(`\u9519\u8BEF: \u65E0\u6548\u7684 URL: ${u}`);
|
|
4990
|
+
process.exit(1);
|
|
4991
|
+
}
|
|
4992
|
+
}
|
|
4993
|
+
console.log(`\u6DFB\u52A0\u5408\u5E76\u8BA2\u9605: ${name} (${urls.length} \u4E2A\u6E90)`);
|
|
4994
|
+
try {
|
|
4995
|
+
addSubscription(url, name);
|
|
4996
|
+
setDefaultSubscription(name);
|
|
4997
|
+
const info = await downloadMergedSubscription(urls, name);
|
|
4998
|
+
console.log(`\u5DF2\u6DFB\u52A0\u5E76\u5207\u6362\u5230 "${name}" (${formatProxySummary(info)}, \u5408\u5E76 ${urls.length} \u6E90)`);
|
|
4999
|
+
} catch (e) {
|
|
5000
|
+
console.error(`\u6DFB\u52A0\u5931\u8D25: ${e.message}`);
|
|
5001
|
+
process.exit(1);
|
|
5002
|
+
}
|
|
5003
|
+
} else {
|
|
5004
|
+
if (!url.startsWith("http")) {
|
|
5005
|
+
console.error("\u9519\u8BEF: \u8BF7\u63D0\u4F9B\u6709\u6548\u7684\u8BA2\u9605 URL");
|
|
5006
|
+
process.exit(1);
|
|
5007
|
+
}
|
|
5008
|
+
console.log(`\u6DFB\u52A0\u8BA2\u9605: ${name}`);
|
|
5009
|
+
try {
|
|
5010
|
+
addSubscription(url, name);
|
|
5011
|
+
setDefaultSubscription(name);
|
|
5012
|
+
const info = await downloadSubscription(url, name);
|
|
5013
|
+
const repoUrl = githubRepoUrl(url);
|
|
5014
|
+
if (repoUrl) saveSubscriptionCache(name, { web_page_url: repoUrl });
|
|
5015
|
+
console.log(`\u5DF2\u6DFB\u52A0\u5E76\u5207\u6362\u5230 "${name}" (${formatProxySummary(info)})`);
|
|
5016
|
+
} catch (e) {
|
|
5017
|
+
console.error(`\u6DFB\u52A0\u5931\u8D25: ${e.message}`);
|
|
5018
|
+
process.exit(1);
|
|
5019
|
+
}
|
|
4837
5020
|
}
|
|
4838
5021
|
console.log("");
|
|
4839
5022
|
await printSubscriptionList();
|
|
@@ -4867,7 +5050,13 @@ async function cmdSubscription(args) {
|
|
|
4867
5050
|
const target = pickSingleSubscription(matches, name);
|
|
4868
5051
|
console.log(`\u66F4\u65B0\u8BA2\u9605: ${target.name}`);
|
|
4869
5052
|
try {
|
|
4870
|
-
|
|
5053
|
+
let info;
|
|
5054
|
+
if (isMultiUrl(target.url)) {
|
|
5055
|
+
const urls = splitUrls(target.url);
|
|
5056
|
+
info = await downloadMergedSubscription(urls, target.name);
|
|
5057
|
+
} else {
|
|
5058
|
+
info = await downloadSubscription(target.url, target.name);
|
|
5059
|
+
}
|
|
4871
5060
|
console.log(`\u5DF2\u66F4\u65B0 (${formatProxySummary(info)})`);
|
|
4872
5061
|
} catch (e) {
|
|
4873
5062
|
console.error(`\u66F4\u65B0\u5931\u8D25: ${e.message}`);
|