mirror of
https://github.com/2dust/v2rayN.git
synced 2026-07-26 09:52:05 +03:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fcf6c1e3ae | ||
|
|
ab907e371b | ||
|
|
ef4dda0996 | ||
|
|
f2e0bd4eba | ||
|
|
bca07f801d | ||
|
|
54a3388c99 | ||
|
|
5140954b37 | ||
|
|
c7efe39e1c | ||
|
|
a74ca3f9d0 | ||
|
|
ed61f5c420 | ||
|
|
bf98c4007f | ||
|
|
78dcf51c3c | ||
|
|
1e59344074 | ||
|
|
1d2442d58a | ||
|
|
3448782925 | ||
|
|
e6f4a57913 | ||
|
|
4ae5c021fd | ||
|
|
0a357fd1a7 | ||
|
|
5ba5a805ce | ||
|
|
807f0aba06 | ||
|
|
f4a2086dfb | ||
|
|
ccb0ffb3b6 | ||
|
|
1fa1246c0b | ||
|
|
14cc37d07a | ||
|
|
c7519f8ea7 | ||
|
|
0c796a157b | ||
|
|
a9824fe6ec | ||
|
|
f18758d4bf | ||
|
|
c1a009a409 |
@@ -100,7 +100,6 @@ csharp_style_prefer_tuple_swap = true:warning
|
||||
csharp_style_prefer_utf8_string_literals = true:warning
|
||||
csharp_style_throw_expression = true:warning
|
||||
csharp_style_unused_value_assignment_preference = discard_variable:warning
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable:warning
|
||||
csharp_using_directive_placement = outside_namespace:warning
|
||||
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:warning
|
||||
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:warning
|
||||
|
||||
6
.github/workflows/build-linux.yml
vendored
6
.github/workflows/build-linux.yml
vendored
@@ -335,9 +335,9 @@ jobs:
|
||||
|
||||
env:
|
||||
RELEASE_TAG: ${{ case(inputs.release_tag != '', inputs.release_tag, github.ref_name) }}
|
||||
QCOW2_URL: https://github.com/xujiegb/debian-loong64-qcow2/releases/download/13.4/debian13-loong64.qcow2
|
||||
EFI_CODE_URL: https://github.com/xujiegb/debian-loong64-qcow2/releases/download/13.4/edk2-loongarch64-code.fd
|
||||
EFI_VARS_URL: https://github.com/xujiegb/debian-loong64-qcow2/releases/download/13.4/edk2-loongarch64-vars.fd
|
||||
QCOW2_URL: https://github.com/xujiegb/debian-loong64-qcow2/releases/download/13.5/debian13-loong64.qcow2
|
||||
EFI_CODE_URL: https://github.com/xujiegb/debian-loong64-qcow2/releases/download/13.5/edk2-loongarch64-code.fd
|
||||
EFI_VARS_URL: https://github.com/xujiegb/debian-loong64-qcow2/releases/download/13.5/edk2-loongarch64-vars.fd
|
||||
QCOW2_IMAGE: debian13-loong64.qcow2
|
||||
EFI_CODE: edk2-loongarch64-code.fd
|
||||
EFI_VARS: edk2-loongarch64-vars.fd
|
||||
|
||||
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -52,7 +52,7 @@ jobs:
|
||||
fetch-depth: '0'
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v5.2.0
|
||||
uses: actions/setup-dotnet@v5.3.0
|
||||
with:
|
||||
dotnet-version: '10.0.1xx'
|
||||
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
fetch-depth: '0'
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v5.2.0
|
||||
uses: actions/setup-dotnet@v5.3.0
|
||||
with:
|
||||
dotnet-version: '8.0.x'
|
||||
|
||||
|
||||
@@ -22,7 +22,17 @@ cat >"$PackagePath/v2rayN.app/Contents/Info.plist" <<-EOF
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<string>en</string>
|
||||
<key>CFBundleLocalizations</key>
|
||||
<array>
|
||||
<string>zh-Hans</string>
|
||||
<string>zh-Hant</string>
|
||||
<string>en</string>
|
||||
<string>fa</string>
|
||||
<string>fr</string>
|
||||
<string>ru</string>
|
||||
<string>hu</string>
|
||||
</array>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>v2rayN</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>7.22.2</Version>
|
||||
<Version>7.22.5</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.4.1" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.13" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.15" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.15" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.16" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.16" />
|
||||
<PackageVersion Include="AwesomeAssertions" Version="9.4.0" />
|
||||
<PackageVersion Include="DialogHost.Avalonia" Version="0.11.0" />
|
||||
<PackageVersion Include="ReactiveUI.Avalonia" Version="11.4.12" />
|
||||
@@ -26,7 +26,7 @@
|
||||
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7.3" />
|
||||
<PackageVersion Include="NLog" Version="6.1.3" />
|
||||
<PackageVersion Include="sqlite-net-e" Version="1.11.0" />
|
||||
<PackageVersion Include="Repobot.SQLite.Unofficial" Version="3.53.1.4" />
|
||||
<PackageVersion Include="Repobot.SQLite.Unofficial" Version="3.53.1.7" />
|
||||
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
|
||||
<PackageVersion Include="Tmds.DBus.Protocol" Version="0.21.3" />
|
||||
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
|
||||
@@ -34,6 +34,6 @@
|
||||
<PackageVersion Include="xunit.v3" Version="3.2.2" />
|
||||
<PackageVersion Include="YamlDotNet" Version="17.1.0" />
|
||||
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.22" />
|
||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.4-preview.1.1" />
|
||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.4" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
92
v2rayN/ServiceLib/Common/CountryExtension.cs
Normal file
92
v2rayN/ServiceLib/Common/CountryExtension.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
namespace ServiceLib.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for country code utilities
|
||||
/// </summary>
|
||||
public static class CountryExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Country code to emoji flag mapping for common countries
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, string> CountryEmojiMap = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
// Asia
|
||||
{ "CN", "🇨🇳" }, // China
|
||||
{ "HK", "🇭🇰" }, // Hong Kong
|
||||
{ "TW", "🇹🇼" }, // Taiwan
|
||||
{ "JP", "🇯🇵" }, // Japan
|
||||
{ "SG", "🇸🇬" }, // Singapore
|
||||
{ "KR", "🇰🇷" }, // South Korea
|
||||
{ "TH", "🇹🇭" }, // Thailand
|
||||
{ "VN", "🇻🇳" }, // Vietnam
|
||||
{ "ID", "🇮🇩" }, // Indonesia
|
||||
{ "PH", "🇵🇭" }, // Philippines
|
||||
{ "MY", "🇲🇾" }, // Malaysia
|
||||
{ "IN", "🇮🇳" }, // India
|
||||
{ "PK", "🇵🇰" }, // Pakistan
|
||||
{ "BD", "🇧🇩" }, // Bangladesh
|
||||
{ "LK", "🇱🇰" }, // Sri Lanka
|
||||
{ "KH", "🇰🇭" }, // Cambodia
|
||||
{ "LA", "🇱🇦" }, // Laos
|
||||
{ "MM", "🇲🇲" }, // Myanmar
|
||||
|
||||
// Americas
|
||||
{ "US", "🇺🇸" }, // United States
|
||||
{ "CA", "🇨🇦" }, // Canada
|
||||
{ "MX", "🇲🇽" }, // Mexico
|
||||
{ "BR", "🇧🇷" }, // Brazil
|
||||
{ "AR", "🇦🇷" }, // Argentina
|
||||
{ "CL", "🇨🇱" }, // Chile
|
||||
{ "CO", "🇨🇴" }, // Colombia
|
||||
|
||||
// Europe
|
||||
{ "GB", "🇬🇧" }, // United Kingdom
|
||||
{ "DE", "🇩🇪" }, // Germany
|
||||
{ "FR", "🇫🇷" }, // France
|
||||
{ "IT", "🇮🇹" }, // Italy
|
||||
{ "ES", "🇪🇸" }, // Spain
|
||||
{ "RU", "🇷🇺" }, // Russia
|
||||
{ "NL", "🇳🇱" }, // Netherlands
|
||||
{ "CH", "🇨🇭" }, // Switzerland
|
||||
{ "SE", "🇸🇪" }, // Sweden
|
||||
{ "NO", "🇳🇴" }, // Norway
|
||||
{ "DK", "🇩🇰" }, // Denmark
|
||||
{ "FI", "🇫🇮" }, // Finland
|
||||
{ "PL", "🇵🇱" }, // Poland
|
||||
{ "CZ", "🇨🇿" }, // Czech Republic
|
||||
{ "AT", "🇦🇹" }, // Austria
|
||||
{ "GR", "🇬🇷" }, // Greece
|
||||
{ "PT", "🇵🇹" }, // Portugal
|
||||
{ "TR", "🇹🇷" }, // Turkey
|
||||
{ "UA", "🇺🇦" }, // Ukraine
|
||||
{ "RO", "🇷🇴" }, // Romania
|
||||
|
||||
// Middle East & Central Asia
|
||||
{ "AE", "🇦🇪" }, // United Arab Emirates
|
||||
{ "SA", "🇸🇦" }, // Saudi Arabia
|
||||
{ "IL", "🇮🇱" }, // Israel
|
||||
{ "KZ", "🇰🇿" }, // Kazakhstan
|
||||
|
||||
// Oceania
|
||||
{ "AU", "🇦🇺" }, // Australia
|
||||
{ "NZ", "🇳🇿" }, // New Zealand
|
||||
|
||||
// Africa
|
||||
{ "ZA", "🇿🇦" }, // South Africa
|
||||
{ "EG", "🇪🇬" }, // Egypt
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Converts country code to flag emoji using predefined mapping
|
||||
/// Example: "US" -> "🇺🇸", "CN" -> "🇨🇳"
|
||||
/// </summary>
|
||||
public static string? CountryToEmoji(this string? countryCode)
|
||||
{
|
||||
if (countryCode.IsNullOrEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return CountryEmojiMap.TryGetValue(countryCode, out var emoji) ? emoji : null;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ public enum EServerColName
|
||||
SubRemarks,
|
||||
DelayVal,
|
||||
SpeedVal,
|
||||
IpInfo,
|
||||
|
||||
TodayDown,
|
||||
TodayUp,
|
||||
|
||||
@@ -7,6 +7,7 @@ public static class AppEvents
|
||||
public static readonly EventChannel<Unit> AddServerViaScanRequested = new();
|
||||
public static readonly EventChannel<Unit> AddServerViaClipboardRequested = new();
|
||||
public static readonly EventChannel<bool> SubscriptionsUpdateRequested = new();
|
||||
public static readonly EventChannel<bool> HasUpdateNotified = new();
|
||||
|
||||
public static readonly EventChannel<Unit> ProfilesRefreshRequested = new();
|
||||
public static readonly EventChannel<Unit> SubscriptionsRefreshRequested = new();
|
||||
|
||||
@@ -55,7 +55,7 @@ public class Global
|
||||
public const string DnsOutboundTag = "dns";
|
||||
public const string DnsTag = "dns-module";
|
||||
public const string DirectDnsTag = "direct-dns";
|
||||
public const string BalancerTagSuffix = "-round";
|
||||
public const string BalancerTagSuffix = "-balancer";
|
||||
public const string StreamSecurity = "tls";
|
||||
public const string StreamSecurityReality = "reality";
|
||||
public const string Loopback = "127.0.0.1";
|
||||
@@ -149,6 +149,9 @@ public class Global
|
||||
public static readonly List<string> SpeedTestUrls =
|
||||
[
|
||||
@"https://cachefly.cachefly.net/50mb.test",
|
||||
@"https://cachefly.cachefly.net/100mb.test",
|
||||
@"https://cachefly.cachefly.net/1mb.test",
|
||||
@"https://cachefly.cachefly.net/10mb.test",
|
||||
@"https://speed.cloudflare.com/__down?bytes=10000000",
|
||||
@"https://speed.cloudflare.com/__down?bytes=50000000",
|
||||
@"https://speed.cloudflare.com/__down?bytes=99999999",
|
||||
@@ -157,6 +160,8 @@ public class Global
|
||||
public static readonly List<string> SpeedPingTestUrls =
|
||||
[
|
||||
@"https://www.google.com/generate_204",
|
||||
@"https://www.youtube.com/generate_204",
|
||||
@"https://www.googlevideo.com/generate_204",
|
||||
@"https://www.gstatic.com/generate_204",
|
||||
@"https://www.apple.com/library/test/success.html",
|
||||
@"http://www.msftconnecttest.com/connecttest.txt"
|
||||
@@ -207,6 +212,10 @@ public class Global
|
||||
|
||||
public const string NaiveQuicProtocolShare = "naive+quic://";
|
||||
|
||||
public const string SOCKS5Protocol = "socks5://";
|
||||
|
||||
public const string SOCKS4Protocol = "socks4://";
|
||||
|
||||
public static readonly Dictionary<EConfigType, string> ProtocolShares = new()
|
||||
{
|
||||
{ EConfigType.VMess, "vmess://" },
|
||||
@@ -676,15 +685,15 @@ public class Global
|
||||
{ "one.one.one.one", ["1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001"] },
|
||||
{ "1dot1dot1dot1.cloudflare-dns.com", ["1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001"] },
|
||||
{ "cloudflare-dns.com", ["104.16.249.249", "104.16.248.249", "2606:4700::6810:f8f9", "2606:4700::6810:f9f9"] },
|
||||
{ "dns.cloudflare.com", ["104.16.132.229", "104.16.133.229", "2606:4700::6810:84e5", "2606:4700::6810:85e5"] },
|
||||
{ "dns.cloudflare.com", ["162.159.61.8", "172.64.41.8", "2a06:98c1:52::8", "2803:f800:53::8"] },
|
||||
{ "dot.pub", ["1.12.12.12", "120.53.53.53"] },
|
||||
{ "doh.pub", ["1.12.12.12", "120.53.53.53"] },
|
||||
{ "dns.quad9.net", ["9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9"] },
|
||||
{ "dns.yandex.net", ["77.88.8.8", "77.88.8.1", "2a02:6b8::feed:0ff", "2a02:6b8:0:1::feed:0ff"] },
|
||||
{ "dns.sb", ["185.222.222.222", "2a09::"] },
|
||||
{ "dns.sb", ["45.11.45.11", "185.222.222.222", "2a09::", "2a11::"] },
|
||||
{ "dns.umbrella.com", ["208.67.220.220", "208.67.222.222", "2620:119:35::35", "2620:119:53::53"] },
|
||||
{ "dns.sse.cisco.com", ["208.67.220.220", "208.67.222.222", "2620:119:35::35", "2620:119:53::53"] },
|
||||
{ "engage.cloudflareclient.com", ["162.159.192.1"] }
|
||||
{ "engage.cloudflareclient.com", ["162.159.192.1", "2606:4700:d0::a29f:c001"] }
|
||||
};
|
||||
|
||||
public static readonly List<string> ExpectedIPs =
|
||||
|
||||
@@ -111,7 +111,8 @@ public static class AutoStartupHandler
|
||||
task.Settings.RunOnlyIfIdle = false;
|
||||
task.Settings.IdleSettings.StopOnIdleEnd = false;
|
||||
task.Settings.ExecutionTimeLimit = TimeSpan.Zero;
|
||||
task.Triggers.Add(new Microsoft.Win32.TaskScheduler.LogonTrigger { UserId = logonUser, Delay = TimeSpan.FromSeconds(30) });
|
||||
task.Settings.Priority = ProcessPriorityClass.Normal;
|
||||
task.Triggers.Add(new Microsoft.Win32.TaskScheduler.LogonTrigger { UserId = logonUser });
|
||||
task.Principal.RunLevel = Microsoft.Win32.TaskScheduler.TaskRunLevel.Highest;
|
||||
task.Actions.Add(new Microsoft.Win32.TaskScheduler.ExecAction(fileName.AppendQuotes(), null, Path.GetDirectoryName(fileName)));
|
||||
|
||||
|
||||
@@ -134,6 +134,12 @@ public class NodeValidator
|
||||
{
|
||||
v.Error(string.Format(ResUI.MsgInvalidProperty, "TLS Certificate"));
|
||||
}
|
||||
|
||||
// Check for deprecated allowInsecure property when TLS is enabled
|
||||
if (item.AllowInsecure == "true")
|
||||
{
|
||||
v.Warning(ResUI.MsgAllowInsecureDeprecated);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.StreamSecurity == Global.StreamSecurityReality)
|
||||
|
||||
@@ -923,6 +923,7 @@ public static class ConfigHandler
|
||||
Delay = t33?.Delay ?? 0,
|
||||
Speed = t33?.Speed ?? 0,
|
||||
Sort = t33?.Sort ?? 0,
|
||||
IpInfo = t33?.IpInfo ?? string.Empty,
|
||||
TodayDown = (t22?.TodayDown ?? 0).ToString("D16"),
|
||||
TodayUp = (t22?.TodayUp ?? 0).ToString("D16"),
|
||||
TotalDown = (t22?.TotalDown ?? 0).ToString("D16"),
|
||||
@@ -943,6 +944,7 @@ public static class ConfigHandler
|
||||
EServerColName.StreamSecurity => lstProfile.OrderBy(t => t.StreamSecurity).ToList(),
|
||||
EServerColName.DelayVal => lstProfile.OrderBy(t => t.Delay).ToList(),
|
||||
EServerColName.SpeedVal => lstProfile.OrderBy(t => t.Speed).ToList(),
|
||||
EServerColName.IpInfo => lstProfile.OrderBy(t => t.IpInfo).ToList(),
|
||||
EServerColName.SubRemarks => lstProfile.OrderBy(t => t.Subid).ToList(),
|
||||
EServerColName.TodayDown => lstProfile.OrderBy(t => t.TodayDown).ToList(),
|
||||
EServerColName.TodayUp => lstProfile.OrderBy(t => t.TodayUp).ToList(),
|
||||
@@ -963,6 +965,7 @@ public static class ConfigHandler
|
||||
EServerColName.StreamSecurity => lstProfile.OrderByDescending(t => t.StreamSecurity).ToList(),
|
||||
EServerColName.DelayVal => lstProfile.OrderByDescending(t => t.Delay).ToList(),
|
||||
EServerColName.SpeedVal => lstProfile.OrderByDescending(t => t.Speed).ToList(),
|
||||
EServerColName.IpInfo => lstProfile.OrderByDescending(t => t.IpInfo).ToList(),
|
||||
EServerColName.SubRemarks => lstProfile.OrderByDescending(t => t.Subid).ToList(),
|
||||
EServerColName.TodayDown => lstProfile.OrderByDescending(t => t.TodayDown).ToList(),
|
||||
EServerColName.TodayUp => lstProfile.OrderByDescending(t => t.TodayUp).ToList(),
|
||||
|
||||
@@ -4,53 +4,41 @@ public static class ConnectionHandler
|
||||
{
|
||||
private static readonly string _tag = "ConnectionHandler";
|
||||
|
||||
/// <summary>
|
||||
/// Runs ping and IP checks and returns a formatted result string.
|
||||
/// </summary>
|
||||
public static async Task<string> RunAvailabilityCheck()
|
||||
{
|
||||
var time = await GetRealPingTimeInfo();
|
||||
var ip = time > 0 ? await GetIPInfo() ?? Global.None : Global.None;
|
||||
var ip = time > 0 ? await GetIPInfo() : Global.None;
|
||||
|
||||
return string.Format(ResUI.TestMeOutput, time, ip);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets IP information using the default local proxy.
|
||||
/// </summary>
|
||||
private static async Task<string?> GetIPInfo()
|
||||
{
|
||||
var url = AppManager.Instance.Config.SpeedTestItem.IPAPIUrl;
|
||||
if (url.IsNullOrEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var webProxy = await GetWebProxy();
|
||||
|
||||
var downloadHandle = new DownloadService();
|
||||
var result = await downloadHandle.TryDownloadString(url, true, "");
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var ipInfo = JsonUtils.Deserialize<IPAPIInfo>(result);
|
||||
if (ipInfo == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var ip = ipInfo.ip ?? ipInfo.clientIp ?? ipInfo.ip_addr ?? ipInfo.query;
|
||||
var country = ipInfo.country_code ?? ipInfo.country ?? ipInfo.countryCode ?? ipInfo.location?.country_code;
|
||||
|
||||
return $"({country ?? "unknown"}) {ip}";
|
||||
var ipInfo = await GetIPInfo(webProxy);
|
||||
return ipInfo?.ToString() ?? Global.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Measures real ping time using configured test URL.
|
||||
/// </summary>
|
||||
private static async Task<int> GetRealPingTimeInfo()
|
||||
{
|
||||
var responseTime = -1;
|
||||
try
|
||||
{
|
||||
var port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{port}");
|
||||
var url = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
|
||||
var webProxy = await GetWebProxy();
|
||||
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
responseTime = await GetRealPingTime(url, webProxy, 10);
|
||||
responseTime = await GetRealPingTime(webProxy, 10);
|
||||
if (responseTime > 0)
|
||||
{
|
||||
break;
|
||||
@@ -66,8 +54,21 @@ public static class ConnectionHandler
|
||||
return responseTime;
|
||||
}
|
||||
|
||||
public static async Task<int> GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout)
|
||||
/// <summary>
|
||||
/// Creates local SOCKS proxy instance.
|
||||
/// </summary>
|
||||
private static async Task<WebProxy?> GetWebProxy()
|
||||
{
|
||||
var port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
return new WebProxy($"socks5://{Global.Loopback}:{port}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Measures response time by sending HTTP requests through proxy.
|
||||
/// </summary>
|
||||
public static async Task<int> GetRealPingTime(IWebProxy? webProxy, int downloadTimeout)
|
||||
{
|
||||
var url = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
|
||||
var responseTime = -1;
|
||||
try
|
||||
{
|
||||
@@ -95,4 +96,41 @@ public static class ConnectionHandler
|
||||
}
|
||||
return responseTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets IP and country information through specified proxy.
|
||||
/// </summary>
|
||||
public static async Task<IpInfoResult?> GetIPInfo(IWebProxy? webProxy)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = AppManager.Instance.Config.SpeedTestItem.IPAPIUrl;
|
||||
if (url.IsNullOrEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var downloadHandle = new DownloadService();
|
||||
var result = await downloadHandle.TryDownloadString(url, webProxy, "");
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var ipInfo = JsonUtils.Deserialize<IPAPIInfo>(result);
|
||||
if (ipInfo == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var ip = ipInfo.ip ?? ipInfo.clientIp ?? ipInfo.ip_addr ?? ipInfo.query;
|
||||
var country = ipInfo.country_code ?? ipInfo.country ?? ipInfo.countryCode ?? ipInfo.location?.country_code ?? "unknown";
|
||||
|
||||
return new IpInfoResult(country, ip);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,9 @@ public class FmtHandler
|
||||
{
|
||||
return ShadowsocksFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.SOCKS]))
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.SOCKS])
|
||||
|| str.StartsWith(Global.SOCKS5Protocol)
|
||||
|| str.StartsWith(Global.SOCKS4Protocol))
|
||||
{
|
||||
return SocksFmt.Resolve(str, out msg);
|
||||
}
|
||||
@@ -65,7 +67,8 @@ public class FmtHandler
|
||||
{
|
||||
return VLESSFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Hysteria2]) || str.StartsWith(Global.Hysteria2ProtocolShare))
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Hysteria2])
|
||||
|| str.StartsWith(Global.Hysteria2ProtocolShare))
|
||||
{
|
||||
return Hysteria2Fmt.Resolve(str, out msg);
|
||||
}
|
||||
|
||||
@@ -99,12 +99,17 @@ public class SocksFmt : BaseFmt
|
||||
};
|
||||
// parse base64 UserInfo
|
||||
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
||||
var userInfo = Utils.Base64Decode(rawUserInfo);
|
||||
var userInfoParts = userInfo.Split([':'], 2);
|
||||
if (userInfoParts.Length == 2)
|
||||
if (rawUserInfo.IsNotEmpty())
|
||||
{
|
||||
item.Username = userInfoParts.First();
|
||||
item.Password = userInfoParts[1];
|
||||
var userInfoParts = rawUserInfo.Contains(':')
|
||||
? rawUserInfo.Split(":", 2)
|
||||
: Utils.Base64Decode(rawUserInfo).Split(":", 2);
|
||||
|
||||
if (userInfoParts.Length == 2)
|
||||
{
|
||||
item.Username = userInfoParts.First();
|
||||
item.Password = userInfoParts.Last();
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
|
||||
@@ -113,6 +113,7 @@ public sealed class CoreInfoManager
|
||||
DownloadUrlLinux64 = urlN + "/download/{0}/v2rayN-linux-64.zip",
|
||||
DownloadUrlLinuxArm64 = urlN + "/download/{0}/v2rayN-linux-arm64.zip",
|
||||
DownloadUrlLinuxRiscV64 = urlN + "/download/{0}/v2rayN-linux-riscv64.zip",
|
||||
DownloadUrlLinuxLoong64 = urlN + "/download/{0}/v2rayN-linux-loong64.zip",
|
||||
DownloadUrlOSX64 = urlN + "/download/{0}/v2rayN-macos-64.zip",
|
||||
DownloadUrlOSXArm64 = urlN + "/download/{0}/v2rayN-macos-arm64.zip",
|
||||
},
|
||||
@@ -157,6 +158,7 @@ public sealed class CoreInfoManager
|
||||
DownloadUrlLinux64 = urlXray + "/download/{0}/Xray-linux-64.zip",
|
||||
DownloadUrlLinuxArm64 = urlXray + "/download/{0}/Xray-linux-arm64-v8a.zip",
|
||||
DownloadUrlLinuxRiscV64 = urlXray + "/download/{0}/Xray-linux-riscv64.zip",
|
||||
DownloadUrlLinuxLoong64 = urlXray + "/download/{0}/Xray-linux-loong64.zip",
|
||||
DownloadUrlOSX64 = urlXray + "/download/{0}/Xray-macos-64.zip",
|
||||
DownloadUrlOSXArm64 = urlXray + "/download/{0}/Xray-macos-arm64-v8a.zip",
|
||||
Match = "Xray",
|
||||
@@ -180,6 +182,7 @@ public sealed class CoreInfoManager
|
||||
DownloadUrlLinux64 = urlMihomo + "/download/{0}/mihomo-linux-amd64-v1-{0}.gz",
|
||||
DownloadUrlLinuxArm64 = urlMihomo + "/download/{0}/mihomo-linux-arm64-{0}.gz",
|
||||
DownloadUrlLinuxRiscV64 = urlMihomo + "/download/{0}/mihomo-linux-riscv64-{0}.gz",
|
||||
DownloadUrlLinuxLoong64 = urlMihomo + "/download/{0}/mihomo-linux-loong64-abi2-{0}.gz",
|
||||
DownloadUrlOSX64 = urlMihomo + "/download/{0}/mihomo-darwin-amd64-v1-{0}.gz",
|
||||
DownloadUrlOSXArm64 = urlMihomo + "/download/{0}/mihomo-darwin-arm64-{0}.gz",
|
||||
Match = "Mihomo",
|
||||
@@ -223,6 +226,7 @@ public sealed class CoreInfoManager
|
||||
DownloadUrlLinux64 = urlSingbox + "/download/{0}/sing-box-{1}-linux-amd64.tar.gz",
|
||||
DownloadUrlLinuxArm64 = urlSingbox + "/download/{0}/sing-box-{1}-linux-arm64.tar.gz",
|
||||
DownloadUrlLinuxRiscV64 = urlSingbox + "/download/{0}/sing-box-{1}-linux-riscv64.tar.gz",
|
||||
DownloadUrlLinuxLoong64 = urlSingbox + "/download/{0}/sing-box-{1}-linux-loong64.tar.gz",
|
||||
DownloadUrlOSX64 = urlSingbox + "/download/{0}/sing-box-{1}-darwin-amd64.tar.gz",
|
||||
DownloadUrlOSXArm64 = urlSingbox + "/download/{0}/sing-box-{1}-darwin-arm64.tar.gz",
|
||||
Match = "sing-box",
|
||||
@@ -314,6 +318,7 @@ public sealed class CoreInfoManager
|
||||
names.Add("mihomo-linux-amd64");
|
||||
names.Add("mihomo-linux-arm64");
|
||||
names.Add("mihomo-linux-riscv64");
|
||||
names.Add("mihomo-linux-loong64-abi2");
|
||||
}
|
||||
else if (Utils.IsMacOS())
|
||||
{
|
||||
|
||||
@@ -150,6 +150,14 @@ public class ProfileExManager
|
||||
IndexIdEnqueue(indexId);
|
||||
}
|
||||
|
||||
public void SetTestIpInfo(string indexId, string ipInfo)
|
||||
{
|
||||
var profileEx = GetProfileExItem(indexId);
|
||||
|
||||
profileEx.IpInfo = ipInfo;
|
||||
IndexIdEnqueue(indexId);
|
||||
}
|
||||
|
||||
public void SetSort(string indexId, int sort)
|
||||
{
|
||||
var profileEx = GetProfileExItem(indexId);
|
||||
|
||||
@@ -142,5 +142,10 @@ public class TaskManager
|
||||
await _updateFunc?.Invoke(false, msg);
|
||||
}
|
||||
NoticeManager.Instance.Enqueue(string.Join("\n", msgs));
|
||||
|
||||
if (msgs.Count > 0)
|
||||
{
|
||||
AppEvents.HasUpdateNotified.Publish(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +159,8 @@ public class SpeedTestItem
|
||||
public int MixedConcurrencyCount { get; set; }
|
||||
public string IPAPIUrl { get; set; }
|
||||
public string UdpTestTarget { get; set; }
|
||||
public int? SpeedTestPageSize { get; set; }
|
||||
public int? SpeedTestDelayInterval { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -13,6 +13,7 @@ public class CoreInfo
|
||||
public string? DownloadUrlLinux64 { get; set; }
|
||||
public string? DownloadUrlLinuxArm64 { get; set; }
|
||||
public string? DownloadUrlLinuxRiscV64 { get; set; }
|
||||
public string? DownloadUrlLinuxLoong64 { get; set; }
|
||||
public string? DownloadUrlOSX64 { get; set; }
|
||||
public string? DownloadUrlOSXArm64 { get; set; }
|
||||
public string? Match { get; set; }
|
||||
|
||||
@@ -276,6 +276,7 @@ public class BalancersItem4Ray
|
||||
public List<string>? selector { get; set; }
|
||||
public BalancersStrategy4Ray? strategy { get; set; }
|
||||
public string? tag { get; set; }
|
||||
public string? fallbackTag { get; set; }
|
||||
}
|
||||
|
||||
public class BalancersStrategy4Ray
|
||||
|
||||
@@ -17,3 +17,12 @@ public class LocationInfo
|
||||
{
|
||||
public string? country_code { get; set; }
|
||||
}
|
||||
|
||||
public readonly record struct IpInfoResult(string Country, string? Ip)
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
var emoji = Country.CountryToEmoji();
|
||||
return $"{emoji}({Country}) {Ip}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@ public class ProfileItemModel : ReactiveObject
|
||||
[Reactive]
|
||||
public string SpeedVal { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string IpInfo { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string TodayUp { get; set; }
|
||||
|
||||
|
||||
@@ -8,4 +8,6 @@ public class SpeedTestResult
|
||||
public string? Delay { get; set; }
|
||||
|
||||
public string? Speed { get; set; }
|
||||
|
||||
public string? IpInfo { get; set; }
|
||||
}
|
||||
|
||||
@@ -10,4 +10,5 @@ public class ProfileExItem
|
||||
public decimal Speed { get; set; }
|
||||
public int Sort { get; set; }
|
||||
public string? Message { get; set; }
|
||||
public string? IpInfo { get; set; }
|
||||
}
|
||||
|
||||
27
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
27
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
@@ -564,6 +564,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 IP Info 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string LvTestIpInfo {
|
||||
get {
|
||||
return ResourceManager.GetString("LvTestIpInfo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Speed (MB/s) 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -1311,6 +1320,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 New Update 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuNewUpdate {
|
||||
get {
|
||||
return ResourceManager.GetString("menuNewUpdate", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Open the storage location 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -1896,6 +1914,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Warning: Xray will disable allowInsecure (skip certificate verification) in August 2026. Please switch to pinnedPeerCertSha256 (fixed certificate fingerprint) as soon as possible. allowInsecure will not be usable after its expiration. 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string MsgAllowInsecureDeprecated {
|
||||
get {
|
||||
return ResourceManager.GetString("MsgAllowInsecureDeprecated", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 {0} has a new version available: {1} 的本地化字符串。
|
||||
/// </summary>
|
||||
|
||||
@@ -1740,4 +1740,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||
<data name="MsgNotSupport" xml:space="preserve">
|
||||
<value>Not Support</value>
|
||||
</data>
|
||||
<data name="LvTestIpInfo" xml:space="preserve">
|
||||
<value>IP Info</value>
|
||||
</data>
|
||||
<data name="menuNewUpdate" xml:space="preserve">
|
||||
<value>New Update</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1675,7 +1675,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||
<value>Ajouter [NaïveProxy]</value>
|
||||
</data>
|
||||
<data name="TbInsecureConcurrency" xml:space="preserve">
|
||||
<value>Insecure Concurrency</value>
|
||||
<value>Concurrence non sûre</value>
|
||||
</data>
|
||||
<data name="TbUsername" xml:space="preserve">
|
||||
<value>Nom d’utilisateur</value>
|
||||
@@ -1699,10 +1699,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||
<value>Allow insecure cert fetch (self-signed)</value>
|
||||
</data>
|
||||
<data name="TbAllowInsecureCertFetchTips" xml:space="preserve">
|
||||
<value>Only for fetching self-signed certificates. This may expose you to MITM risks.</value>
|
||||
<value>Pour obtenir des certificats auto-signés uniquement. Risque MITM.</value>
|
||||
</data>
|
||||
<data name="TbSettingsSendThrough" xml:space="preserve">
|
||||
<value>Adresse sortante locale (SendThrough)</value>
|
||||
<value>Adresse de sortie locale (SendThrough)</value>
|
||||
</data>
|
||||
<data name="TbSettingsSendThroughTip" xml:space="preserve">
|
||||
<value>Pour environnements multi-interfaces, entrez l'adresse IPv4 de la machine locale</value>
|
||||
@@ -1717,24 +1717,30 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||
<value>Pour les environnements multi-interfaces, entrez le nom de l'interface à lier. Ne fonctionne que sur les systèmes Windows et en mode TUN</value>
|
||||
</data>
|
||||
<data name="menuUdpTestServer" xml:space="preserve">
|
||||
<value>Test Configurations UDP Delay</value>
|
||||
<value>Tester la latence UDP des profils</value>
|
||||
</data>
|
||||
<data name="TbSettingsUdpTestUrl" xml:space="preserve">
|
||||
<value>UDP Test Url</value>
|
||||
<value>URL de test UDP</value>
|
||||
</data>
|
||||
<data name="TbPreSharedKey" xml:space="preserve">
|
||||
<value>PreSharedKey</value>
|
||||
</data>
|
||||
<data name="menuExport2InnerUri" xml:space="preserve">
|
||||
<value>Export v2rayN Internal Share Link to Clipboard</value>
|
||||
<value>Exporter le lien interne v2rayN vers le presse-papiers</value>
|
||||
</data>
|
||||
<data name="MsgCheckUpdateHasNewVersion" xml:space="preserve">
|
||||
<value>{0} has a new version available: {1}</value>
|
||||
<value>{0} a une nouvelle version disponible: {1}</value>
|
||||
</data>
|
||||
<data name="menuCheckOnly" xml:space="preserve">
|
||||
<value>Only Check</value>
|
||||
<value>Uniquement vérifier</value>
|
||||
</data>
|
||||
<data name="MsgNotSupport" xml:space="preserve">
|
||||
<value>Not Support</value>
|
||||
<value>Non supporté</value>
|
||||
</data>
|
||||
<data name="LvTestIpInfo" xml:space="preserve">
|
||||
<value>Détails IP</value>
|
||||
</data>
|
||||
<data name="menuNewUpdate" xml:space="preserve">
|
||||
<value>Nouvelle MAJ</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1740,4 +1740,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||
<data name="MsgNotSupport" xml:space="preserve">
|
||||
<value>Not Support</value>
|
||||
</data>
|
||||
<data name="LvTestIpInfo" xml:space="preserve">
|
||||
<value>IP Info</value>
|
||||
</data>
|
||||
<data name="menuNewUpdate" xml:space="preserve">
|
||||
<value>New Update</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1740,4 +1740,13 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||
<data name="MsgNotSupport" xml:space="preserve">
|
||||
<value>Not Support</value>
|
||||
</data>
|
||||
<data name="LvTestIpInfo" xml:space="preserve">
|
||||
<value>IP Info</value>
|
||||
</data>
|
||||
<data name="menuNewUpdate" xml:space="preserve">
|
||||
<value>New Update</value>
|
||||
</data>
|
||||
<data name="MsgAllowInsecureDeprecated" xml:space="preserve">
|
||||
<value>Warning: Xray will disable allowInsecure (skip certificate verification) in August 2026. Please switch to pinnedPeerCertSha256 (fixed certificate fingerprint) as soon as possible. allowInsecure will not be usable after its expiration.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -121,7 +121,7 @@
|
||||
<value>Ссылка успешно скопирована в буфер обмена</value>
|
||||
</data>
|
||||
<data name="CheckServerSettings" xml:space="preserve">
|
||||
<value>Сначала проверьте настройки сервера</value>
|
||||
<value>Недопустимая конфигурация, проверьте или выберите заново</value>
|
||||
</data>
|
||||
<data name="ConfigurationFormatIncorrect" xml:space="preserve">
|
||||
<value>Недопустимый формат конфигурации</value>
|
||||
@@ -166,7 +166,7 @@
|
||||
<value>Некорректная конфигурация. Проверьте</value>
|
||||
</data>
|
||||
<data name="InitialConfiguration" xml:space="preserve">
|
||||
<value>Исходная конфигурация</value>
|
||||
<value>Инициализация конфигурации</value>
|
||||
</data>
|
||||
<data name="IsLatestCore" xml:space="preserve">
|
||||
<value>{0} {1} является последней версией</value>
|
||||
@@ -256,7 +256,7 @@
|
||||
<value>Операция не удалась, проверьте и повторите попытку</value>
|
||||
</data>
|
||||
<data name="PleaseFillRemarks" xml:space="preserve">
|
||||
<value>Введите примечания</value>
|
||||
<value>Введите псевдоним</value>
|
||||
</data>
|
||||
<data name="PleaseSelectEncryption" xml:space="preserve">
|
||||
<value>Выберите метод шифрования</value>
|
||||
@@ -307,7 +307,7 @@
|
||||
<value>{0}: одно из обязательных полей</value>
|
||||
</data>
|
||||
<data name="LvRemarks" xml:space="preserve">
|
||||
<value>Примечания</value>
|
||||
<value>Псевдоним</value>
|
||||
</data>
|
||||
<data name="LvUrl" xml:space="preserve">
|
||||
<value>URL (необязательно)</value>
|
||||
@@ -334,13 +334,13 @@
|
||||
<value>Введите корректный пользовательский DNS</value>
|
||||
</data>
|
||||
<data name="TransportPathTip1" xml:space="preserve">
|
||||
<value>*Путь ws/http upgrade/xhttp</value>
|
||||
<value>*Путь ws/httpupgrade/xhttp</value>
|
||||
</data>
|
||||
<data name="TransportPathTip2" xml:space="preserve">
|
||||
<value>*HTTP2-путь</value>
|
||||
</data>
|
||||
<data name="TransportPathTip3" xml:space="preserve">
|
||||
<value>*QUIC-ключ / KCP-seed</value>
|
||||
<value>*Ключ шифрования QUIC</value>
|
||||
</data>
|
||||
<data name="TransportPathTip4" xml:space="preserve">
|
||||
<value>Имя сервиса gRPC</value>
|
||||
@@ -349,13 +349,13 @@
|
||||
<value>*http-хосты, разделённые запятыми (,)</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip2" xml:space="preserve">
|
||||
<value>*Хост ws/http upgrade/xhttp</value>
|
||||
<value>*Хост ws/httpupgrade/xhttp</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip3" xml:space="preserve">
|
||||
<value>*HTTP2-хосты, разделённые запятыми (,)</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip4" xml:space="preserve">
|
||||
<value>Безопасность *QUIC</value>
|
||||
<value>*Метод шифрования QUIC</value>
|
||||
</data>
|
||||
<data name="TransportHeaderType1" xml:space="preserve">
|
||||
<value>Тип raw-камуфляжа</value>
|
||||
@@ -400,7 +400,7 @@
|
||||
<value>Фильтр, нажмите Enter для выполнения</value>
|
||||
</data>
|
||||
<data name="menuCheckUpdate" xml:space="preserve">
|
||||
<value>Проверить обновления</value>
|
||||
<value>Обновить</value>
|
||||
</data>
|
||||
<data name="menuClose" xml:space="preserve">
|
||||
<value>Закрыть</value>
|
||||
@@ -415,13 +415,13 @@
|
||||
<value>Помощь</value>
|
||||
</data>
|
||||
<data name="menuOptionSetting" xml:space="preserve">
|
||||
<value>Настройки</value>
|
||||
<value>Параметры</value>
|
||||
</data>
|
||||
<data name="menuPromotion" xml:space="preserve">
|
||||
<value>Продвижение</value>
|
||||
</data>
|
||||
<data name="menuReload" xml:space="preserve">
|
||||
<value>Перезагрузить</value>
|
||||
<value>Перезапустить службу</value>
|
||||
</data>
|
||||
<data name="menuRoutingSetting" xml:space="preserve">
|
||||
<value>Настройки маршрутизации</value>
|
||||
@@ -445,10 +445,10 @@
|
||||
<value>Настройки групп подписки</value>
|
||||
</data>
|
||||
<data name="menuSubUpdate" xml:space="preserve">
|
||||
<value>Обновить подписку без прокси</value>
|
||||
<value>Обновить все подписки без прокси</value>
|
||||
</data>
|
||||
<data name="menuSubUpdateViaProxy" xml:space="preserve">
|
||||
<value>Обновить подписку с прокси</value>
|
||||
<value>Обновить все подписки через прокси</value>
|
||||
</data>
|
||||
<data name="menuSystemproxy" xml:space="preserve">
|
||||
<value>Системный прокси</value>
|
||||
@@ -562,7 +562,7 @@
|
||||
<value>Сортировка</value>
|
||||
</data>
|
||||
<data name="LvUserAgent" xml:space="preserve">
|
||||
<value>User-Agent</value>
|
||||
<value>User-Agent (необязательно)</value>
|
||||
</data>
|
||||
<data name="TbCancel" xml:space="preserve">
|
||||
<value>Отмена</value>
|
||||
@@ -577,7 +577,7 @@
|
||||
<value>Адрес</value>
|
||||
</data>
|
||||
<data name="TbAllowInsecure" xml:space="preserve">
|
||||
<value>Разрешить небезопасные</value>
|
||||
<value>Пропустить проверку сертификата (allowInsecure)</value>
|
||||
</data>
|
||||
<data name="TbAlpn" xml:space="preserve">
|
||||
<value>ALPN</value>
|
||||
@@ -616,10 +616,10 @@
|
||||
<value>TLS</value>
|
||||
</data>
|
||||
<data name="TipNetwork" xml:space="preserve">
|
||||
<value>*По-умолчанию raw</value>
|
||||
<value>*По умолчанию raw; при неверном выборе подключение будет невозможно</value>
|
||||
</data>
|
||||
<data name="TbCoreType" xml:space="preserve">
|
||||
<value>Ядро</value>
|
||||
<value>Тип ядра</value>
|
||||
</data>
|
||||
<data name="TbFlow5" xml:space="preserve">
|
||||
<value>Управление потоком (Flow)</value>
|
||||
@@ -640,7 +640,7 @@
|
||||
<value>Шифрование</value>
|
||||
</data>
|
||||
<data name="TbSecurity4" xml:space="preserve">
|
||||
<value>Пользователь (необязательно)</value>
|
||||
<value>Имя пользователя (необязательно)</value>
|
||||
</data>
|
||||
<data name="TbSecurity5" xml:space="preserve">
|
||||
<value>Шифрование</value>
|
||||
@@ -649,7 +649,7 @@
|
||||
<value>Порт SOCKS</value>
|
||||
</data>
|
||||
<data name="TipPreSocksPort" xml:space="preserve">
|
||||
<value>* После установки этого значения служба SOCKS будет запущена с использованием Xray/sing-box(TUN) для обеспечения таких функций, как отображение скорости</value>
|
||||
<value>*Порт SOCKS пользовательской конфигурации можно не указывать. Если он задан, Xray/sing-box (TUN) дополнительно запустит предварительную службу SOCKS для разделения трафика, отображения скорости и других функций</value>
|
||||
</data>
|
||||
<data name="TbBrowse" xml:space="preserve">
|
||||
<value>Обзор</value>
|
||||
@@ -682,7 +682,7 @@
|
||||
<value>Настройки типа ядра</value>
|
||||
</data>
|
||||
<data name="TbSettingsDefAllowInsecure" xml:space="preserve">
|
||||
<value>Разрешить небезопасные</value>
|
||||
<value>По умолчанию пропускать проверку сертификата (allowInsecure)</value>
|
||||
</data>
|
||||
<data name="TbSettingsDomainStrategy4Freedom" xml:space="preserve">
|
||||
<value>«Freedom»: стратегия обработки доменов исходящего трафика</value>
|
||||
@@ -703,7 +703,7 @@
|
||||
<value>Показывать скорость в реальном времени (требуется перезапуск)</value>
|
||||
</data>
|
||||
<data name="TbSettingsKeepOlderDedupl" xml:space="preserve">
|
||||
<value>Сохранить старые при удалении дублей</value>
|
||||
<value>При удалении дубликатов сохранять элементы с меньшим порядковым номером</value>
|
||||
</data>
|
||||
<data name="TbSettingsLogEnabled" xml:space="preserve">
|
||||
<value>Записывать логи</value>
|
||||
@@ -730,13 +730,13 @@
|
||||
<value>Смешанный порт</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBoot" xml:space="preserve">
|
||||
<value>Запускать при старте системы</value>
|
||||
<value>Запускать при старте системы (может не сработать)</value>
|
||||
</data>
|
||||
<data name="TbSettingsStatistics" xml:space="preserve">
|
||||
<value>Включить статистику трафика (требуется перезапуск)</value>
|
||||
</data>
|
||||
<data name="TbSettingsSubConvert" xml:space="preserve">
|
||||
<value>URL конвертации подписок</value>
|
||||
<value>URL конвертации подписок (необязательно)</value>
|
||||
</data>
|
||||
<data name="TbSettingsSystemproxy" xml:space="preserve">
|
||||
<value>Настройки системного прокси</value>
|
||||
@@ -787,13 +787,13 @@
|
||||
<value>Запущено от имени администратора</value>
|
||||
</data>
|
||||
<data name="menuMoveBottom" xml:space="preserve">
|
||||
<value>Спуститься вниз</value>
|
||||
<value>Переместить в самый низ</value>
|
||||
</data>
|
||||
<data name="menuMoveDown" xml:space="preserve">
|
||||
<value>Вниз</value>
|
||||
</data>
|
||||
<data name="menuMoveTop" xml:space="preserve">
|
||||
<value>Подняться наверх</value>
|
||||
<value>Переместить в самый верх</value>
|
||||
</data>
|
||||
<data name="menuMoveUp" xml:space="preserve">
|
||||
<value>Вверх</value>
|
||||
@@ -805,7 +805,7 @@
|
||||
<value>Веб-сайт {0}</value>
|
||||
</data>
|
||||
<data name="menuRoutingAdvancedAdd" xml:space="preserve">
|
||||
<value>Добавить</value>
|
||||
<value>Добавить набор правил</value>
|
||||
</data>
|
||||
<data name="menuRoutingAdvancedImportRules" xml:space="preserve">
|
||||
<value>Импортировать правила</value>
|
||||
@@ -841,7 +841,7 @@
|
||||
<value>Добавить правило</value>
|
||||
</data>
|
||||
<data name="menuRuleExportSelected" xml:space="preserve">
|
||||
<value>Экспорт выделенных правил</value>
|
||||
<value>Экспортировать выбранные правила в буфер обмена</value>
|
||||
</data>
|
||||
<data name="menuRuleList" xml:space="preserve">
|
||||
<value>Список правил</value>
|
||||
@@ -859,7 +859,7 @@
|
||||
<value>Документация RuleObject</value>
|
||||
</data>
|
||||
<data name="TbDnsObjectDoc" xml:space="preserve">
|
||||
<value>Поддерживаются DNS-объекты, нажмите для просмотра документации</value>
|
||||
<value>Заполните DNS-объект (формат JSON), нажмите для просмотра документации</value>
|
||||
</data>
|
||||
<data name="SubUrlTips" xml:space="preserve">
|
||||
<value>Для группы оставьте это поле пустым</value>
|
||||
@@ -871,13 +871,13 @@
|
||||
<value>Системные прокси изменены</value>
|
||||
</data>
|
||||
<data name="TbSettingsRouteOnly" xml:space="preserve">
|
||||
<value>Только маршрут</value>
|
||||
<value>Только для маршрутизации (routeOnly)</value>
|
||||
</data>
|
||||
<data name="TbSettingsNotProxyLocalAddress" xml:space="preserve">
|
||||
<value>Не использовать прокси для локальных (интранет) адресов</value>
|
||||
</data>
|
||||
<data name="menuMixedTestServer" xml:space="preserve">
|
||||
<value>Тест задержки и скорости всех серверов (Ctrl+E)</value>
|
||||
<value>Многопоточный тест задержки и скорости (Ctrl+E)</value>
|
||||
</data>
|
||||
<data name="LvTestDelay" xml:space="preserve">
|
||||
<value>Задержка (мс)</value>
|
||||
@@ -886,10 +886,10 @@
|
||||
<value>Скорость (МБ/с)</value>
|
||||
</data>
|
||||
<data name="FailedToRunCore" xml:space="preserve">
|
||||
<value>Не удалось запустить ядро, посмотрите логи</value>
|
||||
<value>Не удалось запустить ядро, см. сообщение</value>
|
||||
</data>
|
||||
<data name="LvFilter" xml:space="preserve">
|
||||
<value>Фильтр по примечаниям (регулярные выражения)</value>
|
||||
<value>Фильтр по псевдониму (регулярные выражения)</value>
|
||||
</data>
|
||||
<data name="TbDisplayLog" xml:space="preserve">
|
||||
<value>Отображать журнал</value>
|
||||
@@ -937,7 +937,7 @@
|
||||
<value>Шрифт (требуется перезапуск)</value>
|
||||
</data>
|
||||
<data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve">
|
||||
<value>Скопируйте файл шрифта TTF/TTC в каталог guiFonts и заново откройте окно настроек</value>
|
||||
<value>Скопируйте файл шрифта TTF/TTC в каталог guiFonts и перезапустите приложение</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||
<value>PAC-порт = +3; Xray API порт = +4; mihomo API порт = +5;</value>
|
||||
@@ -1009,7 +1009,7 @@
|
||||
<value>Пользовательский DNS для sing-box</value>
|
||||
</data>
|
||||
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
||||
<value>Заполните структуру DNS, нажмите, чтобы открыть документ</value>
|
||||
<value>Заполните структуру DNS (формат JSON), нажмите для просмотра документации</value>
|
||||
</data>
|
||||
<data name="TbSettingDnsImportDefConfig" xml:space="preserve">
|
||||
<value>Нажмите, чтобы импортировать конфигурацию DNS по умолчанию</value>
|
||||
@@ -1033,7 +1033,7 @@
|
||||
<value>Добавить сервер [Hysteria2]</value>
|
||||
</data>
|
||||
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
|
||||
<value>Максимальная пропускная способность Hysteria (загрузка/отдача)</value>
|
||||
<value>Максимальная пропускная способность Hysteria (отдача/загрузка)</value>
|
||||
</data>
|
||||
<data name="TbSettingsUseSystemHosts" xml:space="preserve">
|
||||
<value>Использовать системный файл hosts</value>
|
||||
@@ -1045,13 +1045,13 @@
|
||||
<value>Управление перегрузками</value>
|
||||
</data>
|
||||
<data name="LvPrevProfile" xml:space="preserve">
|
||||
<value>Примечания к предыдущему прокси</value>
|
||||
<value>Псевдоним предыдущего прокси</value>
|
||||
</data>
|
||||
<data name="LvNextProfile" xml:space="preserve">
|
||||
<value>Примечания к следующему прокси</value>
|
||||
<value>Псевдоним следующего прокси</value>
|
||||
</data>
|
||||
<data name="LvPrevProfileTip" xml:space="preserve">
|
||||
<value>Убедитесь, что примечание существует и является уникальным</value>
|
||||
<value>Убедитесь, что псевдоним существует и является уникальным</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunAutoRoute" xml:space="preserve">
|
||||
<value>Автоматическая маршрутизация</value>
|
||||
@@ -1177,7 +1177,7 @@
|
||||
<value>Глобальный режим</value>
|
||||
</data>
|
||||
<data name="menuModeNothing" xml:space="preserve">
|
||||
<value>Не менять</value>
|
||||
<value>Как в исходной конфигурации</value>
|
||||
</data>
|
||||
<data name="menuModeRule" xml:space="preserve">
|
||||
<value>Правила</value>
|
||||
@@ -1210,7 +1210,7 @@
|
||||
<value>Экспорт ссылок в формате Base64 в буфер обмена</value>
|
||||
</data>
|
||||
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
|
||||
<value>Экспортировать полную конфигурацию в буфер обмена</value>
|
||||
<value>Экспортировать выбранную полную конфигурацию в буфер обмена</value>
|
||||
</data>
|
||||
<data name="menuShowOrHideMainWindow" xml:space="preserve">
|
||||
<value>Показать или скрыть главное окно</value>
|
||||
@@ -1300,7 +1300,7 @@
|
||||
<value>Не используйте небезопасный адрес подписки по протоколу HTTP</value>
|
||||
</data>
|
||||
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
|
||||
<value>Установите шрифт в систему, выберите или введите имя шрифта, перезапустите настройки</value>
|
||||
<value>Установите шрифт в систему, выберите или введите имя шрифта и перезапустите приложение</value>
|
||||
</data>
|
||||
<data name="menuExitTips" xml:space="preserve">
|
||||
<value>Вы уверены, что хотите выйти?</value>
|
||||
@@ -1318,13 +1318,13 @@
|
||||
<value>XHTTP-режим</value>
|
||||
</data>
|
||||
<data name="TransportExtraTip" xml:space="preserve">
|
||||
<value>Сырой JSON, формат: { XHTTP Object }</value>
|
||||
<value>Сырой JSON, формат: { XHTTPObject }</value>
|
||||
</data>
|
||||
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
||||
<value>Сворачивать в трей при закрытии окна</value>
|
||||
</data>
|
||||
<data name="TbSettingsMixedConcurrencyCount" xml:space="preserve">
|
||||
<value>Количество одновременно выполняемых тестов при многоэтапном тестировании</value>
|
||||
<value>Количество параллельных задач при многопоточном тестировании</value>
|
||||
</data>
|
||||
<data name="TbSettingsExceptionTip2" xml:space="preserve">
|
||||
<value>Исключения: не использовать прокси для указанных адресов. Разделяйте запятой (,)</value>
|
||||
@@ -1336,7 +1336,7 @@
|
||||
<value>Включить второй смешанный порт</value>
|
||||
</data>
|
||||
<data name="TbRoutingInboundTagTips" xml:space="preserve">
|
||||
<value>socks: локальный порт, socks2: второй локальный порт, socks3: LAN-порт</value>
|
||||
<value>tun: входящий TUN, socks: локальный порт, socks2: второй локальный порт, socks3: LAN-порт</value>
|
||||
</data>
|
||||
<data name="TbSettingsTheme" xml:space="preserve">
|
||||
<value>Тема</value>
|
||||
@@ -1357,7 +1357,7 @@
|
||||
<value>Удалено {0} недействительных</value>
|
||||
</data>
|
||||
<data name="TbPorts7" xml:space="preserve">
|
||||
<value>Диапазон портов сервера</value>
|
||||
<value>Диапазон портов для переключения (Port Hopping)</value>
|
||||
</data>
|
||||
<data name="TbPorts7Tips" xml:space="preserve">
|
||||
<value>Заменит указанный порт, перечисляйте через запятую (,)</value>
|
||||
@@ -1369,7 +1369,7 @@
|
||||
<value>URL для тестирования текущего соединения</value>
|
||||
</data>
|
||||
<data name="TbRuleOutboundTagTip" xml:space="preserve">
|
||||
<value>Можно указать название (Remarks) из конфигурации, убедитесь, что оно существует и уникально</value>
|
||||
<value>Можно указать псевдоним из конфигурации, убедитесь, что он существует и уникален</value>
|
||||
</data>
|
||||
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
|
||||
<value>Неверный пароль, попробуйте ещё раз.</value>
|
||||
@@ -1384,7 +1384,7 @@
|
||||
<value>Удалённый DNS</value>
|
||||
</data>
|
||||
<data name="TbDomesticDNS" xml:space="preserve">
|
||||
<value>Внутренний DNS</value>
|
||||
<value>DNS для прямых подключений</value>
|
||||
</data>
|
||||
<data name="TbDirectResolveStrategy" xml:space="preserve">
|
||||
<value>Стратегия разрешения прямых соединений</value>
|
||||
@@ -1489,13 +1489,13 @@
|
||||
<value>Тип группы политик</value>
|
||||
</data>
|
||||
<data name="menuAddPolicyGroupServer" xml:space="preserve">
|
||||
<value>Добавить группу политик </value>
|
||||
<value>Добавить группу политик</value>
|
||||
</data>
|
||||
<data name="menuAddProxyChainServer" xml:space="preserve">
|
||||
<value>Добавить цепочку прокси</value>
|
||||
</data>
|
||||
<data name="menuAddChildServer" xml:space="preserve">
|
||||
<value>Добавить дочернюю конфигурацию </value>
|
||||
<value>Добавить дочернюю конфигурацию</value>
|
||||
</data>
|
||||
<data name="menuRemoveChildServer" xml:space="preserve">
|
||||
<value>Удалить дочернюю конфигурацию </value>
|
||||
@@ -1534,7 +1534,7 @@
|
||||
<value>Bootstrap DNS</value>
|
||||
</data>
|
||||
<data name="TbBootstrapDNSTips" xml:space="preserve">
|
||||
<value>Разрешает домены DNS-серверов, требуется IP-адрес</value>
|
||||
<value>Для разрешения доменных имён DNS-серверов необходимо указать IP-адрес</value>
|
||||
</data>
|
||||
<data name="menuFastRealPing" xml:space="preserve">
|
||||
<value>Тест реальной задержки</value>
|
||||
@@ -1547,7 +1547,7 @@
|
||||
</data>
|
||||
<data name="TbCertPinningTips" xml:space="preserve">
|
||||
<value>Привязанный сертификат (заполните любое из полей)
|
||||
При указании сертификат будет привязан, а «Разрешить небезопасные» отключится.
|
||||
При указании сертификат будет привязан, а «Пропустить проверку сертификата» отключится.
|
||||
|
||||
Получение сертификата может завершиться неудачей при использовании самоподписанного сертификата или при наличии ненадёжного / вредоносного ЦС в системе.</value>
|
||||
</data>
|
||||
@@ -1732,12 +1732,18 @@
|
||||
<value>Экспорт внутренней ссылки v2rayN в буфер обмена</value>
|
||||
</data>
|
||||
<data name="MsgCheckUpdateHasNewVersion" xml:space="preserve">
|
||||
<value>{0} has a new version available: {1}</value>
|
||||
<value>Доступна новая версия {0}: {1}</value>
|
||||
</data>
|
||||
<data name="menuCheckOnly" xml:space="preserve">
|
||||
<value>Only Check</value>
|
||||
<value>Проверить</value>
|
||||
</data>
|
||||
<data name="MsgNotSupport" xml:space="preserve">
|
||||
<value>Not Support</value>
|
||||
<value>Не поддерживается</value>
|
||||
</data>
|
||||
<data name="LvTestIpInfo" xml:space="preserve">
|
||||
<value>Информация об IP</value>
|
||||
</data>
|
||||
<data name="menuNewUpdate" xml:space="preserve">
|
||||
<value>Доступно обновление</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1737,4 +1737,13 @@
|
||||
<data name="MsgNotSupport" xml:space="preserve">
|
||||
<value>不支持</value>
|
||||
</data>
|
||||
<data name="LvTestIpInfo" xml:space="preserve">
|
||||
<value>IP 信息</value>
|
||||
</data>
|
||||
<data name="menuNewUpdate" xml:space="preserve">
|
||||
<value>有更新</value>
|
||||
</data>
|
||||
<data name="MsgAllowInsecureDeprecated" xml:space="preserve">
|
||||
<value>警告:Xray 将在 2026.8.1 禁用跳过证书验证 allowInsecure ,请尽快改用证书固定指纹 pinnedPeerCertSha256。到期后无法使用 </value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1737,4 +1737,13 @@
|
||||
<data name="MsgNotSupport" xml:space="preserve">
|
||||
<value>不支援</value>
|
||||
</data>
|
||||
<data name="LvTestIpInfo" xml:space="preserve">
|
||||
<value>IP 資訊</value>
|
||||
</data>
|
||||
<data name="menuNewUpdate" xml:space="preserve">
|
||||
<value>有更新</value>
|
||||
</data>
|
||||
<data name="MsgAllowInsecureDeprecated" xml:space="preserve">
|
||||
<value>警告:Xray 將在 2026.8.1 停用跳過憑證驗證 allowInsecure ,請盡快改用憑證固定指紋 pinnedPeerCertSha256。到期後無法使用 allowInsecure。</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -104,6 +104,7 @@ public partial class CoreConfigV2rayService
|
||||
},
|
||||
},
|
||||
tag = balancerTag,
|
||||
fallbackTag = multipleLoad == EMultipleLoad.Fallback ? Global.DirectTag : null,
|
||||
};
|
||||
_coreConfig.routing.balancers ??= new();
|
||||
_coreConfig.routing.balancers.Add(balancer);
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Net.Http.Headers;
|
||||
namespace ServiceLib.Services;
|
||||
|
||||
/// <summary>
|
||||
///Download
|
||||
/// Download
|
||||
/// </summary>
|
||||
public class DownloadService
|
||||
{
|
||||
@@ -13,7 +13,10 @@ public class DownloadService
|
||||
|
||||
private static readonly string _tag = "DownloadService";
|
||||
|
||||
public async Task<int> DownloadDataAsync(string url, WebProxy webProxy, int downloadTimeout, Func<bool, string, Task> updateFunc)
|
||||
/// <summary>
|
||||
/// Downloads data with the specified proxy and reports progress messages.
|
||||
/// </summary>
|
||||
public async Task<int> DownloadDataAsync(string url, IWebProxy webProxy, int downloadTimeout, Func<bool, string, Task> updateFunc)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -36,6 +39,9 @@ public class DownloadService
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads a file and reports progress through events.
|
||||
/// </summary>
|
||||
public async Task DownloadFileAsync(string url, string fileName, bool blProxy, int downloadTimeout)
|
||||
{
|
||||
try
|
||||
@@ -64,6 +70,9 @@ public class DownloadService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets redirect target URL without following redirects automatically.
|
||||
/// </summary>
|
||||
public async Task<string?> UrlRedirectAsync(string url, bool blProxy)
|
||||
{
|
||||
var webRequestHandler = new SocketsHttpHandler
|
||||
@@ -86,11 +95,23 @@ public class DownloadService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to download string content using proxy switch setting.
|
||||
/// </summary>
|
||||
public async Task<string?> TryDownloadString(string url, bool blProxy, string userAgent)
|
||||
{
|
||||
var webProxy = await GetWebProxy(blProxy);
|
||||
return await TryDownloadString(url, webProxy, userAgent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to download string content with a specified proxy.
|
||||
/// </summary>
|
||||
public async Task<string?> TryDownloadString(string url, IWebProxy? webProxy, string userAgent)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result1 = await DownloadStringAsync(url, blProxy, userAgent, 15);
|
||||
var result1 = await DownloadStringAsync(url, webProxy, userAgent, 15);
|
||||
if (result1.IsNotEmpty())
|
||||
{
|
||||
return result1;
|
||||
@@ -108,7 +129,7 @@ public class DownloadService
|
||||
|
||||
try
|
||||
{
|
||||
var result2 = await DownloadStringViaDownloader(url, blProxy, userAgent, 15);
|
||||
var result2 = await DownloadStringViaDownloader(url, webProxy, userAgent, 15);
|
||||
if (result2.IsNotEmpty())
|
||||
{
|
||||
return result2;
|
||||
@@ -128,14 +149,12 @@ public class DownloadService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DownloadString
|
||||
/// Downloads string content via HttpClient.
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
private async Task<string?> DownloadStringAsync(string url, bool blProxy, string userAgent, int timeout)
|
||||
private async Task<string?> DownloadStringAsync(string url, IWebProxy? webProxy, string userAgent, int timeout)
|
||||
{
|
||||
try
|
||||
{
|
||||
var webProxy = await GetWebProxy(blProxy);
|
||||
var client = new HttpClient(new SocketsHttpHandler()
|
||||
{
|
||||
Proxy = webProxy,
|
||||
@@ -172,15 +191,12 @@ public class DownloadService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DownloadString
|
||||
/// Downloads string content via DownloaderHelper.
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
private async Task<string?> DownloadStringViaDownloader(string url, bool blProxy, string userAgent, int timeout)
|
||||
private async Task<string?> DownloadStringViaDownloader(string url, IWebProxy? webProxy, string userAgent, int timeout)
|
||||
{
|
||||
try
|
||||
{
|
||||
var webProxy = await GetWebProxy(blProxy);
|
||||
|
||||
if (userAgent.IsNullOrEmpty())
|
||||
{
|
||||
userAgent = Utils.GetVersion(false);
|
||||
@@ -200,6 +216,9 @@ public class DownloadService
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates local SOCKS proxy when proxy switch is enabled.
|
||||
/// </summary>
|
||||
private async Task<WebProxy?> GetWebProxy(bool blProxy)
|
||||
{
|
||||
if (!blProxy)
|
||||
@@ -215,6 +234,9 @@ public class DownloadService
|
||||
return new WebProxy($"socks5://{Global.Loopback}:{port}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the specified TCP endpoint is reachable.
|
||||
/// </summary>
|
||||
private async Task<bool> SocketCheck(string ip, int port)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -8,6 +8,8 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||
private readonly Config? _config = config;
|
||||
private readonly Func<SpeedTestResult, Task>? _updateFunc = updateFunc;
|
||||
private static readonly ConcurrentBag<string> _lstExitLoop = new();
|
||||
private readonly int _speedTestPageSize = config.SpeedTestItem.SpeedTestPageSize ?? Global.SpeedTestPageSize;
|
||||
private readonly TimeSpan _delayInterval = TimeSpan.FromSeconds(config.SpeedTestItem.SpeedTestDelayInterval ?? 1);
|
||||
|
||||
public void RunLoop(ESpeedActionType actionType, List<ProfileItem> selecteds)
|
||||
{
|
||||
@@ -135,32 +137,39 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||
|
||||
private async Task RunTcpingAsync(List<ServerTestItem> selecteds)
|
||||
{
|
||||
List<Task> tasks = [];
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
tasks.Add(Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var responseTime = await GetTcpingTime(it.Address, it.Port);
|
||||
var pageSize = Math.Min(selecteds.Count, _speedTestPageSize);
|
||||
var lstBatch = GetTestBatchItem(selecteds, pageSize);
|
||||
|
||||
ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime);
|
||||
await UpdateFunc(it.IndexId, responseTime.ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
foreach (var lst in lstBatch)
|
||||
{
|
||||
List<Task> tasks = [];
|
||||
foreach (var it in lst)
|
||||
{
|
||||
tasks.Add(Task.Run(async () =>
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}));
|
||||
try
|
||||
{
|
||||
var responseTime = await GetTcpingTime(it.Address, it.Port);
|
||||
|
||||
ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime);
|
||||
await UpdateFunc(it.IndexId, responseTime.ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}));
|
||||
}
|
||||
await Task.WhenAll(tasks);
|
||||
await Task.Delay(_delayInterval);
|
||||
}
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
private async Task RunRealPingBatchAsync(List<ServerTestItem> lstSelected, string exitLoopKey, int pageSize = 0)
|
||||
{
|
||||
if (pageSize <= 0)
|
||||
{
|
||||
pageSize = lstSelected.Count < Global.SpeedTestPageSize ? lstSelected.Count : Global.SpeedTestPageSize;
|
||||
pageSize = Math.Min(lstSelected.Count, _speedTestPageSize);
|
||||
}
|
||||
var lstTest = GetTestBatchItem(lstSelected, pageSize);
|
||||
|
||||
@@ -172,7 +181,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||
{
|
||||
lstFailed.AddRange(lst);
|
||||
}
|
||||
await Task.Delay(100);
|
||||
await Task.Delay(_delayInterval);
|
||||
}
|
||||
|
||||
//Retest the failed part
|
||||
@@ -249,7 +258,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||
{
|
||||
if (pageSize <= 0)
|
||||
{
|
||||
pageSize = lstSelected.Count < Global.SpeedTestPageSize ? lstSelected.Count : Global.SpeedTestPageSize;
|
||||
pageSize = Math.Min(lstSelected.Count, _speedTestPageSize);
|
||||
}
|
||||
var lstTest = GetTestBatchItem(lstSelected, pageSize);
|
||||
|
||||
@@ -261,7 +270,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||
{
|
||||
lstFailed.AddRange(lst);
|
||||
}
|
||||
await Task.Delay(100);
|
||||
await Task.Delay(_delayInterval);
|
||||
}
|
||||
|
||||
//Retest the failed part
|
||||
@@ -392,10 +401,23 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||
private async Task<int> DoRealPing(ServerTestItem it)
|
||||
{
|
||||
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
|
||||
var responseTime = await ConnectionHandler.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
|
||||
var responseTime = await ConnectionHandler.GetRealPingTime(webProxy, 10);
|
||||
|
||||
ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime);
|
||||
await UpdateFunc(it.IndexId, responseTime.ToString());
|
||||
|
||||
if (responseTime > 0)
|
||||
{
|
||||
var ipInfo = await ConnectionHandler.GetIPInfo(webProxy);
|
||||
var ipStr = ipInfo?.ToString() ?? Global.None;
|
||||
ProfileExManager.Instance.SetTestIpInfo(it.IndexId, ipStr);
|
||||
await UpdateIpInfoFunc(it.IndexId, ipStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
await UpdateIpInfoFunc(it.IndexId, ResUI.SpeedtestingSkip);
|
||||
}
|
||||
|
||||
return responseTime;
|
||||
}
|
||||
|
||||
@@ -491,4 +513,9 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||
ProfileExManager.Instance.SetTestMessage(indexId, speed);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateIpInfoFunc(string indexId, string ip)
|
||||
{
|
||||
await _updateFunc?.Invoke(new() { IndexId = indexId, IpInfo = ip });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +117,11 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
|
||||
var msgs = new List<string>();
|
||||
foreach (var type in CoreInfoManager.Instance.GetCheckUpdateCoreTypes())
|
||||
{
|
||||
if (!(_config.CheckUpdateItem.SelectedCoreTypes?.Contains(type.ToString()) ?? true))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var result = await CheckHasUpdateOnly(type, preRelease);
|
||||
if (result.Success && result.Version != null)
|
||||
{
|
||||
@@ -337,6 +342,7 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
|
||||
{
|
||||
Architecture.Arm64 => coreInfo?.DownloadUrlLinuxArm64,
|
||||
Architecture.RiscV64 => coreInfo?.DownloadUrlLinuxRiscV64,
|
||||
Architecture.LoongArch64 => coreInfo?.DownloadUrlLinuxLoong64,
|
||||
Architecture.X64 => coreInfo?.DownloadUrlLinux64,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
@@ -36,7 +36,7 @@ public class CheckUpdateViewModel : MyReactiveObject
|
||||
this.WhenAnyValue(
|
||||
x => x.EnableCheckPreReleaseUpdate,
|
||||
y => y == true)
|
||||
.Subscribe(c => _config.CheckUpdateItem.CheckPreReleaseUpdate = EnableCheckPreReleaseUpdate);
|
||||
.Subscribe(c => _ = OnCheckPreReleaseUpdateChanged());
|
||||
|
||||
RefreshCheckUpdateItems();
|
||||
}
|
||||
@@ -87,12 +87,23 @@ public class CheckUpdateViewModel : MyReactiveObject
|
||||
};
|
||||
}
|
||||
|
||||
private async Task OnCheckPreReleaseUpdateChanged()
|
||||
{
|
||||
if (_config.CheckUpdateItem.CheckPreReleaseUpdate == EnableCheckPreReleaseUpdate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_config.CheckUpdateItem.CheckPreReleaseUpdate = EnableCheckPreReleaseUpdate;
|
||||
await SaveSelectedCoreTypes();
|
||||
}
|
||||
|
||||
private async Task SaveSelectedCoreTypes()
|
||||
{
|
||||
_config.CheckUpdateItem.SelectedCoreTypes = CheckUpdateModels
|
||||
.Where(t => t.IsSelected == true)
|
||||
.Select(t => t.CoreTypeForStorage)
|
||||
.ToList();
|
||||
_config.CheckUpdateItem.SelectedCoreTypes =
|
||||
CheckUpdateModels.Where(t => t.IsSelected == true)
|
||||
.Select(t => t.CoreTypeForStorage)
|
||||
.ToList();
|
||||
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@ public class MainWindowViewModel : MyReactiveObject
|
||||
|
||||
[Reactive] public bool BlIsWindows { get; set; }
|
||||
|
||||
[Reactive] public bool BlNewUpdate { get; set; }
|
||||
|
||||
#endregion Menu
|
||||
|
||||
#region Init
|
||||
@@ -251,6 +253,11 @@ public class MainWindowViewModel : MyReactiveObject
|
||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||
.Subscribe(async blProxy => await UpdateSubscriptionProcess("", blProxy));
|
||||
|
||||
AppEvents.HasUpdateNotified
|
||||
.AsObservable()
|
||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||
.Subscribe(async bl => BlNewUpdate = bl);
|
||||
|
||||
#endregion AppEvents
|
||||
|
||||
_ = Init();
|
||||
@@ -297,10 +304,22 @@ public class MainWindowViewModel : MyReactiveObject
|
||||
{
|
||||
var indexIdOld = _config.IndexId;
|
||||
await RefreshServers();
|
||||
if (indexIdOld != _config.IndexId)
|
||||
|
||||
// If indexId changed or subIndexId is empty, directly reload.
|
||||
if (indexIdOld != _config.IndexId || _config.SubIndexId.IsNullOrEmpty())
|
||||
{
|
||||
await Reload();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The activity config belongs to the current group.
|
||||
var profile = await AppManager.Instance.GetProfileItem(_config.IndexId);
|
||||
if (profile != null && profile.Subid == _config.SubIndexId)
|
||||
{
|
||||
await Reload();
|
||||
}
|
||||
}
|
||||
|
||||
if (_config.UiItem.EnableAutoAdjustMainLvColWidth)
|
||||
{
|
||||
AppEvents.AdjustMainLvColWidthRequested.Publish();
|
||||
|
||||
@@ -4,29 +4,29 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
{
|
||||
#region Core
|
||||
|
||||
[Reactive] public int localPort { get; set; }
|
||||
[Reactive] public int LocalPort { get; set; }
|
||||
[Reactive] public bool SecondLocalPortEnabled { get; set; }
|
||||
[Reactive] public bool udpEnabled { get; set; }
|
||||
[Reactive] public bool sniffingEnabled { get; set; }
|
||||
public IList<string> destOverride { get; set; }
|
||||
[Reactive] public bool routeOnly { get; set; }
|
||||
[Reactive] public bool allowLANConn { get; set; }
|
||||
[Reactive] public bool newPort4LAN { get; set; }
|
||||
[Reactive] public string user { get; set; }
|
||||
[Reactive] public string pass { get; set; }
|
||||
[Reactive] public bool muxEnabled { get; set; }
|
||||
[Reactive] public bool logEnabled { get; set; }
|
||||
[Reactive] public string loglevel { get; set; }
|
||||
[Reactive] public bool defAllowInsecure { get; set; }
|
||||
[Reactive] public string defFingerprint { get; set; }
|
||||
[Reactive] public string defUserAgent { get; set; }
|
||||
[Reactive] public string sendThrough { get; set; }
|
||||
[Reactive] public string bindInterface { get; set; }
|
||||
[Reactive] public string mux4SboxProtocol { get; set; }
|
||||
[Reactive] public bool enableCacheFile4Sbox { get; set; }
|
||||
[Reactive] public int? hyUpMbps { get; set; }
|
||||
[Reactive] public int? hyDownMbps { get; set; }
|
||||
[Reactive] public bool enableFragment { get; set; }
|
||||
[Reactive] public bool UdpEnabled { get; set; }
|
||||
[Reactive] public bool SniffingEnabled { get; set; }
|
||||
public IList<string> DestOverride { get; set; }
|
||||
[Reactive] public bool RouteOnly { get; set; }
|
||||
[Reactive] public bool AllowLANConn { get; set; }
|
||||
[Reactive] public bool NewPort4LAN { get; set; }
|
||||
[Reactive] public string User { get; set; }
|
||||
[Reactive] public string Pass { get; set; }
|
||||
[Reactive] public bool MuxEnabled { get; set; }
|
||||
[Reactive] public bool LogEnabled { get; set; }
|
||||
[Reactive] public string Loglevel { get; set; }
|
||||
[Reactive] public bool DefAllowInsecure { get; set; }
|
||||
[Reactive] public string DefFingerprint { get; set; }
|
||||
[Reactive] public string DefUserAgent { get; set; }
|
||||
[Reactive] public string SendThrough { get; set; }
|
||||
[Reactive] public string BindInterface { get; set; }
|
||||
[Reactive] public string Mux4SboxProtocol { get; set; }
|
||||
[Reactive] public bool EnableCacheFile4Sbox { get; set; }
|
||||
[Reactive] public int? HyUpMbps { get; set; }
|
||||
[Reactive] public int? HyDownMbps { get; set; }
|
||||
[Reactive] public bool EnableFragment { get; set; }
|
||||
|
||||
#endregion Core
|
||||
|
||||
@@ -83,9 +83,9 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
|
||||
#region System proxy
|
||||
|
||||
[Reactive] public bool notProxyLocalAddress { get; set; }
|
||||
[Reactive] public string systemProxyAdvancedProtocol { get; set; }
|
||||
[Reactive] public string systemProxyExceptions { get; set; }
|
||||
[Reactive] public bool NotProxyLocalAddress { get; set; }
|
||||
[Reactive] public string SystemProxyAdvancedProtocol { get; set; }
|
||||
[Reactive] public string SystemProxyExceptions { get; set; }
|
||||
[Reactive] public string CustomSystemProxyPacPath { get; set; }
|
||||
[Reactive] public string CustomSystemProxyScriptPath { get; set; }
|
||||
|
||||
@@ -142,28 +142,28 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
#region Core
|
||||
|
||||
var inbound = _config.Inbound.First();
|
||||
localPort = inbound.LocalPort;
|
||||
LocalPort = inbound.LocalPort;
|
||||
SecondLocalPortEnabled = inbound.SecondLocalPortEnabled;
|
||||
udpEnabled = inbound.UdpEnabled;
|
||||
sniffingEnabled = inbound.SniffingEnabled;
|
||||
routeOnly = inbound.RouteOnly;
|
||||
allowLANConn = inbound.AllowLANConn;
|
||||
newPort4LAN = inbound.NewPort4LAN;
|
||||
user = inbound.User;
|
||||
pass = inbound.Pass;
|
||||
muxEnabled = _config.CoreBasicItem.MuxEnabled;
|
||||
logEnabled = _config.CoreBasicItem.LogEnabled;
|
||||
loglevel = _config.CoreBasicItem.Loglevel;
|
||||
defAllowInsecure = _config.CoreBasicItem.DefAllowInsecure;
|
||||
defFingerprint = _config.CoreBasicItem.DefFingerprint;
|
||||
defUserAgent = _config.CoreBasicItem.DefUserAgent;
|
||||
sendThrough = _config.CoreBasicItem.SendThrough ?? string.Empty;
|
||||
bindInterface = _config.CoreBasicItem.BindInterface ?? string.Empty;
|
||||
mux4SboxProtocol = _config.Mux4SboxItem.Protocol;
|
||||
enableCacheFile4Sbox = _config.CoreBasicItem.EnableCacheFile4Sbox;
|
||||
hyUpMbps = _config.HysteriaItem.UpMbps;
|
||||
hyDownMbps = _config.HysteriaItem.DownMbps;
|
||||
enableFragment = _config.CoreBasicItem.EnableFragment;
|
||||
UdpEnabled = inbound.UdpEnabled;
|
||||
SniffingEnabled = inbound.SniffingEnabled;
|
||||
RouteOnly = inbound.RouteOnly;
|
||||
AllowLANConn = inbound.AllowLANConn;
|
||||
NewPort4LAN = inbound.NewPort4LAN;
|
||||
User = inbound.User;
|
||||
Pass = inbound.Pass;
|
||||
MuxEnabled = _config.CoreBasicItem.MuxEnabled;
|
||||
LogEnabled = _config.CoreBasicItem.LogEnabled;
|
||||
Loglevel = _config.CoreBasicItem.Loglevel;
|
||||
DefAllowInsecure = _config.CoreBasicItem.DefAllowInsecure;
|
||||
DefFingerprint = _config.CoreBasicItem.DefFingerprint;
|
||||
DefUserAgent = _config.CoreBasicItem.DefUserAgent;
|
||||
SendThrough = _config.CoreBasicItem.SendThrough ?? string.Empty;
|
||||
BindInterface = _config.CoreBasicItem.BindInterface ?? string.Empty;
|
||||
Mux4SboxProtocol = _config.Mux4SboxItem.Protocol;
|
||||
EnableCacheFile4Sbox = _config.CoreBasicItem.EnableCacheFile4Sbox;
|
||||
HyUpMbps = _config.HysteriaItem.UpMbps;
|
||||
HyDownMbps = _config.HysteriaItem.DownMbps;
|
||||
EnableFragment = _config.CoreBasicItem.EnableFragment;
|
||||
|
||||
#endregion Core
|
||||
|
||||
@@ -211,9 +211,9 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
|
||||
#region System proxy
|
||||
|
||||
notProxyLocalAddress = _config.SystemProxyItem.NotProxyLocalAddress;
|
||||
systemProxyAdvancedProtocol = _config.SystemProxyItem.SystemProxyAdvancedProtocol;
|
||||
systemProxyExceptions = _config.SystemProxyItem.SystemProxyExceptions;
|
||||
NotProxyLocalAddress = _config.SystemProxyItem.NotProxyLocalAddress;
|
||||
SystemProxyAdvancedProtocol = _config.SystemProxyItem.SystemProxyAdvancedProtocol;
|
||||
SystemProxyExceptions = _config.SystemProxyItem.SystemProxyExceptions;
|
||||
CustomSystemProxyPacPath = _config.SystemProxyItem.CustomSystemProxyPacPath;
|
||||
CustomSystemProxyScriptPath = _config.SystemProxyItem.CustomSystemProxyScriptPath;
|
||||
|
||||
@@ -297,13 +297,13 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
|
||||
private async Task SaveSettingAsync()
|
||||
{
|
||||
if (localPort.ToString().IsNullOrEmpty() || !Utils.IsNumeric(localPort.ToString())
|
||||
|| localPort <= 0 || localPort >= Global.MaxPort)
|
||||
if (LocalPort.ToString().IsNullOrEmpty() || !Utils.IsNumeric(LocalPort.ToString())
|
||||
|| LocalPort <= 0 || LocalPort >= Global.MaxPort)
|
||||
{
|
||||
NoticeManager.Instance.Enqueue(ResUI.FillLocalListeningPort);
|
||||
return;
|
||||
}
|
||||
var sendThroughValue = sendThrough.TrimEx();
|
||||
var sendThroughValue = SendThrough.TrimEx();
|
||||
if (sendThroughValue.IsNotEmpty() && !Utils.IsIpv4(sendThroughValue))
|
||||
{
|
||||
NoticeManager.Instance.Enqueue(ResUI.FillCorrectSendThroughIPv4);
|
||||
@@ -328,33 +328,33 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
//}
|
||||
|
||||
//Core
|
||||
_config.Inbound.First().LocalPort = localPort;
|
||||
_config.Inbound.First().LocalPort = LocalPort;
|
||||
_config.Inbound.First().SecondLocalPortEnabled = SecondLocalPortEnabled;
|
||||
_config.Inbound.First().UdpEnabled = udpEnabled;
|
||||
_config.Inbound.First().SniffingEnabled = sniffingEnabled;
|
||||
_config.Inbound.First().DestOverride = destOverride?.ToList();
|
||||
_config.Inbound.First().RouteOnly = routeOnly;
|
||||
_config.Inbound.First().AllowLANConn = allowLANConn;
|
||||
_config.Inbound.First().NewPort4LAN = newPort4LAN;
|
||||
_config.Inbound.First().User = user;
|
||||
_config.Inbound.First().Pass = pass;
|
||||
_config.Inbound.First().UdpEnabled = UdpEnabled;
|
||||
_config.Inbound.First().SniffingEnabled = SniffingEnabled;
|
||||
_config.Inbound.First().DestOverride = DestOverride?.ToList();
|
||||
_config.Inbound.First().RouteOnly = RouteOnly;
|
||||
_config.Inbound.First().AllowLANConn = AllowLANConn;
|
||||
_config.Inbound.First().NewPort4LAN = NewPort4LAN;
|
||||
_config.Inbound.First().User = User;
|
||||
_config.Inbound.First().Pass = Pass;
|
||||
if (_config.Inbound.Count > 1)
|
||||
{
|
||||
_config.Inbound.RemoveAt(1);
|
||||
}
|
||||
_config.CoreBasicItem.LogEnabled = logEnabled;
|
||||
_config.CoreBasicItem.Loglevel = loglevel;
|
||||
_config.CoreBasicItem.MuxEnabled = muxEnabled;
|
||||
_config.CoreBasicItem.DefAllowInsecure = defAllowInsecure;
|
||||
_config.CoreBasicItem.DefFingerprint = defFingerprint;
|
||||
_config.CoreBasicItem.DefUserAgent = defUserAgent;
|
||||
_config.CoreBasicItem.SendThrough = sendThrough.TrimEx();
|
||||
_config.CoreBasicItem.BindInterface = bindInterface.TrimEx();
|
||||
_config.Mux4SboxItem.Protocol = mux4SboxProtocol;
|
||||
_config.CoreBasicItem.EnableCacheFile4Sbox = enableCacheFile4Sbox;
|
||||
_config.HysteriaItem.UpMbps = hyUpMbps ?? 0;
|
||||
_config.HysteriaItem.DownMbps = hyDownMbps ?? 0;
|
||||
_config.CoreBasicItem.EnableFragment = enableFragment;
|
||||
_config.CoreBasicItem.LogEnabled = LogEnabled;
|
||||
_config.CoreBasicItem.Loglevel = Loglevel;
|
||||
_config.CoreBasicItem.MuxEnabled = MuxEnabled;
|
||||
_config.CoreBasicItem.DefAllowInsecure = DefAllowInsecure;
|
||||
_config.CoreBasicItem.DefFingerprint = DefFingerprint;
|
||||
_config.CoreBasicItem.DefUserAgent = DefUserAgent;
|
||||
_config.CoreBasicItem.SendThrough = SendThrough.TrimEx();
|
||||
_config.CoreBasicItem.BindInterface = BindInterface.TrimEx();
|
||||
_config.Mux4SboxItem.Protocol = Mux4SboxProtocol;
|
||||
_config.CoreBasicItem.EnableCacheFile4Sbox = EnableCacheFile4Sbox;
|
||||
_config.HysteriaItem.UpMbps = HyUpMbps ?? 0;
|
||||
_config.HysteriaItem.DownMbps = HyDownMbps ?? 0;
|
||||
_config.CoreBasicItem.EnableFragment = EnableFragment;
|
||||
|
||||
_config.GuiItem.AutoRun = AutoRun;
|
||||
_config.GuiItem.EnableStatistics = EnableStatistics;
|
||||
@@ -383,9 +383,9 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
_config.SpeedTestItem.IPAPIUrl = IPAPIUrl;
|
||||
|
||||
//systemProxy
|
||||
_config.SystemProxyItem.SystemProxyExceptions = systemProxyExceptions;
|
||||
_config.SystemProxyItem.NotProxyLocalAddress = notProxyLocalAddress;
|
||||
_config.SystemProxyItem.SystemProxyAdvancedProtocol = systemProxyAdvancedProtocol;
|
||||
_config.SystemProxyItem.SystemProxyExceptions = SystemProxyExceptions;
|
||||
_config.SystemProxyItem.NotProxyLocalAddress = NotProxyLocalAddress;
|
||||
_config.SystemProxyItem.SystemProxyAdvancedProtocol = SystemProxyAdvancedProtocol;
|
||||
_config.SystemProxyItem.CustomSystemProxyPacPath = CustomSystemProxyPacPath;
|
||||
_config.SystemProxyItem.CustomSystemProxyScriptPath = CustomSystemProxyScriptPath;
|
||||
|
||||
|
||||
@@ -303,6 +303,10 @@ public class ProfilesViewModel : MyReactiveObject
|
||||
{
|
||||
item.SpeedVal = result.Speed ?? string.Empty;
|
||||
}
|
||||
if (result.IpInfo.IsNotEmpty())
|
||||
{
|
||||
item.IpInfo = result.IpInfo ?? string.Empty;
|
||||
}
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -437,6 +441,7 @@ public class ProfilesViewModel : MyReactiveObject
|
||||
Speed = t33?.Speed ?? 0,
|
||||
DelayVal = t33?.Delay != 0 ? $"{t33?.Delay}" : string.Empty,
|
||||
SpeedVal = t33?.Speed > 0 ? $"{t33?.Speed}" : t33?.Message ?? string.Empty,
|
||||
IpInfo = t33?.IpInfo ?? string.Empty,
|
||||
TodayDown = t22 == null ? "" : Utils.HumanFy(t22.TodayDown),
|
||||
TodayUp = t22 == null ? "" : Utils.HumanFy(t22.TodayUp),
|
||||
TotalDown = t22 == null ? "" : Utils.HumanFy(t22.TotalDown),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using v2rayN.Desktop.Common;
|
||||
using v2rayN.Desktop.Views;
|
||||
|
||||
namespace v2rayN.Desktop;
|
||||
@@ -24,11 +25,34 @@ public partial class App : Application
|
||||
|
||||
desktop.Exit += OnExit;
|
||||
desktop.MainWindow = new MainWindow();
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
Current?.TryGetFeature<IActivatableLifetime>()?.Activated += OnMacOSActivated;
|
||||
}
|
||||
}
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
|
||||
private void OnMacOSActivated(object? sender, ActivatedEventArgs args)
|
||||
{
|
||||
if (args.Kind != ActivationKind.Reopen)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
((ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow as MainWindow)?.ShowHideWindow(true);
|
||||
|
||||
if (!AppManager.Instance.Config.UiItem.MacOSShowInDock)
|
||||
{
|
||||
MacAppUtils.SetActivationPolicyAccessory();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
if (e.ExceptionObject != null)
|
||||
|
||||
27
v2rayN/v2rayN.Desktop/Common/MacAppUtils.cs
Normal file
27
v2rayN/v2rayN.Desktop/Common/MacAppUtils.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace v2rayN.Desktop.Common;
|
||||
|
||||
internal static class MacAppUtils
|
||||
{
|
||||
private const string LibObjC = "/usr/lib/libobjc.dylib";
|
||||
private const nint ActivationPolicyAccessory = 1;
|
||||
|
||||
public static void SetActivationPolicyAccessory()
|
||||
=> objc_msgSend(
|
||||
objc_msgSend(objc_getClass("NSApplication"), sel_registerName("sharedApplication")),
|
||||
sel_registerName("setActivationPolicy:"),
|
||||
ActivationPolicyAccessory);
|
||||
|
||||
[DllImport(LibObjC)]
|
||||
private static extern nint objc_getClass(string name);
|
||||
|
||||
[DllImport(LibObjC)]
|
||||
private static extern nint sel_registerName(string name);
|
||||
|
||||
[DllImport(LibObjC, EntryPoint = "objc_msgSend")]
|
||||
private static extern nint objc_msgSend(nint receiver, nint selector);
|
||||
|
||||
[DllImport(LibObjC, EntryPoint = "objc_msgSend")]
|
||||
private static extern void objc_msgSend(nint receiver, nint selector, nint argument);
|
||||
}
|
||||
@@ -102,6 +102,13 @@
|
||||
|
||||
<MenuItem x:Name="menuClose" Header="{x:Static resx:ResUI.menuExit}" />
|
||||
</Menu>
|
||||
|
||||
<Button
|
||||
x:Name="btnNewUpdate"
|
||||
Margin="{StaticResource MarginLr8}"
|
||||
HorizontalAlignment="Left"
|
||||
Content="{x:Static resx:ResUI.menuNewUpdate}"
|
||||
IsVisible="False" />
|
||||
</DockPanel>
|
||||
|
||||
<view:StatusBarView DockPanel.Dock="Bottom" />
|
||||
|
||||
@@ -25,6 +25,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
||||
menuSettingsSetUWP.Click += MenuSettingsSetUWP_Click;
|
||||
menuPromotion.Click += MenuPromotion_Click;
|
||||
menuCheckUpdate.Click += MenuCheckUpdate_Click;
|
||||
btnNewUpdate.Click += MenuCheckUpdate_Click;
|
||||
menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
|
||||
menuClose.Click += MenuClose_Click;
|
||||
|
||||
@@ -102,6 +103,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.ReloadCmd, v => v.menuReload).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.BlReloadEnabled, v => v.menuReload.IsEnabled).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.BlNewUpdate, v => v.btnNewUpdate.IsVisible).DisposeWith(disposables);
|
||||
|
||||
switch (_config.UiItem.MainGirdOrientation)
|
||||
{
|
||||
@@ -367,6 +369,8 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
||||
{
|
||||
_checkUpdateView ??= new CheckUpdateView();
|
||||
DialogHost.Show(_checkUpdateView);
|
||||
|
||||
AppEvents.HasUpdateNotified.Publish(false);
|
||||
}
|
||||
|
||||
private void MenuBackupAndRestore_Click(object? sender, RoutedEventArgs e)
|
||||
|
||||
@@ -876,7 +876,8 @@
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
HorizontalAlignment="Left"
|
||||
IsEditable="True" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
|
||||
@@ -60,30 +60,30 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
this.Bind(ViewModel, vm => vm.localPort, v => v.txtlocalPort.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.LocalPort, v => v.txtlocalPort.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SecondLocalPortEnabled, v => v.togSecondLocalPortEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.udpEnabled, v => v.togudpEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.sniffingEnabled, v => v.togsniffingEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.routeOnly, v => v.togrouteOnly.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.allowLANConn, v => v.togAllowLANConn.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.newPort4LAN, v => v.togNewPort4LAN.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.newPort4LAN, v => v.txtuser.IsEnabled).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.newPort4LAN, v => v.txtpass.IsEnabled).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.user, v => v.txtuser.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.pass, v => v.txtpass.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.muxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.logEnabled, v => v.toglogEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.loglevel, v => v.cmbloglevel.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.defAllowInsecure, v => v.togdefAllowInsecure.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.defFingerprint, v => v.cmbdefFingerprint.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.defUserAgent, v => v.cmbdefUserAgent.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.bindInterface, v => v.txtbindInterface.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.sendThrough, v => v.txtsendThrough.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.mux4SboxProtocol, v => v.cmbmux4SboxProtocol.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.enableCacheFile4Sbox, v => v.togenableCacheFile4Sbox.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.hyUpMbps, v => v.txtUpMbps.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.hyDownMbps, v => v.txtDownMbps.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.enableFragment, v => v.togenableFragment.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.UdpEnabled, v => v.togudpEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SniffingEnabled, v => v.togsniffingEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.RouteOnly, v => v.togrouteOnly.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AllowLANConn, v => v.togAllowLANConn.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.NewPort4LAN, v => v.togNewPort4LAN.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.NewPort4LAN, v => v.txtuser.IsEnabled).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.NewPort4LAN, v => v.txtpass.IsEnabled).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.User, v => v.txtuser.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Pass, v => v.txtpass.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.LogEnabled, v => v.toglogEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Loglevel, v => v.cmbloglevel.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.DefAllowInsecure, v => v.togdefAllowInsecure.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.DefFingerprint, v => v.cmbdefFingerprint.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.DefUserAgent, v => v.cmbdefUserAgent.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.BindInterface, v => v.txtbindInterface.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SendThrough, v => v.txtsendThrough.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Mux4SboxProtocol, v => v.cmbmux4SboxProtocol.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.EnableCacheFile4Sbox, v => v.togenableCacheFile4Sbox.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.HyUpMbps, v => v.txtUpMbps.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.HyDownMbps, v => v.txtDownMbps.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.EnableFragment, v => v.togenableFragment.IsChecked).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.AutoRun, v => v.togAutoRun.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.EnableStatistics, v => v.togEnableStatistics.IsChecked).DisposeWith(disposables);
|
||||
@@ -104,21 +104,21 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
||||
this.Bind(ViewModel, vm => vm.SubConvertUrl, v => v.cmbSubConvertUrl.Text).DisposeWith(disposables);
|
||||
this.Bind<OptionSettingViewModel, OptionSettingWindow, int, int>(ViewModel,
|
||||
vm => vm.MainGirdOrientation, view => view.cmbMainGirdOrientation.SelectedIndex).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.RoutingRulesSourceUrl, v => v.cmbRoutingRulesSourceUrl.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.RoutingRulesSourceUrl, v => v.cmbRoutingRulesSourceUrl.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.IPAPIUrl, v => v.cmbIPAPIUrl.Text).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.notProxyLocalAddress, v => v.tognotProxyLocalAddress.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.systemProxyExceptions, v => v.txtsystemProxyExceptions.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.NotProxyLocalAddress, v => v.tognotProxyLocalAddress.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SystemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SystemProxyExceptions, v => v.txtsystemProxyExceptions.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.CustomSystemProxyPacPath, v => v.txtCustomSystemProxyPacPath.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.CustomSystemProxyScriptPath, v => v.txtCustomSystemProxyScriptPath.Text).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.TunAutoRoute, v => v.togAutoRoute.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunStrictRoute, v => v.togStrictRoute.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunStack, v => v.cmbStack.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunIcmpRouting, v => v.cmbIcmpRoutingPolicy.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunEnableLegacyProtect, v => v.togEnableLegacyProtect.IsChecked).DisposeWith(disposables);
|
||||
@@ -161,29 +161,18 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
||||
|
||||
private async Task<List<string>> GetFonts()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
var lstFonts = new List<string>();
|
||||
try
|
||||
{
|
||||
if (Utils.IsWindows())
|
||||
{
|
||||
return lstFonts;
|
||||
}
|
||||
else if (Utils.IsNonWindows())
|
||||
{
|
||||
var result = await Utils.GetLinuxFontFamily("zh");
|
||||
if (result.IsNullOrEmpty())
|
||||
{
|
||||
return lstFonts;
|
||||
}
|
||||
|
||||
var lst = result.Split(Environment.NewLine)
|
||||
.Where(t => t.IsNotEmpty())
|
||||
.ToList()
|
||||
.Select(t => t.Split(",").FirstOrDefault() ?? "")
|
||||
.OrderBy(t => t)
|
||||
.ToList();
|
||||
return lst;
|
||||
}
|
||||
var lst = Avalonia.Media.FontManager.Current.SystemFonts
|
||||
.Select(t => t.Name)
|
||||
.Where(t => t.IsNotEmpty())
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(t => t)
|
||||
.ToList();
|
||||
return lst;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -196,7 +185,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
||||
{
|
||||
if (ViewModel != null)
|
||||
{
|
||||
ViewModel.destOverride = clbdestOverride.SelectedItems.Cast<string>().ToList();
|
||||
ViewModel.DestOverride = clbdestOverride.SelectedItems.Cast<string>().ToList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -277,6 +277,12 @@
|
||||
Header="{x:Static resx:ResUI.LvTestSpeed}"
|
||||
Tag="SpeedVal" />
|
||||
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding IpInfo}"
|
||||
Header="{x:Static resx:ResUI.LvTestIpInfo}"
|
||||
Tag="IpInfo" />
|
||||
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding TodayUp}"
|
||||
|
||||
@@ -425,10 +425,14 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
||||
item2.Width = new DataGridLength(item.Width, DataGridLengthUnitType.Pixel);
|
||||
item2.DisplayIndex = displayIndex++;
|
||||
}
|
||||
if (item.Name.ToLower().StartsWith("to"))
|
||||
if (item.Name.StartsWith("to", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
item2.IsVisible = _config.GuiItem.EnableStatistics;
|
||||
}
|
||||
if (item.Name.Equals("IpInfo", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
item2.IsVisible = _config.SpeedTestItem.IPAPIUrl.IsNotEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,6 +313,13 @@
|
||||
Style="{StaticResource MaterialDesignToolForegroundPopupBox}">
|
||||
<ContentControl x:Name="pbTheme" />
|
||||
</materialDesign:PopupBox>
|
||||
|
||||
<Button
|
||||
x:Name="btnNewUpdate"
|
||||
Margin="{StaticResource MarginLeftRight8}"
|
||||
Content="{x:Static resx:ResUI.menuNewUpdate}"
|
||||
Style="{StaticResource DefButton}"
|
||||
Visibility="Hidden" />
|
||||
</ToolBar>
|
||||
</ToolBarTray>
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ public partial class MainWindow
|
||||
menuPromotion.Click += MenuPromotion_Click;
|
||||
menuClose.Click += MenuClose_Click;
|
||||
menuCheckUpdate.Click += MenuCheckUpdate_Click;
|
||||
btnNewUpdate.Click += MenuCheckUpdate_Click;
|
||||
menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
|
||||
|
||||
ViewModel = new MainWindowViewModel(UpdateViewHandler);
|
||||
@@ -102,6 +103,8 @@ public partial class MainWindow
|
||||
this.BindCommand(ViewModel, vm => vm.ReloadCmd, v => v.menuReload).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.BlReloadEnabled, v => v.menuReload.IsEnabled).DisposeWith(disposables);
|
||||
|
||||
this.OneWayBind(ViewModel, vm => vm.BlNewUpdate, v => v.btnNewUpdate.Visibility).DisposeWith(disposables);
|
||||
|
||||
switch (_config.UiItem.MainGirdOrientation)
|
||||
{
|
||||
case EGirdOrientation.Horizontal:
|
||||
@@ -363,6 +366,8 @@ public partial class MainWindow
|
||||
{
|
||||
_checkUpdateView ??= new CheckUpdateView();
|
||||
DialogHost.Show(_checkUpdateView, "RootDialog");
|
||||
|
||||
AppEvents.HasUpdateNotified.Publish(false);
|
||||
}
|
||||
|
||||
private void MenuBackupAndRestore_Click(object sender, RoutedEventArgs e)
|
||||
|
||||
@@ -1141,6 +1141,7 @@
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left"
|
||||
IsEditable="True"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
|
||||
@@ -57,30 +57,30 @@ public partial class OptionSettingWindow
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
this.Bind(ViewModel, vm => vm.localPort, v => v.txtlocalPort.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.LocalPort, v => v.txtlocalPort.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SecondLocalPortEnabled, v => v.togSecondLocalPortEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.udpEnabled, v => v.togudpEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.sniffingEnabled, v => v.togsniffingEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.routeOnly, v => v.togrouteOnly.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.allowLANConn, v => v.togAllowLANConn.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.newPort4LAN, v => v.togNewPort4LAN.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.newPort4LAN, v => v.txtuser.IsEnabled).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.newPort4LAN, v => v.txtpass.IsEnabled).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.user, v => v.txtuser.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.pass, v => v.txtpass.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.muxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.logEnabled, v => v.toglogEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.loglevel, v => v.cmbloglevel.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.defAllowInsecure, v => v.togdefAllowInsecure.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.defFingerprint, v => v.cmbdefFingerprint.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.defUserAgent, v => v.cmbdefUserAgent.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.sendThrough, v => v.txtsendThrough.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.bindInterface, v => v.txtbindInterface.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.mux4SboxProtocol, v => v.cmbmux4SboxProtocol.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.enableCacheFile4Sbox, v => v.togenableCacheFile4Sbox.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.hyUpMbps, v => v.txtUpMbps.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.hyDownMbps, v => v.txtDownMbps.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.enableFragment, v => v.togenableFragment.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.UdpEnabled, v => v.togudpEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SniffingEnabled, v => v.togsniffingEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.RouteOnly, v => v.togrouteOnly.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AllowLANConn, v => v.togAllowLANConn.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.NewPort4LAN, v => v.togNewPort4LAN.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.NewPort4LAN, v => v.txtuser.IsEnabled).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.NewPort4LAN, v => v.txtpass.IsEnabled).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.User, v => v.txtuser.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Pass, v => v.txtpass.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.LogEnabled, v => v.toglogEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Loglevel, v => v.cmbloglevel.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.DefAllowInsecure, v => v.togdefAllowInsecure.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.DefFingerprint, v => v.cmbdefFingerprint.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.DefUserAgent, v => v.cmbdefUserAgent.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SendThrough, v => v.txtsendThrough.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.BindInterface, v => v.txtbindInterface.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Mux4SboxProtocol, v => v.cmbmux4SboxProtocol.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.EnableCacheFile4Sbox, v => v.togenableCacheFile4Sbox.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.HyUpMbps, v => v.txtUpMbps.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.HyDownMbps, v => v.txtDownMbps.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.EnableFragment, v => v.togenableFragment.IsChecked).DisposeWith(disposables);
|
||||
|
||||
//this.Bind(ViewModel, vm => vm.Kcpmtu, v => v.txtKcpmtu.Text).DisposeWith(disposables);
|
||||
//this.Bind(ViewModel, vm => vm.Kcptti, v => v.txtKcptti.Text).DisposeWith(disposables);
|
||||
@@ -114,9 +114,9 @@ public partial class OptionSettingWindow
|
||||
this.Bind(ViewModel, vm => vm.RoutingRulesSourceUrl, v => v.cmbRoutingRulesSourceUrl.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.IPAPIUrl, v => v.cmbIPAPIUrl.Text).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.notProxyLocalAddress, v => v.tognotProxyLocalAddress.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.systemProxyExceptions, v => v.txtsystemProxyExceptions.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.NotProxyLocalAddress, v => v.tognotProxyLocalAddress.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SystemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SystemProxyExceptions, v => v.txtsystemProxyExceptions.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.CustomSystemProxyPacPath, v => v.txtCustomSystemProxyPacPath.Text).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.TunAutoRoute, v => v.togAutoRoute.IsChecked).DisposeWith(disposables);
|
||||
@@ -217,7 +217,7 @@ public partial class OptionSettingWindow
|
||||
{
|
||||
if (ViewModel != null)
|
||||
{
|
||||
ViewModel.destOverride = clbdestOverride.SelectedItems.Cast<string>().ToList();
|
||||
ViewModel.DestOverride = clbdestOverride.SelectedItems.Cast<string>().ToList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -345,6 +345,14 @@
|
||||
Binding="{Binding TodayUp}"
|
||||
ExName="TodayUp"
|
||||
Header="{x:Static resx:ResUI.LvTodayUploadDataAmount}" />
|
||||
|
||||
<base:MyDGTextColumn
|
||||
x:Name="colIpInfo"
|
||||
Width="100"
|
||||
Binding="{Binding IpInfo}"
|
||||
ExName="IpInfo"
|
||||
Header="{x:Static resx:ResUI.LvTestIpInfo}" />
|
||||
|
||||
<base:MyDGTextColumn
|
||||
x:Name="colTodayDown"
|
||||
Width="100"
|
||||
|
||||
@@ -378,10 +378,14 @@ public partial class ProfilesView
|
||||
item2.Width = item.Width;
|
||||
item2.DisplayIndex = displayIndex++;
|
||||
}
|
||||
if (item.Name.ToLower().StartsWith("to"))
|
||||
if (item.Name.StartsWith("to", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
item2.Visibility = _config.GuiItem.EnableStatistics ? Visibility.Visible : Visibility.Hidden;
|
||||
}
|
||||
if (item.Name.Equals("IpInfo", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
item2.Visibility = _config.SpeedTestItem.IPAPIUrl.IsNotEmpty() ? Visibility.Visible : Visibility.Hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user