mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
1.9.0 package
This commit is contained in:
@@ -130,7 +130,7 @@ internal sealed class HttpShardCopyWorker<TStatus> : IDisposable
|
||||
BytesRead = bytesRead;
|
||||
}
|
||||
|
||||
public int BytesRead { get; set; }
|
||||
public int BytesRead { get; }
|
||||
}
|
||||
|
||||
private sealed class ShardProgress : IProgress<ShardStatus>
|
||||
@@ -152,11 +152,11 @@ internal sealed class HttpShardCopyWorker<TStatus> : IDisposable
|
||||
public void Report(ShardStatus value)
|
||||
{
|
||||
Interlocked.Add(ref totalBytesRead, value.BytesRead);
|
||||
if (stopwatch.GetElapsedTime().TotalMilliseconds > 500)
|
||||
if (stopwatch.GetElapsedTime().TotalMilliseconds > 1000 || totalBytesRead == contentLength)
|
||||
{
|
||||
lock (syncRoot)
|
||||
{
|
||||
if (stopwatch.GetElapsedTime().TotalMilliseconds > 500)
|
||||
if (stopwatch.GetElapsedTime().TotalMilliseconds > 1000 || totalBytesRead == contentLength)
|
||||
{
|
||||
workerProgress.Report(statusFactory(totalBytesRead, contentLength));
|
||||
stopwatch = ValueStopwatch.StartNew();
|
||||
|
||||
@@ -65,7 +65,7 @@ internal class StreamCopyWorker<TStatus>
|
||||
await destination.WriteAsync(buffer[..bytesRead]).ConfigureAwait(false);
|
||||
|
||||
totalBytesRead += bytesRead;
|
||||
if (stopwatch.GetElapsedTime().TotalMilliseconds > 500)
|
||||
if (stopwatch.GetElapsedTime().TotalMilliseconds > 1000)
|
||||
{
|
||||
progress.Report(statusFactory(totalBytesRead));
|
||||
stopwatch = ValueStopwatch.StartNew();
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<Identity
|
||||
Name="60568DGPStudio.SnapHutao"
|
||||
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
|
||||
Version="1.8.5.0" />
|
||||
Version="1.9.0.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Snap Hutao</DisplayName>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<Identity
|
||||
Name="60568DGPStudio.SnapHutaoDev"
|
||||
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
|
||||
Version="1.8.4.0" />
|
||||
Version="1.9.0.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Snap Hutao Dev</DisplayName>
|
||||
|
||||
@@ -995,6 +995,9 @@
|
||||
<data name="ServiceUIGFImportUnsupportedVersion" xml:space="preserve">
|
||||
<value>不支持的 UIGF 版本</value>
|
||||
</data>
|
||||
<data name="ServiceUpdateStatusVersionDescription" xml:space="preserve">
|
||||
<value>发现新版本 {0}</value>
|
||||
</data>
|
||||
<data name="ServiceUserCurrentMultiMatched" xml:space="preserve">
|
||||
<value>多个用户记录为选中状态</value>
|
||||
</data>
|
||||
@@ -2621,6 +2624,12 @@
|
||||
<data name="ViewSpiralAbyssUploadRecord" xml:space="preserve">
|
||||
<value>上传数据</value>
|
||||
</data>
|
||||
<data name="ViewTitileUpdatePackageReadyContent" xml:space="preserve">
|
||||
<value>是否立即安装?</value>
|
||||
</data>
|
||||
<data name="ViewTitileUpdatePackageReadyTitle" xml:space="preserve">
|
||||
<value>胡桃 {0} 版本已准备就绪</value>
|
||||
</data>
|
||||
<data name="ViewTitleAutoClicking" xml:space="preserve">
|
||||
<value>自动连点</value>
|
||||
</data>
|
||||
@@ -2877,7 +2886,7 @@
|
||||
<value>国际服 亚服</value>
|
||||
</data>
|
||||
<data name="WebHoyolabRegionOSCHT" xml:space="preserve">
|
||||
<value>国际服 台服</value>
|
||||
<value>国际服 港澳台服</value>
|
||||
</data>
|
||||
<data name="WebHoyolabRegionOSEURO" xml:space="preserve">
|
||||
<value>国际服 欧服</value>
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Abstraction;
|
||||
|
||||
internal interface IUpdateService
|
||||
{
|
||||
}
|
||||
@@ -74,7 +74,7 @@ internal sealed partial class AppOptions : DbStoreOptions
|
||||
set => SetOption(ref currentCulture, SettingEntry.Culture, value, value => value.Name);
|
||||
}
|
||||
|
||||
public List<NameValue<Region>> Regions { get; } = KnownRegions.Get();
|
||||
public Lazy<List<NameValue<Region>>> LazyRegions { get; } = new(KnownRegions.Get);
|
||||
|
||||
public Region Region
|
||||
{
|
||||
|
||||
@@ -16,6 +16,6 @@ internal static class AppOptionsExtension
|
||||
|
||||
public static NameValue<Region>? GetCurrentRegionForSelectionOrDefault(this AppOptions appOptions)
|
||||
{
|
||||
return appOptions.Regions.SingleOrDefault(c => c.Value.Value == appOptions.Region.Value);
|
||||
return appOptions.LazyRegions.Value.SingleOrDefault(c => c.Value.Value == appOptions.Region.Value);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ internal static class SupportedCultures
|
||||
ToNameValue(CultureInfo.GetCultureInfo("zh-Hans")),
|
||||
ToNameValue(CultureInfo.GetCultureInfo("zh-Hant")),
|
||||
ToNameValue(CultureInfo.GetCultureInfo("en")),
|
||||
ToNameValue(CultureInfo.GetCultureInfo("ru")),
|
||||
ToNameValue(CultureInfo.GetCultureInfo("ja")),
|
||||
ToNameValue(CultureInfo.GetCultureInfo("id")),
|
||||
ToNameValue(CultureInfo.GetCultureInfo("ko")),
|
||||
|
||||
13
src/Snap.Hutao/Snap.Hutao/Service/Update/IUpdateService.cs
Normal file
13
src/Snap.Hutao/Snap.Hutao/Service/Update/IUpdateService.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Service.Update;
|
||||
|
||||
namespace Snap.Hutao.Service.Abstraction;
|
||||
|
||||
internal interface IUpdateService
|
||||
{
|
||||
ValueTask<bool> CheckForUpdateAndDownloadAsync(IProgress<UpdateStatus> progress, CancellationToken token = default);
|
||||
|
||||
void LaunchInstaller();
|
||||
}
|
||||
@@ -2,15 +2,17 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.IO;
|
||||
using Snap.Hutao.Core.IO.Hashing;
|
||||
using Snap.Hutao.Core.IO.Http.Sharding;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Web.Hutao;
|
||||
using Snap.Hutao.Web.Hutao.Response;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Snap.Hutao.Service;
|
||||
namespace Snap.Hutao.Service.Update;
|
||||
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Singleton, typeof(IUpdateService))]
|
||||
@@ -18,7 +20,7 @@ internal sealed partial class UpdateService : IUpdateService
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
public async ValueTask<bool> InitializeAsync(IProgress<UpdateStatus> progress, CancellationToken token = default)
|
||||
public async ValueTask<bool> CheckForUpdateAndDownloadAsync(IProgress<UpdateStatus> progress, CancellationToken token = default)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
@@ -34,16 +36,26 @@ internal sealed partial class UpdateService : IUpdateService
|
||||
}
|
||||
|
||||
HutaoVersionInformation versionInformation = response.Data;
|
||||
string msixPath = GetUpdatePackagePath();
|
||||
|
||||
if (scope.ServiceProvider.GetRequiredService<RuntimeOptions>().Version >= versionInformation.Version)
|
||||
{
|
||||
if (File.Exists(msixPath))
|
||||
{
|
||||
File.Delete(msixPath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
progress.Report(new(versionInformation.Version.ToString(), 0, 0));
|
||||
|
||||
if (versionInformation.Sha256 is not { } sha256)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string dataFolder = scope.ServiceProvider.GetRequiredService<RuntimeOptions>().DataFolder;
|
||||
string msixPath = Path.Combine(dataFolder, "UpdateCache/Snap.Hutao.msix");
|
||||
|
||||
if (await CheckUpdateCacheSHA256Async(msixPath, sha256, token).ConfigureAwait(false))
|
||||
if (File.Exists(msixPath) && await CheckUpdateCacheSHA256Async(msixPath, sha256, token).ConfigureAwait(false))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -52,12 +64,29 @@ internal sealed partial class UpdateService : IUpdateService
|
||||
}
|
||||
}
|
||||
|
||||
public void LaunchInstaller()
|
||||
{
|
||||
Process.Start(new ProcessStartInfo()
|
||||
{
|
||||
UseShellExecute = true,
|
||||
FileName = GetUpdatePackagePath(),
|
||||
});
|
||||
}
|
||||
|
||||
private static async ValueTask<bool> CheckUpdateCacheSHA256Async(string filePath, string remoteHash, CancellationToken token = default)
|
||||
{
|
||||
string localHash = await SHA256.HashFileAsync(filePath, token).ConfigureAwait(false);
|
||||
return string.Equals(localHash, remoteHash, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private string GetUpdatePackagePath()
|
||||
{
|
||||
string dataFolder = serviceProvider.GetRequiredService<RuntimeOptions>().DataFolder;
|
||||
string directory = Path.Combine(dataFolder, "UpdateCache");
|
||||
Directory.CreateDirectory(directory);
|
||||
return Path.Combine(directory, "Snap.Hutao.msix");
|
||||
}
|
||||
|
||||
private async ValueTask<bool> DownloadUpdatePackageAsync(HutaoVersionInformation versionInformation, string filePath, IProgress<UpdateStatus> progress, CancellationToken token = default)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
30
src/Snap.Hutao/Snap.Hutao/Service/Update/UpdateStatus.cs
Normal file
30
src/Snap.Hutao/Snap.Hutao/Service/Update/UpdateStatus.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Common;
|
||||
|
||||
namespace Snap.Hutao.Service.Update;
|
||||
|
||||
internal sealed class UpdateStatus
|
||||
{
|
||||
public UpdateStatus(string version, long bytesRead, long totalBytes)
|
||||
{
|
||||
Version = version;
|
||||
VersionDescription = SH.FormatServiceUpdateStatusVersionDescription(Version);
|
||||
BytesRead = bytesRead;
|
||||
TotalBytes = totalBytes;
|
||||
ProgressDescription = bytesRead != totalBytes
|
||||
? $"{Converters.ToFileSizeString(bytesRead)}/{Converters.ToFileSizeString(totalBytes)}"
|
||||
: string.Empty;
|
||||
}
|
||||
|
||||
public string? Version { get; set; }
|
||||
|
||||
public string VersionDescription { get; }
|
||||
|
||||
public double BytesRead { get; set; }
|
||||
|
||||
public double TotalBytes { get; set; }
|
||||
|
||||
public string ProgressDescription { get; }
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Common;
|
||||
|
||||
namespace Snap.Hutao.Service;
|
||||
|
||||
internal sealed class UpdateStatus
|
||||
{
|
||||
public UpdateStatus(string version, long bytesRead, long totalBytes)
|
||||
{
|
||||
Version = version;
|
||||
BytesRead = bytesRead;
|
||||
TotalBytes = totalBytes;
|
||||
ProgressDescription = $"{Converters.ToFileSizeString(bytesRead)}/{Converters.ToFileSizeString(totalBytes)}";
|
||||
}
|
||||
|
||||
public string? Version { get; set; }
|
||||
|
||||
public long BytesRead { get; set; }
|
||||
|
||||
public long TotalBytes { get; set; }
|
||||
|
||||
public string ProgressDescription { get; }
|
||||
}
|
||||
@@ -312,7 +312,7 @@
|
||||
<shc:SizeRestrictedContentControl>
|
||||
<ComboBox
|
||||
DisplayMemberPath="Name"
|
||||
ItemsSource="{Binding AppOptions.Regions}"
|
||||
ItemsSource="{Binding AppOptions.LazyRegions.Value}"
|
||||
SelectedItem="{Binding SelectedRegion, Mode=TwoWay}"/>
|
||||
</shc:SizeRestrictedContentControl>
|
||||
</cwc:SettingsCard>
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
Margin="0,0,6,0"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6"
|
||||
Visibility="{x:Bind RuntimeOptions.IsElevated, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<ToggleButton
|
||||
Margin="0,0,6,0"
|
||||
VerticalAlignment="Center"
|
||||
IsChecked="{x:Bind HotKeyOptions.IsMouseClickRepeatForeverOn, Mode=OneWay}"
|
||||
IsHitTestVisible="False"
|
||||
@@ -49,6 +49,36 @@
|
||||
Text="{x:Bind HotKeyOptions.MouseClickRepeatForeverKeyCombination.DisplayName, Mode=OneWay}"/>
|
||||
</Grid>
|
||||
</ToggleButton>
|
||||
|
||||
<Border
|
||||
Margin="0,6"
|
||||
Style="{ThemeResource BorderCardStyle}"
|
||||
Visibility="{x:Bind UpdateStatus, Converter={StaticResource EmptyObjectToVisibilityConverter}, Mode=OneWay}">
|
||||
<Grid Padding="12,0" ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ProgressBar
|
||||
Grid.ColumnSpan="2"
|
||||
MinHeight="32"
|
||||
Margin="-12,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="Transparent"
|
||||
CornerRadius="{ThemeResource ControlCornerRadius}"
|
||||
Maximum="{x:Bind UpdateStatus.TotalBytes, Mode=OneWay}"
|
||||
Opacity="{ThemeResource LargeBackgroundProgressBarOpacity}"
|
||||
Value="{x:Bind UpdateStatus.BytesRead, Mode=OneWay}"/>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind UpdateStatus.ProgressDescription, Mode=OneWay}"/>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind UpdateStatus.VersionDescription, Mode=OneWay}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,10 +1,15 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.Windowing.HotKey;
|
||||
using Snap.Hutao.Factory.ContentDialog;
|
||||
using Snap.Hutao.Factory.Progress;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Service.Update;
|
||||
|
||||
namespace Snap.Hutao.View;
|
||||
|
||||
@@ -12,19 +17,19 @@ namespace Snap.Hutao.View;
|
||||
/// 标题视图
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[INotifyPropertyChanged]
|
||||
internal sealed partial class TitleView : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的标题视图
|
||||
/// </summary>
|
||||
private CancellationTokenSource checkUpdateTaskCancellationTokenSource = new();
|
||||
private UpdateStatus? updateStatus;
|
||||
|
||||
public TitleView()
|
||||
{
|
||||
Loaded += OnTitleViewLoaded;
|
||||
Unloaded += OnTitleViewUnloaded;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标题
|
||||
/// </summary>
|
||||
public string Title
|
||||
{
|
||||
[SuppressMessage("", "IDE0027")]
|
||||
@@ -38,9 +43,6 @@ internal sealed partial class TitleView : UserControl
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取可拖动区域
|
||||
/// </summary>
|
||||
public FrameworkElement DragArea
|
||||
{
|
||||
get => DragableGrid;
|
||||
@@ -49,4 +51,44 @@ internal sealed partial class TitleView : UserControl
|
||||
public RuntimeOptions RuntimeOptions { get; } = Ioc.Default.GetRequiredService<RuntimeOptions>();
|
||||
|
||||
public HotKeyOptions HotKeyOptions { get; } = Ioc.Default.GetRequiredService<HotKeyOptions>();
|
||||
}
|
||||
|
||||
public UpdateStatus? UpdateStatus { get => updateStatus; set => SetProperty(ref updateStatus, value); }
|
||||
|
||||
private void OnTitleViewLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DoCheckUpdateAsync(checkUpdateTaskCancellationTokenSource.Token).SafeForget();
|
||||
Loaded -= OnTitleViewLoaded;
|
||||
}
|
||||
|
||||
private void OnTitleViewUnloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
checkUpdateTaskCancellationTokenSource.Cancel();
|
||||
Unloaded -= OnTitleViewUnloaded;
|
||||
}
|
||||
|
||||
private async ValueTask DoCheckUpdateAsync(CancellationToken token)
|
||||
{
|
||||
IServiceProvider serviceProvider = Ioc.Default;
|
||||
IUpdateService updateService = serviceProvider.GetRequiredService<IUpdateService>();
|
||||
|
||||
IProgressFactory progressFactory = serviceProvider.GetRequiredService<IProgressFactory>();
|
||||
IProgress<UpdateStatus> progress = progressFactory.CreateForMainThread<UpdateStatus>(status => UpdateStatus = status);
|
||||
if (await updateService.CheckForUpdateAndDownloadAsync(progress, token).ConfigureAwait(false))
|
||||
{
|
||||
ContentDialogResult result = await serviceProvider
|
||||
.GetRequiredService<IContentDialogFactory>()
|
||||
.CreateForConfirmCancelAsync(
|
||||
SH.FormatViewTitileUpdatePackageReadyTitle(UpdateStatus?.Version),
|
||||
SH.ViewTitileUpdatePackageReadyContent,
|
||||
ContentDialogButton.Primary)
|
||||
.ConfigureAwait(false);
|
||||
if (result == ContentDialogResult.Primary)
|
||||
{
|
||||
updateService.LaunchInstaller();
|
||||
}
|
||||
}
|
||||
|
||||
await serviceProvider.GetRequiredService<ITaskContext>().SwitchToMainThreadAsync();
|
||||
UpdateStatus = null;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Snap.Hutao.Web.Hutao;
|
||||
|
||||
internal sealed class IPInformation
|
||||
internal sealed partial class IPInformation
|
||||
{
|
||||
private const string Unknown = "Unknown";
|
||||
|
||||
@@ -26,6 +28,10 @@ internal sealed class IPInformation
|
||||
return SH.WebHutaoServiceUnAvailable;
|
||||
}
|
||||
|
||||
return SH.FormatViewPageSettingDeviceIpDescription(Ip, Division);
|
||||
string maskedIp = IpRegex().Replace(Ip, "$1.$2.*.*");
|
||||
return SH.FormatViewPageSettingDeviceIpDescription(maskedIp, Division);
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"(\d+)\.(\d+)\.\d+\.\d+")]
|
||||
private static partial Regex IpRegex();
|
||||
}
|
||||
Reference in New Issue
Block a user