From f0d05a7d4e9e832d88a0177c157b8ce0ee147403 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sun, 1 Sep 2024 16:39:45 +0800 Subject: [PATCH] Refactor check updates --- v2rayN/ServiceLib/Common/DownloaderHelper.cs | 4 + v2rayN/ServiceLib/Enums/EViewAction.cs | 2 + v2rayN/ServiceLib/Handler/UpdateHandler.cs | 23 +- v2rayN/ServiceLib/Models/CheckUpdateItem.cs | 17 + .../ViewModels/CheckUpdateViewModel.cs | 323 ++++++++++++++++++ .../ViewModels/MainWindowViewModel.cs | 2 +- v2rayN/v2rayN/Views/CheckUpdateView.xaml | 112 ++++++ v2rayN/v2rayN/Views/CheckUpdateView.xaml.cs | 50 +++ v2rayN/v2rayN/Views/MainWindow.xaml | 23 +- v2rayN/v2rayN/Views/MainWindow.xaml.cs | 24 +- 10 files changed, 541 insertions(+), 39 deletions(-) create mode 100644 v2rayN/ServiceLib/Models/CheckUpdateItem.cs create mode 100644 v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs create mode 100644 v2rayN/v2rayN/Views/CheckUpdateView.xaml create mode 100644 v2rayN/v2rayN/Views/CheckUpdateView.xaml.cs diff --git a/v2rayN/ServiceLib/Common/DownloaderHelper.cs b/v2rayN/ServiceLib/Common/DownloaderHelper.cs index b41640392d..b08286e796 100644 --- a/v2rayN/ServiceLib/Common/DownloaderHelper.cs +++ b/v2rayN/ServiceLib/Common/DownloaderHelper.cs @@ -168,6 +168,10 @@ public async Task DownloadFileAsync(IWebProxy? webProxy, string url, string file { progress.Report(101); } + else if (value.Error != null) + { + throw value.Error; + } } }; diff --git a/v2rayN/ServiceLib/Enums/EViewAction.cs b/v2rayN/ServiceLib/Enums/EViewAction.cs index 88954a78b7..9992daa306 100644 --- a/v2rayN/ServiceLib/Enums/EViewAction.cs +++ b/v2rayN/ServiceLib/Enums/EViewAction.cs @@ -38,5 +38,7 @@ public enum EViewAction DispatcherReload, DispatcherRefreshServersBiz, DispatcherRefreshIcon, + DispatcherCheckUpdate, + DispatcherCheckUpdateFinished, } } \ No newline at end of file diff --git a/v2rayN/ServiceLib/Handler/UpdateHandler.cs b/v2rayN/ServiceLib/Handler/UpdateHandler.cs index 96b35ac601..15c49e9e6c 100644 --- a/v2rayN/ServiceLib/Handler/UpdateHandler.cs +++ b/v2rayN/ServiceLib/Handler/UpdateHandler.cs @@ -50,6 +50,7 @@ public void CheckUpdateGuiN(Config config, Action update, bool pre downloadHandle.Error += (sender2, args) => { _updateFunc(false, args.GetException().Message); + _updateFunc(false, ""); }; AbsoluteCompleted += (sender2, args) => { @@ -61,19 +62,20 @@ public void CheckUpdateGuiN(Config config, Action update, bool pre url = args.Url; AskToDownload(downloadHandle, url, true).ContinueWith(task => { - _updateFunc(false, url); + _updateFunc(false, ""); }); } else { _updateFunc(false, args.Msg); + _updateFunc(false, ""); } }; _updateFunc(false, string.Format(ResUI.MsgStartUpdating, ECoreType.v2rayN)); - CheckUpdateAsync(ECoreType.v2rayN, preRelease); + CheckUpdateAsync(downloadHandle, ECoreType.v2rayN, preRelease); } - public void CheckUpdateCore(ECoreType type, Config config, Action update, bool preRelease) + public async void CheckUpdateCore(ECoreType type, Config config, Action update, bool preRelease) { _config = config; _updateFunc = update; @@ -103,7 +105,8 @@ public void CheckUpdateCore(ECoreType type, Config config, Action }; downloadHandle.Error += (sender2, args) => { - _updateFunc(true, args.GetException().Message); + _updateFunc(false, args.GetException().Message); + _updateFunc(false, ""); }; AbsoluteCompleted += (sender2, args) => @@ -116,16 +119,17 @@ public void CheckUpdateCore(ECoreType type, Config config, Action url = args.Url; AskToDownload(downloadHandle, url, true).ContinueWith(task => { - _updateFunc(false, url); + _updateFunc(false, ""); }); } else { _updateFunc(false, args.Msg); + _updateFunc(false, ""); } }; _updateFunc(false, string.Format(ResUI.MsgStartUpdating, type)); - CheckUpdateAsync(type, preRelease); + CheckUpdateAsync(downloadHandle, type, preRelease); } public void UpdateSubscriptionProcess(Config config, string subId, bool blProxy, Action update) @@ -267,6 +271,7 @@ public void UpdateGeoFileAll(Config config, Action update) { await UpdateGeoFile("geosite", _config, update); await UpdateGeoFile("geoip", _config, update); + _updateFunc(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo")); }); } @@ -282,14 +287,14 @@ public void RunAvailabilityCheck(Action update) #region private - private async void CheckUpdateAsync(ECoreType type, bool preRelease) + private async void CheckUpdateAsync(DownloadHandler downloadHandle, ECoreType type, bool preRelease) { try { var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(type); string url = coreInfo.coreReleaseApiUrl; - var result = await (new DownloadHandler()).DownloadStringAsync(url, true, Global.AppName); + var result = await downloadHandle.DownloadStringAsync(url, true, Global.AppName); if (!Utils.IsNullOrEmpty(result)) { ResponseHandler(type, result, preRelease); @@ -483,7 +488,7 @@ private async Task AskToDownload(DownloadHandler downloadHandle, string url, boo //} //if (blDownload) //{ - await downloadHandle.DownloadFileAsync(url, true, 600); + await downloadHandle.DownloadFileAsync(url, true, 60); //} } diff --git a/v2rayN/ServiceLib/Models/CheckUpdateItem.cs b/v2rayN/ServiceLib/Models/CheckUpdateItem.cs new file mode 100644 index 0000000000..d3886d41bc --- /dev/null +++ b/v2rayN/ServiceLib/Models/CheckUpdateItem.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ServiceLib.Models +{ + public class CheckUpdateItem + { + public bool? isSelected { get; set; } + public string coreType { get; set; } + public string? remarks { get; set; } + public string? fileName { get; set; } + public bool? isFinished { get; set; } + } +} diff --git a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs new file mode 100644 index 0000000000..882b66844e --- /dev/null +++ b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs @@ -0,0 +1,323 @@ +using DynamicData; +using DynamicData.Binding; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using Splat; +using System.Diagnostics; +using System.Reactive; + +namespace ServiceLib.ViewModels +{ + public class CheckUpdateViewModel : MyReactiveObject + { + private const string _geo = "GeoFiles"; + private List _lstUpdated = []; + + private IObservableCollection _checkUpdateItem = new ObservableCollectionExtended(); + public IObservableCollection CheckUpdateItems => _checkUpdateItem; + public ReactiveCommand CheckUpdateCmd { get; } + [Reactive] public bool EnableCheckPreReleaseUpdate { get; set; } + [Reactive] public bool IsCheckUpdate { get; set; } + [Reactive] public bool AutoRun { get; set; } + + public CheckUpdateViewModel(Func>? updateView) + { + _config = LazyConfig.Instance.Config; + _updateView = updateView; + _noticeHandler = Locator.Current.GetService(); + + RefreshSubItems(); + + CheckUpdateCmd = ReactiveCommand.Create(() => + { + CheckUpdate(); + }); + EnableCheckPreReleaseUpdate = _config.guiItem.checkPreReleaseUpdate; + IsCheckUpdate = true; + + this.WhenAnyValue( + x => x.EnableCheckPreReleaseUpdate, + y => y == true) + .Subscribe(c => { _config.guiItem.checkPreReleaseUpdate = EnableCheckPreReleaseUpdate; }); + } + + private void RefreshSubItems() + { + _checkUpdateItem.Clear(); + + _checkUpdateItem.Add(new CheckUpdateItem() + { + isSelected = false, + coreType = ECoreType.v2rayN.ToString(), + remarks = ResUI.menuCheckUpdate, + }); + _checkUpdateItem.Add(new CheckUpdateItem() + { + isSelected = true, + coreType = ECoreType.Xray.ToString(), + remarks = ResUI.menuCheckUpdate, + }); + _checkUpdateItem.Add(new CheckUpdateItem() + { + isSelected = true, + coreType = ECoreType.mihomo.ToString(), + remarks = ResUI.menuCheckUpdate, + }); + _checkUpdateItem.Add(new CheckUpdateItem() + { + isSelected = true, + coreType = ECoreType.sing_box.ToString(), + remarks = ResUI.menuCheckUpdate, + }); + _checkUpdateItem.Add(new CheckUpdateItem() + { + isSelected = true, + coreType = _geo, + remarks = ResUI.menuCheckUpdate, + }); + } + + private void CheckUpdate() + { + _lstUpdated.Clear(); + + for (int k = _checkUpdateItem.Count - 1; k >= 0; k--) + { + var item = _checkUpdateItem[k]; + if (item.isSelected == true) + { + IsCheckUpdate = false; + _lstUpdated.Add(new CheckUpdateItem() { coreType = item.coreType }); + UpdateView(item.coreType, "..."); + if (item.coreType == _geo) + { + CheckUpdateGeo(); + } + else if (item.coreType == ECoreType.v2rayN.ToString()) + { + CheckUpdateN(EnableCheckPreReleaseUpdate); + } + else if (item.coreType == ECoreType.mihomo.ToString()) + { + CheckUpdateCore(item, false); + } + else + { + CheckUpdateCore(item, EnableCheckPreReleaseUpdate); + } + } + } + } + + private void UpdatedPlusPlus(string coreType, string fileName) + { + var item = _lstUpdated.FirstOrDefault(x => x.coreType == coreType); + if (item == null) + { + return; + } + item.isFinished = true; + if (!fileName.IsNullOrEmpty()) + { + item.fileName = fileName; + } + } + + private void CheckUpdateGeo() + { + void _updateUI(bool success, string msg) + { + UpdateView(_geo, msg); + if (success) + { + UpdatedPlusPlus(_geo, ""); + UpdateFinished(); + } + } + (new UpdateHandler()).UpdateGeoFileAll(_config, _updateUI); + } + + private void CheckUpdateN(bool preRelease) + { + //Check for standalone windows .Net version + if (Utils.IsWindows() + && File.Exists(Path.Combine(Utils.StartupPath(), "wpfgfx_cor3.dll")) + && File.Exists(Path.Combine(Utils.StartupPath(), "D3DCompiler_47_cor3.dll")) + ) + { + UpdateView(ResUI.UpdateStandalonePackageTip, ResUI.UpdateStandalonePackageTip); + return; + } + + void _updateUI(bool success, string msg) + { + if (success) + { + UpdateView(ECoreType.v2rayN.ToString(), ResUI.OperationSuccess); + UpdatedPlusPlus(ECoreType.v2rayN.ToString(), msg); + UpdateFinished(); + } + else + { + if (msg.IsNullOrEmpty()) + { + UpdatedPlusPlus(ECoreType.v2rayN.ToString(), ""); + UpdateFinished(); + } + else + { + UpdateView(ECoreType.v2rayN.ToString(), msg); + } + } + } + (new UpdateHandler()).CheckUpdateGuiN(_config, _updateUI, preRelease); + } + + private void CheckUpdateCore(CheckUpdateItem item, bool preRelease) + { + void _updateUI(bool success, string msg) + { + if (success) + { + UpdateView(item.coreType, ResUI.MsgUpdateV2rayCoreSuccessfullyMore); + + UpdatedPlusPlus(item.coreType, msg); + UpdateFinished(); + } + else + { + if (msg.IsNullOrEmpty()) + { + UpdatedPlusPlus(item.coreType, ""); + UpdateFinished(); + } + else + { + UpdateView(item.coreType, msg); + } + } + } + var type = (ECoreType)Enum.Parse(typeof(ECoreType), item.coreType); + (new UpdateHandler()).CheckUpdateCore(type, _config, _updateUI, preRelease); + } + + private void UpdateFinished() + { + if (_lstUpdated.Count > 0 && _lstUpdated.Count(x => x.isFinished == true) == _lstUpdated.Count) + { + _updateView?.Invoke(EViewAction.DispatcherCheckUpdateFinished, false); + + UpgradeCore(); + + if (_lstUpdated.Any(x => x.coreType == ECoreType.v2rayN.ToString() && x.isFinished == true)) + { + UpgradeN(); + } + _updateView?.Invoke(EViewAction.DispatcherCheckUpdateFinished, true); + } + } + + public void UpdateFinishedResult(bool blReload) + { + if (blReload) + { + IsCheckUpdate = true; + Locator.Current.GetService()?.Reload(); + } + else + { + Locator.Current.GetService()?.CloseCore(); + } + } + + private void UpgradeN() + { + try + { + var fileName = _lstUpdated.FirstOrDefault(x => x.coreType == ECoreType.v2rayN.ToString())?.fileName; + if (fileName.IsNullOrEmpty()) + { + return; + } + + Process process = new() + { + StartInfo = new ProcessStartInfo + { + FileName = "v2rayUpgrade", + Arguments = fileName.AppendQuotes(), + WorkingDirectory = Utils.StartupPath() + } + }; + process.Start(); + if (process.Id > 0) + { + Locator.Current.GetService()?.MyAppExitAsync(false); + } + } + catch (Exception ex) + { + UpdateView(ECoreType.v2rayN.ToString(), ex.Message); + } + } + + private void UpgradeCore() + { + foreach (var item in _lstUpdated) + { + if (item.fileName.IsNullOrEmpty()) + { + continue; + } + + var fileName = Utils.GetTempPath(Utils.GetDownloadFileName(item.fileName)); + if (!File.Exists(fileName)) + { + continue; + } + string toPath = Utils.GetBinPath("", item.coreType); + + if (fileName.Contains(".tar.gz")) + { + //It's too complicated to unzip. TODO + } + else if (fileName.Contains(".gz")) + { + FileManager.UncompressedFile(fileName, toPath, item.coreType); + } + else + { + FileManager.ZipExtractToFile(fileName, toPath, _config.guiItem.ignoreGeoUpdateCore ? "geo" : ""); + } + + UpdateView(item.coreType, ResUI.MsgUpdateV2rayCoreSuccessfully); + + if (File.Exists(fileName)) + { + File.Delete(fileName); + } + } + } + + private void UpdateView(string coreType, string msg) + { + var item = new CheckUpdateItem() + { + coreType = coreType, + remarks = msg, + }; + _updateView?.Invoke(EViewAction.DispatcherCheckUpdate, item); + } + + public void UpdateViewResult(CheckUpdateItem item) + { + var found = _checkUpdateItem.FirstOrDefault(t => t.coreType == item.coreType); + if (found != null) + { + var itemCopy = JsonUtils.DeepCopy(found); + itemCopy.remarks = item.remarks; + _checkUpdateItem.Replace(found, itemCopy); + } + } + } +} \ No newline at end of file diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index bff2d462ff..7651f25c08 100644 --- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -852,7 +852,7 @@ await Task.Run(() => }); } - private void CloseCore() + public void CloseCore() { ConfigHandler.SaveConfig(_config, false); diff --git a/v2rayN/v2rayN/Views/CheckUpdateView.xaml b/v2rayN/v2rayN/Views/CheckUpdateView.xaml new file mode 100644 index 0000000000..e20138a3a5 --- /dev/null +++ b/v2rayN/v2rayN/Views/CheckUpdateView.xaml @@ -0,0 +1,112 @@ + + + + + + + + +