diff --git a/MAA.sln.DotSettings b/MAA.sln.DotSettings
index d895a48022..6fa9bc7786 100644
--- a/MAA.sln.DotSettings
+++ b/MAA.sln.DotSettings
@@ -30,6 +30,7 @@
SSS
WSA
XYAZ
+ True
True
True
True
@@ -95,6 +96,7 @@
True
True
True
+ True
True
True
True
@@ -123,6 +125,7 @@
True
True
True
+ True
True
True
True
diff --git a/src/MaaWpfGui/Constants/MaaUrls.cs b/src/MaaWpfGui/Constants/MaaUrls.cs
index 75e78ae4b7..e223512d10 100644
--- a/src/MaaWpfGui/Constants/MaaUrls.cs
+++ b/src/MaaWpfGui/Constants/MaaUrls.cs
@@ -88,6 +88,7 @@ namespace MaaWpfGui.Constants
public const string MirrorChyanWebsite = "https://mirrorc.top";
public const string MirrorChyanLine2 = "http://47.96.114.138:8800";
+ public const string MirrorChyanAppUpdate = $"{MirrorChyanWebsite}/api/resources/MAA/latest";
public const string MirrorChyanResourceUpdate = $"{MirrorChyanWebsite}/api/resources/MaaResource/latest";
}
}
diff --git a/src/MaaWpfGui/Extensions/StringExtensions.cs b/src/MaaWpfGui/Extensions/StringExtensions.cs
new file mode 100644
index 0000000000..af54eba4e1
--- /dev/null
+++ b/src/MaaWpfGui/Extensions/StringExtensions.cs
@@ -0,0 +1,37 @@
+//
+// MaaWpfGui - A part of the MaaCoreArknights project
+// Copyright (C) 2021 MistEO and Contributors
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License v3.0 only as published by
+// the Free Software Foundation, either version 3 of the License, or
+// any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY
+//
+namespace MaaWpfGui.Extensions
+{
+ public static class StringExtensions
+ {
+ ///
+ /// Mask the string with a visible start and end.
+ ///
+ /// The string to be masked.
+ /// The number of characters to remain visible at the start of the string.
+ /// The number of characters to remain visible at the end of the string.
+ /// The character used for masking.
+ /// A masked string with the specified number of visible characters at the start and end.
+ public static string Mask(this string input, int visibleStartLength = 3, int visibleEndLength = 3, char maskChar = '*')
+ {
+ if (input.Length <= visibleStartLength + visibleEndLength)
+ {
+ return new(maskChar, input.Length);
+ }
+
+ var maskLength = input.Length - visibleStartLength - visibleEndLength;
+ var maskedPart = new string(maskChar, maskLength);
+ return input[..visibleStartLength] + maskedPart + input[^visibleEndLength..];
+ }
+ }
+}
diff --git a/src/MaaWpfGui/Models/ResourceUpdater.cs b/src/MaaWpfGui/Models/ResourceUpdater.cs
index fffa8c2aab..52d1081fee 100644
--- a/src/MaaWpfGui/Models/ResourceUpdater.cs
+++ b/src/MaaWpfGui/Models/ResourceUpdater.cs
@@ -22,6 +22,7 @@ using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using MaaWpfGui.Constants;
+using MaaWpfGui.Extensions;
using MaaWpfGui.Helper;
using MaaWpfGui.Main;
using MaaWpfGui.ViewModels;
@@ -86,7 +87,7 @@ namespace MaaWpfGui.Models
// 只有 Release 版本才会检查更新
// ReSharper disable once UnusedMember.Global
- public static async void UpdateAndToastAsync()
+ public static async Task UpdateAndToastAsync()
{
var ret = await UpdateAsync();
@@ -496,7 +497,7 @@ namespace MaaWpfGui.Models
var url = $"{MaaUrls.MirrorChyanResourceUpdate}?current_version={currentVersion}&cdk={cdk}&user_agent=MaaWpfGui";
var response = await Instances.HttpService.GetAsync(new(url), logUri: false);
- _logger.Information($"current_version: {currentVersion}, cdk: {((cdk.Length > 6) ? (cdk[..3] + new string('*', cdk.Length - 6) + cdk[^3..]) : cdk)}");
+ _logger.Information($"current_version: {currentVersion}, cdk: {cdk.Mask()}");
if (response is null)
{
@@ -546,9 +547,9 @@ namespace MaaWpfGui.Models
return (CheckUpdateRetT.AlreadyLatest, null);
}
+ // 到这里已经确定有新版本了
_logger.Information($"New version found: {version}");
- // 到这里已经确定有新版本了
if (string.IsNullOrEmpty(cdk))
{
ToastNotification.ShowDirect(LocalizationHelper.GetString("MirrorChyanResourceUpdateTip"));
@@ -706,10 +707,10 @@ namespace MaaWpfGui.Models
file.CopyTo(tempPath, true); // 覆盖现有文件
}
- foreach (DirectoryInfo subdir in dirs)
+ foreach (DirectoryInfo subDir in dirs)
{
- string tempPath = Path.Combine(destDirName, subdir.Name);
- DirectoryMerge(subdir.FullName, tempPath);
+ string tempPath = Path.Combine(destDirName, subDir.Name);
+ DirectoryMerge(subDir.FullName, tempPath);
}
}
}
diff --git a/src/MaaWpfGui/Services/Web/HttpService.cs b/src/MaaWpfGui/Services/Web/HttpService.cs
index a28cea0dc3..8fe04b1d80 100755
--- a/src/MaaWpfGui/Services/Web/HttpService.cs
+++ b/src/MaaWpfGui/Services/Web/HttpService.cs
@@ -231,7 +231,7 @@ namespace MaaWpfGui.Services.Web
try
{
var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
- using (var tempFileStream = new FileStream(fullFilePathWithTemp, FileMode.Create, FileAccess.Write))
+ await using (var tempFileStream = new FileStream(fullFilePathWithTemp, FileMode.Create, FileAccess.Write))
{
// 记录初始化
long value = 0;
diff --git a/src/MaaWpfGui/ViewModels/UI/RootViewModel.cs b/src/MaaWpfGui/ViewModels/UI/RootViewModel.cs
index 6773d4e0a8..9960eb7e06 100644
--- a/src/MaaWpfGui/ViewModels/UI/RootViewModel.cs
+++ b/src/MaaWpfGui/ViewModels/UI/RootViewModel.cs
@@ -19,7 +19,7 @@ using HandyControl.Tools;
using MaaWpfGui.Constants;
using MaaWpfGui.Helper;
using MaaWpfGui.Main;
-using Microsoft.VisualBasic.Logging;
+using MaaWpfGui.ViewModels.UserControl.Settings;
using Microsoft.WindowsAPICodePack.Taskbar;
using Stylet;
@@ -42,7 +42,7 @@ namespace MaaWpfGui.ViewModels.UI
InitViewModels();
InitProxy();
- if (SettingsViewModel.VersionUpdateSettings.UpdateNightly && !SettingsViewModel.VersionUpdateSettings.HasAcknowledgedNightlyWarning)
+ if (SettingsViewModel.VersionUpdateSettings.VersionType == VersionUpdateSettingsUserControlModel.UpdateVersionType.Nightly && !SettingsViewModel.VersionUpdateSettings.HasAcknowledgedNightlyWarning)
{
MessageBoxHelper.Show(LocalizationHelper.GetString("NightlyWarning"));
}
@@ -66,7 +66,7 @@ namespace MaaWpfGui.ViewModels.UI
}
});
- Instances.VersionUpdateViewModel.ShowUpdateOrDownload();
+ _ = Instances.VersionUpdateViewModel.ShowUpdateOrDownload();
}
private static async void InitProxy()
diff --git a/src/MaaWpfGui/ViewModels/UI/VersionUpdateViewModel.cs b/src/MaaWpfGui/ViewModels/UI/VersionUpdateViewModel.cs
index 7de0be6eaa..d7a6f89c28 100644
--- a/src/MaaWpfGui/ViewModels/UI/VersionUpdateViewModel.cs
+++ b/src/MaaWpfGui/ViewModels/UI/VersionUpdateViewModel.cs
@@ -15,7 +15,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
-using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
@@ -23,14 +22,14 @@ using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
-using HandyControl.Controls;
-using HandyControl.Data;
using MaaWpfGui.Constants;
+using MaaWpfGui.Extensions;
using MaaWpfGui.Helper;
using MaaWpfGui.Main;
using MaaWpfGui.Models;
using MaaWpfGui.Services;
using MaaWpfGui.States;
+using MaaWpfGui.ViewModels.UserControl.Settings;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Semver;
@@ -176,6 +175,10 @@ public class VersionUpdateViewModel : Screen
private JObject? _latestJson;
private JObject? _assetsObject;
+ private string? _mirrorcDownloadUrl;
+ private string? _mirrorcVersionName;
+ private string? _mirrorcReleaseNote;
+
///
/// 检查是否有已下载的更新包
///
@@ -224,9 +227,29 @@ public class VersionUpdateViewModel : Screen
}
string removeListFile = Path.Combine(extractDir, "removelist.txt");
+ string[] removeList = [];
if (File.Exists(removeListFile))
{
- string[] removeList = File.ReadAllLines(removeListFile);
+ removeList = File.ReadAllLines(removeListFile);
+ }
+
+ string mirrorChyanChangeFile = Path.Combine(extractDir, "changes.json");
+ if (File.Exists(mirrorChyanChangeFile))
+ {
+ try
+ {
+ string json = File.ReadAllText(mirrorChyanChangeFile);
+ var jObject = JObject.Parse(json);
+ removeList = jObject["deleted"]?.ToObject() ?? [];
+ }
+ catch (Exception e)
+ {
+ _logger.Error($"parse mirrorChyan changes.json error: {e.Message}");
+ }
+ }
+
+ if (removeList.Length > 0)
+ {
foreach (string file in removeList)
{
string path = Path.Combine(curDir, file);
@@ -392,6 +415,19 @@ public class VersionUpdateViewModel : Screen
OnlyGameResourceUpdated,
}
+ public enum AppUpdateSource
+ {
+ ///
+ /// Maa API
+ ///
+ MaaApi,
+
+ ///
+ /// MirrorChyan
+ ///
+ MirrorChyan,
+ }
+
// ReSharper disable once IdentifierTypo
// ReSharper disable once UnusedMember.Global
public enum Downloader
@@ -420,7 +456,8 @@ public class VersionUpdateViewModel : Screen
///
/// 如果是在更新后第一次启动,显示ReleaseNote弹窗,否则检查更新并下载更新包。
///
- public async void ShowUpdateOrDownload()
+ /// Task
+ public async Task ShowUpdateOrDownload()
{
if (IsFirstBootAfterUpdate)
{
@@ -448,7 +485,6 @@ public class VersionUpdateViewModel : Screen
}
else
{
- // var ret = await CheckAndDownloadUpdate();
// await ResourceUpdater.CheckAndDownloadUpdate();
// 跑个空任务避免 async warning
await Task.Run(() => { });
@@ -470,200 +506,243 @@ public class VersionUpdateViewModel : Screen
/// 操作成功返回 ,反之则返回 。
private async Task CheckAndDownloadVersionUpdate()
{
- var checkResult = await CheckUpdateInner();
-
- return checkResult;
-
- async Task CheckUpdateInner()
+ // 检查更新
+ var (checkRet, source) = await CheckUpdate();
+ if (checkRet != CheckUpdateRetT.OK)
{
- // 检查更新
- var checkRet = await CheckUpdate();
- if (checkRet != CheckUpdateRetT.OK)
+ return checkRet;
+ }
+
+ return source switch
+ {
+ AppUpdateSource.MaaApi => await HandleUpdateFromMaaApi(),
+ AppUpdateSource.MirrorChyan => await HandleUpdateFromMirrorChyan(),
+ _ => CheckUpdateRetT.UnknownError,
+ };
+ }
+
+ private async Task HandleUpdateFromMaaApi()
+ {
+ // 保存新版本的信息
+ var name = _latestJson?["name"]?.ToString();
+ UpdateTag = string.IsNullOrEmpty(name) ? (_latestJson?["tag_name"]?.ToString() ?? string.Empty) : name;
+ var body = _latestJson?["body"]?.ToString() ?? string.Empty;
+ if (string.IsNullOrEmpty(body))
+ {
+ var curHash = ComparableHash(_curVersion);
+ var latestHash = ComparableHash(_latestVersion);
+
+ if (curHash != null && latestHash != null)
{
- return checkRet;
- }
-
- // 保存新版本的信息
- var name = _latestJson?["name"]?.ToString();
- UpdateTag = string.IsNullOrEmpty(name) ? (_latestJson?["tag_name"]?.ToString() ?? string.Empty) : name;
- var body = _latestJson?["body"]?.ToString() ?? string.Empty;
- if (string.IsNullOrEmpty(body))
- {
- var curHash = ComparableHash(_curVersion);
- var latestHash = ComparableHash(_latestVersion);
-
- if (curHash != null && latestHash != null)
- {
- body = $"**Full Changelog**: [{curHash} -> {latestHash}](https://github.com/MaaAssistantArknights/MaaAssistantArknights/compare/{curHash}...{latestHash})";
- }
- }
-
- UpdateInfo = body;
- UpdateUrl = _latestJson?["html_url"]?.ToString() ?? string.Empty;
-
- bool otaFound = _assetsObject != null;
- bool goDownload = otaFound && SettingsViewModel.VersionUpdateSettings.AutoDownloadUpdatePackage;
- (string text, Action action) = (
- LocalizationHelper.GetString("NewVersionFoundButtonGoWebpage"),
- () =>
- {
- if (!string.IsNullOrWhiteSpace(UpdateUrl))
- {
- Process.Start(new ProcessStartInfo(UpdateUrl) { UseShellExecute = true });
- }
- }
- );
-
- {
- using var toast = new ToastNotification((otaFound ? LocalizationHelper.GetString("NewVersionFoundTitle") : LocalizationHelper.GetString("NewVersionFoundButNoPackageTitle")) + " : " + UpdateTag);
- if (goDownload)
- {
- OutputDownloadProgress(downloading: false, output: LocalizationHelper.GetString("NewVersionDownloadPreparing"));
- toast.AppendContentText(LocalizationHelper.GetString("NewVersionFoundDescDownloading"));
- }
-
- if (!otaFound)
- {
- toast.AppendContentText(LocalizationHelper.GetString("NewVersionFoundButNoPackageDesc"));
- }
-
- int count = 0;
- foreach (var line in UpdateInfo.Split('\n'))
- {
- if (line.StartsWith("#") || string.IsNullOrWhiteSpace(line))
- {
- continue;
- }
-
- toast.AppendContentText(line);
- if (++count >= 10)
- {
- break;
- }
- }
-
- toast.AddButton(text, ToastNotification.GetActionTagForOpenWeb(UpdateUrl));
- toast.ShowUpdateVersion();
- }
-
- UpdatePackageName = _assetsObject?["name"]?.ToString() ?? string.Empty;
-
- if (!goDownload || string.IsNullOrWhiteSpace(UpdatePackageName))
- {
- OutputDownloadProgress(string.Empty);
- return CheckUpdateRetT.NoNeedToUpdate;
- }
-
- if (_assetsObject == null)
- {
- return CheckUpdateRetT.FailedToGetInfo;
- }
-
- string? rawUrl = _assetsObject["browser_download_url"]?.ToString();
- var mirrors = _assetsObject["mirrors"]?.ToObject>();
-
- var urls = new List();
- if (mirrors != null)
- {
- urls.AddRange(mirrors);
- }
-
- // 负载均衡
- // var rand = new Random();
- // urls = urls.OrderBy(_ => rand.Next()).ToList();
- if (rawUrl != null)
- {
- urls.Add(rawUrl);
- }
-
- _logger.Information("Start test legacy download urls");
-
- // run latency test parallel
- var tasks = urls.ConvertAll(url => Instances.HttpService.HeadAsync(new Uri(url)));
- var latencies = await Task.WhenAll(tasks);
-
- var proxy = ConfigurationHelper.GetValue(ConfigurationKeys.UpdateProxy, string.Empty);
- var hasProxy = !string.IsNullOrEmpty(proxy);
-
- // select the fastest mirror
- _logger.Information("Selecting the fastest mirror:");
- var selected = 0;
- for (int i = 0; i < latencies.Length; i++)
- {
- // ReSharper disable once StringLiteralTypo
- var isInChina = urls[i].Contains("s3.maa-org.net") || urls[i].Contains("maa-ota.annangela.cn");
-
- if (latencies[i] < 0)
- {
- _logger.Warning("\turl: {CDNUrl} not available", urls[i]);
- continue;
- }
-
- _logger.Information("\turl: {CDNUrl}, legacy: {1:0.00}ms", urls[i], latencies[i]);
-
- if (hasProxy && isInChina)
- {
- // 如果设置了代理,国内镜像的延迟加上一个固定值
- latencies[i] += 6480;
- }
-
- if (latencies[selected] < 0 || (latencies[i] >= 0 && latencies[i] < latencies[selected]))
- {
- selected = i;
- }
- }
-
- if (latencies[selected] < 0)
- {
- _logger.Error("All mirrors are not available");
- return CheckUpdateRetT.NetworkError;
- }
-
- _logger.Information("Selected mirror: {CDNUrl}", urls[selected]);
-
- var downloaded = await DownloadGithubAssets(urls[selected], _assetsObject);
- if (downloaded)
- {
- OutputDownloadProgress(downloading: false, output: LocalizationHelper.GetString("NewVersionDownloadCompletedTitle"));
- }
- else
- {
- OutputDownloadProgress(downloading: false, output: LocalizationHelper.GetString("NewVersionDownloadFailedTitle"));
- {
- var toast = new ToastNotification(LocalizationHelper.GetString("NewVersionDownloadFailedTitle"));
- toast.AppendContentText(LocalizationHelper.GetString("NewVersionDownloadFailedDesc"))
- .AddButton(text, ToastNotification.GetActionTagForOpenWeb(UpdateUrl))
- .Show();
- }
-
- return CheckUpdateRetT.NoNeedToUpdate;
- }
-
- return CheckUpdateRetT.OK;
-
- string? ComparableHash(string version)
- {
- if (IsStdVersion(version))
- {
- return version;
- }
- else if (SemVersion.TryParse(version, SemVersionStyles.AllowLowerV, out var semVersion) &&
- IsNightlyVersion(semVersion))
- {
- // v4.6.6-1.g{Hash}
- // v4.6.7-beta.2.8.g{Hash}
- var commitHash = semVersion.PrereleaseIdentifiers.Last().ToString();
- if (commitHash.StartsWith("g"))
- {
- commitHash = commitHash.Remove(0, 1);
- }
-
- return commitHash;
- }
-
- return null;
+ body = $"**Full Changelog**: [{curHash} -> {latestHash}](https://github.com/MaaAssistantArknights/MaaAssistantArknights/compare/{curHash}...{latestHash})";
}
}
+
+ UpdateInfo = body;
+ UpdateUrl = _latestJson?["html_url"]?.ToString() ?? string.Empty;
+
+ bool otaFound = _assetsObject != null;
+ bool goDownload = otaFound && SettingsViewModel.VersionUpdateSettings.AutoDownloadUpdatePackage;
+
+ ShowUpdateInfo(otaFound, LocalizationHelper.GetString("NewVersionFoundButtonGoWebpage"));
+
+ UpdatePackageName = _assetsObject?["name"]?.ToString() ?? string.Empty;
+
+ if (!goDownload || string.IsNullOrWhiteSpace(UpdatePackageName))
+ {
+ OutputDownloadProgress(string.Empty);
+ return CheckUpdateRetT.NoNeedToUpdate;
+ }
+
+ if (_assetsObject == null)
+ {
+ return CheckUpdateRetT.FailedToGetInfo;
+ }
+
+ string? rawUrl = _assetsObject["browser_download_url"]?.ToString();
+ var mirrors = _assetsObject["mirrors"]?.ToObject>();
+
+ var urls = new List();
+ if (mirrors != null)
+ {
+ urls.AddRange(mirrors);
+ }
+
+ // 负载均衡
+ // var rand = new Random();
+ // urls = urls.OrderBy(_ => rand.Next()).ToList();
+ if (rawUrl != null)
+ {
+ urls.Add(rawUrl);
+ }
+
+ _logger.Information("Start test legacy download urls");
+
+ // run latency test parallel
+ var tasks = urls.ConvertAll(url => Instances.HttpService.HeadAsync(new Uri(url)));
+ var latencies = await Task.WhenAll(tasks);
+
+ var proxy = ConfigurationHelper.GetValue(ConfigurationKeys.UpdateProxy, string.Empty);
+ var hasProxy = !string.IsNullOrEmpty(proxy);
+
+ // select the fastest mirror
+ _logger.Information("Selecting the fastest mirror:");
+ var selected = 0;
+ for (int i = 0; i < latencies.Length; i++)
+ {
+ // ReSharper disable once StringLiteralTypo
+ var isInChina = urls[i].Contains("s3.maa-org.net") || urls[i].Contains("maa-ota.annangela.cn");
+
+ if (latencies[i] < 0)
+ {
+ _logger.Warning("\turl: {CDNUrl} not available", urls[i]);
+ continue;
+ }
+
+ _logger.Information("\turl: {CDNUrl}, legacy: {1:0.00}ms", urls[i], latencies[i]);
+
+ if (hasProxy && isInChina)
+ {
+ // 如果设置了代理,国内镜像的延迟加上一个固定值
+ latencies[i] += 6480;
+ }
+
+ if (latencies[selected] < 0 || (latencies[i] >= 0 && latencies[i] < latencies[selected]))
+ {
+ selected = i;
+ }
+ }
+
+ if (latencies[selected] < 0)
+ {
+ _logger.Error("All mirrors are not available");
+ return CheckUpdateRetT.NetworkError;
+ }
+
+ _logger.Information("Selected mirror: {CDNUrl}", urls[selected]);
+
+ var downloaded = await DownloadGithubAssets(urls[selected], _assetsObject);
+ if (downloaded)
+ {
+ OutputDownloadProgress(downloading: false, output: LocalizationHelper.GetString("NewVersionDownloadCompletedTitle"));
+ }
+ else
+ {
+ OutputDownloadProgress(downloading: false, output: LocalizationHelper.GetString("NewVersionDownloadFailedTitle"));
+ {
+ var toast = new ToastNotification(LocalizationHelper.GetString("NewVersionDownloadFailedTitle"));
+ toast.AppendContentText(LocalizationHelper.GetString("NewVersionDownloadFailedDesc"))
+ .AddButton(LocalizationHelper.GetString("NewVersionFoundButtonGoWebpage"), ToastNotification.GetActionTagForOpenWeb(UpdateUrl))
+ .Show();
+ }
+
+ return CheckUpdateRetT.NoNeedToUpdate;
+ }
+
+ return CheckUpdateRetT.OK;
+
+ string? ComparableHash(string version)
+ {
+ if (IsStdVersion(version))
+ {
+ return version;
+ }
+
+ if (!SemVersion.TryParse(version, SemVersionStyles.AllowLowerV, out var semVersion) ||
+ !IsNightlyVersion(semVersion))
+ {
+ return null;
+ }
+
+ // v4.6.6-1.g{Hash}
+ // v4.6.7-beta.2.8.g{Hash}
+ var commitHash = semVersion.PrereleaseIdentifiers[^1].ToString();
+ if (commitHash.StartsWith('g'))
+ {
+ commitHash = commitHash.Remove(0, 1);
+ }
+
+ return commitHash;
+ }
+ }
+
+ private void ShowUpdateInfo(bool otaFound, string? text)
+ {
+ bool goDownload = otaFound && SettingsViewModel.VersionUpdateSettings.AutoDownloadUpdatePackage;
+
+ using var toast = new ToastNotification((otaFound ? LocalizationHelper.GetString("NewVersionFoundTitle") : LocalizationHelper.GetString("NewVersionFoundButNoPackageTitle")) + " : " + UpdateTag);
+ if (goDownload)
+ {
+ OutputDownloadProgress(downloading: false, output: LocalizationHelper.GetString("NewVersionDownloadPreparing"));
+ toast.AppendContentText(LocalizationHelper.GetString("NewVersionFoundDescDownloading"));
+ }
+
+ if (!otaFound)
+ {
+ toast.AppendContentText(LocalizationHelper.GetString("NewVersionFoundButNoPackageDesc"));
+ }
+
+ int count = 0;
+ foreach (var line in UpdateInfo.Split('\n'))
+ {
+ if (line.StartsWith('#') || string.IsNullOrWhiteSpace(line))
+ {
+ continue;
+ }
+
+ toast.AppendContentText(line);
+ if (++count >= 10)
+ {
+ break;
+ }
+ }
+
+ if (!string.IsNullOrEmpty(text))
+ {
+ toast.AddButton(text, ToastNotification.GetActionTagForOpenWeb(UpdateUrl));
+ }
+
+ toast.ShowUpdateVersion();
+ }
+
+ private async Task HandleUpdateFromMirrorChyan()
+ {
+ if (string.IsNullOrEmpty(_mirrorcDownloadUrl))
+ {
+ return CheckUpdateRetT.FailedToGetInfo;
+ }
+
+ UpdateTag = _mirrorcVersionName ?? string.Empty;
+ UpdateInfo = _mirrorcReleaseNote ?? string.Empty;
+
+ bool goDownload = SettingsViewModel.VersionUpdateSettings.AutoDownloadUpdatePackage;
+ if (!goDownload)
+ {
+ OutputDownloadProgress(string.Empty);
+ return CheckUpdateRetT.NoNeedToUpdate;
+ }
+
+ UpdatePackageName = "MirrorChyanApp" + _mirrorcVersionName + ".zip";
+ var downloaded = await DownloadFromMirrorChyan(_mirrorcDownloadUrl,
+ UpdatePackageName);
+
+ if (downloaded)
+ {
+ OutputDownloadProgress(downloading: false, output: LocalizationHelper.GetString("NewVersionDownloadCompletedTitle"));
+ }
+ else
+ {
+ OutputDownloadProgress(downloading: false, output: LocalizationHelper.GetString("NewVersionDownloadFailedTitle"));
+ {
+ var toast = new ToastNotification(LocalizationHelper.GetString("NewVersionDownloadFailedTitle"));
+ toast.AppendContentText(LocalizationHelper.GetString("NewVersionDownloadFailedDesc"))
+ .Show();
+ }
+
+ return CheckUpdateRetT.NoNeedToUpdate;
+ }
+
+ return CheckUpdateRetT.OK;
}
public async Task AskToRestart(bool isResource = false)
@@ -693,22 +772,43 @@ public class VersionUpdateViewModel : Screen
/// 检查更新。
///
/// 检查到更新返回 ,反之则返回 。
- private async Task CheckUpdate()
+ private async Task<(CheckUpdateRetT Ret, AppUpdateSource? Source)> CheckUpdate()
{
// 调试版不检查更新
if (IsDebugVersion())
{
- return CheckUpdateRetT.NoNeedToUpdateDebugVersion;
+ return (CheckUpdateRetT.NoNeedToUpdateDebugVersion, null);
+ }
+
+ // mirrorChyan 暂时没有支持 nightly,之后加一加
+ if ((SettingsViewModel.VersionUpdateSettings.VersionType is
+ VersionUpdateSettingsUserControlModel.UpdateVersionType.Beta or
+ VersionUpdateSettingsUserControlModel.UpdateVersionType.Stable)
+ && !string.IsNullOrEmpty(SettingsViewModel.VersionUpdateSettings.MirrorChyanCdk))
+ {
+ try
+ {
+ var ret = await CheckUpdateByMirrorChyan();
+ if (ret is CheckUpdateRetT.OK or CheckUpdateRetT.AlreadyLatest)
+ {
+ return (ret, AppUpdateSource.MirrorChyan);
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, "Failed to check update by MirrorChyan, rollback to maaApi");
+ }
}
try
{
- return await CheckUpdateByMaaApi();
+ var ret = await CheckUpdateByMaaApi();
+ return (ret, AppUpdateSource.MaaApi);
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to check update by Maa API.");
- return CheckUpdateRetT.FailedToGetInfo;
+ return (CheckUpdateRetT.FailedToGetInfo, AppUpdateSource.MaaApi);
}
}
@@ -727,23 +827,15 @@ public class VersionUpdateViewModel : Screen
return CheckUpdateRetT.FailedToGetInfo;
}
- string? latestVersion;
- string? detailUrl;
- if (SettingsViewModel.VersionUpdateSettings.UpdateNightly)
+ string versionType = SettingsViewModel.VersionUpdateSettings.VersionType switch
{
- latestVersion = json["alpha"]?["version"]?.ToString();
- detailUrl = json["alpha"]?["detail"]?.ToString();
- }
- else if (SettingsViewModel.VersionUpdateSettings.UpdateBeta)
- {
- latestVersion = json["beta"]?["version"]?.ToString();
- detailUrl = json["beta"]?["detail"]?.ToString();
- }
- else
- {
- latestVersion = json["stable"]?["version"]?.ToString();
- detailUrl = json["stable"]?["detail"]?.ToString();
- }
+ VersionUpdateSettingsUserControlModel.UpdateVersionType.Beta => "beta",
+ VersionUpdateSettingsUserControlModel.UpdateVersionType.Nightly => "alpha",
+ _ => "stable",
+ };
+
+ var latestVersion = json[versionType]?["version"]?.ToString();
+ var detailUrl = json[versionType]?["detail"]?.ToString();
latestVersion ??= string.Empty;
detailUrl ??= string.Empty;
@@ -834,6 +926,94 @@ public class VersionUpdateViewModel : Screen
return CheckUpdateRetT.OK;
}
+ private async Task CheckUpdateByMirrorChyan()
+ {
+ var cdk = SettingsViewModel.VersionUpdateSettings.MirrorChyanCdk.Trim();
+
+ string channel = SettingsViewModel.VersionUpdateSettings.VersionType switch
+ {
+ VersionUpdateSettingsUserControlModel.UpdateVersionType.Beta => "beta",
+ VersionUpdateSettingsUserControlModel.UpdateVersionType.Nightly => "alpha",
+ _ => "stable",
+ };
+
+ var arch = IsArm ? "arm64" : "x64";
+ var url = $"{MaaUrls.MirrorChyanAppUpdate}?current_version={_curVersion}&cdk={cdk}&user_agent=MaaWpfGui&os=win&arch={arch}&channel={channel}";
+
+ var response = await Instances.HttpService.GetAsync(new(url), logUri: false);
+ _logger.Information($"current_version: {_curVersion}, cdk: {cdk.Mask()}, arch: {arch}, channel: {channel}");
+
+ if (response is null)
+ {
+ _logger.Error("response is null, try mirrorc line2");
+ url = url.Replace(MaaUrls.MirrorChyanWebsite, MaaUrls.MirrorChyanLine2);
+ response = await Instances.HttpService.GetAsync(new(url), logUri: false);
+ if (response is null)
+ {
+ _logger.Error("mirrorc line2 failed too");
+ return CheckUpdateRetT.NetworkError;
+ }
+ }
+
+ var jsonStr = await response.Content.ReadAsStringAsync();
+ _logger.Information(jsonStr);
+ JObject? data = null;
+ try
+ {
+ data = (JObject?)JsonConvert.DeserializeObject(jsonStr);
+ }
+ catch (Exception ex)
+ {
+ _logger.Error("Failed to deserialize json: " + ex.Message);
+ }
+
+ if (data is null)
+ {
+ return CheckUpdateRetT.UnknownError;
+ }
+
+ if (data["code"]?.ToString() != "0")
+ {
+ var msg = data["msg"]?.ToString();
+ if (!string.IsNullOrEmpty(msg))
+ {
+ ToastNotification.ShowDirect(msg);
+ }
+
+ return CheckUpdateRetT.UnknownError;
+ }
+
+ var version = data["data"]?["version_name"]?.ToString();
+ if (string.IsNullOrEmpty(version))
+ {
+ return CheckUpdateRetT.UnknownError;
+ }
+
+ if (!NeedToUpdate(version))
+ {
+ return CheckUpdateRetT.AlreadyLatest;
+ }
+
+ // 到这里已经确定有新版本了
+ _logger.Information($"New version found: {version}");
+
+ _mirrorcVersionName = version;
+ _mirrorcReleaseNote = data["data"]?["release_note"]?.ToString();
+
+ if (string.IsNullOrEmpty(cdk))
+ {
+ ToastNotification.ShowDirect(LocalizationHelper.GetString("MirrorChyanResourceUpdateTip"));
+
+ // 现在仅有cdk才会调用到这里,不应该没cdk的,直接先报个错
+ // 以后如果所有人都使用mirrorc检查更新,这里再改一下
+ return CheckUpdateRetT.UnknownError;
+ }
+
+ _mirrorcDownloadUrl = data["data"]?["url"]?.ToString();
+
+ return CheckUpdateRetT.OK;
+ }
+
private bool NeedToUpdate(string latestVersion)
{
if (IsDebugVersion())
@@ -847,10 +1027,8 @@ public class VersionUpdateViewModel : Screen
{
return curVersionObj.CompareSortOrderTo(latestVersionObj) < 0;
}
- else
- {
- return string.CompareOrdinal(_curVersion, latestVersion) < 0;
- }
+
+ return string.CompareOrdinal(_curVersion, latestVersion) < 0;
}
///
@@ -859,14 +1037,14 @@ public class VersionUpdateViewModel : Screen
/// 下载链接
/// Github Assets 对象
/// 操作成功返回 true,反之则返回 false
- private async Task DownloadGithubAssets(string url, JObject assetsObject)
+ private static async Task DownloadGithubAssets(string url, JObject assetsObject)
{
_logItemViewModels = Instances.TaskQueueViewModel.LogItemViewModels;
try
{
return await Instances.HttpService.DownloadFileAsync(
- new Uri(url),
- assetsObject["name"].ToString(),
+ new(url),
+ assetsObject["name"]!.ToString(),
assetsObject["content_type"]?.ToString())
.ConfigureAwait(false);
}
@@ -876,23 +1054,32 @@ public class VersionUpdateViewModel : Screen
}
}
- private static ObservableCollection _logItemViewModels;
+ private static async Task DownloadFromMirrorChyan(string url, string filename)
+ {
+ _logItemViewModels = Instances.TaskQueueViewModel.LogItemViewModels;
+ try
+ {
+ return await Instances.HttpService.DownloadFileAsync(
+ new(url), filename)
+ .ConfigureAwait(false);
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ private static ObservableCollection? _logItemViewModels;
public static void OutputDownloadProgress(long value = 0, long maximum = 1, int len = 0, double ts = 1)
{
string progress = $"[{value / 1048576.0:F}MiB/{maximum / 1048576.0:F}MiB ({value * 100.0 / maximum:F}%)";
double speedInKiBPerSecond = len / ts / 1024.0;
- string speedDisplay;
- if (speedInKiBPerSecond >= 1024)
- {
- speedDisplay = $"{speedInKiBPerSecond / 1024.0:F} MiB/s";
- }
- else
- {
- speedDisplay = $"{speedInKiBPerSecond:F} KiB/s";
- }
+ var speedDisplay = speedInKiBPerSecond >= 1024
+ ? $"{speedInKiBPerSecond / 1024.0:F} MiB/s"
+ : $"{speedInKiBPerSecond:F} KiB/s";
OutputDownloadProgress(progress + $" {speedDisplay}");
}
@@ -953,7 +1140,7 @@ public class VersionUpdateViewModel : Screen
return false;
}
- if (version.StartsWith("c") || version.StartsWith("20") || version.Contains("Local"))
+ if (version.StartsWith('c') || version.StartsWith("20") || version.Contains("Local"))
{
return false;
}
@@ -978,30 +1165,6 @@ public class VersionUpdateViewModel : Screen
// v4.6.7-beta.2.1.g1234567
// v4.6.8-5.g1234567
var lastId = version.PrereleaseIdentifiers.LastOrDefault().ToString();
- return lastId.StartsWith("g") && lastId.Length >= 7;
+ return lastId.StartsWith('g') && lastId.Length >= 7;
}
-
- /*
- ///
- /// 复制文件夹内容并覆盖已存在的相同名字的文件
- ///
- /// 源文件夹
- /// 目标文件夹
- public static void CopyFilesRecursively(string sourcePath, string targetPath)
- {
- Directory.CreateDirectory(targetPath);
-
- // Now Create all of the directories
- foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories))
- {
- Directory.CreateDirectory(dirPath.Replace(sourcePath, targetPath));
- }
-
- // Copy all the files & Replaces any files with the same name
- foreach (string newPath in Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories))
- {
- File.Copy(newPath, newPath.Replace(sourcePath, targetPath), true);
- }
- }
- */
}
diff --git a/src/MaaWpfGui/ViewModels/UserControl/Settings/VersionUpdateSettingsUserControlModel.cs b/src/MaaWpfGui/ViewModels/UserControl/Settings/VersionUpdateSettingsUserControlModel.cs
index 24544c3067..6fd11a95e4 100644
--- a/src/MaaWpfGui/ViewModels/UserControl/Settings/VersionUpdateSettingsUserControlModel.cs
+++ b/src/MaaWpfGui/ViewModels/UserControl/Settings/VersionUpdateSettingsUserControlModel.cs
@@ -210,22 +210,6 @@ public class VersionUpdateSettingsUserControlModel : PropertyChangedBase
}
}
- ///
- /// Gets a value indicating whether to update nightly.
- ///
- public bool UpdateNightly
- {
- get => _versionType == UpdateVersionType.Nightly;
- }
-
- ///
- /// Gets a value indicating whether to update beta version.
- ///
- public bool UpdateBeta
- {
- get => _versionType == UpdateVersionType.Beta;
- }
-
private bool _updateCheck = Convert.ToBoolean(ConfigurationHelper.GetGlobalValue(ConfigurationKeys.UpdateCheck, bool.TrueString));
public List> ResourceUpdateSourceList { get; } = [
@@ -244,7 +228,7 @@ public class VersionUpdateSettingsUserControlModel : PropertyChangedBase
set
{
SetAndNotify(ref _resourceUpdateSource, value);
- ConfigurationHelper.SetGlobalValue(ConfigurationKeys.ResourceUpdateSource, value.ToString());
+ ConfigurationHelper.SetGlobalValue(ConfigurationKeys.ResourceUpdateSource, value);
}
}