Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a372d8902e | ||
|
|
c38c62e4c3 | ||
|
|
8aceff7480 | ||
|
|
736c450161 | ||
|
|
3b63a3d308 | ||
|
|
4f56174c8f | ||
|
|
4d2eb324f1 | ||
|
|
3f2ab8ddcb | ||
|
|
fd8f863c5b | ||
|
|
246f1d7df0 | ||
|
|
ccacda9bf5 | ||
|
|
5494d63878 | ||
|
|
c32b9812a7 | ||
|
|
9eb9898b61 | ||
|
|
834e05999f | ||
|
|
b4c37d9906 | ||
|
|
9326b450d7 | ||
|
|
019ee8b1ba | ||
|
|
f043645397 | ||
|
|
3b173f0b3e | ||
|
|
f36c06389d | ||
|
|
d3a0b44247 | ||
|
|
e4d3a98aa8 | ||
|
|
f685682214 | ||
|
|
1c6323315b | ||
|
|
1ff4839be1 | ||
|
|
558e5bb340 | ||
|
|
48a9d208e6 |
31
.github/workflows/winget-publish.yml
vendored
Normal file
31
.github/workflows/winget-publish.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: WinGet submission on release
|
||||
# based off of https://github.com/nushell/nushell/blob/main/.github/workflows/winget-submission.yml
|
||||
# inspired by https://github.com/microsoft/PowerToys/blob/main/.github/workflows/package-submissions.yml
|
||||
# Modified by @MerrickZ https://github.com/anpho
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
jobs:
|
||||
winget:
|
||||
name: Publish winget package
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Submit v2ray package to Windows Package Manager Community Repository
|
||||
run: |
|
||||
|
||||
$wingetPackage = "2dust.v2rayN"
|
||||
$gitToken = "${{ secrets.PT_WINGET }}"
|
||||
|
||||
$github = Invoke-RestMethod -uri "https://api.github.com/repos/2dust/v2rayN/releases"
|
||||
|
||||
$targetRelease = $github | Where-Object -Property prerelease -match 'False' | Select -First 1
|
||||
$installerUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'v2rayN-windows-64-With-Core\.zip*' | Select -ExpandProperty browser_download_url
|
||||
|
||||
$ver = $targetRelease.tag_name
|
||||
|
||||
# getting latest wingetcreate file
|
||||
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
|
||||
.\wingetcreate.exe update $wingetPackage -s -v $ver -u "$installerUrl|x64" -t $gitToken
|
||||
@@ -1,5 +1,5 @@
|
||||
# v2rayN
|
||||
A GUI client for Windows and Linux, support [Xray core](https://github.com/XTLS/Xray-core) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
|
||||
A GUI client for Windows and Linux, support [Xray core](https://github.com/XTLS/Xray-core) and [sing-box-core](https://github.com/SagerNet/sing-box/releases) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
|
||||
|
||||
|
||||
[](https://github.com/2dust/v2rayN/commits/master)
|
||||
|
||||
@@ -31,8 +31,8 @@
|
||||
var parts = this.version.Split('.');
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
this.major = int.Parse(parts[0]);
|
||||
this.minor = int.Parse(parts[1]);
|
||||
this.major = int.Parse(parts.First());
|
||||
this.minor = int.Parse(parts.Last());
|
||||
this.patch = 0;
|
||||
}
|
||||
else if (parts.Length is 3 or 4)
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace ServiceLib.Common
|
||||
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
|
||||
{
|
||||
if (s.IsNullOrEmpty()) return false;
|
||||
return chars.Contains(s[0]);
|
||||
return chars.Contains(s.First());
|
||||
}
|
||||
|
||||
private static bool IsWhiteSpace(this string value)
|
||||
@@ -61,7 +61,7 @@ namespace ServiceLib.Common
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return char.ToUpper(value[0]) + value[1..];
|
||||
return char.ToUpper(value.First()) + value[1..];
|
||||
}
|
||||
|
||||
public static string AppendQuotes(this string value)
|
||||
|
||||
@@ -313,8 +313,8 @@ namespace ServiceLib.Common
|
||||
continue;
|
||||
}
|
||||
|
||||
var key = Uri.UnescapeDataString(keyValue[0]);
|
||||
var val = Uri.UnescapeDataString(keyValue[1]);
|
||||
var key = Uri.UnescapeDataString(keyValue.First());
|
||||
var val = Uri.UnescapeDataString(keyValue.Last());
|
||||
|
||||
if (result[key] is null)
|
||||
{
|
||||
@@ -622,8 +622,8 @@ namespace ServiceLib.Common
|
||||
{
|
||||
if (host.StartsWith("#")) continue;
|
||||
var hostItem = host.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (hostItem.Length < 2) continue;
|
||||
systemHosts.Add(hostItem[1], hostItem[0]);
|
||||
if (hostItem.Length != 2) continue;
|
||||
systemHosts.Add(hostItem.Last(), hostItem.First());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
v2fly = 1,
|
||||
Xray = 2,
|
||||
v2fly_v5 = 4,
|
||||
|
||||
mihomo = 13,
|
||||
|
||||
hysteria = 21,
|
||||
naiveproxy = 22,
|
||||
tuic = 23,
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
public enum EInboundProtocol
|
||||
{
|
||||
socks = 0,
|
||||
http,
|
||||
socks2,
|
||||
http2,
|
||||
pac,
|
||||
api,
|
||||
api2,
|
||||
mixed,
|
||||
speedtest = 21
|
||||
}
|
||||
}
|
||||
@@ -182,14 +182,13 @@
|
||||
public static readonly List<string> Flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" };
|
||||
public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "xhttp", "h2", "quic", "grpc" };
|
||||
public static readonly List<string> KcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" };
|
||||
public static readonly List<string> CoreTypes = new() { "v2fly", "Xray", "sing_box" };
|
||||
public static readonly List<string> CoreTypes4VLESS = new() { "Xray", "sing_box" };
|
||||
public static readonly List<string> CoreTypes = new() { "Xray", "sing_box" };
|
||||
public static readonly List<string> DomainStrategies = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" };
|
||||
public static readonly List<string> DomainStrategies4Singbox = new() { "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6", "" };
|
||||
public static readonly List<string> DomainMatchers = new() { "linear", "mph", "" };
|
||||
public static readonly List<string> Fingerprints = new() { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized", "" };
|
||||
public static readonly List<string> UserAgent = new() { "chrome", "firefox", "safari", "edge", "none" };
|
||||
public static readonly List<string> XhttpMode = new() { "auto", "packet-up", "stream-up" };
|
||||
public static readonly List<string> XhttpMode = new() { "auto", "packet-up", "stream-up", "stream-one" };
|
||||
|
||||
public static readonly List<string> AllowInsecure = new() { "true", "false", "" };
|
||||
public static readonly List<string> DomainStrategy4Freedoms = new() { "AsIs", "UseIP", "UseIPv4", "UseIPv6", "" };
|
||||
@@ -199,7 +198,7 @@
|
||||
public static readonly List<string> Languages = new() { "zh-Hans", "zh-Hant", "en", "fa-Ir", "ru" };
|
||||
public static readonly List<string> Alpns = new() { "h3", "h2", "http/1.1", "h3,h2", "h2,http/1.1", "h3,h2,http/1.1", "" };
|
||||
public static readonly List<string> LogLevels = new() { "debug", "info", "warning", "error", "none" };
|
||||
public static readonly List<string> InboundTags = new() { "socks", "http", "socks2", "http2" };
|
||||
public static readonly List<string> InboundTags = new() { "socks", "socks2" };
|
||||
public static readonly List<string> RuleProtocols = new() { "http", "tls", "bittorrent" };
|
||||
public static readonly List<string> RuleNetworks = new() { "", "tcp", "udp", "tcp,udp" };
|
||||
public static readonly List<string> destOverrideProtocols = ["http", "tls", "quic", "fakedns", "fakedns+others"];
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
get
|
||||
{
|
||||
_statePort2 ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api2));
|
||||
return _statePort2.Value;
|
||||
return _statePort2.Value + (_config.TunModeItem.EnableTun ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,11 +46,13 @@
|
||||
|
||||
public bool InitApp()
|
||||
{
|
||||
_config = ConfigHandler.LoadConfig();
|
||||
if (_config == null)
|
||||
Logging.Setup();
|
||||
var config = ConfigHandler.LoadConfig();
|
||||
if (config == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_config = config;
|
||||
Thread.CurrentThread.CurrentUICulture = new(_config.UiItem.CurrentLanguage);
|
||||
|
||||
//Under Win10
|
||||
@@ -70,15 +72,21 @@
|
||||
|
||||
public bool InitComponents()
|
||||
{
|
||||
Logging.Setup();
|
||||
Logging.LoggingEnabled(_config.GuiItem.EnableLog);
|
||||
Logging.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}");
|
||||
Logging.SaveLog($"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
|
||||
Logging.LoggingEnabled(_config.GuiItem.EnableLog);
|
||||
Logging.ClearLogs();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Reset()
|
||||
{
|
||||
_statePort = null;
|
||||
_statePort2 = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion Init
|
||||
|
||||
#region Config
|
||||
@@ -257,16 +265,8 @@
|
||||
return (ECoreType)profileItem.CoreType;
|
||||
}
|
||||
|
||||
if (_config.CoreTypeItem == null)
|
||||
{
|
||||
return ECoreType.Xray;
|
||||
}
|
||||
var item = _config.CoreTypeItem.FirstOrDefault(it => it.ConfigType == eConfigType);
|
||||
if (item == null)
|
||||
{
|
||||
return ECoreType.Xray;
|
||||
}
|
||||
return item.CoreType;
|
||||
var item = _config.CoreTypeItem?.FirstOrDefault(it => it.ConfigType == eConfigType);
|
||||
return item?.CoreType ?? ECoreType.Xray;
|
||||
}
|
||||
|
||||
#endregion Core Type
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace ServiceLib.Handler
|
||||
await SetTaskLinux();
|
||||
}
|
||||
}
|
||||
else if(Utils.IsOSX())
|
||||
else if (Utils.IsOSX())
|
||||
{
|
||||
//TODO
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
if (config.Inbound.Count > 0)
|
||||
{
|
||||
config.Inbound[0].Protocol = EInboundProtocol.socks.ToString();
|
||||
config.Inbound.First().Protocol = EInboundProtocol.socks.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace ServiceLib.Handler
|
||||
|
||||
if (Utils.IsNullOrEmpty(config.RoutingBasicItem.DomainStrategy))
|
||||
{
|
||||
config.RoutingBasicItem.DomainStrategy = Global.DomainStrategies[0];//"IPIfNonMatch";
|
||||
config.RoutingBasicItem.DomainStrategy = Global.DomainStrategies.First();//"IPIfNonMatch";
|
||||
}
|
||||
|
||||
config.KcpItem ??= new KcpItem
|
||||
@@ -111,7 +111,7 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
if (Thread.CurrentThread.CurrentCulture.Name.Equals("zh-cn", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
config.UiItem.CurrentLanguage = Global.Languages[0];
|
||||
config.UiItem.CurrentLanguage = Global.Languages.First();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -132,7 +132,7 @@ namespace ServiceLib.Handler
|
||||
}
|
||||
if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedTestUrl))
|
||||
{
|
||||
config.SpeedTestItem.SpeedTestUrl = Global.SpeedTestUrls[0];
|
||||
config.SpeedTestItem.SpeedTestUrl = Global.SpeedTestUrls.First();
|
||||
}
|
||||
if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedPingTestUrl))
|
||||
{
|
||||
@@ -148,7 +148,7 @@ namespace ServiceLib.Handler
|
||||
|
||||
config.Mux4SboxItem ??= new()
|
||||
{
|
||||
Protocol = Global.SingboxMuxs[0],
|
||||
Protocol = Global.SingboxMuxs.First(),
|
||||
MaxConnections = 8
|
||||
};
|
||||
|
||||
@@ -429,7 +429,7 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
sort = ProfileExHandler.Instance.GetSort(lstProfile[0].IndexId) - 1;
|
||||
sort = ProfileExHandler.Instance.GetSort(lstProfile.First().IndexId) - 1;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1029,12 +1029,11 @@ namespace ServiceLib.Handler
|
||||
public static async Task<ProfileItem?> GetPreSocksItem(Config config, ProfileItem node, ECoreType coreType)
|
||||
{
|
||||
ProfileItem? itemSocks = null;
|
||||
var preCoreType = ECoreType.sing_box;
|
||||
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun)
|
||||
{
|
||||
itemSocks = new ProfileItem()
|
||||
{
|
||||
CoreType = preCoreType,
|
||||
CoreType = ECoreType.sing_box,
|
||||
ConfigType = EConfigType.SOCKS,
|
||||
Address = Global.Loopback,
|
||||
Sni = node.Address, //Tun2SocksAddress
|
||||
@@ -1043,7 +1042,7 @@ namespace ServiceLib.Handler
|
||||
}
|
||||
else if ((node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0))
|
||||
{
|
||||
preCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
|
||||
var preCoreType = config.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
|
||||
itemSocks = new ProfileItem()
|
||||
{
|
||||
CoreType = preCoreType,
|
||||
|
||||
@@ -97,16 +97,12 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
if (_process != null)
|
||||
{
|
||||
await KillProcess(_process);
|
||||
_process.Dispose();
|
||||
_process = null;
|
||||
_process = await KillProcess(_process);
|
||||
}
|
||||
|
||||
if (_processPre != null)
|
||||
{
|
||||
await KillProcess(_processPre);
|
||||
_processPre.Dispose();
|
||||
_processPre = null;
|
||||
_processPre = await KillProcess(_processPre);
|
||||
}
|
||||
|
||||
if (_linuxSudoPid > 0)
|
||||
@@ -125,8 +121,7 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
try
|
||||
{
|
||||
var _p = Process.GetProcessById(pid);
|
||||
await KillProcess(_p);
|
||||
await KillProcess(Process.GetProcessById(pid));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -179,7 +174,7 @@ namespace ServiceLib.Handler
|
||||
var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType);
|
||||
if (itemSocks != null)
|
||||
{
|
||||
var preCoreType = _config.RunningCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
|
||||
var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
|
||||
var fileName = Utils.GetConfigPath(Global.CorePreConfigFileName);
|
||||
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
|
||||
if (result.Success)
|
||||
@@ -326,32 +321,18 @@ namespace ServiceLib.Handler
|
||||
}
|
||||
}
|
||||
|
||||
private async Task KillProcess(Process? proc)
|
||||
private async Task<Process?> KillProcess(Process? proc)
|
||||
{
|
||||
if (proc is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
proc?.Kill();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
return null;
|
||||
}
|
||||
try { proc?.Kill(true); } catch { }
|
||||
try { proc?.Close(); } catch { }
|
||||
try { proc?.Dispose(); } catch { }
|
||||
|
||||
await Task.Delay(100);
|
||||
if (proc?.HasExited == false)
|
||||
{
|
||||
try
|
||||
{
|
||||
proc?.Kill();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion Process
|
||||
@@ -375,7 +356,7 @@ namespace ServiceLib.Handler
|
||||
|
||||
private async Task KillProcessAsLinuxSudo()
|
||||
{
|
||||
var cmdLine = $"kill -9 {_linuxSudoPid}";
|
||||
var cmdLine = $"kill {_linuxSudoPid}";
|
||||
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
|
||||
Process proc = new()
|
||||
{
|
||||
@@ -392,26 +373,37 @@ namespace ServiceLib.Handler
|
||||
|
||||
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
|
||||
{
|
||||
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
|
||||
await Task.Delay(10);
|
||||
await proc.StandardInput.WriteLineAsync(pwd);
|
||||
await Task.Delay(10);
|
||||
await proc.StandardInput.WriteLineAsync(pwd);
|
||||
try
|
||||
{
|
||||
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
|
||||
await Task.Delay(10);
|
||||
await proc.StandardInput.WriteLineAsync(pwd);
|
||||
await Task.Delay(10);
|
||||
await proc.StandardInput.WriteLineAsync(pwd);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
||||
await proc.WaitForExitAsync(timeout.Token);
|
||||
await Task.Delay(1000);
|
||||
await Task.Delay(3000);
|
||||
}
|
||||
|
||||
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
|
||||
{
|
||||
//Shell scripts
|
||||
var shFilePath = Utils.GetBinPath(fileName);
|
||||
var shFilePath = Utils.GetBinPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName);
|
||||
File.Delete(shFilePath);
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("#!/bin/sh");
|
||||
if (_config.TunModeItem.LinuxSudoPwd.IsNullOrEmpty())
|
||||
if (AppHandler.Instance.IsAdministrator)
|
||||
{
|
||||
sb.AppendLine($"{cmdLine}");
|
||||
}
|
||||
else if (_config.TunModeItem.LinuxSudoPwd.IsNullOrEmpty())
|
||||
{
|
||||
sb.AppendLine($"pkexec {cmdLine}");
|
||||
}
|
||||
|
||||
@@ -99,8 +99,8 @@ namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
return null;
|
||||
}
|
||||
item.Security = userInfoParts[0];
|
||||
item.Id = Utils.UrlDecode(userInfoParts[1]);
|
||||
item.Security = userInfoParts.First();
|
||||
item.Id = Utils.UrlDecode(userInfoParts.Last());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -111,8 +111,8 @@ namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
return null;
|
||||
}
|
||||
item.Security = userInfoParts[0];
|
||||
item.Id = userInfoParts[1];
|
||||
item.Security = userInfoParts.First();
|
||||
item.Id = userInfoParts.Last();
|
||||
}
|
||||
|
||||
var queryParameters = Utils.ParseQueryString(parsedUrl.Query);
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
ProfileItem? item;
|
||||
|
||||
item = ResolveSocksNew(str) ?? ResolveSocks(str);
|
||||
var item = ResolveSocksNew(str) ?? ResolveSocks(str);
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
@@ -25,19 +24,13 @@
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
var url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
var remark = string.Empty;
|
||||
if (Utils.IsNotEmpty(item.Remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
//url = string.Format("{0}:{1}@{2}:{3}",
|
||||
// item.security,
|
||||
// item.id,
|
||||
// item.address,
|
||||
// item.port);
|
||||
//url = Utile.Base64Encode(url);
|
||||
//new
|
||||
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
|
||||
return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark);
|
||||
@@ -51,7 +44,7 @@
|
||||
};
|
||||
result = result[Global.ProtocolShares[EConfigType.SOCKS].Length..];
|
||||
//remark
|
||||
int indexRemark = result.IndexOf("#");
|
||||
var indexRemark = result.IndexOf("#");
|
||||
if (indexRemark > 0)
|
||||
{
|
||||
try
|
||||
@@ -62,7 +55,7 @@
|
||||
result = result[..indexRemark];
|
||||
}
|
||||
//part decode
|
||||
int indexS = result.IndexOf("@");
|
||||
var indexS = result.IndexOf("@");
|
||||
if (indexS > 0)
|
||||
{
|
||||
}
|
||||
@@ -71,21 +64,20 @@
|
||||
result = Utils.Base64Decode(result);
|
||||
}
|
||||
|
||||
string[] arr1 = result.Split('@');
|
||||
var arr1 = result.Split('@');
|
||||
if (arr1.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
string[] arr21 = arr1[0].Split(':');
|
||||
//string[] arr22 = arr1[1].Split(':');
|
||||
int indexPort = arr1[1].LastIndexOf(":");
|
||||
var arr21 = arr1.First().Split(':');
|
||||
var indexPort = arr1.Last().LastIndexOf(":");
|
||||
if (arr21.Length != 2 || indexPort < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
item.Address = arr1[1][..indexPort];
|
||||
item.Port = Utils.ToInt(arr1[1][(indexPort + 1)..]);
|
||||
item.Security = arr21[0];
|
||||
item.Security = arr21.First();
|
||||
item.Id = arr21[1];
|
||||
|
||||
return item;
|
||||
@@ -106,10 +98,10 @@
|
||||
// parse base64 UserInfo
|
||||
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
||||
var userInfo = Utils.Base64Decode(rawUserInfo);
|
||||
var userInfoParts = userInfo.Split(new[] { ':' }, 2);
|
||||
var userInfoParts = userInfo.Split([':'], 2);
|
||||
if (userInfoParts.Length == 2)
|
||||
{
|
||||
item.Security = userInfoParts[0];
|
||||
item.Security = userInfoParts.First();
|
||||
item.Id = userInfoParts[1];
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length == 2)
|
||||
{
|
||||
item.Id = userInfoParts[0];
|
||||
item.Security = userInfoParts[1];
|
||||
item.Id = userInfoParts.First();
|
||||
item.Security = userInfoParts.Last();
|
||||
}
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
|
||||
try
|
||||
{
|
||||
var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.http);
|
||||
var portSocks = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
if (port <= 0)
|
||||
{
|
||||
return false;
|
||||
@@ -23,7 +22,7 @@
|
||||
{
|
||||
case ESysProxyType.ForcedChange when Utils.IsWindows():
|
||||
{
|
||||
GetWindowsProxyString(config, port, portSocks, out var strProxy, out var strExceptions);
|
||||
GetWindowsProxyString(config, port, out var strProxy, out var strExceptions);
|
||||
ProxySettingWindows.SetProxy(strProxy, strExceptions, 2);
|
||||
break;
|
||||
}
|
||||
@@ -64,12 +63,12 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void GetWindowsProxyString(Config config, int port, int portSocks, out string strProxy, out string strExceptions)
|
||||
private static void GetWindowsProxyString(Config config, int port, out string strProxy, out string strExceptions)
|
||||
{
|
||||
strExceptions = "";
|
||||
strExceptions = $"{config.ConstItem.DefIEProxyExceptions};{config.SystemProxyItem.SystemProxyExceptions}";
|
||||
if (config.SystemProxyItem.NotProxyLocalAddress)
|
||||
{
|
||||
strExceptions = $"<local>;{config.ConstItem.DefIEProxyExceptions};{config.SystemProxyItem.SystemProxyExceptions}";
|
||||
strExceptions = $"<local>;{strExceptions}";
|
||||
}
|
||||
|
||||
strProxy = string.Empty;
|
||||
@@ -82,7 +81,7 @@
|
||||
strProxy = config.SystemProxyItem.SystemProxyAdvancedProtocol
|
||||
.Replace("{ip}", Global.Loopback)
|
||||
.Replace("{http_port}", port.ToString())
|
||||
.Replace("{socks_port}", portSocks.ToString());
|
||||
.Replace("{socks_port}", port.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -171,6 +171,7 @@
|
||||
public int SpeedTestTimeout { get; set; }
|
||||
public string SpeedTestUrl { get; set; }
|
||||
public string SpeedPingTestUrl { get; set; }
|
||||
public int SpeedTestPageSize { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -222,7 +223,6 @@
|
||||
public int ProxiesSorting { get; set; }
|
||||
public bool ProxiesAutoRefresh { get; set; }
|
||||
public int ProxiesAutoDelayTestInterval { get; set; } = 10;
|
||||
public int ConnectionsSorting { get; set; }
|
||||
public bool ConnectionsAutoRefresh { get; set; }
|
||||
public int ConnectionsRefreshInterval { get; set; } = 2;
|
||||
}
|
||||
|
||||
@@ -349,20 +349,20 @@ namespace ServiceLib.Models
|
||||
public string? path { get; set; }
|
||||
public string? host { get; set; }
|
||||
public string? mode { get; set; }
|
||||
public string? scMaxEachPostBytes { get; set; }
|
||||
public string? scMaxConcurrentPosts { get; set; }
|
||||
public string? scMinPostsIntervalMs { get; set; }
|
||||
public Xmux4Ray? xmux { get; set; }
|
||||
public object? scMaxEachPostBytes { get; set; }
|
||||
public object? scMaxConcurrentPosts { get; set; }
|
||||
public object? scMinPostsIntervalMs { get; set; }
|
||||
//public Xmux4Ray? xmux { get; set; }
|
||||
public object? extra { get; set; }
|
||||
}
|
||||
|
||||
public class Xmux4Ray
|
||||
{
|
||||
public int? maxConcurrency { get; set; }
|
||||
public int? maxConnections { get; set; }
|
||||
public int? cMaxReuseTimes { get; set; }
|
||||
public int? cMaxLifetimeMs { get; set; }
|
||||
}
|
||||
//public class Xmux4Ray
|
||||
//{
|
||||
// public object? maxConcurrency { get; set; }
|
||||
// public object? maxConnections { get; set; }
|
||||
// public object? cMaxReuseTimes { get; set; }
|
||||
// public object? cMaxLifetimeMs { get; set; }
|
||||
//}
|
||||
|
||||
public class HttpSettings4Ray
|
||||
{
|
||||
|
||||
15
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
15
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
@@ -3140,7 +3140,7 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Linux system sudo password 的本地化字符串。
|
||||
/// 查找类似 System sudo password 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSettingsLinuxSudoPassword {
|
||||
get {
|
||||
@@ -3311,7 +3311,7 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 SOCKS Port 的本地化字符串。
|
||||
/// 查找类似 Mixed Port 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSettingsSocksPort {
|
||||
get {
|
||||
@@ -3320,7 +3320,7 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6; 的本地化字符串。
|
||||
/// 查找类似 Pac port = +2; Xray API port = +3; mihomo API port = +4; 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSettingsSocksPortTip {
|
||||
get {
|
||||
@@ -3337,6 +3337,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Number per time for auto batch during speedtest(max 1000) 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSettingsSpeedTestPageSize {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSettingsSpeedTestPageSize", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 SpeedTest Single Timeout Value 的本地化字符串。
|
||||
/// </summary>
|
||||
|
||||
@@ -752,7 +752,7 @@
|
||||
<value>Turn on Sniffing</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPort" xml:space="preserve">
|
||||
<value>ساکس Port</value>
|
||||
<value>Mixed Port</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBoot" xml:space="preserve">
|
||||
<value>درهنگام راه ائدازی شروع شود</value>
|
||||
@@ -1244,7 +1244,7 @@
|
||||
<value>Copy the font TTF/TTC file to the directory guiFonts, restart the settings</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||
<value>http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6;</value>
|
||||
<value>Pac port = +2; Xray API port = +3; mihomo API port = +4;</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
||||
<value>Set this with admin privileges, get admin privileges after startup</value>
|
||||
@@ -1364,7 +1364,7 @@
|
||||
<value>(Domain or IP or ProcName) and Port and Protocol and InboundTag => OutboundTag</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
|
||||
<value>Linux system sudo password</value>
|
||||
<value>System sudo password</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||
<value>The password is encrypted and stored only in local files.</value>
|
||||
@@ -1384,4 +1384,7 @@
|
||||
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
||||
<value>Hide to tray when closing the window</value>
|
||||
</data>
|
||||
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
|
||||
<value>Number per time for auto batch during speedtest(max 1000)</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -755,7 +755,7 @@
|
||||
<value>Turn on Sniffing</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPort" xml:space="preserve">
|
||||
<value>SOCKS Port</value>
|
||||
<value>Mixed Port</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBoot" xml:space="preserve">
|
||||
<value>Start on boot</value>
|
||||
@@ -992,7 +992,7 @@
|
||||
<value>Copy the font TTF/TTC file to the directory guiFonts, restart the settings</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||
<value>http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6;</value>
|
||||
<value>Pac port = +2; Xray API port = +3; mihomo API port = +4;</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
||||
<value>Set this with admin privileges, get admin privileges after startup</value>
|
||||
@@ -1364,7 +1364,7 @@
|
||||
<value>Remarks Memo</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
|
||||
<value>Linux system sudo password</value>
|
||||
<value>System sudo password</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||
<value>The password is encrypted and stored only in local files.</value>
|
||||
@@ -1384,4 +1384,7 @@
|
||||
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
||||
<value>Hide to tray when closing the window</value>
|
||||
</data>
|
||||
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
|
||||
<value>Number per time for auto batch during speedtest(max 1000)</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -761,7 +761,7 @@
|
||||
<value>Включить сниффинг</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPort" xml:space="preserve">
|
||||
<value>Порт Socks</value>
|
||||
<value>Mixed Port</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBoot" xml:space="preserve">
|
||||
<value>Автозапуск</value>
|
||||
@@ -1310,7 +1310,7 @@
|
||||
<value>Move up and down</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||
<value>http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6;</value>
|
||||
<value>Pac port = +2; Xray API port = +3; mihomo API port = +4;</value>
|
||||
</data>
|
||||
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
|
||||
<value>Install the font to the system and restart the settings</value>
|
||||
@@ -1364,7 +1364,7 @@
|
||||
<value>Remote (WebDAV)</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
|
||||
<value>Linux system sudo password</value>
|
||||
<value>System sudo password</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||
<value>The password is encrypted and stored only in local files.</value>
|
||||
@@ -1384,4 +1384,7 @@
|
||||
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
||||
<value>Hide to tray when closing the window</value>
|
||||
</data>
|
||||
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
|
||||
<value>Number per time for auto batch during speedtest(max 1000)</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -755,7 +755,7 @@
|
||||
<value>开启流量探测</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPort" xml:space="preserve">
|
||||
<value>本地socks监听端口</value>
|
||||
<value>本地混合监听端口</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBoot" xml:space="preserve">
|
||||
<value>开机启动(可能会不成功)</value>
|
||||
@@ -992,7 +992,7 @@
|
||||
<value>拷贝字体TTF/TTC文件到目录guiFonts,重启设置</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||
<value>http端口= +1;Pac端口= +4;*ray API端口= +5;mihomo API端口= +6;</value>
|
||||
<value>Pac端口= +2;Xray API端口= +3;mihomo API端口= +4;</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
||||
<value>以管理员权限设置此项,在启动后获得管理员权限</value>
|
||||
@@ -1361,7 +1361,7 @@
|
||||
<value>备注备忘</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
|
||||
<value>Linux系统的sudo密码</value>
|
||||
<value>系统的sudo密码</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||
<value>密码已加密且只存储在本地文件中,无密码则每次都要输入</value>
|
||||
@@ -1381,4 +1381,7 @@
|
||||
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
||||
<value>关闭窗口时隐藏至托盘</value>
|
||||
</data>
|
||||
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
|
||||
<value>测速时自动分批的每批数量(最大1000)</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -755,7 +755,7 @@
|
||||
<value>開啟流量探測</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPort" xml:space="preserve">
|
||||
<value>本機SOCKS偵聽埠</value>
|
||||
<value>本機混合偵聽埠</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBoot" xml:space="preserve">
|
||||
<value>開機啟動(可能會不成功)</value>
|
||||
@@ -992,7 +992,7 @@
|
||||
<value>複製字型TTF/TTC檔案到目錄guiFonts,重啟設定</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||
<value>http連接埠= +1;Pac連接埠= +4;*ray API連接埠= +5;mihomo API連接埠= +6;</value>
|
||||
<value>Pac連接埠= +2;Xray API連接埠= +3;mihomo API連接埠= +4;</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
||||
<value>以管理員權限設定此項,在啟動後獲得管理員權限</value>
|
||||
@@ -1361,7 +1361,7 @@
|
||||
<value>混淆密碼(obfs password)</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
|
||||
<value>Linux系統的sudo密碼</value>
|
||||
<value>系統的sudo密碼</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||
<value>密碼已加密且只儲存在本機檔案中,無密碼則每次都要輸入</value>
|
||||
@@ -1381,4 +1381,7 @@
|
||||
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
||||
<value>關閉視窗時隱藏至托盤</value>
|
||||
</data>
|
||||
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
|
||||
<value>測速時自動分批的每批數量(最大1000)</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -4,17 +4,17 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>7.2.2</Version>
|
||||
<Version>7.3.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Downloader" Version="3.3.0" />
|
||||
<PackageReference Include="Downloader" Version="3.3.1" />
|
||||
<PackageReference Include="ReactiveUI" Version="20.1.63" />
|
||||
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
|
||||
<PackageReference Include="Splat.NLog" Version="15.2.22" />
|
||||
<PackageReference Include="WebDav.Client" Version="2.8.0" />
|
||||
<PackageReference Include="YamlDotNet" Version="16.2.0" />
|
||||
<PackageReference Include="YamlDotNet" Version="16.2.1" />
|
||||
<PackageReference Include="QRCoder" Version="1.6.0" />
|
||||
<PackageReference Include="CliWrap" Version="3.6.7" />
|
||||
<PackageReference Include="SkiaSharp.QrCode" Version="0.7.0" />
|
||||
|
||||
@@ -78,17 +78,15 @@ namespace ServiceLib.Services.CoreConfig
|
||||
return ret;
|
||||
}
|
||||
|
||||
//port
|
||||
fileContent["port"] = AppHandler.Instance.GetLocalPort(EInboundProtocol.http);
|
||||
//socks-port
|
||||
fileContent["socks-port"] = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
//mixed-port
|
||||
fileContent["mixed-port"] = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
//log-level
|
||||
fileContent["log-level"] = GetLogLevel(_config.CoreBasicItem.Loglevel);
|
||||
|
||||
//external-controller
|
||||
fileContent["external-controller"] = $"{Global.Loopback}:{AppHandler.Instance.StatePort2}";
|
||||
//allow-lan
|
||||
if (_config.Inbound[0].AllowLANConn)
|
||||
if (_config.Inbound.First().AllowLANConn)
|
||||
{
|
||||
fileContent["allow-lan"] = "true";
|
||||
fileContent["bind-address"] = "*";
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
|
||||
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
|
||||
{
|
||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
||||
return ret;
|
||||
@@ -52,7 +52,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
|
||||
await GenInbounds(singboxConfig);
|
||||
|
||||
await GenOutbound(node, singboxConfig.outbounds[0]);
|
||||
await GenOutbound(node, singboxConfig.outbounds.First());
|
||||
|
||||
await GenMoreOutbounds(node, singboxConfig);
|
||||
|
||||
@@ -174,7 +174,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
{
|
||||
listen = Global.Loopback,
|
||||
listen_port = port,
|
||||
type = EInboundProtocol.http.ToString(),
|
||||
type = EInboundProtocol.mixed.ToString(),
|
||||
};
|
||||
inbound.tag = inbound.type + inbound.listen_port.ToString();
|
||||
singboxConfig.inbounds.Add(inbound);
|
||||
@@ -488,15 +488,15 @@ namespace ServiceLib.Services.CoreConfig
|
||||
{
|
||||
var inbound = new Inbound4Sbox()
|
||||
{
|
||||
type = EInboundProtocol.socks.ToString(),
|
||||
type = EInboundProtocol.mixed.ToString(),
|
||||
tag = EInboundProtocol.socks.ToString(),
|
||||
listen = Global.Loopback,
|
||||
};
|
||||
singboxConfig.inbounds.Add(inbound);
|
||||
|
||||
inbound.listen_port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
inbound.sniff = _config.Inbound[0].SniffingEnabled;
|
||||
inbound.sniff_override_destination = _config.Inbound[0].RouteOnly ? false : _config.Inbound[0].SniffingEnabled;
|
||||
inbound.sniff = _config.Inbound.First().SniffingEnabled;
|
||||
inbound.sniff_override_destination = _config.Inbound.First().RouteOnly ? false : _config.Inbound.First().SniffingEnabled;
|
||||
inbound.domain_strategy = Utils.IsNullOrEmpty(_config.RoutingBasicItem.DomainStrategy4Singbox) ? null : _config.RoutingBasicItem.DomainStrategy4Singbox;
|
||||
|
||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
||||
@@ -505,33 +505,23 @@ namespace ServiceLib.Services.CoreConfig
|
||||
inbound.domain_strategy = routing.DomainStrategy4Singbox;
|
||||
}
|
||||
|
||||
//http
|
||||
var inbound2 = GetInbound(inbound, EInboundProtocol.http, false);
|
||||
singboxConfig.inbounds.Add(inbound2);
|
||||
|
||||
if (_config.Inbound[0].AllowLANConn)
|
||||
if (_config.Inbound.First().AllowLANConn)
|
||||
{
|
||||
if (_config.Inbound[0].NewPort4LAN)
|
||||
if (_config.Inbound.First().NewPort4LAN)
|
||||
{
|
||||
var inbound3 = GetInbound(inbound, EInboundProtocol.socks2, true);
|
||||
inbound3.listen = listen;
|
||||
singboxConfig.inbounds.Add(inbound3);
|
||||
|
||||
var inbound4 = GetInbound(inbound, EInboundProtocol.http2, false);
|
||||
inbound4.listen = listen;
|
||||
singboxConfig.inbounds.Add(inbound4);
|
||||
|
||||
//auth
|
||||
if (Utils.IsNotEmpty(_config.Inbound[0].User) && Utils.IsNotEmpty(_config.Inbound[0].Pass))
|
||||
if (Utils.IsNotEmpty(_config.Inbound.First().User) && Utils.IsNotEmpty(_config.Inbound.First().Pass))
|
||||
{
|
||||
inbound3.users = new() { new() { username = _config.Inbound[0].User, password = _config.Inbound[0].Pass } };
|
||||
inbound4.users = new() { new() { username = _config.Inbound[0].User, password = _config.Inbound[0].Pass } };
|
||||
inbound3.users = new() { new() { username = _config.Inbound.First().User, password = _config.Inbound.First().Pass } };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
inbound.listen = listen;
|
||||
inbound2.listen = listen;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -540,20 +530,20 @@ namespace ServiceLib.Services.CoreConfig
|
||||
{
|
||||
if (_config.TunModeItem.Mtu <= 0)
|
||||
{
|
||||
_config.TunModeItem.Mtu = Utils.ToInt(Global.TunMtus[0]);
|
||||
_config.TunModeItem.Mtu = Utils.ToInt(Global.TunMtus.First());
|
||||
}
|
||||
if (Utils.IsNullOrEmpty(_config.TunModeItem.Stack))
|
||||
{
|
||||
_config.TunModeItem.Stack = Global.TunStacks[0];
|
||||
_config.TunModeItem.Stack = Global.TunStacks.First();
|
||||
}
|
||||
|
||||
var tunInbound = JsonUtils.Deserialize<Inbound4Sbox>(Utils.GetEmbedText(Global.TunSingboxInboundFileName)) ?? new Inbound4Sbox { };
|
||||
tunInbound.interface_name = Utils.IsOSX()? $"utun{new Random().Next(99)}": "singbox_tun";
|
||||
tunInbound.interface_name = Utils.IsOSX() ? $"utun{new Random().Next(99)}" : "singbox_tun";
|
||||
tunInbound.mtu = _config.TunModeItem.Mtu;
|
||||
tunInbound.strict_route = _config.TunModeItem.StrictRoute;
|
||||
tunInbound.stack = _config.TunModeItem.Stack;
|
||||
tunInbound.sniff = _config.Inbound[0].SniffingEnabled;
|
||||
//tunInbound.sniff_override_destination = _config.inbound[0].routeOnly ? false : _config.inbound[0].sniffingEnabled;
|
||||
tunInbound.sniff = _config.Inbound.First().SniffingEnabled;
|
||||
//tunInbound.sniff_override_destination = _config.inbound.First().routeOnly ? false : _config.inbound.First().sniffingEnabled;
|
||||
if (_config.TunModeItem.EnableIPv6Address == false)
|
||||
{
|
||||
tunInbound.address = ["172.18.0.1/30"];
|
||||
@@ -574,7 +564,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
var inbound = JsonUtils.DeepCopy(inItem);
|
||||
inbound.tag = protocol.ToString();
|
||||
inbound.listen_port = inItem.listen_port + (int)protocol;
|
||||
inbound.type = bSocks ? EInboundProtocol.socks.ToString() : EInboundProtocol.http.ToString();
|
||||
inbound.type = EInboundProtocol.mixed.ToString();
|
||||
return inbound;
|
||||
}
|
||||
|
||||
@@ -867,7 +857,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
}
|
||||
|
||||
//current proxy
|
||||
var outbound = singboxConfig.outbounds[0];
|
||||
var outbound = singboxConfig.outbounds.First();
|
||||
var txtOutbound = Utils.GetEmbedText(Global.SingboxSampleOutbound);
|
||||
|
||||
//Previous proxy
|
||||
@@ -910,7 +900,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
try
|
||||
{
|
||||
var dnsOutbound = "dns_out";
|
||||
if (!_config.Inbound[0].SniffingEnabled)
|
||||
if (!_config.Inbound.First().SniffingEnabled)
|
||||
{
|
||||
singboxConfig.route.rules.Add(new()
|
||||
{
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
|
||||
await GenRouting(v2rayConfig);
|
||||
|
||||
await GenOutbound(node, v2rayConfig.outbounds[0]);
|
||||
await GenOutbound(node, v2rayConfig.outbounds.First());
|
||||
|
||||
await GenMoreOutbounds(node, v2rayConfig);
|
||||
|
||||
@@ -322,7 +322,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
{
|
||||
listen = Global.Loopback,
|
||||
port = port,
|
||||
protocol = EInboundProtocol.http.ToString(),
|
||||
protocol = EInboundProtocol.socks.ToString(),
|
||||
};
|
||||
inbound.tag = inbound.protocol + inbound.port.ToString();
|
||||
v2rayConfig.inbounds.Add(inbound);
|
||||
@@ -391,39 +391,27 @@ namespace ServiceLib.Services.CoreConfig
|
||||
var listen = "0.0.0.0";
|
||||
v2rayConfig.inbounds = [];
|
||||
|
||||
Inbounds4Ray? inbound = GetInbound(_config.Inbound[0], EInboundProtocol.socks, true);
|
||||
var inbound = GetInbound(_config.Inbound.First(), EInboundProtocol.socks, true);
|
||||
v2rayConfig.inbounds.Add(inbound);
|
||||
|
||||
//http
|
||||
Inbounds4Ray? inbound2 = GetInbound(_config.Inbound[0], EInboundProtocol.http, false);
|
||||
v2rayConfig.inbounds.Add(inbound2);
|
||||
|
||||
if (_config.Inbound[0].AllowLANConn)
|
||||
if (_config.Inbound.First().AllowLANConn)
|
||||
{
|
||||
if (_config.Inbound[0].NewPort4LAN)
|
||||
if (_config.Inbound.First().NewPort4LAN)
|
||||
{
|
||||
var inbound3 = GetInbound(_config.Inbound[0], EInboundProtocol.socks2, true);
|
||||
var inbound3 = GetInbound(_config.Inbound.First(), EInboundProtocol.socks2, true);
|
||||
inbound3.listen = listen;
|
||||
v2rayConfig.inbounds.Add(inbound3);
|
||||
|
||||
var inbound4 = GetInbound(_config.Inbound[0], EInboundProtocol.http2, false);
|
||||
inbound4.listen = listen;
|
||||
v2rayConfig.inbounds.Add(inbound4);
|
||||
|
||||
//auth
|
||||
if (Utils.IsNotEmpty(_config.Inbound[0].User) && Utils.IsNotEmpty(_config.Inbound[0].Pass))
|
||||
if (Utils.IsNotEmpty(_config.Inbound.First().User) && Utils.IsNotEmpty(_config.Inbound.First().Pass))
|
||||
{
|
||||
inbound3.settings.auth = "password";
|
||||
inbound3.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.Inbound[0].User, pass = _config.Inbound[0].Pass } };
|
||||
|
||||
inbound4.settings.auth = "password";
|
||||
inbound4.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.Inbound[0].User, pass = _config.Inbound[0].Pass } };
|
||||
inbound3.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.Inbound.First().User, pass = _config.Inbound.First().Pass } };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
inbound.listen = listen;
|
||||
inbound2.listen = listen;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -449,7 +437,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
}
|
||||
inbound.tag = protocol.ToString();
|
||||
inbound.port = inItem.LocalPort + (int)protocol;
|
||||
inbound.protocol = bSocks ? EInboundProtocol.socks.ToString() : EInboundProtocol.http.ToString();
|
||||
inbound.protocol = EInboundProtocol.socks.ToString();
|
||||
inbound.settings.udp = inItem.UdpEnabled;
|
||||
inbound.sniffing.enabled = inItem.SniffingEnabled;
|
||||
inbound.sniffing.destOverride = inItem.DestOverride;
|
||||
@@ -587,7 +575,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
}
|
||||
else
|
||||
{
|
||||
vnextItem = outbound.settings.vnext[0];
|
||||
vnextItem = outbound.settings.vnext.First();
|
||||
}
|
||||
vnextItem.address = node.Address;
|
||||
vnextItem.port = node.Port;
|
||||
@@ -600,7 +588,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
}
|
||||
else
|
||||
{
|
||||
usersItem = vnextItem.users[0];
|
||||
usersItem = vnextItem.users.First();
|
||||
}
|
||||
//远程服务器用户ID
|
||||
usersItem.id = node.Id;
|
||||
@@ -630,7 +618,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
}
|
||||
else
|
||||
{
|
||||
serversItem = outbound.settings.servers[0];
|
||||
serversItem = outbound.settings.servers.First();
|
||||
}
|
||||
serversItem.address = node.Address;
|
||||
serversItem.port = node.Port;
|
||||
@@ -656,7 +644,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
}
|
||||
else
|
||||
{
|
||||
serversItem = outbound.settings.servers[0];
|
||||
serversItem = outbound.settings.servers.First();
|
||||
}
|
||||
serversItem.address = node.Address;
|
||||
serversItem.port = node.Port;
|
||||
@@ -691,7 +679,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
}
|
||||
else
|
||||
{
|
||||
vnextItem = outbound.settings.vnext[0];
|
||||
vnextItem = outbound.settings.vnext.First();
|
||||
}
|
||||
vnextItem.address = node.Address;
|
||||
vnextItem.port = node.Port;
|
||||
@@ -704,7 +692,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
}
|
||||
else
|
||||
{
|
||||
usersItem = vnextItem.users[0];
|
||||
usersItem = vnextItem.users.First();
|
||||
}
|
||||
usersItem.id = node.Id;
|
||||
usersItem.email = Global.UserEMail;
|
||||
@@ -740,7 +728,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
}
|
||||
else
|
||||
{
|
||||
serversItem = outbound.settings.servers[0];
|
||||
serversItem = outbound.settings.servers.First();
|
||||
}
|
||||
serversItem.address = node.Address;
|
||||
serversItem.port = node.Port;
|
||||
@@ -1167,7 +1155,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
{
|
||||
//fragment proxy
|
||||
if (_config.CoreBasicItem.EnableFragment
|
||||
&& Utils.IsNotEmpty(v2rayConfig.outbounds[0].streamSettings?.security))
|
||||
&& Utils.IsNotEmpty(v2rayConfig.outbounds.First().streamSettings?.security))
|
||||
{
|
||||
var fragmentOutbound = new Outbounds4Ray
|
||||
{
|
||||
@@ -1185,7 +1173,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
};
|
||||
|
||||
v2rayConfig.outbounds.Add(fragmentOutbound);
|
||||
v2rayConfig.outbounds[0].streamSettings.sockopt = new()
|
||||
v2rayConfig.outbounds.First().streamSettings.sockopt = new()
|
||||
{
|
||||
dialerProxy = fragmentOutbound.tag
|
||||
};
|
||||
@@ -1205,7 +1193,7 @@ namespace ServiceLib.Services.CoreConfig
|
||||
}
|
||||
|
||||
//current proxy
|
||||
var outbound = v2rayConfig.outbounds[0];
|
||||
var outbound = v2rayConfig.outbounds.First();
|
||||
var txtOutbound = Utils.GetEmbedText(Global.V2raySampleOutbound);
|
||||
|
||||
//Previous proxy
|
||||
|
||||
@@ -280,7 +280,7 @@ namespace ServiceLib.Services
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var httpPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.http);
|
||||
var httpPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
if (await SocketCheck(Global.Loopback, httpPort) == false)
|
||||
{
|
||||
return null;
|
||||
|
||||
@@ -8,19 +8,16 @@ namespace ServiceLib.Services
|
||||
public class SpeedtestService
|
||||
{
|
||||
private Config? _config;
|
||||
private List<ServerTestItem> _selecteds;
|
||||
private ESpeedActionType _actionType;
|
||||
private Action<SpeedTestResult>? _updateFunc;
|
||||
|
||||
private bool _exitLoop = false;
|
||||
|
||||
public SpeedtestService(Config config, List<ProfileItem> selecteds, ESpeedActionType actionType, Action<SpeedTestResult> updateFunc)
|
||||
{
|
||||
_config = config;
|
||||
|
||||
_actionType = actionType;
|
||||
_updateFunc = updateFunc;
|
||||
|
||||
_selecteds = new List<ServerTestItem>();
|
||||
var lstSelected = new List<ServerTestItem>();
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
@@ -31,7 +28,7 @@ namespace ServiceLib.Services
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_selecteds.Add(new ServerTestItem()
|
||||
lstSelected.Add(new ServerTestItem()
|
||||
{
|
||||
IndexId = it.IndexId,
|
||||
Address = it.Address,
|
||||
@@ -39,8 +36,9 @@ namespace ServiceLib.Services
|
||||
ConfigType = it.ConfigType
|
||||
});
|
||||
}
|
||||
|
||||
//clear test result
|
||||
foreach (var it in _selecteds)
|
||||
foreach (var it in lstSelected)
|
||||
{
|
||||
switch (actionType)
|
||||
{
|
||||
@@ -63,25 +61,59 @@ namespace ServiceLib.Services
|
||||
}
|
||||
}
|
||||
|
||||
switch (actionType)
|
||||
{
|
||||
case ESpeedActionType.Tcping:
|
||||
Task.Run(RunTcping);
|
||||
break;
|
||||
|
||||
case ESpeedActionType.Realping:
|
||||
Task.Run(RunRealPing);
|
||||
break;
|
||||
|
||||
case ESpeedActionType.Speedtest:
|
||||
Task.Run(RunSpeedTestAsync);
|
||||
break;
|
||||
|
||||
case ESpeedActionType.Mixedtest:
|
||||
Task.Run(RunMixedtestAsync);
|
||||
break;
|
||||
}
|
||||
MessageBus.Current.Listen<string>(EMsgCommand.StopSpeedtest.ToString()).Subscribe(ExitLoop);
|
||||
|
||||
Task.Run(async () => { await RunAsync(actionType, lstSelected); });
|
||||
}
|
||||
|
||||
private async Task RunAsync(ESpeedActionType actionType, List<ServerTestItem> lstSelected)
|
||||
{
|
||||
if (actionType == ESpeedActionType.Tcping)
|
||||
{
|
||||
await RunTcpingAsync(lstSelected);
|
||||
return;
|
||||
}
|
||||
|
||||
var pageSize = _config.SpeedTestItem.SpeedTestPageSize;
|
||||
if (pageSize is <= 0 or > 1000)
|
||||
{
|
||||
pageSize = 1000;
|
||||
}
|
||||
|
||||
List<List<ServerTestItem>> lstTest = new();
|
||||
var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard)).ToList();
|
||||
var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard).ToList();
|
||||
|
||||
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
|
||||
{
|
||||
lstTest.Add(lst1.Skip(num * pageSize).Take(pageSize).ToList());
|
||||
}
|
||||
for (var num = 0; num < (int)Math.Ceiling(lst2.Count * 1.0 / pageSize); num++)
|
||||
{
|
||||
lstTest.Add(lst2.Skip(num * pageSize).Take(pageSize).ToList());
|
||||
}
|
||||
|
||||
foreach (var lst in lstTest)
|
||||
{
|
||||
switch (actionType)
|
||||
{
|
||||
case ESpeedActionType.Realping:
|
||||
await RunRealPingAsync(lst);
|
||||
break;
|
||||
|
||||
case ESpeedActionType.Speedtest:
|
||||
await RunSpeedTestAsync(lst);
|
||||
break;
|
||||
|
||||
case ESpeedActionType.Mixedtest:
|
||||
await RunMixedTestAsync(lst);
|
||||
break;
|
||||
}
|
||||
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
UpdateFunc("", ResUI.SpeedtestingCompleted);
|
||||
}
|
||||
|
||||
private void ExitLoop(string x)
|
||||
@@ -91,12 +123,12 @@ namespace ServiceLib.Services
|
||||
UpdateFunc("", ResUI.SpeedtestingStop);
|
||||
}
|
||||
|
||||
private async Task RunTcping()
|
||||
private async Task RunTcpingAsync(List<ServerTestItem> selecteds)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<Task> tasks = [];
|
||||
foreach (var it in _selecteds)
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
{
|
||||
@@ -106,7 +138,7 @@ namespace ServiceLib.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
int time = await GetTcpingTime(it.Address, it.Port);
|
||||
var time = await GetTcpingTime(it.Address, it.Port);
|
||||
var output = FormatOut(time, Global.DelayUnit);
|
||||
|
||||
ProfileExHandler.Instance.SetTestDelay(it.IndexId, output);
|
||||
@@ -130,24 +162,22 @@ namespace ServiceLib.Services
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunRealPing()
|
||||
private async Task RunRealPingAsync(List<ServerTestItem> selecteds)
|
||||
{
|
||||
int pid = -1;
|
||||
var pid = -1;
|
||||
try
|
||||
{
|
||||
string msg = string.Empty;
|
||||
|
||||
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(_selecteds);
|
||||
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
|
||||
if (pid < 0)
|
||||
{
|
||||
UpdateFunc("", ResUI.FailedToRunCore);
|
||||
return;
|
||||
}
|
||||
|
||||
DownloadService downloadHandle = new DownloadService();
|
||||
var downloadHandle = new DownloadService();
|
||||
|
||||
List<Task> tasks = new();
|
||||
foreach (var it in _selecteds)
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (!it.AllowTest)
|
||||
{
|
||||
@@ -162,11 +192,11 @@ namespace ServiceLib.Services
|
||||
try
|
||||
{
|
||||
WebProxy webProxy = new(Global.Loopback, it.Port);
|
||||
string output = await GetRealPingTime(downloadHandle, webProxy);
|
||||
var output = await GetRealPingTime(downloadHandle, webProxy);
|
||||
|
||||
ProfileExHandler.Instance.SetTestDelay(it.IndexId, output);
|
||||
UpdateFunc(it.IndexId, output);
|
||||
int.TryParse(output, out int delay);
|
||||
int.TryParse(output, out var delay);
|
||||
it.Delay = delay;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -185,33 +215,28 @@ namespace ServiceLib.Services
|
||||
{
|
||||
if (pid > 0)
|
||||
{
|
||||
CoreHandler.Instance.CoreStopPid(pid);
|
||||
await CoreHandler.Instance.CoreStopPid(pid);
|
||||
}
|
||||
await ProfileExHandler.Instance.SaveTo();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunSpeedTestAsync()
|
||||
private async Task RunSpeedTestAsync(List<ServerTestItem> selecteds)
|
||||
{
|
||||
int pid = -1;
|
||||
//if (_actionType == ESpeedActionType.Mixedtest)
|
||||
//{
|
||||
// _selecteds = _selecteds.OrderBy(t => t.delay).ToList();
|
||||
//}
|
||||
|
||||
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(_selecteds);
|
||||
var pid = -1;
|
||||
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
|
||||
if (pid < 0)
|
||||
{
|
||||
UpdateFunc("", ResUI.FailedToRunCore);
|
||||
return;
|
||||
}
|
||||
|
||||
string url = _config.SpeedTestItem.SpeedTestUrl;
|
||||
var url = _config.SpeedTestItem.SpeedTestUrl;
|
||||
var timeout = _config.SpeedTestItem.SpeedTestTimeout;
|
||||
|
||||
DownloadService downloadHandle = new();
|
||||
|
||||
foreach (var it in _selecteds)
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (_exitLoop)
|
||||
{
|
||||
@@ -241,7 +266,7 @@ namespace ServiceLib.Services
|
||||
|
||||
await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
|
||||
{
|
||||
decimal.TryParse(msg, out decimal dec);
|
||||
decimal.TryParse(msg, out var dec);
|
||||
if (dec > 0)
|
||||
{
|
||||
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg);
|
||||
@@ -252,28 +277,27 @@ namespace ServiceLib.Services
|
||||
|
||||
if (pid > 0)
|
||||
{
|
||||
CoreHandler.Instance.CoreStopPid(pid);
|
||||
await CoreHandler.Instance.CoreStopPid(pid);
|
||||
}
|
||||
UpdateFunc("", ResUI.SpeedtestingCompleted);
|
||||
await ProfileExHandler.Instance.SaveTo();
|
||||
}
|
||||
|
||||
private async Task RunSpeedTestMulti()
|
||||
private async Task RunSpeedTestMulti(List<ServerTestItem> selecteds)
|
||||
{
|
||||
int pid = -1;
|
||||
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(_selecteds);
|
||||
var pid = -1;
|
||||
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
|
||||
if (pid < 0)
|
||||
{
|
||||
UpdateFunc("", ResUI.FailedToRunCore);
|
||||
return;
|
||||
}
|
||||
|
||||
string url = _config.SpeedTestItem.SpeedTestUrl;
|
||||
var url = _config.SpeedTestItem.SpeedTestUrl;
|
||||
var timeout = _config.SpeedTestItem.SpeedTestTimeout;
|
||||
|
||||
DownloadService downloadHandle = new();
|
||||
|
||||
foreach (var it in _selecteds)
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (_exitLoop)
|
||||
{
|
||||
@@ -303,7 +327,7 @@ namespace ServiceLib.Services
|
||||
WebProxy webProxy = new(Global.Loopback, it.Port);
|
||||
_ = downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
|
||||
{
|
||||
decimal.TryParse(msg, out decimal dec);
|
||||
decimal.TryParse(msg, out var dec);
|
||||
if (dec > 0)
|
||||
{
|
||||
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg);
|
||||
@@ -317,38 +341,37 @@ namespace ServiceLib.Services
|
||||
|
||||
if (pid > 0)
|
||||
{
|
||||
CoreHandler.Instance.CoreStopPid(pid);
|
||||
await CoreHandler.Instance.CoreStopPid(pid);
|
||||
}
|
||||
UpdateFunc("", ResUI.SpeedtestingCompleted);
|
||||
await ProfileExHandler.Instance.SaveTo();
|
||||
}
|
||||
|
||||
private async Task RunMixedtestAsync()
|
||||
private async Task RunMixedTestAsync(List<ServerTestItem> selecteds)
|
||||
{
|
||||
await RunRealPing();
|
||||
await RunRealPingAsync(selecteds);
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
await RunSpeedTestMulti();
|
||||
await RunSpeedTestMulti(selecteds);
|
||||
}
|
||||
|
||||
private async Task<string> GetRealPingTime(DownloadService downloadHandle, IWebProxy webProxy)
|
||||
{
|
||||
int responseTime = await downloadHandle.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
|
||||
var responseTime = await downloadHandle.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
|
||||
//string output = Utile.IsNullOrEmpty(status) ? FormatOut(responseTime, "ms") : status;
|
||||
return FormatOut(responseTime, Global.DelayUnit);
|
||||
}
|
||||
|
||||
private async Task<int> GetTcpingTime(string url, int port)
|
||||
{
|
||||
int responseTime = -1;
|
||||
var responseTime = -1;
|
||||
|
||||
try
|
||||
{
|
||||
if (!IPAddress.TryParse(url, out IPAddress? ipAddress))
|
||||
if (!IPAddress.TryParse(url, out var ipAddress))
|
||||
{
|
||||
IPHostEntry ipHostInfo = Dns.GetHostEntry(url);
|
||||
ipAddress = ipHostInfo.AddressList[0];
|
||||
var ipHostInfo = await Dns.GetHostEntryAsync(url);
|
||||
ipAddress = ipHostInfo.AddressList.First();
|
||||
}
|
||||
|
||||
var timer = Stopwatch.StartNew();
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace ServiceLib.Services.Statistics
|
||||
private Config _config;
|
||||
private bool _exitFlag;
|
||||
private ClientWebSocket? webSocket;
|
||||
private string url = string.Empty;
|
||||
private Action<ServerSpeedItem>? _updateFunc;
|
||||
private string Url => $"ws://{Global.Loopback}:{AppHandler.Instance.StatePort2}/traffic";
|
||||
|
||||
public StatisticsSingboxService(Config config, Action<ServerSpeedItem> updateFunc)
|
||||
{
|
||||
@@ -26,12 +26,10 @@ namespace ServiceLib.Services.Statistics
|
||||
|
||||
try
|
||||
{
|
||||
url = $"ws://{Global.Loopback}:{AppHandler.Instance.StatePort2}/traffic";
|
||||
|
||||
if (webSocket == null)
|
||||
{
|
||||
webSocket = new ClientWebSocket();
|
||||
await webSocket.ConnectAsync(new Uri(url), CancellationToken.None);
|
||||
await webSocket.ConnectAsync(new Uri(Url), CancellationToken.None);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
@@ -3,18 +3,17 @@
|
||||
public class StatisticsXrayService
|
||||
{
|
||||
private const long linkBase = 1024;
|
||||
private string _url;
|
||||
private ServerSpeedItem _serverSpeedItem = new();
|
||||
private Config _config;
|
||||
private bool _exitFlag;
|
||||
private Action<ServerSpeedItem>? _updateFunc;
|
||||
private string Url => $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort}/debug/vars";
|
||||
|
||||
public StatisticsXrayService(Config config, Action<ServerSpeedItem> updateFunc)
|
||||
{
|
||||
_config = config;
|
||||
_updateFunc = updateFunc;
|
||||
_exitFlag = false;
|
||||
_url = $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort}/debug/vars";
|
||||
|
||||
Task.Run(Run);
|
||||
}
|
||||
@@ -36,7 +35,7 @@
|
||||
continue;
|
||||
}
|
||||
|
||||
var result = await HttpClientHelper.Instance.TryGetAsync(_url);
|
||||
var result = await HttpClientHelper.Instance.TryGetAsync(Url);
|
||||
if (result != null)
|
||||
{
|
||||
var server = ParseOutput(result) ?? new ServerSpeedItem();
|
||||
|
||||
@@ -21,9 +21,6 @@ namespace ServiceLib.ViewModels
|
||||
[Reactive]
|
||||
public string HostFilter { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public int SortingSelected { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool AutoRefresh { get; set; }
|
||||
|
||||
@@ -31,18 +28,12 @@ namespace ServiceLib.ViewModels
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
SortingSelected = _config.ClashUIItem.ConnectionsSorting;
|
||||
AutoRefresh = _config.ClashUIItem.ConnectionsAutoRefresh;
|
||||
|
||||
var canEditRemove = this.WhenAnyValue(
|
||||
x => x.SelectedSource,
|
||||
selectedSource => selectedSource != null && Utils.IsNotEmpty(selectedSource.Id));
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.SortingSelected,
|
||||
y => y >= 0)
|
||||
.Subscribe(async c => await DoSortingSelected(c));
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.AutoRefresh,
|
||||
y => y == true)
|
||||
@@ -84,20 +75,6 @@ namespace ServiceLib.ViewModels
|
||||
});
|
||||
}
|
||||
|
||||
private async Task DoSortingSelected(bool c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (SortingSelected != _config.ClashUIItem.ConnectionsSorting)
|
||||
{
|
||||
_config.ClashUIItem.ConnectionsSorting = SortingSelected;
|
||||
}
|
||||
|
||||
await GetClashConnections();
|
||||
}
|
||||
|
||||
private async Task GetClashConnections()
|
||||
{
|
||||
var ret = await ClashApiHandler.Instance.GetClashConnectionsAsync(_config);
|
||||
@@ -115,7 +92,7 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
var dtNow = DateTime.Now;
|
||||
var lstModel = new List<ClashConnectionModel>();
|
||||
foreach (var item in connections ?? [])
|
||||
foreach (var item in connections ?? new())
|
||||
{
|
||||
var host = $"{(Utils.IsNullOrEmpty(item.metadata.host) ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}";
|
||||
if (HostFilter.IsNotEmpty() && !host.Contains(HostFilter))
|
||||
@@ -131,45 +108,14 @@ namespace ServiceLib.ViewModels
|
||||
model.Host = host;
|
||||
var sp = (dtNow - item.start);
|
||||
model.Time = sp.TotalSeconds < 0 ? 1 : sp.TotalSeconds;
|
||||
model.Upload = item.upload;
|
||||
model.Download = item.download;
|
||||
model.UploadTraffic = $"{Utils.HumanFy((long)item.upload)}";
|
||||
model.DownloadTraffic = $"{Utils.HumanFy((long)item.download)}";
|
||||
model.Elapsed = sp.ToString(@"hh\:mm\:ss");
|
||||
model.Chain = item.chains?.Count > 0 ? item.chains[0] : string.Empty;
|
||||
item.chains?.Reverse();
|
||||
model.Chain = $"{item.rule} , {string.Join("->", item.chains ?? new())}";
|
||||
|
||||
lstModel.Add(model);
|
||||
}
|
||||
if (lstModel.Count <= 0) { return; }
|
||||
|
||||
//sort
|
||||
switch (SortingSelected)
|
||||
{
|
||||
case 0:
|
||||
lstModel = lstModel.OrderBy(t => t.Upload / t.Time).ToList();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
lstModel = lstModel.OrderBy(t => t.Download / t.Time).ToList();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
lstModel = lstModel.OrderBy(t => t.Upload).ToList();
|
||||
break;
|
||||
|
||||
case 3:
|
||||
lstModel = lstModel.OrderBy(t => t.Download).ToList();
|
||||
break;
|
||||
|
||||
case 4:
|
||||
lstModel = lstModel.OrderBy(t => t.Time).ToList();
|
||||
break;
|
||||
|
||||
case 5:
|
||||
lstModel = lstModel.OrderBy(t => t.Host).ToList();
|
||||
break;
|
||||
}
|
||||
|
||||
_connectionItems.AddRange(lstModel);
|
||||
}
|
||||
|
||||
|
||||
@@ -239,7 +239,7 @@ namespace ServiceLib.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedGroup = _proxyGroups[0];
|
||||
SelectedGroup = _proxyGroups.First();
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -283,7 +283,6 @@ namespace ServiceLib.ViewModels
|
||||
try
|
||||
{
|
||||
Logging.SaveLog("MyAppExit Begin");
|
||||
//if (blWindowsShutDown)
|
||||
await SysProxyHandler.UpdateSysProxy(_config, true);
|
||||
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
@@ -297,7 +296,10 @@ namespace ServiceLib.ViewModels
|
||||
catch { }
|
||||
finally
|
||||
{
|
||||
_updateView?.Invoke(EViewAction.Shutdown, null);
|
||||
if (!blWindowsShutDown)
|
||||
{
|
||||
_updateView?.Invoke(EViewAction.Shutdown, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace ServiceLib.ViewModels
|
||||
[Reactive] public int SpeedTestTimeout { get; set; }
|
||||
[Reactive] public string SpeedTestUrl { get; set; }
|
||||
[Reactive] public string SpeedPingTestUrl { get; set; }
|
||||
[Reactive] public int SpeedTestPageSize { get; set; }
|
||||
[Reactive] public bool EnableHWA { get; set; }
|
||||
[Reactive] public string SubConvertUrl { get; set; }
|
||||
[Reactive] public int MainGirdOrientation { get; set; }
|
||||
@@ -122,7 +123,7 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
#region Core
|
||||
|
||||
var inbound = _config.Inbound[0];
|
||||
var inbound = _config.Inbound.First();
|
||||
localPort = inbound.LocalPort;
|
||||
udpEnabled = inbound.UdpEnabled;
|
||||
sniffingEnabled = inbound.SniffingEnabled;
|
||||
@@ -175,6 +176,7 @@ namespace ServiceLib.ViewModels
|
||||
CurrentFontFamily = _config.UiItem.CurrentFontFamily;
|
||||
SpeedTestTimeout = _config.SpeedTestItem.SpeedTestTimeout;
|
||||
SpeedTestUrl = _config.SpeedTestItem.SpeedTestUrl;
|
||||
SpeedTestPageSize = _config.SpeedTestItem.SpeedTestPageSize;
|
||||
SpeedPingTestUrl = _config.SpeedTestItem.SpeedPingTestUrl;
|
||||
EnableHWA = _config.GuiItem.EnableHWA;
|
||||
SubConvertUrl = _config.ConstItem.SubConvertUrl;
|
||||
@@ -285,15 +287,15 @@ namespace ServiceLib.ViewModels
|
||||
//}
|
||||
|
||||
//Core
|
||||
_config.Inbound[0].LocalPort = localPort;
|
||||
_config.Inbound[0].UdpEnabled = udpEnabled;
|
||||
_config.Inbound[0].SniffingEnabled = sniffingEnabled;
|
||||
_config.Inbound[0].DestOverride = destOverride?.ToList();
|
||||
_config.Inbound[0].RouteOnly = routeOnly;
|
||||
_config.Inbound[0].AllowLANConn = allowLANConn;
|
||||
_config.Inbound[0].NewPort4LAN = newPort4LAN;
|
||||
_config.Inbound[0].User = user;
|
||||
_config.Inbound[0].Pass = pass;
|
||||
_config.Inbound.First().LocalPort = localPort;
|
||||
_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);
|
||||
@@ -325,6 +327,7 @@ namespace ServiceLib.ViewModels
|
||||
_config.GuiItem.TrayMenuServersLimit = TrayMenuServersLimit;
|
||||
_config.UiItem.CurrentFontFamily = CurrentFontFamily;
|
||||
_config.SpeedTestItem.SpeedTestTimeout = SpeedTestTimeout;
|
||||
_config.SpeedTestItem.SpeedTestPageSize = SpeedTestPageSize;
|
||||
_config.SpeedTestItem.SpeedTestUrl = SpeedTestUrl;
|
||||
_config.SpeedTestItem.SpeedPingTestUrl = SpeedPingTestUrl;
|
||||
_config.GuiItem.EnableHWA = EnableHWA;
|
||||
@@ -356,6 +359,7 @@ namespace ServiceLib.ViewModels
|
||||
if (await ConfigHandler.SaveConfig(_config) == 0)
|
||||
{
|
||||
await AutoStartupHandler.UpdateTask(_config);
|
||||
AppHandler.Instance.Reset();
|
||||
|
||||
NoticeHandler.Instance.Enqueue(needReboot ? ResUI.NeedRebootTips : ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
|
||||
@@ -374,7 +374,7 @@ namespace ServiceLib.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedProfile = lstModel[0];
|
||||
SelectedProfile = lstModel.First();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -395,7 +395,7 @@ namespace ServiceLib.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedSub = _subItems[0];
|
||||
SelectedSub = _subItems.First();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -639,6 +639,7 @@ namespace ServiceLib.ViewModels
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
|
||||
RefreshServers();
|
||||
SelectedMoveToGroup = null;
|
||||
SelectedMoveToGroup = new();
|
||||
//Reload();
|
||||
}
|
||||
|
||||
@@ -451,19 +451,15 @@ namespace ServiceLib.ViewModels
|
||||
public async Task InboundDisplayStatus()
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.Append($"[{EInboundProtocol.socks}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}]");
|
||||
sb.Append(" | ");
|
||||
sb.Append($"[{EInboundProtocol.http}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.http)}]");
|
||||
sb.Append($"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}]");
|
||||
InboundDisplay = $"{ResUI.LabLocal}:{sb}";
|
||||
|
||||
if (_config.Inbound[0].AllowLANConn)
|
||||
if (_config.Inbound.First().AllowLANConn)
|
||||
{
|
||||
if (_config.Inbound[0].NewPort4LAN)
|
||||
if (_config.Inbound.First().NewPort4LAN)
|
||||
{
|
||||
StringBuilder sb2 = new();
|
||||
sb2.Append($"[{EInboundProtocol.socks}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks2)}]");
|
||||
sb2.Append(" | ");
|
||||
sb2.Append($"[{EInboundProtocol.http}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.http2)}]");
|
||||
sb2.Append($"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks2)}]");
|
||||
InboundLanDisplay = $"{ResUI.LabLAN}:{sb2}";
|
||||
}
|
||||
else
|
||||
|
||||
@@ -26,20 +26,10 @@ namespace v2rayN.Desktop.Views
|
||||
|
||||
ViewModel = new AddServerViewModel(profileItem, UpdateViewHandler);
|
||||
|
||||
if (profileItem.ConfigType == EConfigType.VLESS)
|
||||
Global.CoreTypes.ForEach(it =>
|
||||
{
|
||||
Global.CoreTypes4VLESS.ForEach(it =>
|
||||
{
|
||||
cmbCoreType.Items.Add(it);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Global.CoreTypes.ForEach(it =>
|
||||
{
|
||||
cmbCoreType.Items.Add(it);
|
||||
});
|
||||
}
|
||||
cmbCoreType.Items.Add(it);
|
||||
});
|
||||
cmbCoreType.Items.Add(string.Empty);
|
||||
|
||||
cmbStreamSecurity.Items.Add(string.Empty);
|
||||
|
||||
@@ -25,22 +25,6 @@
|
||||
VerticalContentAlignment="Center"
|
||||
Watermark="{x:Static resx:ResUI.ConnectionsHostFilterTitle}" />
|
||||
|
||||
<TextBlock
|
||||
Margin="8,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSorting}" />
|
||||
<ComboBox
|
||||
x:Name="cmbSorting"
|
||||
Width="100"
|
||||
Margin="8,0">
|
||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingUpSpeed}" />
|
||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDownSpeed}" />
|
||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingUpTraffic}" />
|
||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDownTraffic}" />
|
||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingTime}" />
|
||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingHost}" />
|
||||
</ComboBox>
|
||||
|
||||
<Button
|
||||
x:Name="btnConnectionCloseAll"
|
||||
Width="30"
|
||||
@@ -58,6 +42,23 @@
|
||||
</Button.Content>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
x:Name="btnAutofitColumnWidth"
|
||||
Width="30"
|
||||
Height="30"
|
||||
Margin="8,0"
|
||||
Classes="Success"
|
||||
Theme="{DynamicResource BorderlessButton}"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}">
|
||||
<Button.Content>
|
||||
<PathIcon
|
||||
Width="20"
|
||||
Height="20"
|
||||
Data="{StaticResource building_fit}"
|
||||
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
|
||||
</Button.Content>
|
||||
</Button>
|
||||
|
||||
<TextBlock
|
||||
Margin="8,0"
|
||||
VerticalAlignment="Center"
|
||||
@@ -85,11 +86,11 @@
|
||||
</DataGrid.ContextMenu>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn
|
||||
Width="240"
|
||||
Width="300"
|
||||
Binding="{Binding Host}"
|
||||
Header="{x:Static resx:ResUI.TbSortingHost}" />
|
||||
<DataGridTextColumn
|
||||
Width="160"
|
||||
Width="500"
|
||||
Binding="{Binding Chain}"
|
||||
Header="{x:Static resx:ResUI.TbSortingChain}" />
|
||||
<DataGridTextColumn
|
||||
@@ -97,17 +98,9 @@
|
||||
Binding="{Binding Network}"
|
||||
Header="{x:Static resx:ResUI.TbSortingNetwork}" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Width="160"
|
||||
Binding="{Binding Type}"
|
||||
Header="{x:Static resx:ResUI.TbSortingType}" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding UploadTraffic}"
|
||||
Header="{x:Static resx:ResUI.TbSortingUpTraffic}" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding DownloadTraffic}"
|
||||
Header="{x:Static resx:ResUI.TbSortingDownTraffic}" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding Elapsed}"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using Avalonia.Threading;
|
||||
@@ -12,6 +13,7 @@ namespace v2rayN.Desktop.Views
|
||||
{
|
||||
InitializeComponent();
|
||||
ViewModel = new ClashConnectionsViewModel(UpdateViewHandler);
|
||||
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
@@ -22,7 +24,6 @@ namespace v2rayN.Desktop.Views
|
||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.menuConnectionCloseAll).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.HostFilter, v => v.txtHostFilter.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SortingSelected, v => v.cmbSorting.SelectedIndex).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
|
||||
});
|
||||
@@ -43,6 +44,19 @@ namespace v2rayN.Desktop.Views
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
|
||||
private void BtnAutofitColumnWidth_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
AutofitColumnWidth();
|
||||
}
|
||||
|
||||
private void AutofitColumnWidth()
|
||||
{
|
||||
foreach (var it in lstConnections.Columns)
|
||||
{
|
||||
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
|
||||
}
|
||||
}
|
||||
|
||||
private void btnClose_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel?.ClashConnectionClose(false);
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace v2rayN.Desktop.Views
|
||||
private WindowNotificationManager? _manager;
|
||||
private CheckUpdateView? _checkUpdateView;
|
||||
private BackupAndRestoreView? _backupAndRestoreView;
|
||||
private bool _blCloseByUser = false;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
@@ -40,7 +41,34 @@ namespace v2rayN.Desktop.Views
|
||||
ViewModel = new MainWindowViewModel(UpdateViewHandler);
|
||||
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
|
||||
|
||||
//WindowsHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null);
|
||||
switch (_config.UiItem.MainGirdOrientation)
|
||||
{
|
||||
case EGirdOrientation.Horizontal:
|
||||
tabProfiles.Content ??= new ProfilesView(this);
|
||||
tabMsgView.Content ??= new MsgView();
|
||||
tabClashProxies.Content ??= new ClashProxiesView();
|
||||
tabClashConnections.Content ??= new ClashConnectionsView();
|
||||
gridMain.IsVisible = true;
|
||||
break;
|
||||
|
||||
case EGirdOrientation.Vertical:
|
||||
tabProfiles1.Content ??= new ProfilesView(this);
|
||||
tabMsgView1.Content ??= new MsgView();
|
||||
tabClashProxies1.Content ??= new ClashProxiesView();
|
||||
tabClashConnections1.Content ??= new ClashConnectionsView();
|
||||
gridMain1.IsVisible = true;
|
||||
break;
|
||||
|
||||
case EGirdOrientation.Tab:
|
||||
default:
|
||||
tabProfiles2.Content ??= new ProfilesView(this);
|
||||
tabMsgView2.Content ??= new MsgView();
|
||||
tabClashProxies2.Content ??= new ClashProxiesView();
|
||||
tabClashConnections2.Content ??= new ClashConnectionsView();
|
||||
gridMain2.IsVisible = true;
|
||||
break;
|
||||
}
|
||||
conTheme.Content ??= new ThemeSettingView();
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
@@ -83,7 +111,6 @@ namespace v2rayN.Desktop.Views
|
||||
switch (_config.UiItem.MainGirdOrientation)
|
||||
{
|
||||
case EGirdOrientation.Horizontal:
|
||||
gridMain.IsVisible = true;
|
||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView.IsVisible).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies.IsVisible).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections.IsVisible).DisposeWith(disposables);
|
||||
@@ -91,7 +118,6 @@ namespace v2rayN.Desktop.Views
|
||||
break;
|
||||
|
||||
case EGirdOrientation.Vertical:
|
||||
gridMain1.IsVisible = true;
|
||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView1.IsVisible).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies1.IsVisible).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections1.IsVisible).DisposeWith(disposables);
|
||||
@@ -100,7 +126,6 @@ namespace v2rayN.Desktop.Views
|
||||
|
||||
case EGirdOrientation.Tab:
|
||||
default:
|
||||
gridMain2.IsVisible = true;
|
||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies2.IsVisible).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections2.IsVisible).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain2.SelectedIndex).DisposeWith(disposables);
|
||||
@@ -128,34 +153,9 @@ namespace v2rayN.Desktop.Views
|
||||
}
|
||||
menuAddServerViaScan.IsVisible = false;
|
||||
|
||||
switch (_config.UiItem.MainGirdOrientation)
|
||||
{
|
||||
case EGirdOrientation.Horizontal:
|
||||
tabProfiles.Content ??= new ProfilesView(this);
|
||||
tabMsgView.Content ??= new MsgView();
|
||||
tabClashProxies.Content ??= new ClashProxiesView();
|
||||
tabClashConnections.Content ??= new ClashConnectionsView();
|
||||
break;
|
||||
|
||||
case EGirdOrientation.Vertical:
|
||||
tabProfiles1.Content ??= new ProfilesView(this);
|
||||
tabMsgView1.Content ??= new MsgView();
|
||||
tabClashProxies1.Content ??= new ClashProxiesView();
|
||||
tabClashConnections1.Content ??= new ClashConnectionsView();
|
||||
break;
|
||||
|
||||
case EGirdOrientation.Tab:
|
||||
default:
|
||||
tabProfiles2.Content ??= new ProfilesView(this);
|
||||
tabMsgView2.Content ??= new MsgView();
|
||||
tabClashProxies2.Content ??= new ClashProxiesView();
|
||||
tabClashConnections2.Content ??= new ClashConnectionsView();
|
||||
break;
|
||||
}
|
||||
conTheme.Content ??= new ThemeSettingView();
|
||||
|
||||
RestoreUI();
|
||||
AddHelpMenuItem();
|
||||
//WindowsHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null);
|
||||
}
|
||||
|
||||
#region Event
|
||||
@@ -279,6 +279,11 @@ namespace v2rayN.Desktop.Views
|
||||
|
||||
protected override async void OnClosing(WindowClosingEventArgs e)
|
||||
{
|
||||
if (_blCloseByUser)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.SaveLog("OnClosing -> " + e.CloseReason.ToString());
|
||||
|
||||
switch (e.CloseReason)
|
||||
@@ -374,6 +379,8 @@ namespace v2rayN.Desktop.Views
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_blCloseByUser = true;
|
||||
StorageUI();
|
||||
|
||||
await ViewModel?.MyAppExitAsync(false);
|
||||
|
||||
@@ -532,26 +532,40 @@
|
||||
Classes="Margin8" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="16"
|
||||
Grid.Row="15"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamily}" />
|
||||
<ComboBox
|
||||
x:Name="cmbcurrentFontFamily"
|
||||
Grid.Row="16"
|
||||
Grid.Row="15"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Classes="Margin8"
|
||||
MaxDropDownHeight="1000" />
|
||||
<TextBlock
|
||||
Grid.Row="16"
|
||||
Grid.Row="15"
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamilyLinuxTip}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="16"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbSettingsSpeedTestPageSize}" />
|
||||
<TextBox
|
||||
x:Name="txtSpeedTestPageSize"
|
||||
Grid.Row="16"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin8" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="17"
|
||||
Grid.Column="0"
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace v2rayN.Desktop.Views
|
||||
{
|
||||
clbdestOverride.Items.Add(it);
|
||||
});
|
||||
_config.Inbound[0].DestOverride?.ForEach(it =>
|
||||
_config.Inbound.First().DestOverride?.ForEach(it =>
|
||||
{
|
||||
clbdestOverride.SelectedItems.Add(it);
|
||||
});
|
||||
@@ -138,6 +138,7 @@ namespace v2rayN.Desktop.Views
|
||||
this.Bind(ViewModel, vm => vm.SpeedTestTimeout, v => v.cmbSpeedTestTimeout.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SpeedTestUrl, v => v.cmbSpeedTestUrl.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SpeedPingTestUrl, v => v.cmbSpeedPingTestUrl.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SpeedTestPageSize, v => v.txtSpeedTestPageSize.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SubConvertUrl, v => v.cmbSubConvertUrl.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.MainGirdOrientation, v => v.cmbMainGirdOrientation.SelectedIndex).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.SelectedValue).DisposeWith(disposables);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
Title="{x:Static resx:ResUI.menuRoutingRuleSetting}"
|
||||
Width="960"
|
||||
Width="1000"
|
||||
Height="700"
|
||||
x:DataType="vms:RoutingRuleSettingViewModel"
|
||||
ShowInTaskbar="False"
|
||||
@@ -237,11 +237,11 @@
|
||||
Binding="{Binding Network}"
|
||||
Header="network" />
|
||||
<DataGridTextColumn
|
||||
Width="200"
|
||||
Width="*"
|
||||
Binding="{Binding Domains}"
|
||||
Header="domain" />
|
||||
<DataGridTextColumn
|
||||
Width="200"
|
||||
Width="*"
|
||||
Binding="{Binding Ips}"
|
||||
Header="ip" />
|
||||
</DataGrid.Columns>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
Title="{x:Static resx:ResUI.menuRoutingSetting}"
|
||||
Width="990"
|
||||
Width="1000"
|
||||
Height="700"
|
||||
x:DataType="vms:RoutingSettingViewModel"
|
||||
ShowInTaskbar="False"
|
||||
@@ -109,7 +109,7 @@
|
||||
<DataGrid.Columns>
|
||||
<DataGridCheckBoxColumn Width="40" Binding="{Binding IsActive}" />
|
||||
<DataGridTextColumn
|
||||
Width="250"
|
||||
Width="*"
|
||||
Binding="{Binding Remarks}"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<DataGridTextColumn
|
||||
@@ -121,7 +121,7 @@
|
||||
Binding="{Binding Sort}"
|
||||
Header="{x:Static resx:ResUI.LvSort}" />
|
||||
<DataGridTextColumn
|
||||
Width="300"
|
||||
Width="*"
|
||||
Binding="{Binding Url}"
|
||||
Header="{x:Static resx:ResUI.LvUrl}" />
|
||||
<DataGridTextColumn
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
Title="{x:Static resx:ResUI.menuSubSetting}"
|
||||
Width="700"
|
||||
Height="600"
|
||||
d:DesignHeight="600"
|
||||
d:DesignWidth="800"
|
||||
Height="650"
|
||||
ShowInTaskbar="False"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
mc:Ignorable="d">
|
||||
|
||||
@@ -8,10 +8,8 @@
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
Title="{x:Static resx:ResUI.menuSubSetting}"
|
||||
Width="800"
|
||||
Height="600"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
Width="1000"
|
||||
Height="700"
|
||||
x:DataType="vms:SubSettingViewModel"
|
||||
ShowInTaskbar="False"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
@@ -49,7 +47,7 @@
|
||||
</DataGrid.KeyBindings>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn
|
||||
Width="120"
|
||||
Width="*"
|
||||
Binding="{Binding Remarks}"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<DataGridTextColumn
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.2.2" />
|
||||
<PackageReference Include="DialogHost.Avalonia" Version="0.8.1" />
|
||||
<PackageReference Include="MessageBox.Avalonia" Version="3.2.0" />
|
||||
<PackageReference Include="Semi.Avalonia" Version="11.2.1" />
|
||||
<PackageReference Include="Semi.Avalonia.DataGrid" Version="11.2.1" />
|
||||
<PackageReference Include="Semi.Avalonia" Version="11.2.1.1" />
|
||||
<PackageReference Include="Semi.Avalonia.DataGrid" Version="11.2.1.1" />
|
||||
<PackageReference Include="ReactiveUI" Version="20.1.63" />
|
||||
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -20,20 +20,10 @@ namespace v2rayN.Views
|
||||
|
||||
ViewModel = new AddServerViewModel(profileItem, UpdateViewHandler);
|
||||
|
||||
if (profileItem.ConfigType == EConfigType.VLESS)
|
||||
Global.CoreTypes.ForEach(it =>
|
||||
{
|
||||
Global.CoreTypes4VLESS.ForEach(it =>
|
||||
{
|
||||
cmbCoreType.Items.Add(it);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Global.CoreTypes.ForEach(it =>
|
||||
{
|
||||
cmbCoreType.Items.Add(it);
|
||||
});
|
||||
}
|
||||
cmbCoreType.Items.Add(it);
|
||||
});
|
||||
cmbCoreType.Items.Add(string.Empty);
|
||||
|
||||
cmbStreamSecurity.Items.Add(string.Empty);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<reactiveui:ReactiveUserControl
|
||||
x:Class="v2rayN.Views.ClashConnectionsView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:reactiveui="http://reactiveui.net"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:reactiveui="http://reactiveui.net"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
d:DesignHeight="450"
|
||||
@@ -29,24 +29,6 @@
|
||||
materialDesign:TextFieldAssist.HasClearButton="True"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<TextBlock
|
||||
Margin="{StaticResource MarginLeftRight8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSorting}" />
|
||||
<ComboBox
|
||||
x:Name="cmbSorting"
|
||||
Width="100"
|
||||
Margin="{StaticResource MarginLeftRight8}"
|
||||
Style="{StaticResource DefComboBox}">
|
||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingUpSpeed}" />
|
||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDownSpeed}" />
|
||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingUpTraffic}" />
|
||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDownTraffic}" />
|
||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingTime}" />
|
||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingHost}" />
|
||||
</ComboBox>
|
||||
|
||||
<Button
|
||||
x:Name="btnConnectionCloseAll"
|
||||
Width="24"
|
||||
@@ -57,6 +39,16 @@
|
||||
<materialDesign:PackIcon VerticalAlignment="Center" Kind="Close" />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
x:Name="btnAutofitColumnWidth"
|
||||
Width="24"
|
||||
Height="24"
|
||||
Margin="{StaticResource MarginLeftRight8}"
|
||||
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}"
|
||||
ToolTip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}">
|
||||
<materialDesign:PackIcon VerticalAlignment="Center" Kind="ArrowSplitVertical" />
|
||||
</Button>
|
||||
|
||||
<TextBlock
|
||||
Margin="{StaticResource MarginLeftRight8}"
|
||||
VerticalAlignment="Center"
|
||||
@@ -74,7 +66,6 @@
|
||||
BorderThickness="1"
|
||||
CanUserAddRows="False"
|
||||
CanUserResizeRows="False"
|
||||
CanUserSortColumns="False"
|
||||
EnableRowVirtualization="True"
|
||||
GridLinesVisibility="All"
|
||||
HeadersVisibility="Column"
|
||||
@@ -88,11 +79,11 @@
|
||||
</DataGrid.ContextMenu>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn
|
||||
Width="240"
|
||||
Width="300"
|
||||
Binding="{Binding Host}"
|
||||
Header="{x:Static resx:ResUI.TbSortingHost}" />
|
||||
<DataGridTextColumn
|
||||
Width="160"
|
||||
Width="500"
|
||||
Binding="{Binding Chain}"
|
||||
Header="{x:Static resx:ResUI.TbSortingChain}" />
|
||||
<DataGridTextColumn
|
||||
@@ -100,17 +91,9 @@
|
||||
Binding="{Binding Network}"
|
||||
Header="{x:Static resx:ResUI.TbSortingNetwork}" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Width="160"
|
||||
Binding="{Binding Type}"
|
||||
Header="{x:Static resx:ResUI.TbSortingType}" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding UploadTraffic}"
|
||||
Header="{x:Static resx:ResUI.TbSortingUpTraffic}" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding DownloadTraffic}"
|
||||
Header="{x:Static resx:ResUI.TbSortingDownTraffic}" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding Elapsed}"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using ReactiveUI;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace v2rayN.Views
|
||||
@@ -14,6 +15,7 @@ namespace v2rayN.Views
|
||||
{
|
||||
InitializeComponent();
|
||||
ViewModel = new ClashConnectionsViewModel(UpdateViewHandler);
|
||||
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
@@ -24,7 +26,6 @@ namespace v2rayN.Views
|
||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.menuConnectionCloseAll).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.HostFilter, v => v.txtHostFilter.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SortingSelected, v => v.cmbSorting.SelectedIndex).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
|
||||
});
|
||||
@@ -46,6 +47,18 @@ namespace v2rayN.Views
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
|
||||
private void BtnAutofitColumnWidth_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AutofitColumnWidth();
|
||||
}
|
||||
private void AutofitColumnWidth()
|
||||
{
|
||||
foreach (var it in lstConnections.Columns)
|
||||
{
|
||||
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
|
||||
}
|
||||
}
|
||||
|
||||
private void btnClose_Click(object sender, System.Windows.RoutedEventArgs e)
|
||||
{
|
||||
ViewModel?.ClashConnectionClose(false);
|
||||
|
||||
@@ -40,7 +40,6 @@ namespace v2rayN.Views
|
||||
ViewModel = new MainWindowViewModel(UpdateViewHandler);
|
||||
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
|
||||
|
||||
WindowsHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null);
|
||||
switch (_config.UiItem.MainGirdOrientation)
|
||||
{
|
||||
case EGirdOrientation.Horizontal:
|
||||
@@ -48,6 +47,7 @@ namespace v2rayN.Views
|
||||
tabMsgView.Content ??= new MsgView();
|
||||
tabClashProxies.Content ??= new ClashProxiesView();
|
||||
tabClashConnections.Content ??= new ClashConnectionsView();
|
||||
gridMain.Visibility = Visibility.Visible;
|
||||
break;
|
||||
|
||||
case EGirdOrientation.Vertical:
|
||||
@@ -55,6 +55,7 @@ namespace v2rayN.Views
|
||||
tabMsgView1.Content ??= new MsgView();
|
||||
tabClashProxies1.Content ??= new ClashProxiesView();
|
||||
tabClashConnections1.Content ??= new ClashConnectionsView();
|
||||
gridMain1.Visibility = Visibility.Visible;
|
||||
break;
|
||||
|
||||
case EGirdOrientation.Tab:
|
||||
@@ -63,6 +64,7 @@ namespace v2rayN.Views
|
||||
tabMsgView2.Content ??= new MsgView();
|
||||
tabClashProxies2.Content ??= new ClashProxiesView();
|
||||
tabClashConnections2.Content ??= new ClashConnectionsView();
|
||||
gridMain2.Visibility = Visibility.Visible;
|
||||
break;
|
||||
}
|
||||
pbTheme.Content ??= new ThemeSettingView();
|
||||
@@ -108,7 +110,6 @@ namespace v2rayN.Views
|
||||
switch (_config.UiItem.MainGirdOrientation)
|
||||
{
|
||||
case EGirdOrientation.Horizontal:
|
||||
gridMain.Visibility = Visibility.Visible;
|
||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView.Visibility).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies.Visibility).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections.Visibility).DisposeWith(disposables);
|
||||
@@ -116,7 +117,6 @@ namespace v2rayN.Views
|
||||
break;
|
||||
|
||||
case EGirdOrientation.Vertical:
|
||||
gridMain1.Visibility = Visibility.Visible;
|
||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView1.Visibility).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies1.Visibility).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections1.Visibility).DisposeWith(disposables);
|
||||
@@ -125,7 +125,6 @@ namespace v2rayN.Views
|
||||
|
||||
case EGirdOrientation.Tab:
|
||||
default:
|
||||
gridMain2.Visibility = Visibility.Visible;
|
||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies2.Visibility).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections2.Visibility).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain2.SelectedIndex).DisposeWith(disposables);
|
||||
@@ -142,6 +141,7 @@ namespace v2rayN.Views
|
||||
|
||||
RestoreUI();
|
||||
AddHelpMenuItem();
|
||||
WindowsHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null);
|
||||
}
|
||||
|
||||
#region Event
|
||||
@@ -283,13 +283,6 @@ namespace v2rayN.Views
|
||||
{
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.V:
|
||||
if (_backupAndRestoreView?.IsVisible == true) return;
|
||||
|
||||
var clipboardData = WindowsUtils.GetClipboardData();
|
||||
ViewModel?.AddServerViaClipboardAsync(clipboardData);
|
||||
break;
|
||||
|
||||
case Key.S:
|
||||
ScanScreenTaskAsync().ContinueWith(_ => { });
|
||||
break;
|
||||
|
||||
@@ -698,7 +698,7 @@
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="14"
|
||||
Grid.Row="13"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
@@ -706,6 +706,22 @@
|
||||
Text="{x:Static resx:ResUI.TbSettingsAutoUpdateInterval}" />
|
||||
<TextBox
|
||||
x:Name="txtautoUpdateInterval"
|
||||
Grid.Row="13"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="14"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsTrayMenuServersLimit}" />
|
||||
<TextBox
|
||||
x:Name="txttrayMenuServersLimit"
|
||||
Grid.Row="14"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
@@ -719,15 +735,23 @@
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsTrayMenuServersLimit}" />
|
||||
<TextBox
|
||||
x:Name="txttrayMenuServersLimit"
|
||||
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamily}" />
|
||||
<ComboBox
|
||||
x:Name="cmbcurrentFontFamily"
|
||||
Grid.Row="15"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
MaxDropDownHeight="1000"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
<TextBlock
|
||||
Grid.Row="15"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamilyTip}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="16"
|
||||
@@ -735,23 +759,15 @@
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamily}" />
|
||||
<ComboBox
|
||||
x:Name="cmbcurrentFontFamily"
|
||||
Text="{x:Static resx:ResUI.TbSettingsSpeedTestPageSize}" />
|
||||
<TextBox
|
||||
x:Name="txtSpeedTestPageSize"
|
||||
Grid.Row="16"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
MaxDropDownHeight="1000"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
<TextBlock
|
||||
Grid.Row="16"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamilyTip}"
|
||||
TextWrapping="Wrap" />
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="17"
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace v2rayN.Views
|
||||
{
|
||||
clbdestOverride.Items.Add(it);
|
||||
});
|
||||
_config.Inbound[0].DestOverride?.ForEach(it =>
|
||||
_config.Inbound.First().DestOverride?.ForEach(it =>
|
||||
{
|
||||
clbdestOverride.SelectedItems.Add(it);
|
||||
});
|
||||
@@ -149,6 +149,7 @@ namespace v2rayN.Views
|
||||
this.Bind(ViewModel, vm => vm.SpeedTestTimeout, v => v.cmbSpeedTestTimeout.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SpeedTestUrl, v => v.cmbSpeedTestUrl.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SpeedPingTestUrl, v => v.cmbSpeedPingTestUrl.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SpeedTestPageSize, v => v.txtSpeedTestPageSize.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.EnableHWA, v => v.togEnableHWA.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SubConvertUrl, v => v.cmbSubConvertUrl.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.MainGirdOrientation, v => v.cmbMainGirdOrientation.SelectedIndex).DisposeWith(disposables);
|
||||
@@ -211,7 +212,7 @@ namespace v2rayN.Views
|
||||
{
|
||||
files.AddRange(Directory.GetFiles(path, pattern));
|
||||
}
|
||||
var culture = _config.UiItem.CurrentLanguage == Global.Languages[0] ? "zh-cn" : "en-us";
|
||||
var culture = _config.UiItem.CurrentLanguage == Global.Languages.First() ? "zh-cn" : "en-us";
|
||||
var culture2 = "en-us";
|
||||
foreach (var ttf in files)
|
||||
{
|
||||
|
||||
@@ -228,6 +228,12 @@ namespace v2rayN.Views
|
||||
{
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.V:
|
||||
var clipboardData = WindowsUtils.GetClipboardData();
|
||||
var service = Locator.Current.GetService<MainWindowViewModel>();
|
||||
if (service != null) _ = service.AddServerViaClipboardAsync(clipboardData);
|
||||
break;
|
||||
|
||||
case Key.A:
|
||||
menuSelectAll_Click(null, null);
|
||||
break;
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
<reactiveui:ReactiveWindow
|
||||
x:Class="v2rayN.Views.RoutingRuleSettingWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:reactiveui="http://reactiveui.net"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:reactiveui="http://reactiveui.net"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
Title="{x:Static resx:ResUI.menuRoutingRuleSetting}"
|
||||
Width="960"
|
||||
Width="1000"
|
||||
Height="700"
|
||||
x:TypeArguments="vms:RoutingRuleSettingViewModel"
|
||||
ResizeMode="CanResize"
|
||||
ShowInTaskbar="False"
|
||||
Style="{StaticResource WindowGlobal}"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
@@ -321,11 +322,11 @@
|
||||
Binding="{Binding Network}"
|
||||
Header="network" />
|
||||
<DataGridTextColumn
|
||||
Width="200"
|
||||
Width="*"
|
||||
Binding="{Binding Domains}"
|
||||
Header="domain" />
|
||||
<DataGridTextColumn
|
||||
Width="200"
|
||||
Width="*"
|
||||
Binding="{Binding Ips}"
|
||||
Header="ip" />
|
||||
</DataGrid.Columns>
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
Title="{x:Static resx:ResUI.menuRoutingSetting}"
|
||||
Width="990"
|
||||
Width="1000"
|
||||
Height="700"
|
||||
x:TypeArguments="vms:RoutingSettingViewModel"
|
||||
ResizeMode="CanResize"
|
||||
ShowInTaskbar="False"
|
||||
Style="{StaticResource WindowGlobal}"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
@@ -174,7 +175,7 @@
|
||||
</DataGrid.Resources>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn
|
||||
Width="250"
|
||||
Width="*"
|
||||
Binding="{Binding Remarks}"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<DataGridTextColumn
|
||||
@@ -186,7 +187,7 @@
|
||||
Binding="{Binding Sort}"
|
||||
Header="{x:Static resx:ResUI.LvSort}" />
|
||||
<DataGridTextColumn
|
||||
Width="300"
|
||||
Width="*"
|
||||
Binding="{Binding Url}"
|
||||
Header="{x:Static resx:ResUI.LvUrl}" />
|
||||
<DataGridTextColumn
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<reactiveui:ReactiveWindow
|
||||
x:Class="v2rayN.Views.SubEditWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:reactiveui="http://reactiveui.net"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:reactiveui="http://reactiveui.net"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
Title="{x:Static resx:ResUI.menuSubSetting}"
|
||||
Width="700"
|
||||
Height="600"
|
||||
Height="650"
|
||||
x:TypeArguments="vms:SubEditViewModel"
|
||||
ShowInTaskbar="False"
|
||||
Style="{StaticResource WindowGlobal}"
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
Title="{x:Static resx:ResUI.menuSubSetting}"
|
||||
Width="800"
|
||||
Height="600"
|
||||
Width="1000"
|
||||
Height="700"
|
||||
x:TypeArguments="vms:SubSettingViewModel"
|
||||
ResizeMode="CanResize"
|
||||
ShowInTaskbar="False"
|
||||
Style="{StaticResource WindowGlobal}"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
@@ -92,7 +93,7 @@
|
||||
Style="{StaticResource DefDataGrid}">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn
|
||||
Width="120"
|
||||
Width="*"
|
||||
Binding="{Binding Remarks}"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<DataGridTextColumn
|
||||
|
||||
Reference in New Issue
Block a user