update source selectable

This commit is contained in:
DismissedLight
2024-07-22 22:36:31 +08:00
parent b6a0592102
commit c761d8b7ad
13 changed files with 102 additions and 93 deletions

View File

@@ -1440,7 +1440,7 @@
<value>如果自动更新下载过慢,可以手动下载并安装</value>
</data>
<data name="ViewDialogUpdatePackageMirrorHeader" xml:space="preserve">
<value>下载链接</value>
<value>下载</value>
</data>
<data name="ViewDialogUpdatePackagePrimaryText" xml:space="preserve">
<value>更新</value>

View File

@@ -8,7 +8,6 @@ using System.Diagnostics;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
using Windows.Storage;
using static Snap.Hutao.Win32.ConstValues;
using static Snap.Hutao.Win32.Kernel32;
using static Snap.Hutao.Win32.Macros;

View File

@@ -0,0 +1,15 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Web.Hutao;
namespace Snap.Hutao.Service.Update;
internal sealed class HutaoSelectedMirrorInformation
{
public required Version Version { get; set; }
public required string Validation { get; set; }
public required HutaoPackageMirror Mirror { get; set; }
}

View File

@@ -9,7 +9,7 @@ internal interface IUpdateService
{
ValueTask<CheckUpdateResult> CheckUpdateAsync(IProgress<UpdateStatus> progress, CancellationToken token = default);
ValueTask<bool> DownloadUpdateAsync(CheckUpdateResult checkUpdateResult, IProgress<UpdateStatus> progress, CancellationToken token = default);
ValueTask<bool> DownloadUpdateAsync(HutaoSelectedMirrorInformation mirrorInformation, IProgress<UpdateStatus> progress, CancellationToken token = default);
LaunchUpdaterResult LaunchUpdater();
}

View File

