Add subscription node prefixes
This commit is contained in:
+329
-307
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PODKOP PLUS\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-07-10 04:26+0300\n"
|
||||
"PO-Revision-Date: 2026-07-10 04:26+0300\n"
|
||||
"POT-Creation-Date: 2026-07-10 05:05+0300\n"
|
||||
"PO-Revision-Date: 2026-07-10 05:05+0300\n"
|
||||
"Last-Translator: ushan0v\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: ru\n"
|
||||
@@ -59,6 +59,9 @@ msgstr "Добавить секцию"
|
||||
msgid "Add a server inbound"
|
||||
msgstr "Добавить серверный inbound"
|
||||
|
||||
msgid "Add prefix to nodes"
|
||||
msgstr "Добавлять префикс к узлам"
|
||||
|
||||
msgid "Add URLs or local paths to .lst lists"
|
||||
msgstr "Добавляйте URL или локальные пути к спискам .lst"
|
||||
|
||||
@@ -134,6 +137,9 @@ msgstr "Авто-генерация HWID"
|
||||
msgid "Automatic User-Agent selection"
|
||||
msgstr "Авто-подбор User-Agent"
|
||||
|
||||
msgid "Automatically add text to the name of each server from this subscription for convenient filtering."
|
||||
msgstr "Автоматически добавлять текст к имени каждого сервера из этой подписки для удобной фильтрации."
|
||||
|
||||
msgid "Automatically select the fastest node in the current level"
|
||||
msgstr "Автовыбор самого быстрого узла уровня"
|
||||
|
||||
@@ -1310,6 +1316,9 @@ msgstr "Порты"
|
||||
msgid "Preferred IP"
|
||||
msgstr "Предпочитаемый IP"
|
||||
|
||||
msgid "Prefix text"
|
||||
msgstr "Текст префикса"
|
||||
|
||||
msgid "Priority"
|
||||
msgstr "Приоритет"
|
||||
|
||||
|
||||
@@ -255,6 +255,8 @@ function hydrateConfigSections(configSections: Podkop.ConfigSection[]) {
|
||||
auto_hwid: item.auto_hwid,
|
||||
hwid: item.hwid,
|
||||
show_dashboard_metadata: item.show_dashboard_metadata,
|
||||
prefix_nodes: item.prefix_nodes,
|
||||
node_prefix: item.node_prefix,
|
||||
include_urltest_groups: item.include_urltest_groups,
|
||||
hide_urltest_group_outbounds: item.hide_urltest_group_outbounds,
|
||||
hide_detour_outbounds: item.hide_detour_outbounds,
|
||||
|
||||
@@ -326,6 +326,8 @@ export namespace Podkop {
|
||||
auto_hwid?: '0' | '1';
|
||||
hwid?: string;
|
||||
show_dashboard_metadata?: '0' | '1';
|
||||
prefix_nodes?: '0' | '1';
|
||||
node_prefix?: string;
|
||||
include_urltest_groups?: '0' | '1';
|
||||
hide_urltest_group_outbounds?: '0' | '1';
|
||||
hide_detour_outbounds?: '0' | '1';
|
||||
|
||||
@@ -3244,6 +3244,8 @@ function hydrateConfigSections(configSections) {
|
||||
auto_hwid: item.auto_hwid,
|
||||
hwid: item.hwid,
|
||||
show_dashboard_metadata: item.show_dashboard_metadata,
|
||||
prefix_nodes: item.prefix_nodes,
|
||||
node_prefix: item.node_prefix,
|
||||
include_urltest_groups: item.include_urltest_groups,
|
||||
hide_urltest_group_outbounds: item.hide_urltest_group_outbounds,
|
||||
hide_detour_outbounds: item.hide_detour_outbounds
|
||||
|
||||
@@ -1635,6 +1635,8 @@ function subscriptionUrlSettingsKeys() {
|
||||
"auto_hwid",
|
||||
"hwid",
|
||||
"show_dashboard_metadata",
|
||||
"prefix_nodes",
|
||||
"node_prefix",
|
||||
"include_urltest_groups",
|
||||
"hide_urltest_group_outbounds",
|
||||
"hide_detour_outbounds",
|
||||
@@ -1652,6 +1654,8 @@ function defaultSubscriptionUrlSettings() {
|
||||
auto_hwid: "1",
|
||||
hwid: "",
|
||||
show_dashboard_metadata: "1",
|
||||
prefix_nodes: "0",
|
||||
node_prefix: "",
|
||||
include_urltest_groups: "1",
|
||||
hide_urltest_group_outbounds: "1",
|
||||
hide_detour_outbounds: "1",
|
||||
@@ -1943,6 +1947,21 @@ function addSubscriptionUrlItemOptions(itemSection, options = {}) {
|
||||
o.default = "1";
|
||||
o.rmempty = false;
|
||||
|
||||
o = itemSection.option(
|
||||
form.Flag,
|
||||
"prefix_nodes",
|
||||
_("Add prefix to nodes"),
|
||||
_(
|
||||
"Automatically add text to the name of each server from this subscription for convenient filtering.",
|
||||
),
|
||||
);
|
||||
o.default = "0";
|
||||
o.rmempty = false;
|
||||
|
||||
o = itemSection.option(form.Value, "node_prefix", _("Prefix text"));
|
||||
o.depends("prefix_nodes", "1");
|
||||
o.rmempty = false;
|
||||
|
||||
o = itemSection.option(
|
||||
form.Flag,
|
||||
"include_urltest_groups",
|
||||
|
||||
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PODKOP PLUS\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-07-10 04:26+0300\n"
|
||||
"PO-Revision-Date: 2026-07-10 04:26+0300\n"
|
||||
"POT-Creation-Date: 2026-07-10 05:05+0300\n"
|
||||
"PO-Revision-Date: 2026-07-10 05:05+0300\n"
|
||||
"Last-Translator: ushan0v\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: ru\n"
|
||||
@@ -59,6 +59,9 @@ msgstr "Добавить секцию"
|
||||
msgid "Add a server inbound"
|
||||
msgstr "Добавить серверный inbound"
|
||||
|
||||
msgid "Add prefix to nodes"
|
||||
msgstr "Добавлять префикс к узлам"
|
||||
|
||||
msgid "Add URLs or local paths to .lst lists"
|
||||
msgstr "Добавляйте URL или локальные пути к спискам .lst"
|
||||
|
||||
@@ -134,6 +137,9 @@ msgstr "Авто-генерация HWID"
|
||||
msgid "Automatic User-Agent selection"
|
||||
msgstr "Авто-подбор User-Agent"
|
||||
|
||||
msgid "Automatically add text to the name of each server from this subscription for convenient filtering."
|
||||
msgstr "Автоматически добавлять текст к имени каждого сервера из этой подписки для удобной фильтрации."
|
||||
|
||||
msgid "Automatically select the fastest node in the current level"
|
||||
msgstr "Автовыбор самого быстрого узла уровня"
|
||||
|
||||
@@ -1310,6 +1316,9 @@ msgstr "Порты"
|
||||
msgid "Preferred IP"
|
||||
msgstr "Предпочитаемый IP"
|
||||
|
||||
msgid "Prefix text"
|
||||
msgstr "Текст префикса"
|
||||
|
||||
msgid "Priority"
|
||||
msgstr "Приоритет"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -49,6 +49,8 @@ config settings 'settings'
|
||||
# option subscription_update_interval '1h'
|
||||
# option download_via_proxy_enabled '0'
|
||||
# option show_dashboard_metadata '1'
|
||||
# option prefix_nodes '0'
|
||||
# #option node_prefix 'Provider'
|
||||
# option include_urltest_groups '1'
|
||||
# option hide_urltest_group_outbounds '1'
|
||||
# option hide_detour_outbounds '1'
|
||||
|
||||
@@ -526,6 +526,24 @@ function subscription_hide_detour_outbounds(section, value) {
|
||||
return item_bool(section, "subscription_url_settings", value, "hide_detour_outbounds", true);
|
||||
}
|
||||
|
||||
function subscription_prefix_nodes(section, value) {
|
||||
let child = child_item_by_value(section, "subscription_url", "url", value);
|
||||
if (child != null)
|
||||
return child_bool(child, "prefix_nodes", false);
|
||||
return item_bool(section, "subscription_url_settings", value, "prefix_nodes", false);
|
||||
}
|
||||
|
||||
function subscription_node_prefix(section, value) {
|
||||
if (!subscription_prefix_nodes(section, value))
|
||||
return "";
|
||||
|
||||
let child = child_item_by_value(section, "subscription_url", "url", value);
|
||||
let prefix = child != null
|
||||
? child_option(child, "node_prefix", "")
|
||||
: item_option(section, "subscription_url_settings", value, "node_prefix", "");
|
||||
return trim(as_string(prefix));
|
||||
}
|
||||
|
||||
function subscription_user_agent(section, value) {
|
||||
if (subscription_auto_user_agent(section, value))
|
||||
return "";
|
||||
@@ -884,6 +902,8 @@ return {
|
||||
subscription_include_urltest_groups,
|
||||
subscription_hide_urltest_group_outbounds,
|
||||
subscription_hide_detour_outbounds,
|
||||
subscription_prefix_nodes,
|
||||
subscription_node_prefix,
|
||||
subscription_user_agent,
|
||||
subscription_hwid,
|
||||
subscription_download_section,
|
||||
|
||||
@@ -738,6 +738,7 @@ function migrate_subscription_url_item_settings(ctx, section) {
|
||||
set_option(ctx, child, "user_agent", profile.user_agent);
|
||||
set_option(ctx, child, "auto_hwid", "1");
|
||||
set_option(ctx, child, "show_dashboard_metadata", "1");
|
||||
set_option(ctx, child, "prefix_nodes", "0");
|
||||
set_option(ctx, child, "include_urltest_groups", "1");
|
||||
set_option(ctx, child, "hide_urltest_group_outbounds", "1");
|
||||
set_option(ctx, child, "hide_detour_outbounds", "1");
|
||||
|
||||
@@ -845,6 +845,8 @@ function subscription_urls_signature(section) {
|
||||
auto_hwid: connections.subscription_auto_hwid(section, entry) ? "1" : "0",
|
||||
hwid: connections.subscription_hwid(section, entry),
|
||||
show_dashboard_metadata: connections.subscription_dashboard_metadata_enabled(section, entry) ? "1" : "0",
|
||||
prefix_nodes: connections.subscription_prefix_nodes(section, entry) ? "1" : "0",
|
||||
node_prefix: connections.subscription_node_prefix(section, entry),
|
||||
include_urltest_groups: connections.subscription_include_urltest_groups(section, entry) ? "1" : "0",
|
||||
hide_urltest_group_outbounds: connections.subscription_hide_urltest_group_outbounds(section, entry) ? "1" : "0",
|
||||
hide_detour_outbounds: connections.subscription_hide_detour_outbounds(section, entry) ? "1" : "0"
|
||||
|
||||
@@ -570,7 +570,7 @@ function unique_tag(base, taken) {
|
||||
return base + "-overflow";
|
||||
}
|
||||
|
||||
function add_subscription_source_with_state(config, section, source_index, source_entry, taken, selector_tags, urltest_candidate_tags, state, show_metadata, include_urltest_groups, hide_urltest_group_outbounds, hide_detour_outbounds) {
|
||||
function add_subscription_source_with_state(config, section, source_index, source_entry, taken, selector_tags, urltest_candidate_tags, state, show_metadata, include_urltest_groups, hide_urltest_group_outbounds, hide_detour_outbounds, node_prefix) {
|
||||
let section_name = section[".name"];
|
||||
let source_section = runtime_subscription.source_id(section_name, source_index);
|
||||
if (!runtime_subscription.source_cache_is_current(
|
||||
@@ -590,6 +590,7 @@ function add_subscription_source_with_state(config, section, source_index, sourc
|
||||
let visibility_refs = subscription_visibility_refs(outbounds);
|
||||
if (include_urltest_groups === false)
|
||||
hide_urltest_group_outbounds = false;
|
||||
node_prefix = trim(as_string(node_prefix));
|
||||
let prepared = [];
|
||||
let source_indices = [];
|
||||
let display_names = [];
|
||||
@@ -610,6 +611,10 @@ function add_subscription_source_with_state(config, section, source_index, sourc
|
||||
continue;
|
||||
let display_name = as_string(outbound.remark || outbound.tag || ("server-" + (i + 1)));
|
||||
let base = as_string(outbound.tag || outbound.remark || ("server-" + (i + 1)));
|
||||
if (node_prefix != "") {
|
||||
display_name = node_prefix + " " + display_name;
|
||||
base = display_name;
|
||||
}
|
||||
let new_tag = unique_tag(base, taken);
|
||||
taken[new_tag] = true;
|
||||
tag_map[base] = new_tag;
|
||||
@@ -1780,7 +1785,8 @@ function add_connection_subscriptions(config, state, section, taken, selector_ta
|
||||
connections.subscription_dashboard_metadata_enabled(section, subscription_urls[i]),
|
||||
connections.subscription_include_urltest_groups(section, subscription_urls[i]),
|
||||
connections.subscription_hide_urltest_group_outbounds(section, subscription_urls[i]),
|
||||
connections.subscription_hide_detour_outbounds(section, subscription_urls[i])
|
||||
connections.subscription_hide_detour_outbounds(section, subscription_urls[i]),
|
||||
connections.subscription_node_prefix(section, subscription_urls[i])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -215,6 +215,7 @@ const legacySubSource = childObjects(legacySub, subscriptionUrls)[0];
|
||||
assert(legacySubSource.user_agent === 'Agent/1.0', 'subscription user-agent setting');
|
||||
assert(legacySubSource.auto_user_agent === '0', 'subscription user-agent manual mode');
|
||||
assert(legacySubSource.auto_hwid === '1', 'subscription HWID auto-generation');
|
||||
assert(legacySubSource.prefix_nodes === '0', 'subscription node prefix disabled by default');
|
||||
assert(legacySubSource.include_urltest_groups === '1', 'subscription URLTest groups import default');
|
||||
assert(legacySubSource.hide_urltest_group_outbounds === '1', 'subscription URLTest group member hiding default');
|
||||
assert(legacySubSource.hide_detour_outbounds === '1', 'subscription detour hiding default');
|
||||
|
||||
@@ -314,6 +314,7 @@ cat >"$WORK_DIR/sing-box-signature.json" <<'JSON'
|
||||
"action": "proxy",
|
||||
"selector_proxy_links": [ "vless://one", "vless://two" ],
|
||||
"subscription_urls": [ "https://example.com/sub.txt" ],
|
||||
"subscription_url_settings": "{\"https://example.com/sub.txt\":{\"prefix_nodes\":\"1\",\"node_prefix\":\"Example\"}}",
|
||||
"urltest_enabled": "1",
|
||||
"detect_server_country": "1",
|
||||
"urltest_tolerance": "75",
|
||||
@@ -432,7 +433,7 @@ proxy
|
||||
[rule.proxy1.connection_urls]
|
||||
[ "vless://one", "vless://two" ]
|
||||
[rule.proxy1.subscription_urls]
|
||||
[ { "url": "https://example.com/sub.txt", "subscription_update_enabled": "1", "subscription_update_interval": "1h", "download_via_proxy_section": "", "auto_user_agent": "1", "user_agent": "", "auto_hwid": "1", "hwid": "", "show_dashboard_metadata": "1", "include_urltest_groups": "1", "hide_urltest_group_outbounds": "1", "hide_detour_outbounds": "1" } ]
|
||||
[ { "url": "https://example.com/sub.txt", "subscription_update_enabled": "1", "subscription_update_interval": "1h", "download_via_proxy_section": "", "auto_user_agent": "1", "user_agent": "", "auto_hwid": "1", "hwid": "", "show_dashboard_metadata": "1", "prefix_nodes": "1", "node_prefix": "Example", "include_urltest_groups": "1", "hide_urltest_group_outbounds": "1", "hide_detour_outbounds": "1" } ]
|
||||
[rule.proxy1.interfaces]
|
||||
[ ]
|
||||
[rule.proxy1.outbound_jsons]
|
||||
@@ -1136,11 +1137,11 @@ local.lst https://example.com/mixed.lst
|
||||
[lists.urltest_custom.domain_ip_lists]
|
||||
|
||||
[subscription.list_proxy.subscription_urls]
|
||||
[ { "url": "https://example.com/sub1.txt", "subscription_update_enabled": "1", "subscription_update_interval": "1h", "download_via_proxy_section": "", "auto_user_agent": "1", "user_agent": "", "auto_hwid": "1", "hwid": "", "show_dashboard_metadata": "1", "include_urltest_groups": "1", "hide_urltest_group_outbounds": "1", "hide_detour_outbounds": "1" }, { "url": "https://example.com/sub2.txt", "subscription_update_enabled": "1", "subscription_update_interval": "1h", "download_via_proxy_section": "", "auto_user_agent": "1", "user_agent": "", "auto_hwid": "1", "hwid": "", "show_dashboard_metadata": "1", "include_urltest_groups": "1", "hide_urltest_group_outbounds": "1", "hide_detour_outbounds": "1" } ]
|
||||
[ { "url": "https://example.com/sub1.txt", "subscription_update_enabled": "1", "subscription_update_interval": "1h", "download_via_proxy_section": "", "auto_user_agent": "1", "user_agent": "", "auto_hwid": "1", "hwid": "", "show_dashboard_metadata": "1", "prefix_nodes": "0", "node_prefix": "", "include_urltest_groups": "1", "hide_urltest_group_outbounds": "1", "hide_detour_outbounds": "1" }, { "url": "https://example.com/sub2.txt", "subscription_update_enabled": "1", "subscription_update_interval": "1h", "download_via_proxy_section": "", "auto_user_agent": "1", "user_agent": "", "auto_hwid": "1", "hwid": "", "show_dashboard_metadata": "1", "prefix_nodes": "0", "node_prefix": "", "include_urltest_groups": "1", "hide_urltest_group_outbounds": "1", "hide_detour_outbounds": "1" } ]
|
||||
[subscription.list_proxy.subscription_update_interval]
|
||||
1h
|
||||
[subscription.sub_paused.subscription_urls]
|
||||
[ { "url": "https://example.com/off.txt", "subscription_update_enabled": "0", "subscription_update_interval": "1h", "download_via_proxy_section": "", "auto_user_agent": "1", "user_agent": "", "auto_hwid": "1", "hwid": "", "show_dashboard_metadata": "1", "include_urltest_groups": "1", "hide_urltest_group_outbounds": "1", "hide_detour_outbounds": "1" } ]
|
||||
[ { "url": "https://example.com/off.txt", "subscription_update_enabled": "0", "subscription_update_interval": "1h", "download_via_proxy_section": "", "auto_user_agent": "1", "user_agent": "", "auto_hwid": "1", "hwid": "", "show_dashboard_metadata": "1", "prefix_nodes": "0", "node_prefix": "", "include_urltest_groups": "1", "hide_urltest_group_outbounds": "1", "hide_detour_outbounds": "1" } ]
|
||||
[subscription.sub_paused.subscription_update_interval]
|
||||
|
||||
EOF_CRON_SIG
|
||||
|
||||
@@ -399,6 +399,81 @@ JSON
|
||||
singbox_config="$WORK_DIR/singbox-config.json"
|
||||
generate_config "$WORK_DIR/singbox-fixture.json" "$singbox_config"
|
||||
|
||||
cat >"$WORK_DIR/singbox-prefix-fixture.json" <<'JSON'
|
||||
{
|
||||
"settings": {
|
||||
".name": "settings",
|
||||
".type": "settings",
|
||||
"log_level": "warn"
|
||||
},
|
||||
"section": [
|
||||
{
|
||||
".name": "proxy",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "proxy",
|
||||
"urltest_enabled": "1",
|
||||
"urltest_check_interval": "3m",
|
||||
"urltest_tolerance": "50",
|
||||
"urltest_filter_mode": "include",
|
||||
"urltest_include_regex": [ "^Provider " ],
|
||||
"detect_server_country": "country_is",
|
||||
"subscription_urls": [ "https://singbox.example/sub" ],
|
||||
"subscription_url_settings": "{\"https://singbox.example/sub\":{\"prefix_nodes\":\"1\",\"node_prefix\":\"Provider\"}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
|
||||
singbox_prefix_config="$WORK_DIR/singbox-prefix-config.json"
|
||||
generate_config "$WORK_DIR/singbox-prefix-fixture.json" "$singbox_prefix_config"
|
||||
|
||||
ucode -e '
|
||||
let fs = require("fs");
|
||||
function object_or_empty(value) { return type(value) == "object" ? value : {}; }
|
||||
function outbound_by_tag(config, tag) {
|
||||
for (let outbound in config.outbounds || [])
|
||||
if (outbound && outbound.tag == tag)
|
||||
return outbound;
|
||||
return null;
|
||||
}
|
||||
function contains(values, needle) {
|
||||
for (let value in values || [])
|
||||
if (value == needle)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
let config = json(fs.readfile(ARGV[0]));
|
||||
let cache = json(fs.readfile(ARGV[1]));
|
||||
let names = object_or_empty(object_or_empty(cache.outboundMetadata).names);
|
||||
let groups = object_or_empty(cache.urltestGroups);
|
||||
let imported = outbound_by_tag(config, "Provider Native Group");
|
||||
let native = outbound_by_tag(config, "Provider Native A");
|
||||
let detour = outbound_by_tag(config, "Provider Detour Only");
|
||||
let uses_detour = outbound_by_tag(config, "Provider Uses Detour");
|
||||
let builtin = outbound_by_tag(config, "proxy-urltest-out");
|
||||
if (!imported || !native || !detour || !uses_detour)
|
||||
die("subscription prefix was not applied to every imported outbound\n");
|
||||
if (length(imported.outbounds || []) != 1 || imported.outbounds[0] != "Provider Native A")
|
||||
die("subscription prefix did not rewrite imported URLTest group membership\n");
|
||||
if (uses_detour.detour != "Provider Detour Only")
|
||||
die("subscription prefix did not rewrite detour references\n");
|
||||
if (!builtin || length(builtin.outbounds || []) != 3)
|
||||
die("prefixed node names did not match the configured URLTest filter\n");
|
||||
for (let tag in [ "Provider Native A", "Provider Detour Only", "Provider Uses Detour" ])
|
||||
if (!contains(builtin.outbounds, tag))
|
||||
die("built-in URLTest is missing a prefixed node\n");
|
||||
if (contains(cache.urltestCandidateTags || [], "Provider Native Group"))
|
||||
die("prefixed imported URLTest group must not become a URLTest candidate\n");
|
||||
if (object_or_empty(groups["Provider Native Group"]).displayName != "Provider Native Group")
|
||||
die("prefixed imported URLTest group display name was not retained\n");
|
||||
if (names["Provider Native A"] != "Provider Native A" ||
|
||||
names["Provider Native Group"] != "Provider Native Group")
|
||||
die("prefixed outbound metadata names were not retained\n");
|
||||
if (names["Native A"] != null || names["Native Group"] != null)
|
||||
die("unprefixed outbound metadata names must not remain\n");
|
||||
' "$singbox_prefix_config" "$singbox_prefix_config.section-cache/proxy.json" || fail "subscription node prefix behavior"
|
||||
|
||||
cat >"$WORK_DIR/singbox-reveal-urltest-fixture.json" <<'JSON'
|
||||
{
|
||||
"settings": {
|
||||
|
||||
Reference in New Issue
Block a user