Add subscription support adapted from podkop-evolution

Adapt subscription handling from yandexru45/podkop-evolution to the Podkop Plus rule-based proxy architecture.
This commit is contained in:
ushan0v
2026-05-07 15:02:38 +03:00
parent 8852e8a838
commit 259955ef98
18 changed files with 1961 additions and 398 deletions
+179 -116
View File
@@ -35,7 +35,7 @@
"call": "A latency-tested group of proxy URLs for this section",
"key": "A latency-tested group of proxy URLs for this section",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1731"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1879"
]
},
{
@@ -49,15 +49,15 @@
"call": "A manual group of proxy URLs for this section",
"key": "A manual group of proxy URLs for this section",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1713"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1861"
]
},
{
"call": "Action",
"key": "Action",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1551",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1583"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1631",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1663"
]
},
{
@@ -71,21 +71,21 @@
"call": "Add a section",
"key": "Add a section",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:395"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:401"
]
},
{
"call": "Add URLs or local paths to .lst lists",
"key": "Add URLs or local paths to .lst lists",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2130"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2297"
]
},
{
"call": "Add URLs or local paths to .srs / .json lists",
"key": "Add URLs or local paths to .srs / .json lists",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2115"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2282"
]
},
{
@@ -106,7 +106,7 @@
"call": "An error occurred while saving the form:",
"key": "An error occurred while saving the form:",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:142"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:148"
]
},
{
@@ -114,21 +114,21 @@
"key": "Applicable for SOCKS and Shadowsocks links",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:170",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1826"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1993"
]
},
{
"call": "At least one valid domain must be specified.",
"key": "At least one valid domain must be specified.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:836"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:916"
]
},
{
"call": "At least one valid IP or subnet must be specified.",
"key": "At least one valid IP or subnet must be specified.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:847"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:927"
]
},
{
@@ -149,7 +149,7 @@
"call": "Backend validation failed: %s",
"key": "Backend validation failed: %s",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:938"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1018"
]
},
{
@@ -191,7 +191,7 @@
"call": "Built-in rule sets",
"key": "Built-in rule sets",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2020"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2187"
]
},
{
@@ -281,7 +281,7 @@
"call": "Conditions",
"key": "Conditions",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1539"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1619"
]
},
{
@@ -295,21 +295,21 @@
"call": "Configuration for Podkop Plus service",
"key": "Configuration for Podkop Plus service",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:381"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:387"
]
},
{
"call": "Connection",
"key": "Connection",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1668"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1749"
]
},
{
"call": "Connection Type",
"key": "Connection Type",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1651"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1731"
]
},
{
@@ -317,7 +317,7 @@
"key": "Connection URL",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:19",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1654"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1734"
]
},
{
@@ -338,7 +338,7 @@
"call": "Dashboard",
"key": "Dashboard",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:426"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:432"
]
},
{
@@ -373,7 +373,7 @@
"call": "Diagnostics",
"key": "Diagnostics",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:414"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:420"
]
},
{
@@ -409,7 +409,7 @@
"call": "Dismiss",
"key": "Dismiss",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:149"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:155"
]
},
{
@@ -424,7 +424,7 @@
"key": "DNS over HTTPS (DoH)",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:227",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1891",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2058",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/settings.js:21"
]
},
@@ -433,7 +433,7 @@
"key": "DNS over TLS (DoT)",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:228",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1892",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2059",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/settings.js:22"
]
},
@@ -441,7 +441,7 @@
"call": "DNS protocol",
"key": "DNS protocol",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1889"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2056"
]
},
{
@@ -469,7 +469,7 @@
"call": "DNS server",
"key": "DNS server",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1907"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2074"
]
},
{
@@ -498,28 +498,28 @@
"call": "Domain and IP Lists",
"key": "Domain and IP Lists",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2129"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2296"
]
},
{
"call": "Domain keyword",
"key": "Domain keyword",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1990"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2157"
]
},
{
"call": "Domain regex",
"key": "Domain regex",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1997"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2164"
]
},
{
"call": "Domains",
"key": "Domains",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1962"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2129"
]
},
{
@@ -583,23 +583,23 @@
"call": "Drag rows to change priority. The rule at the top is checked first.",
"key": "Drag rows to change priority. The rule at the top is checked first.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:389"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:395"
]
},
{
"call": "Duplicate value",
"key": "Duplicate value",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:369",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:384",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:404"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:449",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:464",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:484"
]
},
{
"call": "Edit",
"key": "Edit",
"call": "Edit rule action",
"key": "Edit rule action",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:366"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:59"
]
},
{
@@ -608,7 +608,7 @@
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:8",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/ruleset.js:10",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1541"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1621"
]
},
{
@@ -622,7 +622,7 @@
"call": "Enable a dedicated DNS resolver when this section uses an interface",
"key": "Enable a dedicated DNS resolver when this section uses an interface",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1878"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2045"
]
},
{
@@ -636,7 +636,7 @@
"call": "Enable Mixed Proxy",
"key": "Enable Mixed Proxy",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1929"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2096"
]
},
{
@@ -665,46 +665,49 @@
"key": "Enter a complete sing-box outbound object",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:52",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1691"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1772"
]
},
{
"call": "Enter the subscription URL to fetch proxy configurations from your provider",
"key": "Enter the subscription URL to fetch proxy configurations from your provider",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1794"
]
},
{
"call": "Every 3 minutes",
"key": "Every 3 minutes",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:103",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1752"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:103"
]
},
{
"call": "Every 30 seconds",
"key": "Every 30 seconds",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:101",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1750"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:101"
]
},
{
"call": "Every 5 minutes",
"key": "Every 5 minutes",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:104",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1753"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:104"
]
},
{
"call": "Every minute",
"key": "Every minute",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:102",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1751"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:102"
]
},
{
"call": "Exact full domain",
"key": "Exact full domain",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1976"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2143"
]
},
{
@@ -732,14 +735,14 @@
"call": "Expose this section as a local HTTP+SOCKS proxy",
"key": "Expose this section as a local HTTP+SOCKS proxy",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1930"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2097"
]
},
{
"call": "External nfqws config files bypass Podkop Plus queue management and explicit validation.",
"key": "External nfqws config files bypass Podkop Plus queue management and explicit validation.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:999"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1079"
]
},
{
@@ -766,6 +769,8 @@
"key": "Fastest",
"places": [
"src/podkop/methods/custom/getDashboardSections.ts:188",
"src/podkop/methods/custom/getDashboardSections.ts:220",
"src/podkop/methods/custom/getDashboardSections.ts:257",
"src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:59"
]
},
@@ -790,6 +795,20 @@
"src/podkop/tabs/diagnostic/initController.ts:251"
]
},
{
"call": "Group subscription proxies by country",
"key": "Group subscription proxies by country",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1848"
]
},
{
"call": "Group subscription proxies by the country flag at the beginning of the outbound tag",
"key": "Group subscription proxies by the country flag at the beginning of the outbound tag",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1849"
]
},
{
"call": "How often this remote rule set should be refreshed",
"key": "How often this remote rule set should be refreshed",
@@ -801,7 +820,7 @@
"call": "How to configure the proxy connection for this section",
"key": "How to configure the proxy connection for this section",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1652"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1732"
]
},
{
@@ -817,8 +836,8 @@
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:23",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:179",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1658",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1837"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1739",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2004"
]
},
{
@@ -980,7 +999,7 @@
"call": "Invalid port number. Must be between 1 and 65535",
"key": "Invalid port number. Must be between 1 and 65535",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1957",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2124",
"src/validators/validateShadowsocksUrl.ts:85"
]
},
@@ -1164,7 +1183,7 @@
"call": "IPs",
"key": "IPs",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1969"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2136"
]
},
{
@@ -1194,7 +1213,7 @@
"call": "List must be a direct .lst URL or a local .lst path",
"key": "List must be a direct .lst URL or a local .lst path",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1504"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1584"
]
},
{
@@ -1236,42 +1255,42 @@
"call": "Match destination IPs or subnets",
"key": "Match destination IPs or subnets",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1970"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2137"
]
},
{
"call": "Match domains containing a substring",
"key": "Match domains containing a substring",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1991"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2158"
]
},
{
"call": "Match domains including all subdomains",
"key": "Match domains including all subdomains",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1963"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2130"
]
},
{
"call": "Match domains using a regular expression",
"key": "Match domains using a regular expression",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1998"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2165"
]
},
{
"call": "Match only one exact full domain name",
"key": "Match only one exact full domain name",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1977"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2144"
]
},
{
"call": "Match source IPs or subnets",
"key": "Match source IPs or subnets",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2005"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2172"
]
},
{
@@ -1279,7 +1298,7 @@
"key": "Maximum response time delta in milliseconds",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:114",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1764"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1929"
]
},
{
@@ -1293,7 +1312,7 @@
"call": "Mixed Proxy Port",
"key": "Mixed Proxy Port",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1941"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2108"
]
},
{
@@ -1308,7 +1327,7 @@
"key": "Must be a number in the range of 50 - 1000",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:136",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1786"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1952"
]
},
{
@@ -1329,14 +1348,14 @@
"call": "NFQWS Strategy",
"key": "NFQWS Strategy",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1617"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1697"
]
},
{
"call": "NFQWS strategy cannot be empty",
"key": "NFQWS strategy cannot be empty",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1129"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1209"
]
},
{
@@ -1409,6 +1428,13 @@
"src/helpers/withTimeout.ts:7"
]
},
{
"call": "Option \"%s\" contains an invalid input value.",
"key": "Option \"%s\" contains an invalid input value.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:385"
]
},
{
"call": "Optional node used to download this remote rule set",
"key": "Optional node used to download this remote rule set",
@@ -1420,7 +1446,7 @@
"call": "Optional values must be attached with '=' here; a separate token would be ignored by nfqws.",
"key": "Optional values must be attached with '=' here; a separate token would be ignored by nfqws.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1207"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1287"
]
},
{
@@ -1429,8 +1455,8 @@
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:22",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:51",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1657",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1690"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1738",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1771"
]
},
{
@@ -1498,14 +1524,14 @@
"call": "Podkop Plus manages the nfqws process lifecycle itself, so daemon mode is not allowed here.",
"key": "Podkop Plus manages the nfqws process lifecycle itself, so daemon mode is not allowed here.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1017"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1097"
]
},
{
"call": "Podkop Plus Settings",
"key": "Podkop Plus Settings",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:380"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:386"
]
},
{
@@ -1540,14 +1566,14 @@
"call": "Port cannot be empty",
"key": "Port cannot be empty",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1949"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2116"
]
},
{
"call": "Port for the local mixed proxy of this section",
"key": "Port for the local mixed proxy of this section",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1942"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2109"
]
},
{
@@ -1575,7 +1601,7 @@
"call": "Regional options cannot be used together",
"key": "Regional options cannot be used together",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2059"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2226"
]
},
{
@@ -1596,7 +1622,7 @@
"call": "Resolver for interface outbound",
"key": "Resolver for interface outbound",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1877"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2044"
]
},
{
@@ -1610,14 +1636,14 @@
"call": "Resource selection by hostname inside nfqws is not supported here; sing-box selects resources before NFQUEUE.",
"key": "Resource selection by hostname inside nfqws is not supported here; sing-box selects resources before NFQUEUE.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1002"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1082"
]
},
{
"call": "Resource selection by IP or CIDR inside nfqws is not supported here; sing-box selects resources before NFQUEUE.",
"key": "Resource selection by IP or CIDR inside nfqws is not supported here; sing-box selects resources before NFQUEUE.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1005"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1085"
]
},
{
@@ -1638,7 +1664,7 @@
"call": "Rule set must be a direct .srs / .json URL or a local .srs / .json path",
"key": "Rule set must be a direct .srs / .json URL or a local .srs / .json path",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1496"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1576"
]
},
{
@@ -1652,7 +1678,7 @@
"call": "Rule sets",
"key": "Rule sets",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2114"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2281"
]
},
{
@@ -1708,14 +1734,14 @@
"call": "Russia inside restrictions",
"key": "Russia inside restrictions",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2078"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2245"
]
},
{
"call": "Save error",
"key": "Save error",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:141"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:147"
]
},
{
@@ -1729,28 +1755,28 @@
"call": "Section",
"key": "Section",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:394"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:400"
]
},
{
"call": "Section name",
"key": "Section name",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1570"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1650"
]
},
{
"call": "Sections",
"key": "Sections",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:388"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:394"
]
},
{
"call": "Select a predefined list for routing",
"key": "Select a predefined list for routing",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2021"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2188"
]
},
{
@@ -1813,7 +1839,7 @@
"call": "Selector connections",
"key": "Selector connections",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1712"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1860"
]
},
{
@@ -1827,7 +1853,7 @@
"call": "Service list",
"key": "Service list",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2024"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2191"
]
},
{
@@ -1841,8 +1867,8 @@
"call": "Settings",
"key": "Settings",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:402",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1538"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:408",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1618"
]
},
{
@@ -1920,7 +1946,7 @@
"call": "Source IPs",
"key": "Source IPs",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2004"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2171"
]
},
{
@@ -1972,6 +1998,34 @@
"src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:59"
]
},
{
"call": "Subscription",
"key": "Subscription",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1737"
]
},
{
"call": "Subscription update interval",
"key": "Subscription update interval",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1813"
]
},
{
"call": "Subscription URL",
"key": "Subscription URL",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1793"
]
},
{
"call": "Subscription URL cannot be empty",
"key": "Subscription URL cannot be empty",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:395"
]
},
{
"call": "Successfully copied!",
"key": "Successfully copied!",
@@ -2011,7 +2065,7 @@
"call": "The desync fwmark is managed by Podkop Plus for loop prevention and must not be overridden here.",
"key": "The desync fwmark is managed by Podkop Plus for loop prevention and must not be overridden here.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1014"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1094"
]
},
{
@@ -2025,7 +2079,7 @@
"call": "The NFQUEUE number is assigned by Podkop Plus for each rule and must not be overridden here.",
"key": "The NFQUEUE number is assigned by Podkop Plus for each rule and must not be overridden here.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1011"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1091"
]
},
{
@@ -2039,28 +2093,28 @@
"call": "This field must start a working nfqws strategy; --dry-run exits immediately and is not allowed here.",
"key": "This field must start a working nfqws strategy; --dry-run exits immediately and is not allowed here.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1020"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1100"
]
},
{
"call": "This field must start a working nfqws strategy; --version exits immediately and is not allowed here.",
"key": "This field must start a working nfqws strategy; --version exits immediately and is not allowed here.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1023"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1103"
]
},
{
"call": "This flag does not accept a value.",
"key": "This flag does not accept a value.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1192"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1272"
]
},
{
"call": "This option requires a value.",
"key": "This option requires a value.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1223"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1303"
]
},
{
@@ -2124,14 +2178,14 @@
"key": "UDP over TCP",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:169",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1825"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1992"
]
},
{
"call": "Unable to validate the NFQWS strategy through the backend parser.",
"key": "Unable to validate the NFQWS strategy through the backend parser.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:934"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1014"
]
},
{
@@ -2145,7 +2199,7 @@
"call": "Unexpected standalone token. Use explicit flags such as --name or --name=value.",
"key": "Unexpected standalone token. Use explicit flags such as --name or --name=value.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1172"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1252"
]
},
{
@@ -2180,7 +2234,7 @@
"call": "Unknown NFQWS flag.",
"key": "Unknown NFQWS flag.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1183"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1263"
]
},
{
@@ -2216,14 +2270,14 @@
"call": "URLTest connections",
"key": "URLTest connections",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1730"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1878"
]
},
{
"call": "URLTest interval",
"key": "URLTest interval",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1748"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1896"
]
},
{
@@ -2244,7 +2298,7 @@
"call": "URLTest tolerance",
"key": "URLTest tolerance",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1763"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1928"
]
},
{
@@ -2259,7 +2313,7 @@
"key": "URLTest URL",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:139",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1793"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1959"
]
},
{
@@ -2273,13 +2327,14 @@
"call": "Use a network interface as the outbound for this section",
"key": "Use a network interface as the outbound for this section",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1838"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2005"
]
},
{
"call": "Use sing-box duration format like 1d, 12h or 30m",
"key": "Use sing-box duration format like 1d, 12h or 30m",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:374",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/settings.js:312"
]
},
@@ -2287,9 +2342,17 @@
"call": "Use sing-box duration format. Leave empty to disable automatic updates.",
"key": "Use sing-box duration format. Leave empty to disable automatic updates.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1814",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/settings.js:283"
]
},
{
"call": "Use sing-box duration format. Leave empty to disable periodic URLTest checks.",
"key": "Use sing-box duration format. Leave empty to disable periodic URLTest checks.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1897"
]
},
{
"call": "Valid",
"key": "Valid",
@@ -2314,9 +2377,9 @@
"call": "Validation errors:",
"key": "Validation errors:",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:376",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:396",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:400"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:456",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:476",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:480"
]
},
{
@@ -2331,7 +2394,7 @@
"call": "Visible name of this section",
"key": "Visible name of this section",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1571"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1651"
]
},
{
@@ -2339,28 +2402,28 @@
"key": "vless://, ss://, trojan://, socks4/5://, hy2/hysteria2:// links",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:32",
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1669"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1750"
]
},
{
"call": "Warning: %s cannot be used together with %s. Previous selections have been removed.",
"key": "Warning: %s cannot be used together with %s. Previous selections have been removed.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2061"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2228"
]
},
{
"call": "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection.",
"key": "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2080"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2247"
]
},
{
"call": "What Podkop Plus should do when this section matches",
"key": "What Podkop Plus should do when this section matches",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1584"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1664"
]
},
{
@@ -2389,7 +2452,7 @@
"call": "Zapret hostlist templates are not supported here because Podkop Plus does not expand them for per-rule NFQWS strategies.",
"key": "Zapret hostlist templates are not supported here because Podkop Plus does not expand them for per-rule NFQWS strategies.",
"places": [
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1008"
"../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1088"
]
},
{
+151 -116
View File
@@ -1,15 +1,15 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) 2026 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PODKOP PLUS package.
# ushan0v <142658494+ushan0v@users.noreply.github.com>, 2026.
# Podkop Plus <noreply@podkop.plus>, 2026.
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PODKOP PLUS\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-01 20:07+0300\n"
"PO-Revision-Date: 2026-05-01 20:07+0300\n"
"Last-Translator: ushan0v <142658494+ushan0v@users.noreply.github.com>\n"
"POT-Creation-Date: 2026-05-07 09:30+0300\n"
"PO-Revision-Date: 2026-05-07 09:30+0300\n"
"Last-Translator: Podkop Plus <noreply@podkop.plus>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
@@ -36,7 +36,7 @@ msgstr ""
msgid "A latency-tested group of proxy URLs for this node"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1731
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1879
msgid "A latency-tested group of proxy URLs for this section"
msgstr ""
@@ -44,12 +44,12 @@ msgstr ""
msgid "A manual group of proxy URLs for this node"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1713
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1861
msgid "A manual group of proxy URLs for this section"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1551
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1583
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1631
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1663
msgid "Action"
msgstr ""
@@ -57,15 +57,15 @@ msgstr ""
msgid "Active Connections"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:395
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:401
msgid "Add a section"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2130
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2297
msgid "Add URLs or local paths to .lst lists"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2115
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2282
msgid "Add URLs or local paths to .srs / .json lists"
msgstr ""
@@ -77,20 +77,20 @@ msgstr ""
msgid "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:142
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:148
msgid "An error occurred while saving the form:"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:170
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1826
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1993
msgid "Applicable for SOCKS and Shadowsocks links"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:836
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:916
msgid "At least one valid domain must be specified."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:847
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:927
msgid "At least one valid IP or subnet must be specified."
msgstr ""
@@ -102,7 +102,7 @@ msgstr ""
msgid "Available actions"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:938
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1018
msgid "Backend validation failed: %s"
msgstr ""
@@ -126,7 +126,7 @@ msgstr ""
msgid "Browser is using FakeIP correctly"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2020
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2187
msgid "Built-in rule sets"
msgstr ""
@@ -183,7 +183,7 @@ msgstr ""
msgid "Close"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1539
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1619
msgid "Conditions"
msgstr ""
@@ -191,20 +191,20 @@ msgstr ""
msgid "Config File Path"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:381
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:387
msgid "Configuration for Podkop Plus service"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1668
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1749
msgid "Connection"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1651
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1731
msgid "Connection Type"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:19
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1654
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1734
msgid "Connection URL"
msgstr ""
@@ -216,7 +216,7 @@ msgstr ""
msgid "Currently unavailable"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:426
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:432
msgid "Dashboard"
msgstr ""
@@ -236,7 +236,7 @@ msgstr ""
msgid "DHCP has DNS server"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:414
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:420
msgid "Diagnostics"
msgstr ""
@@ -257,7 +257,7 @@ msgstr ""
msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:149
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:155
msgid "Dismiss"
msgstr ""
@@ -266,18 +266,18 @@ msgid "DNS on router"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:227
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1891
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2058
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/settings.js:21
msgid "DNS over HTTPS (DoH)"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:228
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1892
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2059
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/settings.js:22
msgid "DNS over TLS (DoT)"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1889
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2056
msgid "DNS protocol"
msgstr ""
@@ -293,7 +293,7 @@ msgstr ""
msgid "DNS Rewrite TTL"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1907
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2074
msgid "DNS server"
msgstr ""
@@ -310,19 +310,19 @@ msgstr ""
msgid "Do not panic, everything can be fixed, just..."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2129
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2296
msgid "Domain and IP Lists"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1990
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2157
msgid "Domain keyword"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1997
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2164
msgid "Domain regex"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1962
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2129
msgid "Domains"
msgstr ""
@@ -359,24 +359,23 @@ msgstr ""
msgid "Downloading all lists via specific Proxy/VPN"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:389
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:395
msgid "Drag rows to change priority. The rule at the top is checked first."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:369
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:384
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:404
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:449
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:464
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:484
msgid "Duplicate value"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:366
msgctxt "Rule row action"
msgid "Edit"
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:59
msgid "Edit rule action"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:8
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/ruleset.js:10
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1541
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1621
msgid "Enable"
msgstr ""
@@ -384,7 +383,7 @@ msgstr ""
msgid "Enable a dedicated DNS resolver when this node uses an interface"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1878
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2045
msgid "Enable a dedicated DNS resolver when this section uses an interface"
msgstr ""
@@ -392,7 +391,7 @@ msgstr ""
msgid "Enable autostart"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1929
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2096
msgid "Enable Mixed Proxy"
msgstr ""
@@ -409,31 +408,31 @@ msgid "Enable YACD WAN Access"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:52
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1691
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1772
msgid "Enter a complete sing-box outbound object"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1794
msgid "Enter the subscription URL to fetch proxy configurations from your provider"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:103
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1752
msgid "Every 3 minutes"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:101
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1750
msgid "Every 30 seconds"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:104
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1753
msgid "Every 5 minutes"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:102
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1751
msgid "Every minute"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1976
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2143
msgid "Exact full domain"
msgstr ""
@@ -449,11 +448,11 @@ msgstr ""
msgid "Expecting: %s"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1930
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2097
msgid "Expose this section as a local HTTP+SOCKS proxy"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:999
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1079
msgid "External nfqws config files bypass Podkop Plus queue management and explicit validation."
msgstr ""
@@ -471,6 +470,8 @@ msgid "Failed to execute!"
msgstr ""
#: src/podkop/methods/custom/getDashboardSections.ts:188
#: src/podkop/methods/custom/getDashboardSections.ts:220
#: src/podkop/methods/custom/getDashboardSections.ts:257
#: src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:59
msgid "Fastest"
msgstr ""
@@ -487,11 +488,19 @@ msgstr ""
msgid "Global check"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1848
msgid "Group subscription proxies by country"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1849
msgid "Group subscription proxies by the country flag at the beginning of the outbound tag"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/ruleset.js:87
msgid "How often this remote rule set should be refreshed"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1652
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1732
msgid "How to configure the proxy connection for this section"
msgstr ""
@@ -501,8 +510,8 @@ msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:23
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:179
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1658
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1837
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1739
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2004
msgid "Interface"
msgstr ""
@@ -595,7 +604,7 @@ msgstr ""
msgid "Invalid path format. Path must start with \"/\" and contain valid characters"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1957
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2124
#: src/validators/validateShadowsocksUrl.ts:85
msgid "Invalid port number. Must be between 1 and 65535"
msgstr ""
@@ -701,7 +710,7 @@ msgstr ""
msgid "IP address 0.0.0.0 is not allowed"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1969
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2136
msgid "IPs"
msgstr ""
@@ -719,7 +728,7 @@ msgstr ""
msgid "Latest"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1504
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1584
msgid "List must be a direct .lst URL or a local .lst path"
msgstr ""
@@ -743,32 +752,32 @@ msgstr ""
msgid "Main DNS"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1970
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2137
msgid "Match destination IPs or subnets"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1991
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2158
msgid "Match domains containing a substring"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1963
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2130
msgid "Match domains including all subdomains"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1998
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2165
msgid "Match domains using a regular expression"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1977
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2144
msgid "Match only one exact full domain name"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2005
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2172
msgid "Match source IPs or subnets"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:114
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1764
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1929
msgid "Maximum response time delta in milliseconds"
msgstr ""
@@ -776,7 +785,7 @@ msgstr ""
msgid "Memory Usage"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1941
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2108
msgid "Mixed Proxy Port"
msgstr ""
@@ -785,7 +794,7 @@ msgid "Monitored Interfaces"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:136
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1786
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1952
msgid "Must be a number in the range of 50 - 1000"
msgstr ""
@@ -797,11 +806,11 @@ msgstr ""
msgid "NFQUEUE range overlaps with another rule"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1617
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1697
msgid "NFQWS Strategy"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1129
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1209
msgid "NFQWS strategy cannot be empty"
msgstr ""
@@ -848,18 +857,22 @@ msgstr ""
msgid "Operation timed out"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:385
msgid "Option \"%s\" contains an invalid input value."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/ruleset.js:64
msgid "Optional node used to download this remote rule set"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1207
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1287
msgid "Optional values must be attached with '=' here; a separate token would be ignored by nfqws."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:22
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:51
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1657
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1690
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1738
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1771
msgid "Outbound JSON"
msgstr ""
@@ -900,11 +913,11 @@ msgstr ""
msgid "Pending"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1017
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1097
msgid "Podkop Plus manages the nfqws process lifecycle itself, so daemon mode is not allowed here."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:380
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:386
msgid "Podkop Plus Settings"
msgstr ""
@@ -924,11 +937,11 @@ msgstr ""
msgid "Podkop-managed nfqws runtime is ready"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1949
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2116
msgid "Port cannot be empty"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1942
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2109
msgid "Port for the local mixed proxy of this section"
msgstr ""
@@ -944,7 +957,7 @@ msgstr ""
msgid "Proxy traffic is routed via FakeIP"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2059
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2226
msgid "Regional options cannot be used together"
msgstr ""
@@ -956,7 +969,7 @@ msgstr ""
msgid "Remote URL to a sing-box rule set"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1877
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2044
msgid "Resolver for interface outbound"
msgstr ""
@@ -964,11 +977,11 @@ msgstr ""
msgid "Resolver For Interface Outbound"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1002
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1082
msgid "Resource selection by hostname inside nfqws is not supported here; sing-box selects resources before NFQUEUE."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1005
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1085
msgid "Resource selection by IP or CIDR inside nfqws is not supported here; sing-box selects resources before NFQUEUE."
msgstr ""
@@ -980,7 +993,7 @@ msgstr ""
msgid "Routing Excluded IPs"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1496
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1576
msgid "Rule set must be a direct .srs / .json URL or a local .srs / .json path"
msgstr ""
@@ -988,7 +1001,7 @@ msgstr ""
msgid "Rule set URL"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2114
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2281
msgid "Rule sets"
msgstr ""
@@ -1020,11 +1033,11 @@ msgstr ""
msgid "Run Diagnostic"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2078
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2245
msgid "Russia inside restrictions"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:141
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:147
msgid "Save error"
msgstr ""
@@ -1032,19 +1045,19 @@ msgstr ""
msgid "Secret key for authenticating remote access to YACD when WAN access is enabled."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:394
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:400
msgid "Section"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1570
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1650
msgid "Section name"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:388
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:394
msgid "Sections"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2021
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2188
msgid "Select a predefined list for routing"
msgstr ""
@@ -1080,7 +1093,7 @@ msgstr ""
msgid "Select the WAN interfaces to be monitored"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1712
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1860
msgid "Selector connections"
msgstr ""
@@ -1088,7 +1101,7 @@ msgstr ""
msgid "Selector Links"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2024
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2191
msgid "Service list"
msgstr ""
@@ -1096,8 +1109,8 @@ msgstr ""
msgid "Services info"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:402
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1538
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:408
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1618
msgid "Settings"
msgstr ""
@@ -1142,7 +1155,7 @@ msgstr ""
msgid "Source file"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2004
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2171
msgid "Source IPs"
msgstr ""
@@ -1174,6 +1187,22 @@ msgstr ""
msgid "Stop Podkop Plus"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1737
msgid "Subscription"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1813
msgid "Subscription update interval"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1793
msgid "Subscription URL"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:395
msgid "Subscription URL cannot be empty"
msgstr ""
#: src/helpers/copyToClipboard.ts:10
msgid "Successfully copied!"
msgstr ""
@@ -1194,7 +1223,7 @@ msgstr ""
msgid "Test latency"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1014
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1094
msgid "The desync fwmark is managed by Podkop Plus for loop prevention and must not be overridden here."
msgstr ""
@@ -1202,7 +1231,7 @@ msgstr ""
msgid "The DNS server used to look up the IP address of an upstream DNS server"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1011
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1091
msgid "The NFQUEUE number is assigned by Podkop Plus for each rule and must not be overridden here."
msgstr ""
@@ -1210,19 +1239,19 @@ msgstr ""
msgid "There are rules using Zapret"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1020
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1100
msgid "This field must start a working nfqws strategy; --dry-run exits immediately and is not allowed here."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1023
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1103
msgid "This field must start a working nfqws strategy; --version exits immediately and is not allowed here."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1192
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1272
msgid "This flag does not accept a value."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1223
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1303
msgid "This option requires a value."
msgstr ""
@@ -1259,11 +1288,11 @@ msgid "UDP (Unprotected DNS)"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:169
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1825
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1992
msgid "UDP over TCP"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:934
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1014
msgid "Unable to validate the NFQWS strategy through the backend parser."
msgstr ""
@@ -1271,7 +1300,7 @@ msgstr ""
msgid "Unexpected Podkop-managed nfqws runtime is running"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1172
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1252
msgid "Unexpected standalone token. Use explicit flags such as --name or --name=value."
msgstr ""
@@ -1294,7 +1323,7 @@ msgstr ""
msgid "Unknown error"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1183
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1263
msgid "Unknown NFQWS flag."
msgstr ""
@@ -1315,11 +1344,11 @@ msgstr ""
msgid "URL must use one of the following protocols:"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1730
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1878
msgid "URLTest connections"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1748
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1896
msgid "URLTest interval"
msgstr ""
@@ -1331,7 +1360,7 @@ msgstr ""
msgid "URLTest Links"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1763
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1928
msgid "URLTest tolerance"
msgstr ""
@@ -1340,7 +1369,7 @@ msgid "URLTest Tolerance"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:139
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1793
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1959
msgid "URLTest URL"
msgstr ""
@@ -1348,18 +1377,24 @@ msgstr ""
msgid "Use a network interface as the outbound for this node"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1838
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2005
msgid "Use a network interface as the outbound for this section"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:374
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/settings.js:312
msgid "Use sing-box duration format like 1d, 12h or 30m"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1814
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/settings.js:283
msgid "Use sing-box duration format. Leave empty to disable automatic updates."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1897
msgid "Use sing-box duration format. Leave empty to disable periodic URLTest checks."
msgstr ""
#: src/validators/validateDns.ts:14
#: src/validators/validateDns.ts:18
#: src/validators/validateDomain.ts:13
@@ -1377,9 +1412,9 @@ msgstr ""
msgid "Valid"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:376
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:396
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:400
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:456
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:476
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:480
msgid "Validation errors:"
msgstr ""
@@ -1388,24 +1423,24 @@ msgstr ""
msgid "View logs"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1571
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1651
msgid "Visible name of this section"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:32
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1669
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1750
msgid "vless://, ss://, trojan://, socks4/5://, hy2/hysteria2:// links"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2061
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2228
msgid "Warning: %s cannot be used together with %s. Previous selections have been removed."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2080
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2247
msgid "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1584
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1664
msgid "What Podkop Plus should do when this section matches"
msgstr ""
@@ -1422,7 +1457,7 @@ msgstr ""
msgid "Zapret (provider not available)"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1008
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1088
msgid "Zapret hostlist templates are not supported here because Podkop Plus does not expand them for per-rule NFQWS strategies."
msgstr ""
+32 -6
View File
@@ -1,15 +1,15 @@
# RU translations for PODKOP PLUS package.
# Copyright (C) 2026 THE PODKOP PLUS COPYRIGHT HOLDER
# This file is distributed under the same license as the PODKOP PLUS package.
# ushan0v, 2026.
# Automatically generated, 2026.
#
msgid ""
msgstr ""
"Project-Id-Version: PODKOP PLUS\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-01 23:07+0300\n"
"PO-Revision-Date: 2026-05-01 23:07+0300\n"
"Last-Translator: ushan0v\n"
"POT-Creation-Date: 2026-05-07 12:30+0300\n"
"PO-Revision-Date: 2026-05-07 12:30+0300\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
@@ -260,8 +260,7 @@ msgstr "Перетаскивайте строки, чтобы изменить
msgid "Duplicate value"
msgstr "Дублирующееся значение"
msgctxt "Rule row action"
msgid "Edit"
msgid "Edit rule action"
msgstr "Изменить"
msgid "Enable"
@@ -291,6 +290,9 @@ msgstr "Включить доступ YACD WAN"
msgid "Enter a complete sing-box outbound object"
msgstr "Введите полный объект outbound sing-box"
msgid "Enter the subscription URL to fetch proxy configurations from your provider"
msgstr "Введите URL подписки для получения конфигураций прокси от провайдера"
msgid "Every 3 minutes"
msgstr "Каждые 3 минуты"
@@ -339,6 +341,12 @@ msgstr "Получить глобальную проверку"
msgid "Global check"
msgstr "Глобальная проверка"
msgid "Group subscription proxies by country"
msgstr "Группировать прокси подписки по странам"
msgid "Group subscription proxies by the country flag at the beginning of the outbound tag"
msgstr "Группирует прокси подписки по флагу страны в начале тега outbound"
msgid "How often this remote rule set should be refreshed"
msgstr "Как часто обновлять этот удалённый набор правил"
@@ -597,6 +605,9 @@ msgstr "Открыть страницу проекта"
msgid "Operation timed out"
msgstr "Время ожидания истекло"
msgid "Option \"%s\" contains an invalid input value."
msgstr ""
msgid "Optional node used to download this remote rule set"
msgstr "Необязательный узел, через который скачивается этот удалённый набор правил"
@@ -834,6 +845,18 @@ msgstr "Запустить Podkop Plus"
msgid "Stop Podkop Plus"
msgstr "Остановить Podkop Plus"
msgid "Subscription"
msgstr "Подписка"
msgid "Subscription update interval"
msgstr "Интервал обновления подписки"
msgid "Subscription URL"
msgstr "URL подписки"
msgid "Subscription URL cannot be empty"
msgstr "URL подписки не может быть пустым"
msgid "Successfully copied!"
msgstr "Успешно скопировано!"
@@ -966,6 +989,9 @@ msgstr "Используйте формат длительности sing-box,
msgid "Use sing-box duration format. Leave empty to disable automatic updates."
msgstr "Используйте формат длительности sing-box. Оставьте поле пустым, чтобы отключить автоматическое обновление."
msgid "Use sing-box duration format. Leave empty to disable periodic URLTest checks."
msgstr "Используйте формат длительности sing-box. Оставьте поле пустым, чтобы отключить периодические проверки URLTest."
msgid "Valid"
msgstr "Валидно"
@@ -195,6 +195,83 @@ export async function getDashboardSections(): Promise<IGetDashboardSectionsRespo
};
}
if (proxyConfigType === 'subscription') {
const selector = proxies.find(
(proxy) => proxy.code === `${section['.name']}-out`,
);
const fallbackUrltest = proxies.find(
(proxy) => proxy.code === `${section['.name']}-urltest-out`,
);
const selectorOutbounds = (selector?.value?.all ?? []).flatMap(
(code) => {
const item = proxies.find((proxy) => proxy.code === code);
if (!item) {
return [];
}
const isDefaultFastest =
item.code === `${section['.name']}-urltest-out`;
return [
{
code: item.code,
displayName: isDefaultFastest
? _('Fastest')
: item?.value?.name || '',
latency: item?.value?.history?.[0]?.delay || 0,
type: item?.value?.type || '',
selected: selector?.value?.now === item.code,
},
];
},
);
const outbounds = [
...selectorOutbounds.filter(
(item) => item.type?.toLowerCase() === 'urltest',
),
...selectorOutbounds.filter(
(item) => item.type?.toLowerCase() !== 'urltest',
),
];
if (outbounds.length === 0 && fallbackUrltest) {
const fallbackOutbounds = (fallbackUrltest?.value?.all ?? [])
.map((code) => proxies.find((item) => item.code === code))
.map((item) => ({
code: item?.code || '',
displayName: item?.value?.name || '',
latency: item?.value?.history?.[0]?.delay || 0,
type: item?.value?.type || '',
selected: selector?.value?.now === item?.code,
}));
return {
withTagSelect: true,
code: selector?.code || section['.name'],
displayName,
outbounds: [
{
code: fallbackUrltest?.code || '',
displayName: _('Fastest'),
latency: fallbackUrltest?.value?.history?.[0]?.delay || 0,
type: fallbackUrltest?.value?.type || '',
selected: selector?.value?.now === fallbackUrltest?.code,
},
...fallbackOutbounds,
],
};
}
return {
withTagSelect: true,
code: selector?.code || section['.name'],
displayName,
outbounds,
};
}
if (proxyConfigType === 'interface') {
const outbound = proxies.find(
(proxy) => proxy.code === `${section['.name']}-out`,
@@ -94,4 +94,6 @@ export const PodkopShellMethods = {
callBaseMethod<Podkop.GetSystemInfo>(
Podkop.AvailableMethods.GET_SYSTEM_INFO,
),
subscriptionUpdate: async () =>
callBaseMethod<unknown>(Podkop.AvailableMethods.SUBSCRIPTION_UPDATE),
};
+8 -1
View File
@@ -69,6 +69,7 @@ export namespace Podkop {
CHECK_LOGS = 'check_logs',
CHECK_SING_BOX_LOGS = 'check_sing_box_logs',
GET_SYSTEM_INFO = 'get_system_info',
SUBSCRIPTION_UPDATE = 'subscription_update',
}
export enum AvailableClashAPIMethods {
@@ -100,7 +101,8 @@ export namespace Podkop {
| 'selector'
| 'url'
| 'outbound'
| 'interface';
| 'interface'
| 'subscription';
export interface ConfigSection {
'.name': string;
@@ -117,6 +119,11 @@ export namespace Podkop {
nfqws_opt?: string;
selector_proxy_links?: string[];
urltest_proxy_links?: string[];
subscription_url?: string;
subscription_update_interval?: string;
subscription_update_interval_disabled?: '0' | '1';
subscription_group_by_countries?: '0' | '1';
urltest_check_interval_disabled?: '0' | '1';
outbound_json?: string;
interface?: string;
yacd_secret_key?: string;
@@ -8,6 +8,9 @@ const validUrls = [
['With query', 'https://example.com/?q=test'],
['With port', 'http://example.com:8080'],
['With subdomain', 'https://sub.example.com'],
['IPv4 host', 'http://127.0.0.1/podkop-subscription-test.json'],
['IPv4 host with port', 'http://192.168.1.1:8080/sub'],
['localhost host', 'http://localhost/sub'],
];
const invalidUrls = [
@@ -17,6 +20,7 @@ const invalidUrls = [
['Unsupported protocol (ws)', 'ws://example.com'],
['Empty string', ''],
['Without tld', 'https://google'],
['Invalid IPv4 host', 'http://999.0.0.1/sub'],
];
describe('validateUrl', () => {
+8 -1
View File
@@ -21,7 +21,14 @@ export function validateUrl(
const regex = new RegExp(
`^(?:${protocols.map((p) => p.replace(':', '')).join('|')})://` +
`(?:[A-Za-z0-9-]+\\.)+[A-Za-z]{2,}(?::\\d+)?(?:/[^\\s]*)?$`,
`(?:` +
`(?:[A-Za-z0-9-]+\\.)+[A-Za-z]{2,}` +
`|` +
`(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}` +
`|` +
`localhost` +
`)` +
`(?::\\d+)?(?:/[^\\s]*)?$`,
);
if (regex.test(url)) {
@@ -68,7 +68,7 @@ function validateUrl(url, protocols = ["http:", "https:"]) {
message: _("URL must use one of the following protocols:") + " " + protocols.join(", ")
};
const regex = new RegExp(
`^(?:${protocols.map((p) => p.replace(":", "")).join("|")})://(?:[A-Za-z0-9-]+\\.)+[A-Za-z]{2,}(?::\\d+)?(?:/[^\\s]*)?$`
`^(?:${protocols.map((p) => p.replace(":", "")).join("|")})://(?:(?:[A-Za-z0-9-]+\\.)+[A-Za-z]{2,}|(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}|localhost)(?::\\d+)?(?:/[^\\s]*)?$`
);
if (regex.test(url)) {
return { valid: true, message: _("Valid") };
@@ -745,6 +745,7 @@ var Podkop;
AvailableMethods2["CHECK_LOGS"] = "check_logs";
AvailableMethods2["CHECK_SING_BOX_LOGS"] = "check_sing_box_logs";
AvailableMethods2["GET_SYSTEM_INFO"] = "get_system_info";
AvailableMethods2["SUBSCRIPTION_UPDATE"] = "subscription_update";
})(AvailableMethods = Podkop2.AvailableMethods || (Podkop2.AvailableMethods = {}));
let AvailableClashAPIMethods;
((AvailableClashAPIMethods2) => {
@@ -826,7 +827,8 @@ var PodkopShellMethods = {
checkSingBoxLogs: async () => callBaseMethod(Podkop.AvailableMethods.CHECK_SING_BOX_LOGS),
getSystemInfo: async () => callBaseMethod(
Podkop.AvailableMethods.GET_SYSTEM_INFO
)
),
subscriptionUpdate: async () => callBaseMethod(Podkop.AvailableMethods.SUBSCRIPTION_UPDATE)
};
// src/podkop/methods/custom/getDashboardSections.ts
@@ -979,6 +981,70 @@ async function getDashboardSections() {
]
};
}
if (proxyConfigType === "subscription") {
const selector = proxies.find(
(proxy) => proxy.code === `${section[".name"]}-out`
);
const fallbackUrltest = proxies.find(
(proxy) => proxy.code === `${section[".name"]}-urltest-out`
);
const selectorOutbounds = (selector?.value?.all ?? []).flatMap(
(code) => {
const item = proxies.find((proxy) => proxy.code === code);
if (!item) {
return [];
}
const isDefaultFastest = item.code === `${section[".name"]}-urltest-out`;
return [
{
code: item.code,
displayName: isDefaultFastest ? _("Fastest") : item?.value?.name || "",
latency: item?.value?.history?.[0]?.delay || 0,
type: item?.value?.type || "",
selected: selector?.value?.now === item.code
}
];
}
);
const outbounds = [
...selectorOutbounds.filter(
(item) => item.type?.toLowerCase() === "urltest"
),
...selectorOutbounds.filter(
(item) => item.type?.toLowerCase() !== "urltest"
)
];
if (outbounds.length === 0 && fallbackUrltest) {
const fallbackOutbounds = (fallbackUrltest?.value?.all ?? []).map((code) => proxies.find((item) => item.code === code)).map((item) => ({
code: item?.code || "",
displayName: item?.value?.name || "",
latency: item?.value?.history?.[0]?.delay || 0,
type: item?.value?.type || "",
selected: selector?.value?.now === item?.code
}));
return {
withTagSelect: true,
code: selector?.code || section[".name"],
displayName,
outbounds: [
{
code: fallbackUrltest?.code || "",
displayName: _("Fastest"),
latency: fallbackUrltest?.value?.history?.[0]?.delay || 0,
type: fallbackUrltest?.value?.type || "",
selected: selector?.value?.now === fallbackUrltest?.code
},
...fallbackOutbounds
]
};
}
return {
withTagSelect: true,
code: selector?.code || section[".name"],
displayName,
outbounds
};
}
if (proxyConfigType === "interface") {
const outbound = proxies.find(
(proxy) => proxy.code === `${section[".name"]}-out`
@@ -55,6 +55,12 @@ function renderSectionAdd(sectionRef, extra_class) {
return el;
}
function getRuleEditButtonText() {
const label = _("Edit rule action");
return label === "Edit rule action" ? "Edit" : label;
}
function repaintRuleRowColors() {
const container = document.getElementById(`cbi-${CBI_PREFIX}-rule`);
if (!container) {
@@ -363,7 +369,7 @@ function configureGridSection(sectionRef, type, title, addTitle) {
return form.TableSection.prototype.renderRowActions.call(
this,
section_id,
_("Edit", "Rule row action"),
getRuleEditButtonText(),
);
};
@@ -325,6 +325,86 @@ function validateKeyword(_section_id, value) {
return true;
}
function isSingBoxDuration(value) {
return /^([0-9]+(?:\.[0-9]+)?(?:ns|us|ms|s|m|h|d))+$/.test(value);
}
function readDurationOptionWithDefault(section_id, key, defaultValue) {
if (uci.get(UCI_PACKAGE, section_id, `${key}_disabled`) === "1") {
return "";
}
const rawValue = uci.get(UCI_PACKAGE, section_id, key);
if (rawValue == null) {
return defaultValue;
}
return `${rawValue}`;
}
function writeOptionalDurationOption(section_id, key, value) {
const normalized = value ? `${value}`.trim() : "";
const disabledKey = `${key}_disabled`;
if (normalized.length) {
uci.set(UCI_PACKAGE, section_id, key, normalized);
uci.unset(UCI_PACKAGE, section_id, disabledKey);
} else {
uci.unset(UCI_PACKAGE, section_id, key);
uci.set(UCI_PACKAGE, section_id, disabledKey, "1");
}
}
function removeOptionalDurationOption(section_id, key) {
writeOptionalDurationOption(section_id, key, "");
}
function validateOptionalSingBoxDuration(value) {
const normalized = value ? `${value}`.trim() : "";
if (!normalized.length) {
return true;
}
if (isSingBoxDuration(normalized)) {
return true;
}
return _("Use sing-box duration format like 1d, 12h or 30m");
}
function parseRequiredValueOnSave(section_id) {
const active = this.isActive(section_id);
if (active && !this.isValid(section_id)) {
const title = this.stripTags(this.title).trim();
const error = this.getValidationError(section_id);
return Promise.reject(
new TypeError(
`${_("Option \"%s\" contains an invalid input value.").format(title || this.option)} ${error}`,
),
);
}
if (active) {
const formValue = this.formvalue(section_id);
const normalized = formValue ? `${formValue}`.trim() : "";
if (!normalized.length) {
return Promise.reject(new TypeError(_("Subscription URL cannot be empty")));
}
return Promise.resolve(this.write(section_id, normalized));
}
if (!this.retain) {
return Promise.resolve(this.remove(section_id));
}
return Promise.resolve();
}
function getDuplicateTextListErrors(values, normalizeValue, duplicateMessage) {
const seen = new Set();
const duplicates = [];
@@ -472,12 +552,35 @@ function applyTextareaInputAttributes(textarea) {
textarea.style.resize = "vertical";
textarea.style.maxWidth = "100%";
const getRowsMinHeight = () => {
const rows = Number.parseInt(textarea.getAttribute("rows") || "0", 10);
if (!rows || typeof window === "undefined") {
return 0;
}
const style = window.getComputedStyle(textarea);
const fontSize = Number.parseFloat(style.fontSize) || 16;
const lineHeight =
Number.parseFloat(style.lineHeight) || Math.ceil(fontSize * 1.2);
const verticalPadding =
(Number.parseFloat(style.paddingTop) || 0) +
(Number.parseFloat(style.paddingBottom) || 0);
const verticalBorder =
(Number.parseFloat(style.borderTopWidth) || 0) +
(Number.parseFloat(style.borderBottomWidth) || 0);
return Math.ceil(rows * lineHeight + verticalPadding + verticalBorder);
};
const applyMinHeight = () => {
const storedMinHeight = Number.parseFloat(
textarea.getAttribute("data-pdk-default-min-height") || "0",
);
const nextMinHeight =
storedMinHeight > 0 ? storedMinHeight : textarea.offsetHeight;
const nextMinHeight = Math.max(
storedMinHeight,
textarea.offsetHeight,
getRowsMinHeight(),
);
if (nextMinHeight > 0) {
if (storedMinHeight <= 0) {
@@ -500,9 +603,14 @@ function applyTextareaInputAttributes(textarea) {
typeof window.requestAnimationFrame === "function"
) {
window.requestAnimationFrame(() => {
applyMinHeight();
if (!applyMinHeight()) {
window.setTimeout(applyMinHeight, 0);
}
});
}
textarea.addEventListener("focus", applyMinHeight);
textarea.addEventListener("pointerdown", applyMinHeight);
}
function syncAnnotatedTextareaOverlay(textarea, wrapper, overlay) {
@@ -1654,6 +1762,7 @@ function createSectionContent(section) {
o.value("url", _("Connection URL"));
o.value("selector", "Selector");
o.value("urltest", "URLTest");
o.value("subscription", _("Subscription"));
o.value("outbound", _("Outbound JSON"));
o.value("interface", _("Interface"));
o.default = "url";
@@ -1705,6 +1814,73 @@ function createSectionContent(section) {
};
configureTextareaOption(o);
o = section.taboption(
"settings",
form.Value,
"subscription_url",
_("Subscription URL"),
_("Enter the subscription URL to fetch proxy configurations from your provider"),
);
o.depends({ action: "proxy", proxy_config_type: "subscription" });
o.rmempty = true;
o.modalonly = true;
o.parse = parseRequiredValueOnSave;
o.validate = function (_section_id, value) {
if (!value || value.length === 0) {
return true;
}
const validation = main.validateUrl(value);
return validation.valid ? true : validation.message;
};
o = section.taboption(
"settings",
form.Value,
"subscription_update_interval",
_("Subscription update interval"),
_("Use sing-box duration format. Leave empty to disable automatic updates."),
);
o.default = "1h";
o.placeholder = "1h";
o.rmempty = true;
o.forcewrite = true;
o.retain = true;
o.depends({ action: "proxy", proxy_config_type: "subscription" });
o.modalonly = true;
o.cfgvalue = function (section_id) {
return readDurationOptionWithDefault(
section_id,
"subscription_update_interval",
"1h",
);
};
o.write = function (section_id, value) {
writeOptionalDurationOption(
section_id,
"subscription_update_interval",
value,
);
};
o.remove = function (section_id) {
removeOptionalDurationOption(section_id, "subscription_update_interval");
};
o.validate = function (_section_id, value) {
return validateOptionalSingBoxDuration(value);
};
o = section.taboption(
"settings",
form.Flag,
"subscription_group_by_countries",
_("Group subscription proxies by country"),
_("Group subscription proxies by the country flag at the beginning of the outbound tag"),
);
o.default = "0";
o.rmempty = false;
o.depends({ action: "proxy", proxy_config_type: "subscription" });
o.modalonly = true;
o = section.taboption(
"settings",
form.DynamicList,
@@ -1743,18 +1919,35 @@ function createSectionContent(section) {
o = section.taboption(
"settings",
form.ListValue,
form.Value,
"urltest_check_interval",
_("URLTest interval"),
_("Use sing-box duration format. Leave empty to disable periodic URLTest checks."),
);
o.value("30s", _("Every 30 seconds"));
o.value("1m", _("Every minute"));
o.value("3m", _("Every 3 minutes"));
o.value("5m", _("Every 5 minutes"));
o.default = "3m";
o.rmempty = false;
o.placeholder = "3m";
o.rmempty = true;
o.forcewrite = true;
o.retain = true;
o.depends({ action: "proxy", proxy_config_type: "urltest" });
o.depends({ action: "proxy", proxy_config_type: "subscription" });
o.modalonly = true;
o.cfgvalue = function (section_id) {
return readDurationOptionWithDefault(
section_id,
"urltest_check_interval",
"3m",
);
};
o.write = function (section_id, value) {
writeOptionalDurationOption(section_id, "urltest_check_interval", value);
};
o.remove = function (section_id) {
removeOptionalDurationOption(section_id, "urltest_check_interval");
};
o.validate = function (_section_id, value) {
return validateOptionalSingBoxDuration(value);
};
o = section.taboption(
"settings",
@@ -1766,6 +1959,7 @@ function createSectionContent(section) {
o.default = "50";
o.rmempty = false;
o.depends({ action: "proxy", proxy_config_type: "urltest" });
o.depends({ action: "proxy", proxy_config_type: "subscription" });
o.modalonly = true;
o.validate = function (_section_id, value) {
if (!value || value.length === 0) {
@@ -1808,6 +2002,7 @@ function createSectionContent(section) {
o.default = "https://www.gstatic.com/generate_204";
o.rmempty = false;
o.depends({ action: "proxy", proxy_config_type: "urltest" });
o.depends({ action: "proxy", proxy_config_type: "subscription" });
o.modalonly = true;
o.validate = function (_section_id, value) {
if (!value || value.length === 0) {
+32 -6
View File
@@ -1,15 +1,15 @@
# RU translations for PODKOP PLUS package.
# Copyright (C) 2026 THE PODKOP PLUS COPYRIGHT HOLDER
# This file is distributed under the same license as the PODKOP PLUS package.
# ushan0v, 2026.
# Automatically generated, 2026.
#
msgid ""
msgstr ""
"Project-Id-Version: PODKOP PLUS\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-01 23:07+0300\n"
"PO-Revision-Date: 2026-05-01 23:07+0300\n"
"Last-Translator: ushan0v\n"
"POT-Creation-Date: 2026-05-07 12:30+0300\n"
"PO-Revision-Date: 2026-05-07 12:30+0300\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
@@ -260,8 +260,7 @@ msgstr "Перетаскивайте строки, чтобы изменить
msgid "Duplicate value"
msgstr "Дублирующееся значение"
msgctxt "Rule row action"
msgid "Edit"
msgid "Edit rule action"
msgstr "Изменить"
msgid "Enable"
@@ -291,6 +290,9 @@ msgstr "Включить доступ YACD WAN"
msgid "Enter a complete sing-box outbound object"
msgstr "Введите полный объект outbound sing-box"
msgid "Enter the subscription URL to fetch proxy configurations from your provider"
msgstr "Введите URL подписки для получения конфигураций прокси от провайдера"
msgid "Every 3 minutes"
msgstr "Каждые 3 минуты"
@@ -339,6 +341,12 @@ msgstr "Получить глобальную проверку"
msgid "Global check"
msgstr "Глобальная проверка"
msgid "Group subscription proxies by country"
msgstr "Группировать прокси подписки по странам"
msgid "Group subscription proxies by the country flag at the beginning of the outbound tag"
msgstr "Группирует прокси подписки по флагу страны в начале тега outbound"
msgid "How often this remote rule set should be refreshed"
msgstr "Как часто обновлять этот удалённый набор правил"
@@ -597,6 +605,9 @@ msgstr "Открыть страницу проекта"
msgid "Operation timed out"
msgstr "Время ожидания истекло"
msgid "Option \"%s\" contains an invalid input value."
msgstr ""
msgid "Optional node used to download this remote rule set"
msgstr "Необязательный узел, через который скачивается этот удалённый набор правил"
@@ -834,6 +845,18 @@ msgstr "Запустить Podkop Plus"
msgid "Stop Podkop Plus"
msgstr "Остановить Podkop Plus"
msgid "Subscription"
msgstr "Подписка"
msgid "Subscription update interval"
msgstr "Интервал обновления подписки"
msgid "Subscription URL"
msgstr "URL подписки"
msgid "Subscription URL cannot be empty"
msgstr "URL подписки не может быть пустым"
msgid "Successfully copied!"
msgstr "Успешно скопировано!"
@@ -966,6 +989,9 @@ msgstr "Используйте формат длительности sing-box,
msgid "Use sing-box duration format. Leave empty to disable automatic updates."
msgstr "Используйте формат длительности sing-box. Оставьте поле пустым, чтобы отключить автоматическое обновление."
msgid "Use sing-box duration format. Leave empty to disable periodic URLTest checks."
msgstr "Используйте формат длительности sing-box. Оставьте поле пустым, чтобы отключить периодические проверки URLTest."
msgid "Valid"
msgstr "Валидно"
+151 -116
View File
@@ -1,15 +1,15 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) 2026 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PODKOP PLUS package.
# ushan0v <142658494+ushan0v@users.noreply.github.com>, 2026.
# Podkop Plus <noreply@podkop.plus>, 2026.
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PODKOP PLUS\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-01 20:07+0300\n"
"PO-Revision-Date: 2026-05-01 20:07+0300\n"
"Last-Translator: ushan0v <142658494+ushan0v@users.noreply.github.com>\n"
"POT-Creation-Date: 2026-05-07 09:30+0300\n"
"PO-Revision-Date: 2026-05-07 09:30+0300\n"
"Last-Translator: Podkop Plus <noreply@podkop.plus>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
@@ -36,7 +36,7 @@ msgstr ""
msgid "A latency-tested group of proxy URLs for this node"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1731
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1879
msgid "A latency-tested group of proxy URLs for this section"
msgstr ""
@@ -44,12 +44,12 @@ msgstr ""
msgid "A manual group of proxy URLs for this node"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1713
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1861
msgid "A manual group of proxy URLs for this section"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1551
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1583
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1631
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1663
msgid "Action"
msgstr ""
@@ -57,15 +57,15 @@ msgstr ""
msgid "Active Connections"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:395
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:401
msgid "Add a section"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2130
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2297
msgid "Add URLs or local paths to .lst lists"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2115
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2282
msgid "Add URLs or local paths to .srs / .json lists"
msgstr ""
@@ -77,20 +77,20 @@ msgstr ""
msgid "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:142
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:148
msgid "An error occurred while saving the form:"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:170
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1826
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1993
msgid "Applicable for SOCKS and Shadowsocks links"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:836
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:916
msgid "At least one valid domain must be specified."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:847
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:927
msgid "At least one valid IP or subnet must be specified."
msgstr ""
@@ -102,7 +102,7 @@ msgstr ""
msgid "Available actions"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:938
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1018
msgid "Backend validation failed: %s"
msgstr ""
@@ -126,7 +126,7 @@ msgstr ""
msgid "Browser is using FakeIP correctly"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2020
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2187
msgid "Built-in rule sets"
msgstr ""
@@ -183,7 +183,7 @@ msgstr ""
msgid "Close"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1539
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1619
msgid "Conditions"
msgstr ""
@@ -191,20 +191,20 @@ msgstr ""
msgid "Config File Path"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:381
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:387
msgid "Configuration for Podkop Plus service"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1668
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1749
msgid "Connection"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1651
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1731
msgid "Connection Type"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:19
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1654
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1734
msgid "Connection URL"
msgstr ""
@@ -216,7 +216,7 @@ msgstr ""
msgid "Currently unavailable"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:426
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:432
msgid "Dashboard"
msgstr ""
@@ -236,7 +236,7 @@ msgstr ""
msgid "DHCP has DNS server"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:414
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:420
msgid "Diagnostics"
msgstr ""
@@ -257,7 +257,7 @@ msgstr ""
msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:149
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:155
msgid "Dismiss"
msgstr ""
@@ -266,18 +266,18 @@ msgid "DNS on router"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:227
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1891
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2058
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/settings.js:21
msgid "DNS over HTTPS (DoH)"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:228
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1892
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2059
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/settings.js:22
msgid "DNS over TLS (DoT)"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1889
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2056
msgid "DNS protocol"
msgstr ""
@@ -293,7 +293,7 @@ msgstr ""
msgid "DNS Rewrite TTL"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1907
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2074
msgid "DNS server"
msgstr ""
@@ -310,19 +310,19 @@ msgstr ""
msgid "Do not panic, everything can be fixed, just..."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2129
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2296
msgid "Domain and IP Lists"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1990
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2157
msgid "Domain keyword"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1997
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2164
msgid "Domain regex"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1962
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2129
msgid "Domains"
msgstr ""
@@ -359,24 +359,23 @@ msgstr ""
msgid "Downloading all lists via specific Proxy/VPN"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:389
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:395
msgid "Drag rows to change priority. The rule at the top is checked first."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:369
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:384
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:404
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:449
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:464
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:484
msgid "Duplicate value"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:366
msgctxt "Rule row action"
msgid "Edit"
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:59
msgid "Edit rule action"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:8
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/ruleset.js:10
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1541
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1621
msgid "Enable"
msgstr ""
@@ -384,7 +383,7 @@ msgstr ""
msgid "Enable a dedicated DNS resolver when this node uses an interface"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1878
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2045
msgid "Enable a dedicated DNS resolver when this section uses an interface"
msgstr ""
@@ -392,7 +391,7 @@ msgstr ""
msgid "Enable autostart"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1929
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2096
msgid "Enable Mixed Proxy"
msgstr ""
@@ -409,31 +408,31 @@ msgid "Enable YACD WAN Access"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:52
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1691
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1772
msgid "Enter a complete sing-box outbound object"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1794
msgid "Enter the subscription URL to fetch proxy configurations from your provider"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:103
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1752
msgid "Every 3 minutes"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:101
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1750
msgid "Every 30 seconds"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:104
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1753
msgid "Every 5 minutes"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:102
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1751
msgid "Every minute"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1976
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2143
msgid "Exact full domain"
msgstr ""
@@ -449,11 +448,11 @@ msgstr ""
msgid "Expecting: %s"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1930
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2097
msgid "Expose this section as a local HTTP+SOCKS proxy"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:999
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1079
msgid "External nfqws config files bypass Podkop Plus queue management and explicit validation."
msgstr ""
@@ -471,6 +470,8 @@ msgid "Failed to execute!"
msgstr ""
#: src/podkop/methods/custom/getDashboardSections.ts:188
#: src/podkop/methods/custom/getDashboardSections.ts:220
#: src/podkop/methods/custom/getDashboardSections.ts:257
#: src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:59
msgid "Fastest"
msgstr ""
@@ -487,11 +488,19 @@ msgstr ""
msgid "Global check"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1848
msgid "Group subscription proxies by country"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1849
msgid "Group subscription proxies by the country flag at the beginning of the outbound tag"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/ruleset.js:87
msgid "How often this remote rule set should be refreshed"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1652
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1732
msgid "How to configure the proxy connection for this section"
msgstr ""
@@ -501,8 +510,8 @@ msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:23
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:179
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1658
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1837
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1739
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2004
msgid "Interface"
msgstr ""
@@ -595,7 +604,7 @@ msgstr ""
msgid "Invalid path format. Path must start with \"/\" and contain valid characters"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1957
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2124
#: src/validators/validateShadowsocksUrl.ts:85
msgid "Invalid port number. Must be between 1 and 65535"
msgstr ""
@@ -701,7 +710,7 @@ msgstr ""
msgid "IP address 0.0.0.0 is not allowed"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1969
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2136
msgid "IPs"
msgstr ""
@@ -719,7 +728,7 @@ msgstr ""
msgid "Latest"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1504
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1584
msgid "List must be a direct .lst URL or a local .lst path"
msgstr ""
@@ -743,32 +752,32 @@ msgstr ""
msgid "Main DNS"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1970
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2137
msgid "Match destination IPs or subnets"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1991
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2158
msgid "Match domains containing a substring"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1963
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2130
msgid "Match domains including all subdomains"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1998
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2165
msgid "Match domains using a regular expression"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1977
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2144
msgid "Match only one exact full domain name"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2005
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2172
msgid "Match source IPs or subnets"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:114
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1764
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1929
msgid "Maximum response time delta in milliseconds"
msgstr ""
@@ -776,7 +785,7 @@ msgstr ""
msgid "Memory Usage"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1941
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2108
msgid "Mixed Proxy Port"
msgstr ""
@@ -785,7 +794,7 @@ msgid "Monitored Interfaces"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:136
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1786
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1952
msgid "Must be a number in the range of 50 - 1000"
msgstr ""
@@ -797,11 +806,11 @@ msgstr ""
msgid "NFQUEUE range overlaps with another rule"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1617
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1697
msgid "NFQWS Strategy"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1129
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1209
msgid "NFQWS strategy cannot be empty"
msgstr ""
@@ -848,18 +857,22 @@ msgstr ""
msgid "Operation timed out"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:385
msgid "Option \"%s\" contains an invalid input value."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/ruleset.js:64
msgid "Optional node used to download this remote rule set"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1207
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1287
msgid "Optional values must be attached with '=' here; a separate token would be ignored by nfqws."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:22
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:51
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1657
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1690
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1738
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1771
msgid "Outbound JSON"
msgstr ""
@@ -900,11 +913,11 @@ msgstr ""
msgid "Pending"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1017
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1097
msgid "Podkop Plus manages the nfqws process lifecycle itself, so daemon mode is not allowed here."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:380
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:386
msgid "Podkop Plus Settings"
msgstr ""
@@ -924,11 +937,11 @@ msgstr ""
msgid "Podkop-managed nfqws runtime is ready"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1949
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2116
msgid "Port cannot be empty"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1942
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2109
msgid "Port for the local mixed proxy of this section"
msgstr ""
@@ -944,7 +957,7 @@ msgstr ""
msgid "Proxy traffic is routed via FakeIP"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2059
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2226
msgid "Regional options cannot be used together"
msgstr ""
@@ -956,7 +969,7 @@ msgstr ""
msgid "Remote URL to a sing-box rule set"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1877
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2044
msgid "Resolver for interface outbound"
msgstr ""
@@ -964,11 +977,11 @@ msgstr ""
msgid "Resolver For Interface Outbound"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1002
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1082
msgid "Resource selection by hostname inside nfqws is not supported here; sing-box selects resources before NFQUEUE."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1005
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1085
msgid "Resource selection by IP or CIDR inside nfqws is not supported here; sing-box selects resources before NFQUEUE."
msgstr ""
@@ -980,7 +993,7 @@ msgstr ""
msgid "Routing Excluded IPs"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1496
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1576
msgid "Rule set must be a direct .srs / .json URL or a local .srs / .json path"
msgstr ""
@@ -988,7 +1001,7 @@ msgstr ""
msgid "Rule set URL"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2114
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2281
msgid "Rule sets"
msgstr ""
@@ -1020,11 +1033,11 @@ msgstr ""
msgid "Run Diagnostic"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2078
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2245
msgid "Russia inside restrictions"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:141
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:147
msgid "Save error"
msgstr ""
@@ -1032,19 +1045,19 @@ msgstr ""
msgid "Secret key for authenticating remote access to YACD when WAN access is enabled."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:394
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:400
msgid "Section"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1570
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1650
msgid "Section name"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:388
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:394
msgid "Sections"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2021
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2188
msgid "Select a predefined list for routing"
msgstr ""
@@ -1080,7 +1093,7 @@ msgstr ""
msgid "Select the WAN interfaces to be monitored"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1712
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1860
msgid "Selector connections"
msgstr ""
@@ -1088,7 +1101,7 @@ msgstr ""
msgid "Selector Links"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2024
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2191
msgid "Service list"
msgstr ""
@@ -1096,8 +1109,8 @@ msgstr ""
msgid "Services info"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:402
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1538
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/podkop.js:408
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1618
msgid "Settings"
msgstr ""
@@ -1142,7 +1155,7 @@ msgstr ""
msgid "Source file"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2004
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2171
msgid "Source IPs"
msgstr ""
@@ -1174,6 +1187,22 @@ msgstr ""
msgid "Stop Podkop Plus"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1737
msgid "Subscription"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1813
msgid "Subscription update interval"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1793
msgid "Subscription URL"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:395
msgid "Subscription URL cannot be empty"
msgstr ""
#: src/helpers/copyToClipboard.ts:10
msgid "Successfully copied!"
msgstr ""
@@ -1194,7 +1223,7 @@ msgstr ""
msgid "Test latency"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1014
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1094
msgid "The desync fwmark is managed by Podkop Plus for loop prevention and must not be overridden here."
msgstr ""
@@ -1202,7 +1231,7 @@ msgstr ""
msgid "The DNS server used to look up the IP address of an upstream DNS server"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1011
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1091
msgid "The NFQUEUE number is assigned by Podkop Plus for each rule and must not be overridden here."
msgstr ""
@@ -1210,19 +1239,19 @@ msgstr ""
msgid "There are rules using Zapret"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1020
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1100
msgid "This field must start a working nfqws strategy; --dry-run exits immediately and is not allowed here."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1023
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1103
msgid "This field must start a working nfqws strategy; --version exits immediately and is not allowed here."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1192
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1272
msgid "This flag does not accept a value."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1223
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1303
msgid "This option requires a value."
msgstr ""
@@ -1259,11 +1288,11 @@ msgid "UDP (Unprotected DNS)"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:169
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1825
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1992
msgid "UDP over TCP"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:934
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1014
msgid "Unable to validate the NFQWS strategy through the backend parser."
msgstr ""
@@ -1271,7 +1300,7 @@ msgstr ""
msgid "Unexpected Podkop-managed nfqws runtime is running"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1172
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1252
msgid "Unexpected standalone token. Use explicit flags such as --name or --name=value."
msgstr ""
@@ -1294,7 +1323,7 @@ msgstr ""
msgid "Unknown error"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1183
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1263
msgid "Unknown NFQWS flag."
msgstr ""
@@ -1315,11 +1344,11 @@ msgstr ""
msgid "URL must use one of the following protocols:"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1730
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1878
msgid "URLTest connections"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1748
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1896
msgid "URLTest interval"
msgstr ""
@@ -1331,7 +1360,7 @@ msgstr ""
msgid "URLTest Links"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1763
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1928
msgid "URLTest tolerance"
msgstr ""
@@ -1340,7 +1369,7 @@ msgid "URLTest Tolerance"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:139
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1793
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1959
msgid "URLTest URL"
msgstr ""
@@ -1348,18 +1377,24 @@ msgstr ""
msgid "Use a network interface as the outbound for this node"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1838
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2005
msgid "Use a network interface as the outbound for this section"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:374
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/settings.js:312
msgid "Use sing-box duration format like 1d, 12h or 30m"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1814
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/settings.js:283
msgid "Use sing-box duration format. Leave empty to disable automatic updates."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1897
msgid "Use sing-box duration format. Leave empty to disable periodic URLTest checks."
msgstr ""
#: src/validators/validateDns.ts:14
#: src/validators/validateDns.ts:18
#: src/validators/validateDomain.ts:13
@@ -1377,9 +1412,9 @@ msgstr ""
msgid "Valid"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:376
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:396
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:400
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:456
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:476
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:480
msgid "Validation errors:"
msgstr ""
@@ -1388,24 +1423,24 @@ msgstr ""
msgid "View logs"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1571
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1651
msgid "Visible name of this section"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/node.js:32
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1669
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1750
msgid "vless://, ss://, trojan://, socks4/5://, hy2/hysteria2:// links"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2061
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2228
msgid "Warning: %s cannot be used together with %s. Previous selections have been removed."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2080
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:2247
msgid "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection."
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1584
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1664
msgid "What Podkop Plus should do when this section matches"
msgstr ""
@@ -1422,7 +1457,7 @@ msgstr ""
msgid "Zapret (provider not available)"
msgstr ""
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1008
#: ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/section.js:1088
msgid "Zapret hostlist templates are not supported here because Podkop Plus does not expand them for per-rule NFQWS strategies."
msgstr ""
+13
View File
@@ -21,3 +21,16 @@ config settings 'settings'
option exclude_ntp '0'
option shutdown_correctly '0'
#list routing_excluded_ips '192.168.1.3'
#config rule 'subscription_example'
# option label 'Subscription'
# option enabled '1'
# option action 'proxy'
# option proxy_config_type 'subscription'
# option subscription_url 'https://example.com/api/sub'
# option subscription_update_interval '1h'
# option subscription_group_by_countries '0'
# option urltest_check_interval '3m'
# option urltest_tolerance '50'
# option urltest_testing_url 'https://www.gstatic.com/generate_204'
# list community_lists 'russia_inside'
+755 -24
View File
@@ -39,7 +39,9 @@ PODKOP_RUNTIME_STATUS_FILE="$PODKOP_RUNTIME_STATE_DIR/status"
PODKOP_SYSTEM_INFO_CACHE_FILE="$PODKOP_RUNTIME_STATE_DIR/system-info.json"
PODKOP_RELOAD_STATE_FILE="$PODKOP_RUNTIME_STATE_DIR/reload-state"
PODKOP_LIST_UPDATE_STATE_FILE="$PODKOP_RUNTIME_STATE_DIR/list-update.timestamp"
PODKOP_SUBSCRIPTION_UPDATE_STATE_DIR="$PODKOP_RUNTIME_STATE_DIR/subscription-update"
PODKOP_LIST_UPDATE_CRON_MARKER="# podkop-plus-list-update"
PODKOP_SUBSCRIPTION_UPDATE_CRON_MARKER="# podkop-plus-subscription-update"
# sing-box falls back to 1d when update_interval is empty, so use a large
# duration to emulate disabled automatic refreshes for remote rule sets.
SING_BOX_DISABLED_UPDATE_INTERVAL="876000h"
@@ -55,7 +57,171 @@ config_load "$PODKOP_CONFIG_NAME"
ensure_runtime_dirs() {
mkdir -p "$TMP_SING_BOX_FOLDER"
mkdir -p "$TMP_RULESET_FOLDER"
mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
mkdir -p "$PODKOP_RUNTIME_STATE_DIR"
mkdir -p "$PODKOP_SUBSCRIPTION_UPDATE_STATE_DIR"
}
get_subscription_json_path() {
local section="$1"
echo "$TMP_SUBSCRIPTION_FOLDER/${section}.json"
}
get_subscription_url_cache_path() {
local section="$1"
echo "$TMP_SUBSCRIPTION_FOLDER/${section}.url"
}
get_subscription_update_timestamp_path() {
local section="$1"
echo "$PODKOP_SUBSCRIPTION_UPDATE_STATE_DIR/${section}.timestamp"
}
subscription_cache_is_usable() {
local subscription_json_path="$1"
[ -s "$subscription_json_path" ] || return 1
validate_subscription_file "$subscription_json_path"
}
wait_for_subscription_connectivity() {
local section="$1"
local subscription_url="$2"
local service_proxy_address="$3"
local attempts="${4:-12}"
local wait="${5:-5}"
local timeout="${6:-5}"
local attempt
for attempt in $(seq 1 "$attempts"); do
if check_subscription_connectivity "$subscription_url" "$service_proxy_address" 1 0 "$timeout"; then
log "Subscription connectivity check passed for rule '$section'" "info"
return 0
fi
log "Subscription source is unavailable for rule '$section' [$attempt/$attempts]" "warn"
[ "$attempt" -lt "$attempts" ] && sleep "$wait"
done
log "Subscription connectivity check failed for rule '$section' after $attempts attempts" "error"
return 1
}
download_subscription_into_cache() {
local section="$1"
local subscription_url="$2"
local subscription_json_path="$3"
local subscription_url_cache_path="$4"
local service_proxy_address="$5"
local tmpfile
mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
tmpfile="$(mktemp "$TMP_SUBSCRIPTION_FOLDER/${section}.download.XXXXXX")" || return 1
if ! download_subscription "$subscription_url" "$tmpfile" "$service_proxy_address" 3 2 10; then
rm -f "$tmpfile"
return 1
fi
if ! validate_subscription_file "$tmpfile"; then
log "Downloaded subscription for rule '$section' is invalid" "error"
rm -f "$tmpfile"
return 1
fi
if [ -f "$subscription_json_path" ] && cmp -s "$tmpfile" "$subscription_json_path"; then
rm -f "$tmpfile"
printf '%s' "$subscription_url" > "$subscription_url_cache_path"
log "Subscription for rule '$section' is unchanged" "info"
return 2
fi
mv "$tmpfile" "$subscription_json_path" || {
rm -f "$tmpfile"
return 1
}
printf '%s' "$subscription_url" > "$subscription_url_cache_path"
return 0
}
rule_is_subscription_proxy() {
local section="$1"
local action proxy_config_type
rule_is_enabled "$section" || return 1
action="$(get_rule_action "$section")"
[ "$action" = "proxy" ] || return 1
proxy_config_type="$(get_proxy_config_type "$section")"
[ "$proxy_config_type" = "subscription" ]
}
prepare_subscription_cache_for_startup() {
local section="$1"
local subscription_url subscription_json_path subscription_url_cache_path cached_subscription_url
local service_proxy_address had_usable_cache cache_needs_refresh
rule_is_subscription_proxy "$section" || return 0
config_get subscription_url "$section" "subscription_url"
if [ -z "$subscription_url" ]; then
log "Subscription URL is not set for rule '$section'. Aborted." "fatal"
exit 1
fi
subscription_json_path="$(get_subscription_json_path "$section")"
subscription_url_cache_path="$(get_subscription_url_cache_path "$section")"
cached_subscription_url=""
had_usable_cache=0
cache_needs_refresh=0
if subscription_cache_is_usable "$subscription_json_path"; then
had_usable_cache=1
else
rm -f "$subscription_json_path"
fi
if [ -f "$subscription_url_cache_path" ]; then
cached_subscription_url="$(cat "$subscription_url_cache_path" 2> /dev/null)"
fi
if [ "$had_usable_cache" -eq 0 ] || [ "$cached_subscription_url" != "$subscription_url" ]; then
cache_needs_refresh=1
fi
[ "$cache_needs_refresh" -eq 0 ] && return 0
service_proxy_address="$(get_service_proxy_address 2>/dev/null || echo '')"
if wait_for_subscription_connectivity "$section" "$subscription_url" "$service_proxy_address"; then
download_subscription_into_cache \
"$section" "$subscription_url" "$subscription_json_path" "$subscription_url_cache_path" "$service_proxy_address"
case "$?" in
0 | 2) return 0 ;;
esac
fi
if [ "$had_usable_cache" -eq 1 ]; then
log "Keeping cached subscription for rule '$section' until a fresh download succeeds" "warn"
return 0
fi
log "No usable subscription cache for rule '$section'; startup cannot continue" "error"
subscription_startup_blocked=1
return 1
}
prepare_subscription_caches_for_startup() {
subscription_startup_blocked=0
config_foreach prepare_subscription_cache_for_startup "rule"
[ "$subscription_startup_blocked" -eq 0 ]
}
close_inherited_service_lock_fd() {
@@ -288,7 +454,7 @@ append_sing_box_rule_signature() {
local urltest_proxy_links udp_over_tcp urltest_check_interval urltest_tolerance urltest_testing_url
config_get urltest_proxy_links "$section" "urltest_proxy_links"
config_get_bool udp_over_tcp "$section" "enable_udp_over_tcp" 0
config_get urltest_check_interval "$section" "urltest_check_interval" "3m"
urltest_check_interval="$(get_urltest_check_interval_for_rule "$section")"
config_get urltest_tolerance "$section" "urltest_tolerance" 50
config_get urltest_testing_url "$section" "urltest_testing_url" "https://www.gstatic.com/generate_204"
signature_add "rule.$section.urltest_proxy_links" "$urltest_proxy_links"
@@ -310,6 +476,22 @@ append_sing_box_rule_signature() {
signature_add "rule.$section.domain_resolver_dns_server" "$domain_resolver_dns_server"
fi
;;
subscription)
local subscription_url subscription_update_interval subscription_group_by_countries \
urltest_check_interval urltest_tolerance urltest_testing_url
config_get subscription_url "$section" "subscription_url"
subscription_update_interval="$(get_subscription_update_interval_for_rule "$section")"
config_get subscription_group_by_countries "$section" "subscription_group_by_countries"
urltest_check_interval="$(get_urltest_check_interval_for_rule "$section")"
config_get urltest_tolerance "$section" "urltest_tolerance" 50
config_get urltest_testing_url "$section" "urltest_testing_url" "https://www.gstatic.com/generate_204"
signature_add "rule.$section.subscription_url" "$subscription_url"
signature_add "rule.$section.subscription_update_interval" "$subscription_update_interval"
signature_add "rule.$section.subscription_group_by_countries" "$subscription_group_by_countries"
signature_add "rule.$section.urltest_check_interval" "$urltest_check_interval"
signature_add "rule.$section.urltest_tolerance" "$urltest_tolerance"
signature_add "rule.$section.urltest_testing_url" "$urltest_testing_url"
;;
esac
config_get_bool mixed_proxy_enabled "$section" "mixed_proxy_enabled" 0
@@ -499,6 +681,19 @@ build_list_update_signature() {
signature_finish
}
append_subscription_update_cron_signature() {
local section="$1"
local subscription_update_interval subscription_url
rule_is_subscription_proxy "$section" || return 0
config_get subscription_url "$section" "subscription_url"
subscription_update_interval="$(get_subscription_update_interval_for_rule "$section")"
signature_add "subscription.$section.subscription_url" "$subscription_url"
signature_add "subscription.$section.subscription_update_interval" "$subscription_update_interval"
}
build_cron_signature() {
local update_interval
@@ -506,6 +701,7 @@ build_cron_signature() {
config_get update_interval "settings" "update_interval"
signature_add "settings.update_interval" "$update_interval"
config_foreach append_list_update_signature "rule"
config_foreach append_subscription_update_cron_signature "rule"
signature_finish
}
@@ -531,6 +727,34 @@ get_settings_update_interval() {
printf '%s\n' "$update_interval"
}
get_duration_option_with_default_if_unset() {
local section="$1"
local option="$2"
local default_value="$3"
local disabled value
config_get_bool disabled "$section" "${option}_disabled" 0
if [ "$disabled" -eq 1 ]; then
printf '\n'
return 0
fi
config_get value "$section" "$option"
if [ -n "$value" ]; then
printf '%s\n' "$value"
else
printf '%s\n' "$default_value"
fi
}
get_subscription_update_interval_for_rule() {
get_duration_option_with_default_if_unset "$1" "subscription_update_interval" "1h"
}
get_urltest_check_interval_for_rule() {
get_duration_option_with_default_if_unset "$1" "urltest_check_interval" "3m"
}
get_remote_ruleset_update_interval() {
local update_interval
@@ -596,6 +820,24 @@ has_list_update_sources() {
[ "$RELOAD_LIST_SOURCES_PRESENT" -eq 1 ]
}
enabled_rule_has_subscription_update_source() {
local section="$1"
local subscription_update_interval
rule_is_subscription_proxy "$section" || return 0
subscription_update_interval="$(get_subscription_update_interval_for_rule "$section")"
[ -n "$subscription_update_interval" ] || return 0
RELOAD_SUBSCRIPTION_SOURCES_PRESENT=1
}
has_subscription_update_sources() {
RELOAD_SUBSCRIPTION_SOURCES_PRESENT=0
config_foreach enabled_rule_has_subscription_update_source "rule"
[ "$RELOAD_SUBSCRIPTION_SOURCES_PRESENT" -eq 1 ]
}
enabled_rule_has_nft_list_sources() {
local section="$1"
local community_lists remote_subnet_lists domain_ip_lists
@@ -648,22 +890,28 @@ refresh_list_update_cron() {
remove_cron_job
if ! has_list_update_sources; then
return 0
if has_list_update_sources; then
update_interval="$(get_settings_update_interval)"
if [ -z "$update_interval" ]; then
log "Remote list auto-update is disabled"
else
cron_job="$(build_list_update_cron_job)" || return 1
crontab -l 2> /dev/null | {
cat
echo "$cron_job"
} | crontab -
log "The cron job has been created: $cron_job"
fi
fi
update_interval="$(get_settings_update_interval)"
if [ -z "$update_interval" ]; then
log "Remote list auto-update is disabled"
return 0
if has_subscription_update_sources; then
cron_job="$(build_subscription_update_cron_job)" || return 1
crontab -l 2> /dev/null | {
cat
echo "$cron_job"
} | crontab -
log "The subscription cron job has been created: $cron_job"
fi
cron_job="$(build_list_update_cron_job)" || return 1
crontab -l 2> /dev/null | {
cat
echo "$cron_job"
} | crontab -
log "The cron job has been created: $cron_job"
}
reload_sing_box_runtime() {
@@ -690,7 +938,7 @@ get_rule_action() {
infer_rule_action_from_fields() {
local section="$1"
local connection_type proxy_config_type proxy_string selector_proxy_links urltest_proxy_links outbound_json interface \
local connection_type proxy_config_type proxy_string selector_proxy_links urltest_proxy_links subscription_url outbound_json interface \
nfqws_opt
config_get connection_type "$section" "connection_type"
@@ -719,9 +967,10 @@ infer_rule_action_from_fields() {
config_get outbound_json "$section" "outbound_json"
config_get selector_proxy_links "$section" "selector_proxy_links"
config_get urltest_proxy_links "$section" "urltest_proxy_links"
config_get subscription_url "$section" "subscription_url"
config_get proxy_string "$section" "proxy_string"
if [ -n "$interface" ] || [ -n "$outbound_json" ] || [ -n "$selector_proxy_links" ] ||
[ -n "$urltest_proxy_links" ] || [ -n "$proxy_string" ]; then
[ -n "$urltest_proxy_links" ] || [ -n "$subscription_url" ] || [ -n "$proxy_string" ]; then
echo "proxy"
return 0
fi
@@ -750,7 +999,7 @@ get_proxy_config_type() {
infer_proxy_config_type_from_fields() {
local section="$1"
local connection_type interface outbound_json selector_proxy_links urltest_proxy_links proxy_string
local connection_type interface outbound_json selector_proxy_links urltest_proxy_links subscription_url proxy_string
config_get connection_type "$section" "connection_type"
if [ "$connection_type" = "vpn" ]; then
@@ -782,6 +1031,12 @@ infer_proxy_config_type_from_fields() {
return 0
fi
config_get subscription_url "$section" "subscription_url"
if [ -n "$subscription_url" ]; then
echo "subscription"
return 0
fi
config_get proxy_string "$section" "proxy_string"
if [ -n "$proxy_string" ]; then
echo "url"
@@ -1033,18 +1288,19 @@ check_requirements() {
_check_outbound_node() {
local section="$1"
local proxy_string interface outbound_json selector_proxy_links urltest_proxy_links
local proxy_string interface outbound_json selector_proxy_links urltest_proxy_links subscription_url
rule_is_enabled "$section" || return 0
config_get proxy_string "$section" "proxy_string"
config_get selector_proxy_links "$section" "selector_proxy_links"
config_get urltest_proxy_links "$section" "urltest_proxy_links"
config_get subscription_url "$section" "subscription_url"
config_get outbound_json "$section" "outbound_json"
config_get interface "$section" "interface"
if [ -n "$proxy_string" ] || [ -n "$selector_proxy_links" ] || [ -n "$urltest_proxy_links" ] ||
[ -n "$outbound_json" ] || [ -n "$interface" ]; then
[ -n "$subscription_url" ] || [ -n "$outbound_json" ] || [ -n "$interface" ]; then
section_exists=0
fi
}
@@ -1074,6 +1330,10 @@ start_main() {
sleep 1
ensure_runtime_dirs
if ! prepare_subscription_caches_for_startup; then
log "Subscription caches are not ready. Aborted." "fatal"
exit 1
fi
# base
route_table_rule_mark
@@ -1517,6 +1777,20 @@ validate_plain_domain_ip_list_reference() {
exit 1
}
validate_sing_box_duration_option() {
local value="$1"
local label="$2"
[ -z "$value" ] && return 0
if duration_to_seconds "$value" >/dev/null 2>&1; then
return 0
fi
log "Invalid duration value for $label: $value. Use sing-box duration format like 1d, 12h or 30m. Aborted." "fatal"
exit 1
}
ruleset_kind_from_reference_hint() {
local reference="$1"
@@ -1635,7 +1909,7 @@ detect_inline_ruleset_reference_kind() {
process_validate_rule() {
local section="$1"
local action
local action proxy_config_type urltest_check_interval subscription_update_interval
rule_is_enabled "$section" || return 0
@@ -1660,6 +1934,21 @@ process_validate_rule() {
validate_rule_nfqws_opt "$section"
fi
if [ "$action" = "proxy" ]; then
proxy_config_type="$(get_proxy_config_type "$section")"
case "$proxy_config_type" in
urltest | subscription)
urltest_check_interval="$(get_urltest_check_interval_for_rule "$section")"
validate_sing_box_duration_option "$urltest_check_interval" "rule.$section.urltest_check_interval"
;;
esac
if [ "$proxy_config_type" = "subscription" ]; then
subscription_update_interval="$(get_subscription_update_interval_for_rule "$section")"
validate_sing_box_duration_option "$subscription_update_interval" "rule.$section.subscription_update_interval"
fi
fi
config_list_foreach "$section" "community_lists" validate_service
config_list_foreach "$section" "rule_set" validate_ruleset_reference
config_list_foreach "$section" "domain_ip_lists" validate_plain_domain_ip_list_reference
@@ -2215,8 +2504,14 @@ build_list_update_cron_job() {
printf '%s %s list_update_if_due %s\n' "$cron_schedule" "$PODKOP_BIN" "$PODKOP_LIST_UPDATE_CRON_MARKER"
}
build_subscription_update_cron_job() {
printf '* * * * * %s subscription_update_if_due %s\n' "$PODKOP_BIN" "$PODKOP_SUBSCRIPTION_UPDATE_CRON_MARKER"
}
remove_cron_job() {
(crontab -l 2>/dev/null | grep -Fv "$PODKOP_LIST_UPDATE_CRON_MARKER") | crontab -
(crontab -l 2>/dev/null |
grep -Fv "$PODKOP_LIST_UPDATE_CRON_MARKER" |
grep -Fv "$PODKOP_SUBSCRIPTION_UPDATE_CRON_MARKER") | crontab -
log "The cron job removed"
}
@@ -2448,6 +2743,182 @@ list_update() {
[ "$update_status" -eq 0 ] || return 1
}
read_subscription_update_timestamp() {
local section="$1"
sed -n '1p' "$(get_subscription_update_timestamp_path "$section")" 2>/dev/null
}
write_subscription_update_timestamp() {
local section="$1"
local timestamp="$2"
mkdir -p "$PODKOP_SUBSCRIPTION_UPDATE_STATE_DIR"
printf '%s\n' "$timestamp" > "$(get_subscription_update_timestamp_path "$section")"
}
subscription_update_section_is_due() {
local section="$1"
local update_interval interval_seconds now last_run
update_interval="$(get_subscription_update_interval_for_rule "$section")"
[ -n "$update_interval" ] || return 1
interval_seconds="$(duration_to_seconds "$update_interval")" || {
log "Invalid subscription_update_interval value for rule '$section': $update_interval" "error"
return 2
}
now="$(date +%s 2>/dev/null)"
last_run="$(read_subscription_update_timestamp "$section")"
case "$now" in
'' | *[!0-9]*) return 2 ;;
esac
case "$last_run" in
'' | *[!0-9]*) last_run=0 ;;
esac
[ "$last_run" -le 0 ] || [ $((now - last_run)) -ge "$interval_seconds" ]
}
subscription_update_section() {
local section="$1"
local force="$2"
local subscription_url subscription_json_path subscription_url_cache_path service_proxy_address update_result
local outbounds_count now
rule_is_subscription_proxy "$section" || return 3
if [ "$force" -eq 0 ]; then
subscription_update_section_is_due "$section"
case "$?" in
0) ;;
1) return 3 ;;
*) return 1 ;;
esac
fi
config_get subscription_url "$section" "subscription_url"
if [ -z "$subscription_url" ]; then
echolog "Subscription URL is not set for rule '$section'"
return 1
fi
mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
subscription_json_path="$(get_subscription_json_path "$section")"
subscription_url_cache_path="$(get_subscription_url_cache_path "$section")"
service_proxy_address="$(get_service_proxy_address 2>/dev/null || echo '')"
echolog "Updating subscription for rule '$section'..."
if ! wait_for_subscription_connectivity "$section" "$subscription_url" "$service_proxy_address" 6 5 5; then
echolog "Failed to download subscription for rule '$section'"
return 1
fi
download_subscription_into_cache \
"$section" "$subscription_url" "$subscription_json_path" "$subscription_url_cache_path" "$service_proxy_address"
update_result=$?
now="$(date +%s 2>/dev/null)"
case "$now" in
'' | *[!0-9]*) ;;
*) write_subscription_update_timestamp "$section" "$now" ;;
esac
case "$update_result" in
0)
outbounds_count=$(jq -r '[.outbounds[] | select(
.type != "selector" and
.type != "urltest" and
.type != "direct" and
.type != "dns" and
.type != "block"
)] | length' "$subscription_json_path" 2>/dev/null)
echolog "Subscription updated for rule '$section': $outbounds_count outbounds"
return 0
;;
2)
echolog "Subscription for rule '$section' is unchanged"
return 2
;;
*)
echolog "Failed to download subscription for rule '$section'"
return 1
;;
esac
}
subscription_update_handler() {
local section="$1"
local result
subscription_update_section "$section" "$SUBSCRIPTION_UPDATE_FORCE"
result=$?
case "$result" in
0) SUBSCRIPTION_UPDATED_SECTIONS=$((SUBSCRIPTION_UPDATED_SECTIONS + 1)) ;;
1) SUBSCRIPTION_FAILED_SECTIONS=$((SUBSCRIPTION_FAILED_SECTIONS + 1)) ;;
2) SUBSCRIPTION_UNCHANGED_SECTIONS=$((SUBSCRIPTION_UNCHANGED_SECTIONS + 1)) ;;
3) ;;
esac
}
subscription_update_common() {
local force="$1"
local restart_rc
config_load "$PODKOP_CONFIG_NAME"
ensure_runtime_dirs
SUBSCRIPTION_UPDATE_FORCE="$force"
SUBSCRIPTION_UPDATED_SECTIONS=0
SUBSCRIPTION_FAILED_SECTIONS=0
SUBSCRIPTION_UNCHANGED_SECTIONS=0
config_foreach subscription_update_handler "rule"
if [ "$SUBSCRIPTION_UPDATED_SECTIONS" -eq 0 ]; then
if [ "$SUBSCRIPTION_FAILED_SECTIONS" -gt 0 ]; then
echolog "Subscription update finished with errors; keeping the last working cache"
return 1
fi
if [ "$SUBSCRIPTION_UNCHANGED_SECTIONS" -gt 0 ]; then
echolog "Subscription update completed: no changes detected"
else
echolog "No subscription rules are due for update"
fi
return 0
fi
echolog "Restarting Podkop Plus to apply updated subscriptions..."
restart
restart_rc=$?
if [ "$restart_rc" -ne 0 ]; then
echolog "Subscription was downloaded, but Podkop Plus restart failed"
return "$restart_rc"
fi
if [ "$SUBSCRIPTION_FAILED_SECTIONS" -gt 0 ]; then
echolog "Subscription update applied for changed rules; failed rules kept their previous cache"
else
echolog "Subscription update completed"
fi
}
subscription_update_if_due() {
echolog "Starting due subscription update..."
subscription_update_common 0
}
subscription_update() {
echolog "Starting subscription update..."
subscription_update_common 1
}
# sing-box funcs
sing_box_configure_service() {
local sing_box_enabled sing_box_user sing_box_config_path sing_box_conffile
@@ -2521,6 +2992,88 @@ sing_box_configure_outbounds() {
config_foreach configure_outbound_handler "rule"
}
sing_box_get_unique_outbound_tag() {
local config="$1"
local base_tag="$2"
local candidate="$base_tag"
local tag_suffix=1
while printf '%s' "$config" | jq -e --arg tag "$candidate" '.outbounds[]? | select(.tag == $tag)' > /dev/null 2>&1; do
candidate="${base_tag}-${tag_suffix}"
tag_suffix=$((tag_suffix + 1))
done
echo "$candidate"
}
sing_box_build_subscription_country_groups() {
local subscription_outbound_tags_json="$1"
printf '%s' "$subscription_outbound_tags_json" | jq -c '
def is_regional_indicator: . >= 127462 and . <= 127487;
def extract_country_flag:
(. | explode) as $codepoints
| if ($codepoints | length) >= 2
and ($codepoints[0] | is_regional_indicator)
and ($codepoints[1] | is_regional_indicator)
then ($codepoints[0:2] | implode)
else ""
end;
(if type == "array" then . else [] end) as $tags
| reduce $tags[] as $tag (
{country_order: [], country_groups: {}, ungrouped: []};
($tag | extract_country_flag) as $country_flag
| if $country_flag == "" then
.ungrouped += [$tag]
else
.country_groups[$country_flag] = ((.country_groups[$country_flag] // []) + [$tag])
| if (.country_order | index($country_flag)) == null then
.country_order += [$country_flag]
else
.
end
end
)
' 2>/dev/null
}
is_truthy_option() {
local value="$1"
case "$value" in
1 | true | TRUE | True | on | ON | yes | YES | enabled | ENABLED)
return 0
;;
esac
return 1
}
get_urltest_check_interval_for_config() {
local section="$1"
local urltest_check_interval
urltest_check_interval="$(get_urltest_check_interval_for_rule "$section")"
if [ -n "$urltest_check_interval" ]; then
printf '%s\n' "$urltest_check_interval"
else
printf '%s\n' "$SING_BOX_DISABLED_UPDATE_INTERVAL"
fi
}
get_urltest_idle_timeout_for_config() {
local section="$1"
local urltest_check_interval
urltest_check_interval="$(get_urltest_check_interval_for_rule "$section")"
if [ -z "$urltest_check_interval" ]; then
printf '%s\n' "$SING_BOX_DISABLED_UPDATE_INTERVAL"
else
printf '\n'
fi
}
configure_outbound_handler() {
local section="$1"
local action proxy_config_type
@@ -2601,10 +3154,11 @@ configure_outbound_handler() {
;;
urltest)
local urltest_proxy_links udp_over_tcp i urltest_tag selector_tag outbound_tag outbound_tags urltest_outbounds \
selector_outbounds urltest_check_interval urltest_tolerance urltest_testing_url
selector_outbounds urltest_check_interval urltest_idle_timeout urltest_tolerance urltest_testing_url
config_get urltest_proxy_links "$section" "urltest_proxy_links"
config_get udp_over_tcp "$section" "enable_udp_over_tcp"
config_get urltest_check_interval "$section" "urltest_check_interval" "3m"
urltest_check_interval="$(get_urltest_check_interval_for_config "$section")"
urltest_idle_timeout="$(get_urltest_idle_timeout_for_config "$section")"
config_get urltest_tolerance "$section" "urltest_tolerance" 50
config_get urltest_testing_url "$section" "urltest_testing_url" "https://www.gstatic.com/generate_204"
@@ -2630,10 +3184,177 @@ configure_outbound_handler() {
urltest_outbounds="$(comma_string_to_json_array "$outbound_tags")"
selector_outbounds="$(comma_string_to_json_array "$outbound_tags,$urltest_tag")"
config="$(sing_box_cm_add_urltest_outbound "$config" "$urltest_tag" "$urltest_outbounds" \
"$urltest_testing_url" "$urltest_check_interval" "$urltest_tolerance")"
"$urltest_testing_url" "$urltest_check_interval" "$urltest_tolerance" "$urltest_idle_timeout")"
config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$urltest_tag")"
return 0
;;
subscription)
local subscription_url subscription_json_path subscription_url_cache_path cached_subscription_url \
should_download had_usable_cache service_proxy_address urltest_tag selector_tag urltest_outbounds \
selector_outbounds urltest_check_interval urltest_idle_timeout urltest_tolerance urltest_testing_url \
subscription_group_by_countries_raw subscription_group_by_countries subscription_outbound_tags_json \
update_result
config_get subscription_url "$section" "subscription_url"
urltest_check_interval="$(get_urltest_check_interval_for_config "$section")"
urltest_idle_timeout="$(get_urltest_idle_timeout_for_config "$section")"
config_get urltest_tolerance "$section" "urltest_tolerance" 50
config_get urltest_testing_url "$section" "urltest_testing_url" "https://www.gstatic.com/generate_204"
config_get subscription_group_by_countries_raw "$section" "subscription_group_by_countries" ""
if [ -z "$subscription_group_by_countries_raw" ]; then
config_get subscription_group_by_countries_raw "$section" "group_by_countries" ""
fi
if is_truthy_option "$subscription_group_by_countries_raw"; then
subscription_group_by_countries=1
else
subscription_group_by_countries=0
fi
if [ -z "$subscription_url" ]; then
log "Subscription URL is not set for rule '$section'. Aborted." "fatal"
exit 1
fi
mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
subscription_json_path="$(get_subscription_json_path "$section")"
subscription_url_cache_path="$(get_subscription_url_cache_path "$section")"
cached_subscription_url=""
should_download=0
had_usable_cache=0
if subscription_cache_is_usable "$subscription_json_path"; then
had_usable_cache=1
else
rm -f "$subscription_json_path"
should_download=1
fi
if [ -f "$subscription_url_cache_path" ]; then
cached_subscription_url="$(cat "$subscription_url_cache_path" 2>/dev/null)"
fi
if [ "$cached_subscription_url" != "$subscription_url" ]; then
if [ -n "$cached_subscription_url" ]; then
log "Subscription URL changed for rule '$section'" "warn"
fi
if [ "$had_usable_cache" -eq 0 ]; then
should_download=1
else
log "Using cached subscription for rule '$section' until a fresh download succeeds" "warn"
fi
fi
if [ "$should_download" -eq 1 ]; then
log "Downloading subscription for rule '$section'"
service_proxy_address="$(get_service_proxy_address 2>/dev/null || echo '')"
if wait_for_subscription_connectivity "$section" "$subscription_url" "$service_proxy_address" 6 5 5; then
download_subscription_into_cache \
"$section" "$subscription_url" "$subscription_json_path" "$subscription_url_cache_path" "$service_proxy_address"
update_result="$?"
else
update_result=1
fi
case "$update_result" in
0 | 2) ;;
*)
if [ "$had_usable_cache" -eq 1 ]; then
log "Failed to refresh subscription for rule '$section', continuing with cached data" "warn"
else
log "Failed to download subscription for rule '$section'. Aborted." "fatal"
exit 1
fi
;;
esac
fi
if ! sing_box_cf_add_subscription_outbounds "$config" "$section" "$subscription_json_path" > /dev/null; then
log "No proxy outbounds found in subscription for rule '$section'. Aborted." "fatal"
exit 1
fi
config="$SING_BOX_CF_LAST_CONFIG"
if [ -z "$SUBSCRIPTION_OUTBOUND_TAGS" ]; then
log "No proxy outbounds found in subscription for rule '$section'. Aborted." "fatal"
exit 1
fi
subscription_outbound_tags_json="$SUBSCRIPTION_OUTBOUND_TAGS_JSON"
if [ -z "$subscription_outbound_tags_json" ] || [ "$subscription_outbound_tags_json" = "[]" ]; then
subscription_outbound_tags_json="$(comma_string_to_json_array "$SUBSCRIPTION_OUTBOUND_TAGS")"
fi
selector_tag="$(get_outbound_tag_by_section "$section")"
if [ "$subscription_group_by_countries" -eq 1 ]; then
local grouping_json country_flag country_group_outbounds country_group_tag selector_outbounds_json \
selector_default ungrouped_outbounds_json
grouping_json="$(sing_box_build_subscription_country_groups "$subscription_outbound_tags_json")"
if [ -z "$grouping_json" ]; then
log "Failed to build grouped subscription outbounds for rule '$section'. Aborted." "fatal"
exit 1
fi
selector_outbounds_json="[]"
for country_flag in $(echo "$grouping_json" | jq -r '.country_order[]' 2>/dev/null); do
country_group_outbounds="$(echo "$grouping_json" | jq -c --arg country_flag "$country_flag" '.country_groups[$country_flag] // []' 2>/dev/null)"
if [ -z "$country_group_outbounds" ] || [ "$country_group_outbounds" = "[]" ]; then
continue
fi
country_group_tag="$(sing_box_get_unique_outbound_tag "$config" "$country_flag Fastest")"
config="$(sing_box_cm_add_urltest_outbound "$config" "$country_group_tag" "$country_group_outbounds" \
"$urltest_testing_url" "$urltest_check_interval" "$urltest_tolerance" "$urltest_idle_timeout")"
selector_outbounds_json=$(
printf '%s' "$selector_outbounds_json" | jq -ac --arg tag "$country_group_tag" '. + [$tag]' 2>/dev/null
)
done
[ -n "$selector_outbounds_json" ] || selector_outbounds_json="[]"
ungrouped_outbounds_json="$(echo "$grouping_json" | jq -c '.ungrouped // []' 2>/dev/null)"
if [ -n "$ungrouped_outbounds_json" ] && [ "$ungrouped_outbounds_json" != "[]" ]; then
selector_outbounds_json=$(
jq -acn --argjson selector "$selector_outbounds_json" --argjson ungrouped "$ungrouped_outbounds_json" \
'$selector + $ungrouped' 2>/dev/null
)
fi
if [ -z "$selector_outbounds_json" ] || [ "$selector_outbounds_json" = "[]" ]; then
log "No selector outbounds available after grouping subscription outbounds for rule '$section'. Aborted." "fatal"
exit 1
fi
selector_default="$(echo "$selector_outbounds_json" | jq -r '.[0] // ""' 2>/dev/null)"
if [ -z "$selector_default" ] || [ "$selector_default" = "null" ]; then
log "Unable to determine default selector outbound for rule '$section'. Aborted." "fatal"
exit 1
fi
config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds_json" "$selector_default" "true")"
else
urltest_tag="$(get_outbound_tag_by_section "$section-urltest")"
urltest_outbounds="$subscription_outbound_tags_json"
selector_outbounds=$(
jq -acn --argjson outbounds "$subscription_outbound_tags_json" --arg tag "$urltest_tag" \
'$outbounds + [$tag]' 2>/dev/null
)
if [ -z "$selector_outbounds" ]; then
log "Failed to build selector outbounds for subscription rule '$section'. Aborted." "fatal"
exit 1
fi
config="$(sing_box_cm_add_urltest_outbound "$config" "$urltest_tag" "$urltest_outbounds" \
"$urltest_testing_url" "$urltest_check_interval" "$urltest_tolerance" "$urltest_idle_timeout")"
config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$urltest_tag" "true")"
fi
return 0
;;
interface)
local interface_name domain_resolver_enabled domain_resolver_dns_type domain_resolver_dns_server \
domain_resolver_dns_server_address outbound_tag domain_resolver_tag dns_domain_resolver
@@ -4379,6 +5100,7 @@ show_config() {
tmp_config=$(mktemp)
sed -e 's/\(option proxy_string\).*/\1 '\''MASKED'\''/g' \
-e 's/\(option subscription_url\).*/\1 '\''MASKED'\''/g' \
-e '/option outbound_json/,/^}/c\ option outbound_json '\''MASKED'\''' \
-e 's/\(list urltest_proxy_links\).*/\1 '\''MASKED'\''/g' \
-e 's/\(list selector_proxy_links\).*/\1 '\''MASKED'\''/g' \
@@ -5574,6 +6296,9 @@ Available commands:
main Run main Podkop Plus process
list_update Update remote lists
list_update_if_due Update remote lists only when the configured interval has elapsed
subscription_update Update subscription proxies
subscription_update_if_due
Update subscription proxies only when per-rule intervals have elapsed
check_proxy Check proxy connectivity
check_nft Check NFT rules
check_nft_rules Check NFT rules status
@@ -5632,6 +6357,12 @@ list_update)
list_update_if_due)
list_update_if_due
;;
subscription_update)
subscription_update
;;
subscription_update_if_due)
subscription_update_if_due
;;
check_proxy)
check_proxy
;;
+1
View File
@@ -20,6 +20,7 @@ CHECK_PROXY_IP_DOMAIN="ip.podkop.fyi"
FAKEIP_TEST_DOMAIN="fakeip.podkop.fyi"
TMP_SING_BOX_FOLDER="/tmp/sing-box"
TMP_RULESET_FOLDER="$TMP_SING_BOX_FOLDER/rulesets"
TMP_SUBSCRIPTION_FOLDER="$TMP_SING_BOX_FOLDER/subscriptions"
CLOUDFLARE_OCTETS="8.47 162.159 188.114" # Endpoints https://github.com/ampetelin/warp-endpoint-checker
JQ_REQUIRED_VERSION="1.7.1"
COREUTILS_BASE64_REQUIRED_VERSION="9.7"
+143
View File
@@ -275,6 +275,149 @@ download_to_file() {
done
}
get_device_model() {
local model=""
if [ -f /tmp/sysinfo/model ]; then
model="$(cat /tmp/sysinfo/model 2>/dev/null)"
fi
echo "${model:-OpenWrt Router}"
}
get_kernel_version() {
uname -r
}
get_sing_box_version() {
local version=""
if command -v sing-box >/dev/null 2>&1; then
version="$(sing-box version 2>/dev/null | head -1 | awk '{print $NF}')"
fi
echo "${version:-1.0}"
}
generate_hwid() {
local mac="" model="" raw_hash=""
if [ -f /sys/class/net/eth0/address ]; then
mac="$(cat /sys/class/net/eth0/address 2>/dev/null)"
elif [ -f /sys/class/net/br-lan/address ]; then
mac="$(cat /sys/class/net/br-lan/address 2>/dev/null)"
fi
model="$(get_device_model)"
raw_hash="$(printf '%s-%s' "$mac" "$model" | md5sum | cut -c1-16)"
printf '%s-%s-%s-%s' \
"$(echo "$raw_hash" | cut -c1-4)" \
"$(echo "$raw_hash" | cut -c5-8)" \
"$(echo "$raw_hash" | cut -c9-12)" \
"$(echo "$raw_hash" | cut -c13-16)"
}
download_subscription() {
local url="$1"
local filepath="$2"
local http_proxy_address="$3"
local retries="${4:-3}"
local wait="${5:-2}"
local timeout="${6:-10}"
local tmpfile attempt
tmpfile="${filepath}.part.$$"
rm -f "$tmpfile"
for attempt in $(seq 1 "$retries"); do
if [ -n "$http_proxy_address" ]; then
http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" \
wget -T "$timeout" -O "$tmpfile" \
--header "User-Agent: singbox/$(get_sing_box_version)" \
--header "X-HWID: $(generate_hwid)" \
--header "X-Device-OS: OpenWrt Linux" \
--header "X-Device-Model: $(get_device_model)" \
--header "X-Ver-OS: $(get_kernel_version)" \
--header "Accept-Language: ru-RU,en,*" \
--header "X-Device-Locale: EN" \
"$url"
else
wget -T "$timeout" -O "$tmpfile" \
--header "User-Agent: singbox/$(get_sing_box_version)" \
--header "X-HWID: $(generate_hwid)" \
--header "X-Device-OS: OpenWrt Linux" \
--header "X-Device-Model: $(get_device_model)" \
--header "X-Ver-OS: $(get_kernel_version)" \
--header "Accept-Language: ru-RU,en,*" \
--header "X-Device-Locale: EN" \
"$url"
fi
if [ $? -eq 0 ] && [ -s "$tmpfile" ]; then
mv "$tmpfile" "$filepath"
return 0
fi
rm -f "$tmpfile"
log "Attempt $attempt/$retries to download subscription failed" "warn"
sleep "$wait"
done
rm -f "$tmpfile"
return 1
}
check_subscription_connectivity() {
local url="$1"
local http_proxy_address="$2"
local retries="${3:-3}"
local wait="${4:-2}"
local timeout="${5:-5}"
local attempt
for attempt in $(seq 1 "$retries"); do
if [ -n "$http_proxy_address" ]; then
http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" \
wget -q -T "$timeout" -O /dev/null \
--header "User-Agent: singbox/$(get_sing_box_version)" \
--header "X-HWID: $(generate_hwid)" \
--header "X-Device-OS: OpenWrt Linux" \
--header "X-Device-Model: $(get_device_model)" \
--header "X-Ver-OS: $(get_kernel_version)" \
--header "Accept-Language: ru-RU,en,*" \
--header "X-Device-Locale: EN" \
"$url" && return 0
else
wget -q -T "$timeout" -O /dev/null \
--header "User-Agent: singbox/$(get_sing_box_version)" \
--header "X-HWID: $(generate_hwid)" \
--header "X-Device-OS: OpenWrt Linux" \
--header "X-Device-Model: $(get_device_model)" \
--header "X-Ver-OS: $(get_kernel_version)" \
--header "Accept-Language: ru-RU,en,*" \
--header "X-Device-Locale: EN" \
"$url" && return 0
fi
[ "$attempt" -lt "$retries" ] && sleep "$wait"
done
return 1
}
validate_subscription_file() {
local filepath="$1"
[ -s "$filepath" ] || return 1
jq -e '
type == "object" and
(.outbounds | type == "array") and
((.outbounds | length) > 0)
' "$filepath" > /dev/null 2>&1
}
# Converts Windows-style line endings (CRLF) to Unix-style (LF)
convert_crlf_to_lf() {
local filepath="$1"
@@ -328,3 +328,129 @@ sing_box_cf_add_single_key_reject_rule() {
echo "$config"
}
sing_box_cf_add_subscription_outbounds() {
local config="$1"
local section="$2"
local subscription_json_path="$3"
SUBSCRIPTION_OUTBOUND_TAGS=""
SUBSCRIPTION_OUTBOUND_TAGS_JSON="[]"
SUBSCRIPTION_OUTBOUND_NAMES=""
SING_BOX_CF_LAST_CONFIG="$config"
if [ ! -f "$subscription_json_path" ]; then
log "Subscription JSON file not found: $subscription_json_path" "error"
echo "$config"
return 1
fi
local outbounds_count
outbounds_count=$(jq -r '[.outbounds[] | select(
.type != "selector" and
.type != "urltest" and
.type != "direct" and
.type != "dns" and
.type != "block"
)] | length' "$subscription_json_path" 2>/dev/null)
if [ -z "$outbounds_count" ] || [ "$outbounds_count" -eq 0 ]; then
log "No proxy outbounds found in subscription JSON" "error"
echo "$config"
return 1
fi
log "Found $outbounds_count proxy outbounds in subscription" "info"
local i added_count
local outbound_json display_name outbound_tag outbound_type outbound_tls_enabled preferred_tag base_tag tag_suffix
i=1
added_count=0
while [ "$i" -le "$outbounds_count" ]; do
outbound_json=$(jq -c "[.outbounds[] | select(
.type != \"selector\" and
.type != \"urltest\" and
.type != \"direct\" and
.type != \"dns\" and
.type != \"block\"
)][$i - 1]" "$subscription_json_path" 2>/dev/null)
if [ -z "$outbound_json" ] || [ "$outbound_json" = "null" ]; then
i=$((i + 1))
continue
fi
display_name=$(echo "$outbound_json" | jq -r '.remark // .tag // "server-'"$i"'"' 2>/dev/null)
outbound_type=$(echo "$outbound_json" | jq -r '.type // ""' 2>/dev/null)
outbound_tls_enabled=$(echo "$outbound_json" | jq -r '.tls.enabled // false' 2>/dev/null)
if [ "$outbound_type" = "shadowsocks" ] && [ "$outbound_tls_enabled" = "true" ]; then
log "Skip unsupported Shadowsocks outbound with tls: '$display_name'" "warn"
i=$((i + 1))
continue
fi
preferred_tag=$(echo "$outbound_json" | jq -r '.tag // .remark // "server-'"$i"'"' 2>/dev/null)
if [ -z "$preferred_tag" ] || [ "$preferred_tag" = "null" ]; then
preferred_tag="server-$i"
fi
base_tag="$preferred_tag"
outbound_tag="$base_tag"
tag_suffix=1
while printf '%s' "$config" | jq -e --arg tag "$outbound_tag" '.outbounds[]? | select(.tag == $tag)' > /dev/null 2>&1; do
outbound_tag="${base_tag}-${tag_suffix}"
tag_suffix=$((tag_suffix + 1))
done
local clean_outbound updated_config validation_tmp
clean_outbound=$(echo "$outbound_json" | jq -c 'del(.tag) | del(.remark)' 2>/dev/null)
updated_config=$(sing_box_cm_add_raw_outbound "$config" "$outbound_tag" "$clean_outbound" 2>/dev/null)
if [ -z "$updated_config" ]; then
log "Skip invalid outbound from subscription: '$display_name'" "warn"
i=$((i + 1))
continue
fi
validation_tmp="$(mktemp)"
sing_box_cm_save_config_to_file "$updated_config" "$validation_tmp"
if ! sing-box -c "$validation_tmp" check > /dev/null 2>&1; then
rm -f "$validation_tmp"
log "Skip unsupported outbound for current sing-box: '$display_name'" "warn"
i=$((i + 1))
continue
fi
rm -f "$validation_tmp"
config="$updated_config"
if [ -z "$SUBSCRIPTION_OUTBOUND_TAGS" ]; then
SUBSCRIPTION_OUTBOUND_TAGS="$outbound_tag"
else
SUBSCRIPTION_OUTBOUND_TAGS="$SUBSCRIPTION_OUTBOUND_TAGS,$outbound_tag"
fi
SUBSCRIPTION_OUTBOUND_TAGS_JSON=$(
printf '%s' "$SUBSCRIPTION_OUTBOUND_TAGS_JSON" | jq -ac --arg tag "$outbound_tag" '. + [$tag]' 2>/dev/null
)
if [ -z "$SUBSCRIPTION_OUTBOUND_TAGS_JSON" ]; then
SUBSCRIPTION_OUTBOUND_TAGS_JSON="[]"
fi
if [ -z "$SUBSCRIPTION_OUTBOUND_NAMES" ]; then
SUBSCRIPTION_OUTBOUND_NAMES="$display_name"
else
SUBSCRIPTION_OUTBOUND_NAMES="$(printf '%s\n%s' "$SUBSCRIPTION_OUTBOUND_NAMES" "$display_name")"
fi
added_count=$((added_count + 1))
i=$((i + 1))
done
log "Added $added_count subscription outbounds for rule '$section'" "info"
SING_BOX_CF_LAST_CONFIG="$config"
echo "$config"
}