v2rayN/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs

344 lines
10 KiB
C#
Raw Normal View History

2025-01-30 09:10:05 +00:00
using System.Reactive;
2025-08-30 10:01:01 +00:00
using System.Reactive.Disposables;
using System.Reactive.Linq;
2025-01-30 09:10:05 +00:00
using System.Runtime.InteropServices;
2024-09-01 08:39:45 +00:00
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class CheckUpdateViewModel : MyReactiveObject
2024-09-01 08:39:45 +00:00
{
private const string _geo = "GeoFiles";
2025-08-31 07:41:25 +00:00
private readonly string _v2rayN = ECoreType.v2rayN.ToString();
private List<CheckUpdateModel> _lstUpdated = [];
2025-08-31 07:41:25 +00:00
private static readonly string _tag = "CheckUpdateViewModel";
2024-09-01 08:39:45 +00:00
2025-08-31 07:41:25 +00:00
public IObservableCollection<CheckUpdateModel> CheckUpdateModels { get; } = new ObservableCollectionExtended<CheckUpdateModel>();
public ReactiveCommand<Unit, Unit> CheckUpdateCmd { get; }
[Reactive] public bool EnableCheckPreReleaseUpdate { get; set; }
2024-09-01 08:39:45 +00:00
public CheckUpdateViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
{
2025-08-17 09:31:55 +00:00
_config = AppManager.Instance.Config;
_updateView = updateView;
2025-08-31 07:41:25 +00:00
CheckUpdateCmd = ReactiveCommand.CreateFromTask(CheckUpdate);
CheckUpdateCmd.ThrownExceptions.Subscribe(ex =>
2024-09-01 08:39:45 +00:00
{
2025-08-31 07:41:25 +00:00
Logging.SaveLog(_tag, ex);
_ = UpdateView(_v2rayN, ex.Message);
});
2024-09-01 08:39:45 +00:00
EnableCheckPreReleaseUpdate = _config.CheckUpdateItem.CheckPreReleaseUpdate;
2024-10-21 05:46:13 +00:00
this.WhenAnyValue(
x => x.EnableCheckPreReleaseUpdate,
y => y == true)
2025-09-20 06:06:41 +00:00
.Subscribe(c => _config.CheckUpdateItem.CheckPreReleaseUpdate = EnableCheckPreReleaseUpdate);
2024-09-01 08:39:45 +00:00
RefreshCheckUpdateItems();
}
2024-10-21 05:46:13 +00:00
private void RefreshCheckUpdateItems()
{
2025-08-31 07:41:25 +00:00
CheckUpdateModels.Clear();
2024-09-01 08:39:45 +00:00
if (RuntimeInformation.ProcessArchitecture != Architecture.X86)
2024-09-01 08:39:45 +00:00
{
2025-08-31 07:41:25 +00:00
CheckUpdateModels.Add(GetCheckUpdateModel(_v2rayN));
//Not Windows and under Win10
if (!(Utils.IsWindows() && Environment.OSVersion.Version.Major < 10))
2024-09-01 08:39:45 +00:00
{
2025-08-31 07:41:25 +00:00
CheckUpdateModels.Add(GetCheckUpdateModel(ECoreType.Xray.ToString()));
CheckUpdateModels.Add(GetCheckUpdateModel(ECoreType.mihomo.ToString()));
CheckUpdateModels.Add(GetCheckUpdateModel(ECoreType.sing_box.ToString()));
2024-10-24 02:32:19 +00:00
}
2024-11-04 06:15:59 +00:00
}
2025-08-31 07:41:25 +00:00
CheckUpdateModels.Add(GetCheckUpdateModel(_geo));
}
2024-10-24 02:32:19 +00:00
private CheckUpdateModel GetCheckUpdateModel(string coreType)
{
if (coreType == _v2rayN && Utils.IsPackagedInstall())
{
return new()
{
IsSelected = false,
CoreType = coreType,
Remarks = ResUI.menuCheckUpdate + " (Not Support)",
};
}
return new()
2024-11-04 06:15:59 +00:00
{
IsSelected = _config.CheckUpdateItem.SelectedCoreTypes?.Contains(coreType) ?? true,
CoreType = coreType,
Remarks = ResUI.menuCheckUpdate,
};
}
2024-11-04 06:15:59 +00:00
private async Task SaveSelectedCoreTypes()
{
2025-08-31 07:41:25 +00:00
_config.CheckUpdateItem.SelectedCoreTypes = CheckUpdateModels.Where(t => t.IsSelected == true).Select(t => t.CoreType ?? "").ToList();
await ConfigHandler.SaveConfig(_config);
}
2024-09-01 08:39:45 +00:00
private async Task CheckUpdate()
{
await Task.Run(CheckUpdateTask);
}
2025-03-07 04:11:19 +00:00
private async Task CheckUpdateTask()
{
_lstUpdated.Clear();
2025-08-31 07:41:25 +00:00
_lstUpdated = CheckUpdateModels.Where(x => x.IsSelected == true)
.Select(x => new CheckUpdateModel() { CoreType = x.CoreType }).ToList();
await SaveSelectedCoreTypes();
2025-08-31 07:41:25 +00:00
for (var k = CheckUpdateModels.Count - 1; k >= 0; k--)
2024-09-01 08:39:45 +00:00
{
2025-08-31 07:41:25 +00:00
var item = CheckUpdateModels[k];
if (item.IsSelected != true)
{
continue;
}
2024-09-01 08:39:45 +00:00
await UpdateView(item.CoreType, "...");
if (item.CoreType == _geo)
2024-09-01 08:39:45 +00:00
{
await CheckUpdateGeo();
2024-09-01 08:39:45 +00:00
}
else if (item.CoreType == _v2rayN)
2024-09-01 08:39:45 +00:00
{
if (Utils.IsPackagedInstall())
{
await UpdateView(_v2rayN, "Not Support");
continue;
}
await CheckUpdateN(EnableCheckPreReleaseUpdate);
2024-09-01 08:39:45 +00:00
}
else if (item.CoreType == ECoreType.Xray.ToString())
2024-09-01 08:39:45 +00:00
{
await CheckUpdateCore(item, EnableCheckPreReleaseUpdate);
2024-09-01 08:39:45 +00:00
}
else
{
await CheckUpdateCore(item, false);
}
}
await UpdateFinished();
}
private void UpdatedPlusPlus(string coreType, string fileName)
{
var item = _lstUpdated.FirstOrDefault(x => x.CoreType == coreType);
if (item == null)
{
return;
2024-09-01 08:39:45 +00:00
}
item.IsFinished = true;
if (!fileName.IsNullOrEmpty())
{
item.FileName = fileName;
}
}
2024-09-01 08:39:45 +00:00
private async Task CheckUpdateGeo()
{
async Task _updateUI(bool success, string msg)
2024-09-01 08:39:45 +00:00
{
await UpdateView(_geo, msg);
if (success)
2024-09-01 08:39:45 +00:00
{
UpdatedPlusPlus(_geo, "");
2024-09-01 08:39:45 +00:00
}
}
2025-09-20 06:06:41 +00:00
await new UpdateService().UpdateGeoFileAll(_config, _updateUI)
.ContinueWith(t => UpdatedPlusPlus(_geo, ""));
}
2024-09-01 08:39:45 +00:00
private async Task CheckUpdateN(bool preRelease)
{
async Task _updateUI(bool success, string msg)
2024-09-01 08:39:45 +00:00
{
await UpdateView(_v2rayN, msg);
if (success)
2024-09-01 08:39:45 +00:00
{
await UpdateView(_v2rayN, ResUI.OperationSuccess);
UpdatedPlusPlus(_v2rayN, msg);
2024-09-01 08:39:45 +00:00
}
}
2025-09-20 06:06:41 +00:00
await new UpdateService().CheckUpdateGuiN(_config, _updateUI, preRelease)
.ContinueWith(t => UpdatedPlusPlus(_v2rayN, ""));
}
2024-09-01 08:39:45 +00:00
private async Task CheckUpdateCore(CheckUpdateModel model, bool preRelease)
{
async Task _updateUI(bool success, string msg)
2024-09-01 08:39:45 +00:00
{
await UpdateView(model.CoreType, msg);
if (success)
2024-09-01 08:39:45 +00:00
{
await UpdateView(model.CoreType, ResUI.MsgUpdateV2rayCoreSuccessfullyMore);
2024-09-01 08:39:45 +00:00
UpdatedPlusPlus(model.CoreType, msg);
2024-09-01 08:39:45 +00:00
}
}
var type = (ECoreType)Enum.Parse(typeof(ECoreType), model.CoreType);
2025-09-20 06:06:41 +00:00
await new UpdateService().CheckUpdateCore(type, _config, _updateUI, preRelease)
.ContinueWith(t => UpdatedPlusPlus(model.CoreType, ""));
}
2024-09-01 08:39:45 +00:00
private async Task UpdateFinished()
{
if (_lstUpdated.Count > 0 && _lstUpdated.Count(x => x.IsFinished == true) == _lstUpdated.Count)
2024-09-01 08:39:45 +00:00
{
2025-08-30 10:01:01 +00:00
await UpdateFinishedSub(false);
await Task.Delay(2000);
await UpgradeCore();
2024-09-01 08:39:45 +00:00
if (_lstUpdated.Any(x => x.CoreType == _v2rayN && x.IsFinished == true))
{
2024-10-04 09:10:30 +00:00
await Task.Delay(1000);
await UpgradeN();
2024-09-01 08:39:45 +00:00
}
await Task.Delay(1000);
2025-08-30 10:01:01 +00:00
await UpdateFinishedSub(true);
2024-09-01 08:39:45 +00:00
}
}
2024-09-01 08:39:45 +00:00
2025-08-30 10:01:01 +00:00
private async Task UpdateFinishedSub(bool blReload)
{
RxApp.MainThreadScheduler.Schedule(blReload, (scheduler, blReload) =>
{
_ = UpdateFinishedResult(blReload);
return Disposable.Empty;
});
}
public async Task UpdateFinishedResult(bool blReload)
{
if (blReload)
2024-09-01 08:39:45 +00:00
{
AppEvents.ReloadRequested.OnNext(Unit.Default);
2024-09-01 08:39:45 +00:00
}
else
{
await CoreManager.Instance.CoreStop();
}
}
2024-09-01 08:39:45 +00:00
private async Task UpgradeN()
{
try
2024-09-01 08:39:45 +00:00
{
var fileName = _lstUpdated.FirstOrDefault(x => x.CoreType == _v2rayN)?.FileName;
if (fileName.IsNullOrEmpty())
2024-09-01 08:39:45 +00:00
{
return;
2024-09-01 08:39:45 +00:00
}
2025-09-02 09:12:38 +00:00
if (!Utils.UpgradeAppExists(out var upgradeFileName))
2024-09-01 08:39:45 +00:00
{
await UpdateView(_v2rayN, ResUI.UpgradeAppNotExistTip);
2025-09-02 09:12:38 +00:00
NoticeManager.Instance.SendMessageAndEnqueue(ResUI.UpgradeAppNotExistTip);
Logging.SaveLog("UpgradeApp does not exist");
return;
2024-09-01 08:39:45 +00:00
}
2025-09-02 09:12:38 +00:00
var id = ProcUtils.ProcessStart(upgradeFileName, fileName, Utils.StartupPath());
if (id > 0)
{
await AppManager.Instance.AppExitAsync(true);
}
}
catch (Exception ex)
{
await UpdateView(_v2rayN, ex.Message);
2024-09-01 08:39:45 +00:00
}
}
2024-09-01 08:39:45 +00:00
private async Task UpgradeCore()
{
foreach (var item in _lstUpdated)
2024-09-01 08:39:45 +00:00
{
if (item.FileName.IsNullOrEmpty())
2024-09-01 08:39:45 +00:00
{
continue;
}
2024-09-01 08:39:45 +00:00
var fileName = item.FileName;
if (!File.Exists(fileName))
{
continue;
}
var toPath = Utils.GetBinPath("", item.CoreType);
2024-09-01 08:39:45 +00:00
if (fileName.Contains(".tar.gz"))
{
FileManager.DecompressTarFile(fileName, toPath);
var dir = new DirectoryInfo(toPath);
if (dir.Exists)
2024-09-01 08:39:45 +00:00
{
foreach (var subDir in dir.GetDirectories())
2024-10-17 07:28:32 +00:00
{
FileManager.CopyDirectory(subDir.FullName, toPath, false, true);
subDir.Delete(true);
2024-10-17 07:28:32 +00:00
}
2024-09-01 08:39:45 +00:00
}
}
else if (fileName.Contains(".gz"))
{
FileManager.DecompressFile(fileName, toPath, item.CoreType);
}
else
{
FileManager.ZipExtractToFile(fileName, toPath, "geo");
}
2024-09-01 08:39:45 +00:00
if (Utils.IsNonWindows())
{
2025-09-20 06:06:41 +00:00
var filesList = new DirectoryInfo(toPath).GetFiles().Select(u => u.FullName).ToList();
foreach (var file in filesList)
{
await Utils.SetLinuxChmod(Path.Combine(toPath, item.CoreType.ToLower()));
}
}
await UpdateView(item.CoreType, ResUI.MsgUpdateV2rayCoreSuccessfully);
2024-09-01 08:39:45 +00:00
if (File.Exists(fileName))
{
File.Delete(fileName);
2024-09-01 08:39:45 +00:00
}
}
}
2024-09-01 08:39:45 +00:00
private async Task UpdateView(string coreType, string msg)
{
var item = new CheckUpdateModel()
2024-09-01 08:39:45 +00:00
{
CoreType = coreType,
Remarks = msg,
};
2025-08-30 10:01:01 +00:00
RxApp.MainThreadScheduler.Schedule(item, (scheduler, model) =>
{
_ = UpdateViewResult(model);
return Disposable.Empty;
});
}
2024-09-01 08:39:45 +00:00
2025-08-30 10:01:01 +00:00
public async Task UpdateViewResult(CheckUpdateModel model)
{
2025-08-31 07:41:25 +00:00
var found = CheckUpdateModels.FirstOrDefault(t => t.CoreType == model.CoreType);
if (found == null)
{
return;
}
2025-09-10 11:43:11 +00:00
found.Remarks = model.Remarks;
2024-09-01 08:39:45 +00:00
}
2025-01-30 09:10:05 +00:00
}