@@ -82,10 +82,9 @@ internal sealed partial class UpdateService : IUpdateService
}
}
public ValueTask<bool> DownloadUpdateAsync(CheckUpdateResult checkUpdateResult, IProgress<UpdateStatus> progress, CancellationToken token = default)
public ValueTask<bool> DownloadUpdateAsync(HutaoSelectedMirrorInformation mirrorInformation, IProgress<UpdateStatus> progress, CancellationToken token = default)
{
ArgumentNullException.ThrowIfNull(checkUpdateResult.PackageInformation);
return DownloadUpdatePackageAsync(checkUpdateResult.PackageInformation, GetUpdatePackagePath(), progress, token);
return DownloadUpdatePackageAsync(mirrorInformation, GetUpdatePackagePath(), progress, token);
}
public LaunchUpdaterResult LaunchUpdater()
@@ -131,70 +130,68 @@ internal sealed partial class UpdateService : IUpdateService
return runtimeOptions.GetDataFolderUpdateCacheFolderFile("Snap.Hutao.msix");
}
private async ValueTask<bool> DownloadUpdatePackageAsync(HutaoPackageInformation versionInformation, string filePath, IProgress<UpdateStatus> progress, CancellationToken token = default)
private async ValueTask<bool> DownloadUpdatePackageAsync(HutaoSelectedMirrorInformation mirrorInformation, string filePath, IProgress<UpdateStatus> progress, CancellationToken token = default)
{
string version = versionInformation.Version.ToString();
using IServiceScope scope = serviceProvider.CreateScope();
using HttpClient httpClient = scope.ServiceProvider.GetRequiredService<HttpClient>();
foreach (HutaoPackageMirror mirror in versionInformation.Mirrors)
HutaoPackageMirror mirror = mirrorInformation.Mirror;
string version = mirrorInformation.Version.ToString();
try
{
try
using HttpResponseMessage responseMessage = await httpClient.GetAsync(mirror.Url, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false);
long totalBytes = responseMessage.Content.Headers.ContentLength ?? 0;
using Stream webStream = await responseMessage.Content.ReadAsStreamAsync(token).ConfigureAwait(false);
switch (mirror.MirrorType)
{
using HttpResponseMessage responseMessage = await httpClient.GetAsync(mirror.Url, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false);
long totalBytes = responseMessage.Content.Headers.ContentLength ?? 0;
using Stream webStream = await responseMessage.Content.ReadAsStreamAsync(token).ConfigureAwait(false);
case HutaoPackageMirrorType.Direct:
using (FileStream fileStream = File.Create(filePath))
{
StreamCopyWorker<UpdateStatus> worker = new(webStream, fileStream, bytesRead => new UpdateStatus(version, bytesRead, totalBytes));
await worker.CopyAsync(progress).ConfigureAwait(false);
}
switch (mirror.MirrorType)
{
case HutaoPackageMirrorType.Direct:
using (FileStream fileStream = File.Create(filePath))
break;
case HutaoPackageMirrorType.Archive:
using (TempFileStream tempFileStream = new(FileMode.Create, FileAccess.ReadWrite))
{
StreamCopyWorker<UpdateStatus> worker = new(webStream, tempFileStream, bytesRead => new UpdateStatus(version, bytesRead, totalBytes));
await worker.CopyAsync(progress).ConfigureAwait(false);
using ZipArchive archive = new(tempFileStream);
foreach (ZipArchiveEntry entry in archive.Entries)
{
StreamCopyWorker<UpdateStatus> worker = new(webStream, fileStream, bytesRead => new UpdateStatus(version, bytesRead, totalBytes));
await worker.CopyAsync(progress).ConfigureAwait(false);
}
break;
case HutaoPackageMirrorType.Archive:
using (TempFileStream tempFileStream = new(FileMode.Create, FileAccess.ReadWrite))
{
StreamCopyWorker<UpdateStatus> worker = new(webStream, tempFileStream, bytesRead => new UpdateStatus(version, bytesRead, totalBytes));
await worker.CopyAsync(progress).ConfigureAwait(false);
using ZipArchive archive = new(tempFileStream);
foreach (ZipArchiveEntry entry in archive.Entries)
if (!entry.FullName.EndsWith(".msix", StringComparison.OrdinalIgnoreCase))
{
if (!entry.FullName.EndsWith(".msix", StringComparison.OrdinalIgnoreCase))
{
continue;
}
using Stream entryStream = entry.Open();
using FileStream fileStream = File.Create(filePath);
await entryStream.CopyToAsync(fileStream, token).ConfigureAwait(false);
break;
continue;
}
using Stream entryStream = entry.Open();
using FileStream fileStream = File.Create(filePath);
await entryStream.CopyToAsync(fileStream, token).ConfigureAwait(false);
break;
}
}
break;
}
if (!File.Exists(filePath))
{
return false;
}
string? remoteHash = versionInformation.Validation;
ArgumentNullException.ThrowIfNull(remoteHash);
if (await CheckUpdateCacheSHA256Async(filePath, remoteHash, token).ConfigureAwait(false))
{
return true;
}
break;
}
catch
if (!File.Exists(filePath))
{
return false;
}
string? remoteHash = mirrorInformation.Validation;
ArgumentNullException.ThrowIfNull(remoteHash);
if (await CheckUpdateCacheSHA256Async(filePath, remoteHash, token).ConfigureAwait(false))
{
return true;
}
}
catch
{
}
return false;

View File

@@ -6,7 +6,6 @@ using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.Graphics;
using Snap.Hutao.Win32.Foundation;
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;

View File

@@ -22,7 +22,6 @@ using Snap.Hutao.UI.Xaml.Media.Backdrop;
using Snap.Hutao.Win32.Foundation;
using Snap.Hutao.Win32.Graphics.Dwm;
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
using System.IO;
using Windows.Foundation;
using Windows.Graphics;
using Windows.UI;

View File

@@ -15,12 +15,6 @@
Style="{ThemeResource DefaultContentDialogStyle}"
mc:Ignorable="d">
<ContentDialog.Resources>
<x:Double x:Key="SettingsCardWrapThreshold">0</x:Double>
<x:Double x:Key="SettingsCardWrapNoIconThreshold">0</x:Double>
<x:Double x:Key="SettingsCardMinHeight">0</x:Double>
</ContentDialog.Resources>
<StackPanel Spacing="16">
<TextBlock>
<Run Text="{shuxm:ResourceString Name=ViewTitileUpdatePackageDownloadContent}"/>
@@ -31,28 +25,15 @@
<Run/>
</TextBlock>
<cwc:SettingsExpander
Description="{shuxm:ResourceString Name=ViewDialogUpdatePackageMirrorDescription}"
<ListView
Header="{shuxm:ResourceString Name=ViewDialogUpdatePackageMirrorHeader}"
IsExpanded="True"
ItemsSource="{x:Bind Mirrors}">
<cwc:SettingsExpander.ItemTemplate>
ItemsSource="{x:Bind Mirrors}"
SelectedItem="{x:Bind SelectedItem}">
<ListView.ItemTemplate>
<DataTemplate>
<cwc:SettingsCard
MinWidth="0"
Padding="16"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
Header="{Binding MirrorName}"
IsClickEnabled="True">
<mxi:Interaction.Behaviors>
<mxic:EventTriggerBehavior EventName="Click">
<cwb:NavigateToUriAction NavigateUri="{Binding Url}"/>
</mxic:EventTriggerBehavior>
</mxi:Interaction.Behaviors>
</cwc:SettingsCard>
<TextBlock Text="{Binding MirrorName}"/>
</DataTemplate>
</cwc:SettingsExpander.ItemTemplate>
</cwc:SettingsExpander>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</ContentDialog>

View File

@@ -7,10 +7,21 @@ using Snap.Hutao.Web.Hutao;
namespace Snap.Hutao.UI.Xaml.View.Dialog;
[DependencyProperty("Mirrors", typeof(List<HutaoPackageMirror>))]
[DependencyProperty("SelectedItem", typeof(HutaoPackageMirror))]
internal sealed partial class UpdatePackageDownloadConfirmDialog : ContentDialog
{
public UpdatePackageDownloadConfirmDialog()
{
InitializeComponent();
}
public async ValueTask<ValueResult<bool, HutaoPackageMirror>> GetSelectedMirrorAsync()
{
if (await ShowAsync() is ContentDialogResult.Primary)
{
return new(true, SelectedItem);
}
return new(false, default!);
}
}

View File

@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Graphics.Canvas.Effects;
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Core.Caching;
using Snap.Hutao.Core.ExceptionService;
@@ -21,10 +20,6 @@ using System.IO;
namespace Snap.Hutao.ViewModel;
/// <summary>
/// 测试视图模型
/// </summary>
[HighQuality]
[ConstructorGenerated]
[Injection(InjectAs.Scoped)]
internal sealed partial class TestViewModel : Abstraction.ViewModel

View File

@@ -15,6 +15,7 @@ using Snap.Hutao.UI.Xaml.Behavior.Action;
using Snap.Hutao.UI.Xaml.Control;
using Snap.Hutao.UI.Xaml.View.Dialog;
using Snap.Hutao.UI.Xaml.View.Window.WebView2;
using Snap.Hutao.Web.Hutao;
using System.Globalization;
using System.Text;
@@ -95,11 +96,22 @@ internal sealed partial class TitleViewModel : Abstraction.ViewModel
dialog.Title = SH.FormatViewTitileUpdatePackageDownloadTitle(UpdateStatus?.Version);
dialog.Mirrors = checkUpdateResult.PackageInformation?.Mirrors;
dialog.SelectedItem = dialog.Mirrors?.FirstOrDefault();
if (await dialog.ShowAsync() is ContentDialogResult.Primary)
(bool isOk, HutaoPackageMirror mirror) = await dialog.GetSelectedMirrorAsync().ConfigureAwait(false);
if (isOk)
{
ArgumentNullException.ThrowIfNull(checkUpdateResult.PackageInformation);
HutaoSelectedMirrorInformation mirrorInformation = new()
{
Mirror = mirror,
Validation = checkUpdateResult.PackageInformation.Validation,
Version = checkUpdateResult.PackageInformation.Version,
};
// This method will set CheckUpdateResult.Kind to NeedInstall if download success
if (!await DownloadPackageAsync(progress, checkUpdateResult).ConfigureAwait(false))
if (!await DownloadPackageAsync(progress, mirrorInformation, checkUpdateResult).ConfigureAwait(false))
{
infoBarService.Warning(SH.ViewTitileUpdatePackageDownloadFailedMessage);
return;
@@ -139,12 +151,12 @@ internal sealed partial class TitleViewModel : Abstraction.ViewModel
UpdateStatus = null;
}
private async ValueTask<bool> DownloadPackageAsync(IProgress<UpdateStatus> progress, CheckUpdateResult checkUpdateResult)
private async ValueTask<bool> DownloadPackageAsync(IProgress<UpdateStatus> progress, HutaoSelectedMirrorInformation mirrorInformation, CheckUpdateResult checkUpdateResult)
{
bool downloadSuccess = true;
try
{
if (await updateService.DownloadUpdateAsync(checkUpdateResult, progress).ConfigureAwait(false))
if (await updateService.DownloadUpdateAsync(mirrorInformation, progress).ConfigureAwait(false))
{
checkUpdateResult.Kind = CheckUpdateResultKind.NeedInstall;
}

View File

@@ -97,6 +97,7 @@ internal static class ApiOsEndpoints
public const string CalculateWeaponList = $"{SgPublicApi}/event/calculateos/weapon/list";
public const string CalculateCompute = $"{SgPublicApi}/event/calculateos/compute";
public const string CalculateBatchCompute = $"{SgPublicApi}/event/calculateos/batch_compute";
public static string CalculateSyncAvatarDetail(in AvatarId avatarId, in PlayerUid uid)
{
return $"{SgPublicApi}/event/calculateos/sync/avatar/detail?avatar_id={avatarId.Value}&uid={uid.Value}&region={uid.Region}";

View File

@@ -9,7 +9,7 @@ internal sealed class HutaoPackageInformation
public Version Version { get; set; } = default!;
[JsonPropertyName("validation")]
public string? Validation { get; set; } = default!;
public string Validation { get; set; } = default!;
[JsonPropertyName("mirrors")]
public List<HutaoPackageMirror> Mirrors { get; set; } = default!;