Compare commits

...

12 Commits

Author SHA1 Message Date
2dust
d1e6898290 up 7.10.5 2025-03-17 11:07:31 +08:00
2dust
8597332b21 Fixed warnings 2025-03-17 11:07:01 +08:00
2dust
7cc42ae249 Simply check the file hash value and delete the old pac file 2025-03-17 11:05:04 +08:00
Wydy
e054d4487d Update pac (#6924) 2025-03-17 09:06:21 +08:00
2dust
6ef36f521d Optimize and improve code 2025-03-16 15:48:42 +08:00
2dust
a02a122dd1 Update proxy_set_linux_sh
https://github.com/2dust/v2rayN/issues/6886
2025-03-16 11:01:23 +08:00
2dust
701138617c Update Directory.Packages.props 2025-03-16 10:56:33 +08:00
Avery Lynn
d0e2cc9442 Fix server deduplicaiton failure (#6900)
- Add a local method AreEquel to compare string values, return TRUE when compare between 'null' and 'string.Emtpy'
2025-03-13 20:11:10 +08:00
DHR60
d561f10edc Modify default fallback load balancing rule (#6889)
* Modify default fallback load balancing rule

* Refine default fallback load balancing rule based on domain strategy
2025-03-12 18:36:51 +08:00
2dust
2df412476a If it is a Windows WinGet installation, the configuration file is stored in the user directory
https://github.com/2dust/v2rayN/issues/6803
2025-03-09 19:04:49 +08:00
2dust
e6011cfede AI-optimized code 2025-03-07 12:11:19 +08:00
2dust
bcf43e2928 Enhanced subscription customization configuration
https://github.com/2dust/v2rayN/issues/6875
2025-03-07 11:37:01 +08:00
17 changed files with 528 additions and 24311 deletions

View File

@@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>7.10.4</Version> <Version>7.10.5</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View File

@@ -9,8 +9,8 @@
<PackageVersion Include="Avalonia.Desktop" Version="11.2.5" /> <PackageVersion Include="Avalonia.Desktop" Version="11.2.5" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.2.5" /> <PackageVersion Include="Avalonia.Diagnostics" Version="11.2.5" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.2.5" /> <PackageVersion Include="Avalonia.ReactiveUI" Version="11.2.5" />
<PackageVersion Include="CliWrap" Version="3.8.1" /> <PackageVersion Include="CliWrap" Version="3.8.2" />
<PackageVersion Include="Downloader" Version="3.3.3" /> <PackageVersion Include="Downloader" Version="3.3.4" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" /> <PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" />
<PackageVersion Include="MaterialDesignThemes" Version="5.2.1" /> <PackageVersion Include="MaterialDesignThemes" Version="5.2.1" />
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" /> <PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" />
@@ -22,7 +22,7 @@
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.5" /> <PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.5" />
<PackageVersion Include="Splat.NLog" Version="15.3.1" /> <PackageVersion Include="Splat.NLog" Version="15.3.1" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" /> <PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.12.0" /> <PackageVersion Include="TaskScheduler" Version="2.12.1" />
<PackageVersion Include="WebDav.Client" Version="2.8.0" /> <PackageVersion Include="WebDav.Client" Version="2.8.0" />
<PackageVersion Include="YamlDotNet" Version="16.3.0" /> <PackageVersion Include="YamlDotNet" Version="16.3.0" />
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" /> <PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />

View File

@@ -224,6 +224,13 @@ namespace ServiceLib.Common
} }
public static string GetMd5(string str) public static string GetMd5(string str)
{
if (string.IsNullOrEmpty(str))
{
return string.Empty;
}
try
{ {
var byteOld = Encoding.UTF8.GetBytes(str); var byteOld = Encoding.UTF8.GetBytes(str);
var byteNew = MD5.HashData(byteOld); var byteNew = MD5.HashData(byteOld);
@@ -235,6 +242,38 @@ namespace ServiceLib.Common
return sb.ToString(); return sb.ToString();
} }
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return string.Empty;
}
}
public static string GetFileHash(string filePath)
{
if (string.IsNullOrEmpty(filePath))
{
return string.Empty;
}
if (!File.Exists(filePath))
{
return string.Empty;
}
try
{
using var md5 = MD5.Create();
using var stream = File.OpenRead(filePath);
var hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return string.Empty;
}
}
/// <summary> /// <summary>
/// idn to idc /// idn to idc
@@ -564,13 +603,20 @@ namespace ServiceLib.Common
{ {
try try
{ {
var basePath = GetBaseDirectory();
//When this file exists, it is equivalent to having no permission to read and write //When this file exists, it is equivalent to having no permission to read and write
if (File.Exists(Path.Combine(GetBaseDirectory(), "NotStoreConfigHere.txt"))) if (File.Exists(Path.Combine(basePath, "NotStoreConfigHere.txt")))
{ {
return false; return false;
} }
var tempPath = Path.Combine(GetBaseDirectory(), "guiTemps"); //Check if it is installed by Windows WinGet
if (IsWindows() && basePath.Contains("Users") && basePath.Contains("WinGet"))
{
return false;
}
var tempPath = Path.Combine(basePath, "guiTemps");
if (!Directory.Exists(tempPath)) if (!Directory.Exists(tempPath))
{ {
Directory.CreateDirectory(tempPath); Directory.CreateDirectory(tempPath);

View File

@@ -11,9 +11,9 @@ namespace ServiceLib.Handler
private Dictionary<string, ProxiesItem>? _proxies; private Dictionary<string, ProxiesItem>? _proxies;
public Dictionary<string, object> ProfileContent { get; set; } public Dictionary<string, object> ProfileContent { get; set; }
public async Task<Tuple<ClashProxies, ClashProviders>?> GetClashProxiesAsync(Config config) public async Task<Tuple<ClashProxies, ClashProviders>?> GetClashProxiesAsync()
{ {
for (var i = 0; i < 5; i++) for (var i = 0; i < 3; i++)
{ {
var url = $"{GetApiUrl()}/proxies"; var url = $"{GetApiUrl()}/proxies";
var result = await HttpClientHelper.Instance.TryGetAsync(url); var result = await HttpClientHelper.Instance.TryGetAsync(url);
@@ -41,34 +41,26 @@ namespace ServiceLib.Handler
{ {
if (blAll) if (blAll)
{ {
for (var i = 0; i < 5; i++)
{
if (_proxies != null)
{
break;
}
await Task.Delay(5000);
}
if (_proxies == null) if (_proxies == null)
{ {
return; await GetClashProxiesAsync();
} }
lstProxy = new List<ClashProxyModel>(); lstProxy = new List<ClashProxyModel>();
foreach (var kv in _proxies) foreach (var kv in _proxies ?? [])
{ {
if (Global.notAllowTestType.Contains(kv.Value.type.ToLower())) if (Global.notAllowTestType.Contains(kv.Value.type?.ToLower()))
{ {
continue; continue;
} }
lstProxy.Add(new ClashProxyModel() lstProxy.Add(new ClashProxyModel()
{ {
Name = kv.Value.name, Name = kv.Value.name,
Type = kv.Value.type.ToLower(), Type = kv.Value.type?.ToLower(),
}); });
} }
} }
if (lstProxy == null) if (lstProxy is not { Count: > 0 })
{ {
return; return;
} }
@@ -157,7 +149,7 @@ namespace ServiceLib.Handler
} }
} }
public async Task<ClashConnections?> GetClashConnectionsAsync(Config config) public async Task<ClashConnections?> GetClashConnectionsAsync()
{ {
try try
{ {

View File

@@ -871,13 +871,19 @@ namespace ServiceLib.Handler
public static async Task<Tuple<int, int>> DedupServerList(Config config, string subId) public static async Task<Tuple<int, int>> DedupServerList(Config config, string subId)
{ {
var lstProfile = await AppHandler.Instance.ProfileItems(subId); var lstProfile = await AppHandler.Instance.ProfileItems(subId);
if (lstProfile == null)
{
return new Tuple<int, int>(0, 0);
}
List<ProfileItem> lstKeep = new(); List<ProfileItem> lstKeep = new();
List<ProfileItem> lstRemove = new(); List<ProfileItem> lstRemove = new();
if (!config.GuiItem.KeepOlderDedupl) if (!config.GuiItem.KeepOlderDedupl)
{
lstProfile.Reverse(); lstProfile.Reverse();
}
foreach (ProfileItem item in lstProfile) foreach (var item in lstProfile)
{ {
if (!lstKeep.Exists(i => CompareProfileItem(i, item, false))) if (!lstKeep.Exists(i => CompareProfileItem(i, item, false)))
{ {
@@ -944,7 +950,7 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
private static bool CompareProfileItem(ProfileItem o, ProfileItem n, bool remarks) private static bool CompareProfileItem(ProfileItem? o, ProfileItem? n, bool remarks)
{ {
if (o == null || n == null) if (o == null || n == null)
{ {
@@ -952,22 +958,27 @@ namespace ServiceLib.Handler
} }
return o.ConfigType == n.ConfigType return o.ConfigType == n.ConfigType
&& o.Address == n.Address && AreEqual(o.Address, n.Address)
&& o.Port == n.Port && o.Port == n.Port
&& o.Id == n.Id && AreEqual(o.Id, n.Id)
&& o.Security == n.Security && AreEqual(o.Security, n.Security)
&& o.Network == n.Network && AreEqual(o.Network, n.Network)
&& o.HeaderType == n.HeaderType && AreEqual(o.HeaderType, n.HeaderType)
&& o.RequestHost == n.RequestHost && AreEqual(o.RequestHost, n.RequestHost)
&& o.Path == n.Path && AreEqual(o.Path, n.Path)
&& (o.ConfigType == EConfigType.Trojan || o.StreamSecurity == n.StreamSecurity) && (o.ConfigType == EConfigType.Trojan || o.StreamSecurity == n.StreamSecurity)
&& o.Flow == n.Flow && AreEqual(o.Flow, n.Flow)
&& o.Sni == n.Sni && AreEqual(o.Sni, n.Sni)
&& o.Alpn == n.Alpn && AreEqual(o.Alpn, n.Alpn)
&& o.Fingerprint == n.Fingerprint && AreEqual(o.Fingerprint, n.Fingerprint)
&& o.PublicKey == n.PublicKey && AreEqual(o.PublicKey, n.PublicKey)
&& o.ShortId == n.ShortId && AreEqual(o.ShortId, n.ShortId)
&& (!remarks || o.Remarks == n.Remarks); && (!remarks || o.Remarks == n.Remarks);
static bool AreEqual(string? a, string? b)
{
return string.Equals(a, b) || (string.IsNullOrEmpty(a) && string.IsNullOrEmpty(b));
}
} }
private static async Task<int> RemoveProfileItem(Config config, string indexId) private static async Task<int> RemoveProfileItem(Config config, string indexId)
@@ -1005,8 +1016,7 @@ namespace ServiceLib.Handler
return result; return result;
} }
var fileName = configPath; if (!File.Exists(configPath))
if (!File.Exists(fileName))
{ {
return result; return result;
} }

View File

@@ -5,40 +5,35 @@ namespace ServiceLib.Handler.Fmt
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks) public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
{ {
var configObjects = JsonUtils.Deserialize<object[]>(strData); var configObjects = JsonUtils.Deserialize<object[]>(strData);
if (configObjects != null && configObjects.Length > 0) if (configObjects is not { Length: > 0 })
{ {
return null;
}
List<ProfileItem> lstResult = []; List<ProfileItem> lstResult = [];
foreach (var configObject in configObjects) foreach (var configObject in configObjects)
{ {
var objectString = JsonUtils.Serialize(configObject); var objectString = JsonUtils.Serialize(configObject);
var singboxCon = JsonUtils.Deserialize<SingboxConfig>(objectString); var profileIt = ResolveFull(objectString, subRemarks);
if (singboxCon?.inbounds?.Count > 0 if (profileIt != null)
&& singboxCon.outbounds?.Count > 0
&& singboxCon.route != null)
{ {
var fileName = WriteAllText(objectString);
var profileIt = new ProfileItem
{
CoreType = ECoreType.sing_box,
Address = fileName,
Remarks = subRemarks ?? "singbox_custom",
};
lstResult.Add(profileIt); lstResult.Add(profileIt);
} }
} }
return lstResult; return lstResult;
} }
return null;
}
public static ProfileItem? ResolveFull(string strData, string? subRemarks) public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{ {
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(strData); var config = JsonUtils.ParseJson(strData);
if (singboxConfig?.inbounds?.Count > 0 if (config?["inbounds"] == null
&& singboxConfig.outbounds?.Count > 0 || config["outbounds"] == null
&& singboxConfig.route != null) || config["route"] == null
|| config["dns"] == null)
{ {
return null;
}
var fileName = WriteAllText(strData); var fileName = WriteAllText(strData);
var profileItem = new ProfileItem var profileItem = new ProfileItem
{ {
@@ -49,7 +44,5 @@ namespace ServiceLib.Handler.Fmt
return profileItem; return profileItem;
} }
return null;
}
} }
} }

View File

@@ -5,52 +5,45 @@ namespace ServiceLib.Handler.Fmt
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks) public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
{ {
var configObjects = JsonUtils.Deserialize<object[]>(strData); var configObjects = JsonUtils.Deserialize<object[]>(strData);
if (configObjects != null && configObjects.Length > 0) if (configObjects is not { Length: > 0 })
{ {
return null;
}
List<ProfileItem> lstResult = []; List<ProfileItem> lstResult = [];
foreach (var configObject in configObjects) foreach (var configObject in configObjects)
{ {
var objectString = JsonUtils.Serialize(configObject); var objectString = JsonUtils.Serialize(configObject);
var v2rayCon = JsonUtils.Deserialize<V2rayConfig>(objectString); var profileIt = ResolveFull(objectString, subRemarks);
if (v2rayCon?.inbounds?.Count > 0 if (profileIt != null)
&& v2rayCon.outbounds?.Count > 0
&& v2rayCon.routing != null)
{ {
var fileName = WriteAllText(objectString);
var profileIt = new ProfileItem
{
CoreType = ECoreType.Xray,
Address = fileName,
Remarks = v2rayCon.remarks ?? subRemarks ?? "v2ray_custom",
};
lstResult.Add(profileIt); lstResult.Add(profileIt);
} }
} }
return lstResult; return lstResult;
} }
return null;
}
public static ProfileItem? ResolveFull(string strData, string? subRemarks) public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{ {
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(strData); var config = JsonUtils.ParseJson(strData);
if (v2rayConfig?.inbounds?.Count > 0 if (config?["inbounds"] == null
&& v2rayConfig.outbounds?.Count > 0 || config["outbounds"] == null
&& v2rayConfig.routing != null) || config["routing"] == null)
{ {
return null;
}
var fileName = WriteAllText(strData); var fileName = WriteAllText(strData);
var profileItem = new ProfileItem var profileItem = new ProfileItem
{ {
CoreType = ECoreType.Xray, CoreType = ECoreType.Xray,
Address = fileName, Address = fileName,
Remarks = v2rayConfig.remarks ?? subRemarks ?? "v2ray_custom" Remarks = config?["remarks"]?.ToString() ?? subRemarks ?? "v2ray_custom"
}; };
return profileItem; return profileItem;
} }
return null;
}
} }
} }

View File

@@ -33,6 +33,13 @@ namespace ServiceLib.Handler
private static async Task InitText() private static async Task InitText()
{ {
var path = Path.Combine(_configPath, "pac.txt"); var path = Path.Combine(_configPath, "pac.txt");
// Delete the old pac file
if (File.Exists(path) && Utils.GetFileHash(path).Equals("b590c07280f058ef05d5394aa2f927fe"))
{
File.Delete(path);
}
if (!File.Exists(path)) if (!File.Exists(path))
{ {
var pac = EmbedUtils.GetEmbedText(Global.PacFileName); var pac = EmbedUtils.GetEmbedText(Global.PacFileName);

File diff suppressed because it is too large Load Diff

View File

@@ -80,13 +80,19 @@ set_kde_proxy() {
# Detect the current desktop environment # Detect the current desktop environment
detect_desktop_environment() { detect_desktop_environment() {
if [ "$XDG_CURRENT_DESKTOP" == "GNOME" ] || [ "$XDG_CURRENT_DESKTOP" == "ubuntu:GNOME" ] || [ "$XDG_SESSION_DESKTOP" == "GNOME" ] || [ "$XDG_SESSION_DESKTOP" == "ubuntu:GNOME" ]; then if [[ "$XDG_CURRENT_DESKTOP" == *"GNOME"* ]] || [[ "$XDG_SESSION_DESKTOP" == *"GNOME"* ]]; then
echo "gnome" echo "gnome"
elif [ "$XDG_CURRENT_DESKTOP" == "KDE" ] || [ "$XDG_CURRENT_DESKTOP" == "plasma" ] || [ "$XDG_SESSION_DESKTOP" == "KDE" ] || [ "$XDG_SESSION_DESKTOP" == "plasma" ]; then return
echo "kde"
else
echo "unsupported"
fi fi
local KDE_ENVIRONMENTS=("KDE" "plasma")
for ENV in "${KDE_ENVIRONMENTS[@]}"; do
if [ "$XDG_CURRENT_DESKTOP" == "$ENV" ] || [ "$XDG_SESSION_DESKTOP" == "$ENV" ]; then
echo "kde"
return
fi
done
echo "unsupported"
} }
# Main script logic # Main script logic

View File

@@ -182,12 +182,24 @@ namespace ServiceLib.Services.CoreConfig
rule.balancerTag = balancer.tag; rule.balancerTag = balancer.tag;
} }
} }
if (v2rayConfig.routing.domainStrategy == "IPIfNonMatch")
{
v2rayConfig.routing.rules.Add(new()
{
ip = ["0.0.0.0/0", "::/0"],
balancerTag = balancer.tag,
type = "field"
});
}
else
{
v2rayConfig.routing.rules.Add(new() v2rayConfig.routing.rules.Add(new()
{ {
network = "tcp,udp", network = "tcp,udp",
balancerTag = balancer.tag, balancerTag = balancer.tag,
type = "field" type = "field"
}); });
}
ret.Success = true; ret.Success = true;
ret.Data = JsonUtils.Serialize(v2rayConfig); ret.Data = JsonUtils.Serialize(v2rayConfig);
@@ -582,7 +594,7 @@ namespace ServiceLib.Services.CoreConfig
var it = JsonUtils.DeepCopy(rule); var it = JsonUtils.DeepCopy(rule);
it.ip = null; it.ip = null;
it.type = "field"; it.type = "field";
for (int k = it.domain.Count - 1; k >= 0; k--) for (var k = it.domain.Count - 1; k >= 0; k--)
{ {
if (it.domain[k].StartsWith("#")) if (it.domain[k].StartsWith("#"))
{ {

View File

@@ -74,6 +74,14 @@ namespace ServiceLib.ViewModels
} }
private async Task CheckUpdate() private async Task CheckUpdate()
{
await Task.Run(async () =>
{
await CheckUpdateTask();
});
}
private async Task CheckUpdateTask()
{ {
_lstUpdated.Clear(); _lstUpdated.Clear();
_lstUpdated = _checkUpdateModel.Where(x => x.IsSelected == true) _lstUpdated = _checkUpdateModel.Where(x => x.IsSelected == true)

View File

@@ -53,12 +53,12 @@ namespace ServiceLib.ViewModels
private async Task Init() private async Task Init()
{ {
_ = DelayTestTask(); await DelayTestTask();
} }
private async Task GetClashConnections() private async Task GetClashConnections()
{ {
var ret = await ClashApiHandler.Instance.GetClashConnectionsAsync(_config); var ret = await ClashApiHandler.Instance.GetClashConnectionsAsync();
if (ret == null) if (ret == null)
{ {
return; return;

View File

@@ -13,7 +13,7 @@ namespace ServiceLib.ViewModels
{ {
private Dictionary<string, ProxiesItem>? _proxies; private Dictionary<string, ProxiesItem>? _proxies;
private Dictionary<string, ProvidersItem>? _providers; private Dictionary<string, ProvidersItem>? _providers;
private int _delayTimeout = 99999999; private readonly int _delayTimeout = 99999999;
private IObservableCollection<ClashProxyModel> _proxyGroups = new ObservableCollectionExtended<ClashProxyModel>(); private IObservableCollection<ClashProxyModel> _proxyGroups = new ObservableCollectionExtended<ClashProxyModel>();
private IObservableCollection<ClashProxyModel> _proxyDetails = new ObservableCollectionExtended<ClashProxyModel>(); private IObservableCollection<ClashProxyModel> _proxyDetails = new ObservableCollectionExtended<ClashProxyModel>();
@@ -28,8 +28,8 @@ namespace ServiceLib.ViewModels
public ClashProxyModel SelectedDetail { get; set; } public ClashProxyModel SelectedDetail { get; set; }
public ReactiveCommand<Unit, Unit> ProxiesReloadCmd { get; } public ReactiveCommand<Unit, Unit> ProxiesReloadCmd { get; }
public ReactiveCommand<Unit, Unit> ProxiesDelaytestCmd { get; } public ReactiveCommand<Unit, Unit> ProxiesDelayTestCmd { get; }
public ReactiveCommand<Unit, Unit> ProxiesDelaytestPartCmd { get; } public ReactiveCommand<Unit, Unit> ProxiesDelayTestPartCmd { get; }
public ReactiveCommand<Unit, Unit> ProxiesSelectActivityCmd { get; } public ReactiveCommand<Unit, Unit> ProxiesSelectActivityCmd { get; }
[Reactive] [Reactive]
@@ -50,12 +50,12 @@ namespace ServiceLib.ViewModels
{ {
await ProxiesReload(); await ProxiesReload();
}); });
ProxiesDelaytestCmd = ReactiveCommand.CreateFromTask(async () => ProxiesDelayTestCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
await ProxiesDelayTest(true); await ProxiesDelayTest(true);
}); });
ProxiesDelaytestPartCmd = ReactiveCommand.CreateFromTask(async () => ProxiesDelayTestPartCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
await ProxiesDelayTest(false); await ProxiesDelayTest(false);
}); });
@@ -78,7 +78,7 @@ namespace ServiceLib.ViewModels
this.WhenAnyValue( this.WhenAnyValue(
x => x.RuleModeSelected, x => x.RuleModeSelected,
y => y >= 0) y => y >= 0)
.Subscribe(async c => await DoRulemodeSelected(c)); .Subscribe(async c => await DoRuleModeSelected(c));
this.WhenAnyValue( this.WhenAnyValue(
x => x.SortingSelected, x => x.SortingSelected,
@@ -95,10 +95,10 @@ namespace ServiceLib.ViewModels
private async Task Init() private async Task Init()
{ {
_ = DelayTestTask(); await DelayTestTask();
} }
private async Task DoRulemodeSelected(bool c) private async Task DoRuleModeSelected(bool c)
{ {
if (!c) if (!c)
{ {
@@ -158,7 +158,7 @@ namespace ServiceLib.ViewModels
private async Task GetClashProxies(bool refreshUI) private async Task GetClashProxies(bool refreshUI)
{ {
var ret = await ClashApiHandler.Instance.GetClashProxiesAsync(_config); var ret = await ClashApiHandler.Instance.GetClashProxiesAsync();
if (ret?.Item1 == null || ret.Item2 == null) if (ret?.Item1 == null || ret.Item2 == null)
{ {
return; return;
@@ -255,29 +255,23 @@ namespace ServiceLib.ViewModels
} }
_proxies.TryGetValue(name, out var proxy); _proxies.TryGetValue(name, out var proxy);
if (proxy == null || proxy.all == null) if (proxy?.all == null)
{ {
return; return;
} }
var lstDetails = new List<ClashProxyModel>(); var lstDetails = new List<ClashProxyModel>();
foreach (var item in proxy.all) foreach (var item in proxy.all)
{ {
var IsActive = item == proxy.now;
var proxy2 = TryGetProxy(item); var proxy2 = TryGetProxy(item);
if (proxy2 == null) if (proxy2 == null)
{ {
continue; continue;
} }
int delay = -1; var delay = proxy2.history?.Count > 0 ? proxy2.history.Last().delay : -1;
if (proxy2.history.Count > 0)
{
delay = proxy2.history[proxy2.history.Count - 1].delay;
}
lstDetails.Add(new ClashProxyModel() lstDetails.Add(new ClashProxyModel()
{ {
IsActive = IsActive, IsActive = item == proxy.now,
Name = item, Name = item,
Type = proxy2.type, Type = proxy2.type,
Delay = delay <= 0 ? _delayTimeout : delay, Delay = delay <= 0 ? _delayTimeout : delay,
@@ -372,14 +366,9 @@ namespace ServiceLib.ViewModels
private async Task ProxiesDelayTest(bool blAll = true) private async Task ProxiesDelayTest(bool blAll = true)
{ {
ClashApiHandler.Instance.ClashProxiesDelayTest(blAll, _proxyDetails.ToList(), async (item, result) => ClashApiHandler.Instance.ClashProxiesDelayTest(blAll, _proxyDetails.ToList(), (item, result) =>
{ {
if (item == null) if (item == null || result.IsNullOrEmpty())
{
await GetClashProxies(true);
return;
}
if (result.IsNullOrEmpty())
{ {
return; return;
} }
@@ -393,8 +382,11 @@ namespace ServiceLib.ViewModels
{ {
//UpdateHandler(false, $"{item.name}={result}"); //UpdateHandler(false, $"{item.name}={result}");
var detail = _proxyDetails.FirstOrDefault(it => it.Name == result.IndexId); var detail = _proxyDetails.FirstOrDefault(it => it.Name == result.IndexId);
if (detail != null) if (detail == null)
{ {
return;
}
var dicResult = JsonUtils.Deserialize<Dictionary<string, object>>(result.Delay); var dicResult = JsonUtils.Deserialize<Dictionary<string, object>>(result.Delay);
if (dicResult != null && dicResult.TryGetValue("delay", out var value)) if (dicResult != null && dicResult.TryGetValue("delay", out var value))
{ {
@@ -413,7 +405,6 @@ namespace ServiceLib.ViewModels
} }
_proxyDetails.Replace(detail, JsonUtils.DeepCopy(detail)); _proxyDetails.Replace(detail, JsonUtils.DeepCopy(detail));
} }
}
#endregion proxy function #endregion proxy function

View File

@@ -448,7 +448,7 @@ namespace ServiceLib.ViewModels
private async Task<List<ProfileItem>?> GetProfileItems(bool latest) private async Task<List<ProfileItem>?> GetProfileItems(bool latest)
{ {
var lstSelecteds = new List<ProfileItem>(); var lstSelected = new List<ProfileItem>();
if (SelectedProfiles == null || SelectedProfiles.Count <= 0) if (SelectedProfiles == null || SelectedProfiles.Count <= 0)
{ {
return null; return null;
@@ -462,16 +462,16 @@ namespace ServiceLib.ViewModels
var item = await AppHandler.Instance.GetProfileItem(profile.IndexId); var item = await AppHandler.Instance.GetProfileItem(profile.IndexId);
if (item is not null) if (item is not null)
{ {
lstSelecteds.Add(item); lstSelected.Add(item);
} }
} }
} }
else else
{ {
lstSelecteds = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(orderProfiles)); lstSelected = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(orderProfiles));
} }
return lstSelecteds; return lstSelected;
} }
public async Task EditServerAsync(EConfigType eConfigType) public async Task EditServerAsync(EConfigType eConfigType)
@@ -509,8 +509,8 @@ namespace ServiceLib.ViewModels
public async Task RemoveServerAsync() public async Task RemoveServerAsync()
{ {
var lstSelecteds = await GetProfileItems(true); var lstSelected = await GetProfileItems(true);
if (lstSelecteds == null) if (lstSelected == null)
{ {
return; return;
} }
@@ -518,11 +518,11 @@ namespace ServiceLib.ViewModels
{ {
return; return;
} }
var exists = lstSelecteds.Exists(t => t.IndexId == _config.IndexId); var exists = lstSelected.Exists(t => t.IndexId == _config.IndexId);
await ConfigHandler.RemoveServers(_config, lstSelecteds); await ConfigHandler.RemoveServers(_config, lstSelected);
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
if (lstSelecteds.Count == _profileItems.Count) if (lstSelected.Count == _profileItems.Count)
{ {
_profileItems.Clear(); _profileItems.Clear();
} }
@@ -536,19 +536,22 @@ namespace ServiceLib.ViewModels
private async Task RemoveDuplicateServer() private async Task RemoveDuplicateServer()
{ {
var tuple = await ConfigHandler.DedupServerList(_config, _config.SubIndexId); var tuple = await ConfigHandler.DedupServerList(_config, _config.SubIndexId);
if (tuple.Item1 > 0 || tuple.Item2 > 0)
{
RefreshServers(); RefreshServers();
Reload(); Reload();
}
NoticeHandler.Instance.Enqueue(string.Format(ResUI.RemoveDuplicateServerResult, tuple.Item1, tuple.Item2)); NoticeHandler.Instance.Enqueue(string.Format(ResUI.RemoveDuplicateServerResult, tuple.Item1, tuple.Item2));
} }
private async Task CopyServer() private async Task CopyServer()
{ {
var lstSelecteds = await GetProfileItems(false); var lstSelected = await GetProfileItems(false);
if (lstSelecteds == null) if (lstSelected == null)
{ {
return; return;
} }
if (await ConfigHandler.CopyServer(_config, lstSelecteds) == 0) if (await ConfigHandler.CopyServer(_config, lstSelected) == 0)
{ {
RefreshServers(); RefreshServers();
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
@@ -564,7 +567,7 @@ namespace ServiceLib.ViewModels
await SetDefaultServer(SelectedProfile.IndexId); await SetDefaultServer(SelectedProfile.IndexId);
} }
public async Task SetDefaultServer(string indexId) public async Task SetDefaultServer(string? indexId)
{ {
if (indexId.IsNullOrEmpty()) if (indexId.IsNullOrEmpty())
{ {
@@ -594,11 +597,7 @@ namespace ServiceLib.ViewModels
{ {
return; return;
} }
if (SelectedServer == null) if (SelectedServer == null || SelectedServer.ID.IsNullOrEmpty())
{
return;
}
if (SelectedServer.ID.IsNullOrEmpty())
{ {
return; return;
} }
@@ -624,13 +623,13 @@ namespace ServiceLib.ViewModels
private async Task SetDefaultMultipleServer(ECoreType coreType) private async Task SetDefaultMultipleServer(ECoreType coreType)
{ {
var lstSelecteds = await GetProfileItems(true); var lstSelected = await GetProfileItems(true);
if (lstSelecteds == null) if (lstSelected == null)
{ {
return; return;
} }
var ret = await ConfigHandler.AddCustomServer4Multiple(_config, lstSelecteds, coreType); var ret = await ConfigHandler.AddCustomServer4Multiple(_config, lstSelected, coreType);
if (ret.Success != true) if (ret.Success != true)
{ {
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed); NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
@@ -679,19 +678,18 @@ namespace ServiceLib.ViewModels
return; return;
} }
var lstSelecteds = await GetProfileItems(true); var lstSelected = await GetProfileItems(true);
if (lstSelecteds == null) if (lstSelected == null)
{ {
return; return;
} }
await ConfigHandler.MoveToGroup(_config, lstSelecteds, SelectedMoveToGroup.Id); await ConfigHandler.MoveToGroup(_config, lstSelected, SelectedMoveToGroup.Id);
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
RefreshServers(); RefreshServers();
SelectedMoveToGroup = null; SelectedMoveToGroup = null;
SelectedMoveToGroup = new(); SelectedMoveToGroup = new();
//Reload();
} }
public async Task MoveServer(EMove eMove) public async Task MoveServer(EMove eMove)
@@ -703,7 +701,7 @@ namespace ServiceLib.ViewModels
return; return;
} }
int index = _lstProfile.IndexOf(item); var index = _lstProfile.IndexOf(item);
if (index < 0) if (index < 0)
{ {
return; return;
@@ -732,14 +730,14 @@ namespace ServiceLib.ViewModels
{ {
SelectedProfiles = _profileItems; SelectedProfiles = _profileItems;
} }
var lstSelecteds = await GetProfileItems(false); var lstSelected = await GetProfileItems(false);
if (lstSelecteds == null) if (lstSelected == null)
{ {
return; return;
} }
_speedtestService ??= new SpeedtestService(_config, (SpeedTestResult result) => _updateView?.Invoke(EViewAction.DispatcherSpeedTest, result)); _speedtestService ??= new SpeedtestService(_config, (SpeedTestResult result) => _updateView?.Invoke(EViewAction.DispatcherSpeedTest, result));
_speedtestService?.RunLoop(actionType, lstSelecteds); _speedtestService?.RunLoop(actionType, lstSelected);
} }
public void ServerSpeedtestStop() public void ServerSpeedtestStop()
@@ -793,14 +791,14 @@ namespace ServiceLib.ViewModels
public async Task Export2ShareUrlAsync(bool blEncode) public async Task Export2ShareUrlAsync(bool blEncode)
{ {
var lstSelecteds = await GetProfileItems(true); var lstSelected = await GetProfileItems(true);
if (lstSelecteds == null) if (lstSelected == null)
{ {
return; return;
} }
StringBuilder sb = new(); StringBuilder sb = new();
foreach (var it in lstSelecteds) foreach (var it in lstSelected)
{ {
var url = FmtHandler.GetShareUri(it); var url = FmtHandler.GetShareUri(it);
if (url.IsNullOrEmpty()) if (url.IsNullOrEmpty())

View File

@@ -27,9 +27,9 @@ namespace v2rayN.Desktop.Views
this.Bind(ViewModel, vm => vm.SelectedDetail, v => v.lstProxyDetails.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedDetail, v => v.lstProxyDetails.SelectedItem).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesReloadCmd, v => v.menuProxiesReload).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.ProxiesReloadCmd, v => v.menuProxiesReload).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesDelaytestCmd, v => v.menuProxiesDelaytest).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.ProxiesDelayTestCmd, v => v.menuProxiesDelaytest).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesDelaytestPartCmd, v => v.menuProxiesDelaytestPart).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.ProxiesDelayTestPartCmd, v => v.menuProxiesDelaytestPart).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesSelectActivityCmd, v => v.menuProxiesSelectActivity).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.ProxiesSelectActivityCmd, v => v.menuProxiesSelectActivity).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RuleModeSelected, v => v.cmbRulemode.SelectedIndex).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.RuleModeSelected, v => v.cmbRulemode.SelectedIndex).DisposeWith(disposables);

View File

@@ -28,9 +28,9 @@ namespace v2rayN.Views
this.Bind(ViewModel, vm => vm.SelectedDetail, v => v.lstProxyDetails.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedDetail, v => v.lstProxyDetails.SelectedItem).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesReloadCmd, v => v.menuProxiesReload).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.ProxiesReloadCmd, v => v.menuProxiesReload).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesDelaytestCmd, v => v.menuProxiesDelaytest).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.ProxiesDelayTestCmd, v => v.menuProxiesDelaytest).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesDelaytestPartCmd, v => v.menuProxiesDelaytestPart).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.ProxiesDelayTestPartCmd, v => v.menuProxiesDelaytestPart).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesSelectActivityCmd, v => v.menuProxiesSelectActivity).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.ProxiesSelectActivityCmd, v => v.menuProxiesSelectActivity).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RuleModeSelected, v => v.cmbRulemode.SelectedIndex).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.RuleModeSelected, v => v.cmbRulemode.SelectedIndex).DisposeWith(disposables);