From 17ed26cd0626f2aab92342e118a73d73d86f5329 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 12 Mar 2026 14:56:01 +0800 Subject: [PATCH] Improve profile matching for Subscription, remove old option --- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 87 +++++++++++++++++-- v2rayN/ServiceLib/Models/ConfigItems.cs | 1 - v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 9 -- v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 3 - v2rayN/ServiceLib/Resx/ResUI.fr.resx | 3 - v2rayN/ServiceLib/Resx/ResUI.hu.resx | 3 - v2rayN/ServiceLib/Resx/ResUI.resx | 3 - v2rayN/ServiceLib/Resx/ResUI.ru.resx | 3 - v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 5 +- v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 3 - .../ViewModels/OptionSettingViewModel.cs | 3 - .../Views/OptionSettingWindow.axaml | 13 --- .../Views/OptionSettingWindow.axaml.cs | 1 - v2rayN/v2rayN/Views/OptionSettingWindow.xaml | 14 --- .../v2rayN/Views/OptionSettingWindow.xaml.cs | 1 - 15 files changed, 82 insertions(+), 70 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 3c587676..34758618 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -95,10 +95,7 @@ public static class ConfigHandler config.GuiItem ??= new(); config.MsgUIItem ??= new(); - config.UiItem ??= new UIItem() - { - EnableUpdateSubOnlyRemarksExist = true - }; + config.UiItem ??= new(); config.UiItem.MainColumnItem ??= new(); config.UiItem.WindowSizeItem ??= new(); @@ -1132,6 +1129,84 @@ public static class ConfigHandler } } + /// + /// Searches the specified collection for a profile item that matches the target profile item based on a series of + /// criteria. + /// + /// The method attempts to find a match by comparing the target's remarks, address, port, and + /// password in various combinations. The search is performed in order of specificity, starting with the most + /// detailed comparison. If no match is found at any stage, the method returns null. + /// An enumerable collection of profile items to search. This parameter can be null. + /// The profile item to match against items in the source collection. This parameter can be null. + /// A profile item from the source collection that matches the target item according to defined criteria; otherwise, + /// null if no match is found or if either parameter is null. + private static ProfileItem? FindMatchedProfileItem(IEnumerable? source, ProfileItem? target) + { + if (source == null || target == null) + { + return null; + } + + var matchedItem = source.FirstOrDefault(t => CompareProfileItem(t, target, true)); + if (matchedItem != null) + { + return matchedItem; + } + + if (target.Remarks.IsNotEmpty()) + { + matchedItem = source.FirstOrDefault(t => t.Remarks == target.Remarks); + if (matchedItem != null) + { + return matchedItem; + } + } + + if (target.Address.IsNotEmpty() && target.Port > 0 && target.Password.IsNotEmpty()) + { + matchedItem = source.FirstOrDefault(t => + IsSameText(t.Address, target.Address) && + t.Port == target.Port && + IsSameText(t.Password, target.Password)); + if (matchedItem != null) + { + return matchedItem; + } + } + + if (target.Address.IsNotEmpty() && target.Port > 0) + { + matchedItem = source.FirstOrDefault(t => + IsSameText(t.Address, target.Address) && + t.Port == target.Port); + if (matchedItem != null) + { + return matchedItem; + } + } + + if (target.Address.IsNotEmpty()) + { + matchedItem = source.FirstOrDefault(t => IsSameText(t.Address, target.Address)); + if (matchedItem != null) + { + return matchedItem; + } + } + + return null; + + static bool IsSameText(string? left, string? right) + { + if (left.IsNullOrEmpty() || right.IsNullOrEmpty()) + { + return false; + } + + return string.Equals(left.TrimEx(), right.TrimEx(), StringComparison.OrdinalIgnoreCase); + } + } + /// /// Remove a single server profile by its index ID /// Deletes the configuration file if it's a custom config @@ -1636,7 +1711,7 @@ public static class ConfigHandler if (activeProfile != null) { var lstSub = await AppManager.Instance.ProfileItems(subid); - var existItem = lstSub?.FirstOrDefault(t => config.UiItem.EnableUpdateSubOnlyRemarksExist ? t.Remarks == activeProfile.Remarks : CompareProfileItem(t, activeProfile, true)); + var existItem = FindMatchedProfileItem(lstSub, activeProfile); if (existItem != null) { await ConfigHandler.SetDefaultServerIndex(config, existItem.IndexId); @@ -1649,7 +1724,7 @@ public static class ConfigHandler var lstSub = await AppManager.Instance.ProfileItems(subid); foreach (var item in lstSub) { - var existItem = lstOriSub?.FirstOrDefault(t => config.UiItem.EnableUpdateSubOnlyRemarksExist ? t.Remarks == item.Remarks : CompareProfileItem(t, item, true)); + var existItem = FindMatchedProfileItem(lstOriSub, item); if (existItem != null) { await StatisticsManager.Instance.CloneServerStatItem(existItem.IndexId, item.IndexId); diff --git a/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayN/ServiceLib/Models/ConfigItems.cs index 56e5fe47..741c21e5 100644 --- a/v2rayN/ServiceLib/Models/ConfigItems.cs +++ b/v2rayN/ServiceLib/Models/ConfigItems.cs @@ -87,7 +87,6 @@ public class MsgUIItem public class UIItem { public bool EnableAutoAdjustMainLvColWidth { get; set; } - public bool EnableUpdateSubOnlyRemarksExist { get; set; } public int MainGirdHeight1 { get; set; } public int MainGirdHeight2 { get; set; } public EGirdOrientation MainGirdOrientation { get; set; } = EGirdOrientation.Vertical; diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 5edca7bf..2111ba0e 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -3816,15 +3816,6 @@ namespace ServiceLib.Resx { } } - /// - /// 查找类似 Updating subscription, only determining if remarks exist 的本地化字符串。 - /// - public static string TbSettingsEnableUpdateSubOnlyRemarksExist { - get { - return ResourceManager.GetString("TbSettingsEnableUpdateSubOnlyRemarksExist", resourceCulture); - } - } - /// /// 查找类似 Exception 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index b3ffa77e..b6ad108e 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1098,9 +1098,6 @@ آدرس اینترنتی تست پینگ سرعت - - اشتراک در حال به‌روزرسانی، فقط مشخص کنید که ملاحظاتی آیا وجود دارد! - پایان تست... diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx index ad88aca5..0dfe120f 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx @@ -1095,9 +1095,6 @@ Adresse de test de connexion réelle - - Ne vérifier l’existence de l’alias qu’à la maj. des abonnements - Arrêt du test en cours... diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 8c734066..94d7e48f 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1098,9 +1098,6 @@ Sebesség Ping Teszt URL - - Előfizetés frissítése, csak a megjegyzések létezésének ellenőrzése - Teszt megszakítása... diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 3fdd653a..dc24c384 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1098,9 +1098,6 @@ Speed Ping Test URL - - Updating subscription, only determining if remarks exist - Test terminating... diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 4aa03795..28dc34d0 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1098,9 +1098,6 @@ URL для быстрой проверки реальной задержки - - Обновляя подписку, проверять лишь наличие примечаний - Отмена тестирования... diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index 6aef06ec..9f400c17 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1095,9 +1095,6 @@ 真连接测试地址 - - 更新订阅时只判断别名已存在否 - 测试终止中... @@ -1668,4 +1665,4 @@ 按地区分组 - + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index 3ecedfb4..b35b603f 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1095,9 +1095,6 @@ 真連線測試位址 - - 更新訂閱時只判斷別名是否存在 - 測試終止中... diff --git a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs index b5bfe71f..d936c5df 100644 --- a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs @@ -47,7 +47,6 @@ public class OptionSettingViewModel : MyReactiveObject [Reactive] public bool KeepOlderDedupl { get; set; } [Reactive] public bool DisplayRealTimeSpeed { get; set; } [Reactive] public bool EnableAutoAdjustMainLvColWidth { get; set; } - [Reactive] public bool EnableUpdateSubOnlyRemarksExist { get; set; } [Reactive] public bool AutoHideStartup { get; set; } [Reactive] public bool Hide2TrayWhenClose { get; set; } [Reactive] public bool MacOSShowInDock { get; set; } @@ -180,7 +179,6 @@ public class OptionSettingViewModel : MyReactiveObject DisplayRealTimeSpeed = _config.GuiItem.DisplayRealTimeSpeed; KeepOlderDedupl = _config.GuiItem.KeepOlderDedupl; EnableAutoAdjustMainLvColWidth = _config.UiItem.EnableAutoAdjustMainLvColWidth; - EnableUpdateSubOnlyRemarksExist = _config.UiItem.EnableUpdateSubOnlyRemarksExist; AutoHideStartup = _config.UiItem.AutoHideStartup; Hide2TrayWhenClose = _config.UiItem.Hide2TrayWhenClose; MacOSShowInDock = _config.UiItem.MacOSShowInDock; @@ -345,7 +343,6 @@ public class OptionSettingViewModel : MyReactiveObject _config.GuiItem.DisplayRealTimeSpeed = DisplayRealTimeSpeed; _config.GuiItem.KeepOlderDedupl = KeepOlderDedupl; _config.UiItem.EnableAutoAdjustMainLvColWidth = EnableAutoAdjustMainLvColWidth; - _config.UiItem.EnableUpdateSubOnlyRemarksExist = EnableUpdateSubOnlyRemarksExist; _config.UiItem.AutoHideStartup = AutoHideStartup; _config.UiItem.Hide2TrayWhenClose = Hide2TrayWhenClose; _config.UiItem.MacOSShowInDock = MacOSShowInDock; diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml index cfcb48f0..8c4f1fcc 100644 --- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml @@ -410,19 +410,6 @@ Margin="{StaticResource Margin4}" HorizontalAlignment="Left" /> - - - this.Bind(ViewModel, vm => vm.DisplayRealTimeSpeed, v => v.togDisplayRealTimeSpeed.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.KeepOlderDedupl, v => v.togKeepOlderDedupl.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.EnableAutoAdjustMainLvColWidth, v => v.togEnableAutoAdjustMainLvColWidth.IsChecked).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.EnableUpdateSubOnlyRemarksExist, v => v.togEnableUpdateSubOnlyRemarksExist.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.AutoHideStartup, v => v.togAutoHideStartup.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Hide2TrayWhenClose, v => v.togHide2TrayWhenClose.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.MacOSShowInDock, v => v.togMacOSShowInDock.IsChecked).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml index 8f0d24d4..133b4134 100644 --- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml +++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml @@ -630,20 +630,6 @@ Margin="{StaticResource Margin8}" HorizontalAlignment="Left" /> - - - vm.DisplayRealTimeSpeed, v => v.togDisplayRealTimeSpeed.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.KeepOlderDedupl, v => v.togKeepOlderDedupl.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.EnableAutoAdjustMainLvColWidth, v => v.togEnableAutoAdjustMainLvColWidth.IsChecked).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.EnableUpdateSubOnlyRemarksExist, v => v.togEnableUpdateSubOnlyRemarksExist.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.AutoHideStartup, v => v.togAutoHideStartup.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.EnableDragDropSort, v => v.togEnableDragDropSort.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DoubleClick2Activate, v => v.togDoubleClick2Activate.IsChecked).DisposeWith(disposables);