Compare commits

..

7 Commits

Author SHA1 Message Date
qhy040404
a845dff6ee Revert "temporary fix qr login"
This reverts commit d4bd610fe2.
2024-04-14 13:55:16 +08:00
DismissedLight
ee99d0b665 refactor static resource 2024-04-12 21:40:00 +08:00
Lightczx
72b62aa9c6 ServerGarbageCollection 2024-04-12 17:30:10 +08:00
Lightczx
6b031e1866 update static resource setting 2024-04-12 16:52:54 +08:00
Lightczx
59c03c7f3b update WAS to 1.5.2 2024-04-12 11:57:49 +08:00
Lightczx
c03a96b44f refine guide ui 2024-04-11 16:14:04 +08:00
DismissedLight
d5a97903d3 Merge pull request #1545 from DGP-Studio/feat/version1.10guide 2024-04-11 15:57:44 +08:00
17 changed files with 215 additions and 80 deletions

View File

@@ -5,6 +5,9 @@ using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.Core.IO; using Snap.Hutao.Core.IO;
using Snap.Hutao.Core.IO.Hashing; using Snap.Hutao.Core.IO.Hashing;
using Snap.Hutao.Core.Logging; using Snap.Hutao.Core.Logging;
using Snap.Hutao.ViewModel.Guide;
using Snap.Hutao.Web.Request.Builder;
using Snap.Hutao.Web.Request.Builder.Abstraction;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Frozen; using System.Collections.Frozen;
using System.IO; using System.IO;
@@ -36,6 +39,7 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
private readonly ConcurrentDictionary<string, Task> concurrentTasks = new(); private readonly ConcurrentDictionary<string, Task> concurrentTasks = new();
private readonly IHttpClientFactory httpClientFactory; private readonly IHttpClientFactory httpClientFactory;
private readonly IHttpRequestMessageBuilderFactory httpRequestMessageBuilderFactory;
private readonly IServiceProvider serviceProvider; private readonly IServiceProvider serviceProvider;
private readonly ILogger<ImageCache> logger; private readonly ILogger<ImageCache> logger;
@@ -169,16 +173,26 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
HttpClient httpClient = httpClientFactory.CreateClient(nameof(ImageCache)); HttpClient httpClient = httpClientFactory.CreateClient(nameof(ImageCache));
while (retryCount < 3) while (retryCount < 3)
{ {
using (HttpResponseMessage message = await httpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false)) HttpRequestMessageBuilder requestMessageBuilder = httpRequestMessageBuilderFactory
.Create()
.SetRequestUri(uri)
// These headers are only available for our own api
.SetStaticResourceControlHeadersIf(uri.Host.Contains("api.snapgenshin.com", StringComparison.OrdinalIgnoreCase))
.Get();
using (HttpRequestMessage requestMessage = requestMessageBuilder.HttpRequestMessage)
{ {
if (message.RequestMessage is { RequestUri: { } target } && target != uri) using (HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false))
{
if (responseMessage.RequestMessage is { RequestUri: { } target } && target != uri)
{ {
logger.LogDebug("The Request '{Source}' has been redirected to '{Target}'", uri, target); logger.LogDebug("The Request '{Source}' has been redirected to '{Target}'", uri, target);
} }
if (message.IsSuccessStatusCode) if (responseMessage.IsSuccessStatusCode)
{ {
using (Stream httpStream = await message.Content.ReadAsStreamAsync().ConfigureAwait(false)) using (Stream httpStream = await responseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false))
{ {
using (FileStream fileStream = File.Create(baseFile)) using (FileStream fileStream = File.Create(baseFile))
{ {
@@ -188,12 +202,12 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
} }
} }
switch (message.StatusCode) switch (responseMessage.StatusCode)
{ {
case HttpStatusCode.TooManyRequests: case HttpStatusCode.TooManyRequests:
{ {
retryCount++; retryCount++;
TimeSpan delay = message.Headers.RetryAfter?.Delta ?? retryCountToDelay[retryCount]; TimeSpan delay = responseMessage.Headers.RetryAfter?.Delta ?? retryCountToDelay[retryCount];
logger.LogInformation("Retry download '{Uri}' after {Delay}.", uri, delay); logger.LogInformation("Retry download '{Uri}' after {Delay}.", uri, delay);
await Task.Delay(delay).ConfigureAwait(false); await Task.Delay(delay).ConfigureAwait(false);
break; break;
@@ -205,4 +219,5 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
} }
} }
} }
}
} }

View File

@@ -22,7 +22,7 @@ internal static class SettingKeys
public const string DataFolderPath = "DataFolderPath"; public const string DataFolderPath = "DataFolderPath";
public const string Major1Minor10Revision0GuideState = "Major1Minor10Revision0GuideState1"; public const string Major1Minor10Revision0GuideState = "Major1Minor10Revision0GuideState1";
public const string StaticResourceImageQuality = "StaticResourceImageQuality"; public const string StaticResourceImageQuality = "StaticResourceImageQuality";
public const string StaticResourceUseTrimmedArchive = "StaticResourceUseTrimmedArchive"; public const string StaticResourceImageArchive = "StaticResourceImageArchive";
public const string HotKeyMouseClickRepeatForever = "HotKeyMouseClickRepeatForever"; public const string HotKeyMouseClickRepeatForever = "HotKeyMouseClickRepeatForever";
public const string IsAllocConsoleDebugModeEnabled = "IsAllocConsoleDebugModeEnabled2"; public const string IsAllocConsoleDebugModeEnabled = "IsAllocConsoleDebugModeEnabled2";
#endregion #endregion

View File

@@ -14,10 +14,10 @@ namespace Snap.Hutao;
internal sealed partial class GuideWindow : Window, IWindowOptionsSource, IMinMaxInfoHandler internal sealed partial class GuideWindow : Window, IWindowOptionsSource, IMinMaxInfoHandler
{ {
private const int MinWidth = 1000; private const int MinWidth = 1000;
private const int MinHeight = 600; private const int MinHeight = 650;
private const int MaxWidth = 1200; private const int MaxWidth = 1200;
private const int MaxHeight = 750; private const int MaxHeight = 800;
private readonly WindowOptions windowOptions; private readonly WindowOptions windowOptions;

View File

@@ -1382,6 +1382,9 @@
<data name="ViewGachaLogHeader" xml:space="preserve"> <data name="ViewGachaLogHeader" xml:space="preserve">
<value>祈愿记录</value> <value>祈愿记录</value>
</data> </data>
<data name="ViewGuideStaticResourceDownloadSize" xml:space="preserve">
<value>预计下载大小:{0}</value>
</data>
<data name="ViewGuideStepAgreementIHaveReadText" xml:space="preserve"> <data name="ViewGuideStepAgreementIHaveReadText" xml:space="preserve">
<value>我已阅读并同意</value> <value>我已阅读并同意</value>
</data> </data>
@@ -1440,10 +1443,10 @@
<value>图片资源包体</value> <value>图片资源包体</value>
</data> </data>
<data name="ViewGuideStepStaticResourceSettingMinimumOff" xml:space="preserve"> <data name="ViewGuideStepStaticResourceSettingMinimumOff" xml:space="preserve">
<value>全部资源(节省 0% 磁盘空间占用)</value> <value>完整包体</value>
</data> </data>
<data name="ViewGuideStepStaticResourceSettingMinimumOn" xml:space="preserve"> <data name="ViewGuideStepStaticResourceSettingMinimumOn" xml:space="preserve">
<value>部分资源(节省 13.5% 磁盘空间占用)</value> <value>精简包体</value>
</data> </data>
<data name="ViewGuideStepStaticResourceSettingQualityHeader" xml:space="preserve"> <data name="ViewGuideStepStaticResourceSettingQualityHeader" xml:space="preserve">
<value>图片资源质量</value> <value>图片资源质量</value>
@@ -1638,10 +1641,10 @@
<value>下载资源文件中,请稍候</value> <value>下载资源文件中,请稍候</value>
</data> </data>
<data name="ViewModelGuideStaticResourceQualityHigh" xml:space="preserve"> <data name="ViewModelGuideStaticResourceQualityHigh" xml:space="preserve">
<value>高质量(节省 72.8% 磁盘空间占用)</value> <value>高质量</value>
</data> </data>
<data name="ViewModelGuideStaticResourceQualityRaw" xml:space="preserve"> <data name="ViewModelGuideStaticResourceQualityRaw" xml:space="preserve">
<value>原图(节省 0% 磁盘空间占用)</value> <value>原图</value>
</data> </data>
<data name="ViewModelHutaoPassportEmailNotValidHint" xml:space="preserve"> <data name="ViewModelHutaoPassportEmailNotValidHint" xml:space="preserve">
<value>请输入正确的邮箱</value> <value>请输入正确的邮箱</value>

View File

@@ -44,6 +44,8 @@
<SelfContained>true</SelfContained> <SelfContained>true</SelfContained>
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained> <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
<WindowsAppSdkUndockedRegFreeWinRTInitialize>false</WindowsAppSdkUndockedRegFreeWinRTInitialize> <WindowsAppSdkUndockedRegFreeWinRTInitialize>false</WindowsAppSdkUndockedRegFreeWinRTInitialize>
<ServerGarbageCollection>true</ServerGarbageCollection>
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -303,8 +305,8 @@
<PackageReference Include="CommunityToolkit.WinUI.Controls.TokenizingTextBox" Version="8.0.240109" /> <PackageReference Include="CommunityToolkit.WinUI.Controls.TokenizingTextBox" Version="8.0.240109" />
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.0.240109" /> <PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.0.240109" />
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" /> <PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.3"> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.4">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
@@ -319,7 +321,7 @@
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.8.8" /> <PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.8.8" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" /> <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240311000" /> <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240404000" />
<PackageReference Include="QRCoder" Version="1.4.3" /> <PackageReference Include="QRCoder" Version="1.4.3" />
<PackageReference Include="Snap.Discord.GameSDK" Version="1.6.0" /> <PackageReference Include="Snap.Discord.GameSDK" Version="1.6.0" />
<PackageReference Include="Snap.Hutao.Deployment.Runtime" Version="1.16.0"> <PackageReference Include="Snap.Hutao.Deployment.Runtime" Version="1.16.0">

View File

@@ -6,7 +6,6 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mxi="using:Microsoft.Xaml.Interactivity" xmlns:mxi="using:Microsoft.Xaml.Interactivity"
xmlns:shc="using:Snap.Hutao.Control"
xmlns:shcb="using:Snap.Hutao.Control.Behavior" xmlns:shcb="using:Snap.Hutao.Control.Behavior"
xmlns:shcm="using:Snap.Hutao.Control.Markup" xmlns:shcm="using:Snap.Hutao.Control.Markup"
xmlns:shvg="using:Snap.Hutao.ViewModel.Guide" xmlns:shvg="using:Snap.Hutao.ViewModel.Guide"
@@ -224,10 +223,14 @@
ItemsSource="{Binding StaticResourceOptions.ImageQualities}" ItemsSource="{Binding StaticResourceOptions.ImageQualities}"
SelectedItem="{Binding StaticResourceOptions.ImageQuality, Mode=TwoWay}"/> SelectedItem="{Binding StaticResourceOptions.ImageQuality, Mode=TwoWay}"/>
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{shcm:ResourceString Name=ViewGuideStepStaticResourceSettingMinimumHeader}"/> <TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{shcm:ResourceString Name=ViewGuideStepStaticResourceSettingMinimumHeader}"/>
<ToggleSwitch <ListView
IsOn="{Binding StaticResourceOptions.UseTrimmedArchive, Mode=TwoWay}" MinWidth="320"
OffContent="{shcm:ResourceString Name=ViewGuideStepStaticResourceSettingMinimumOff}" Margin="0,8,0,32"
OnContent="{shcm:ResourceString Name=ViewGuideStepStaticResourceSettingMinimumOn}"/> DisplayMemberPath="Name"
ItemsSource="{Binding StaticResourceOptions.ImageArchives}"
SelectedItem="{Binding StaticResourceOptions.ImageArchive, Mode=TwoWay}"/>
<TextBlock Margin="0,16,0,0" Text="{Binding StaticResourceOptions.SizeInformationText, Mode=OneWay}"/>
</StackPanel> </StackPanel>
<TextBlock <TextBlock
@@ -266,12 +269,20 @@
HorizontalAlignment="Center" HorizontalAlignment="Center"
IsHitTestVisible="False" IsHitTestVisible="False"
SelectedIndex="{Binding State, Mode=TwoWay}"> SelectedIndex="{Binding State, Mode=TwoWay}">
<!--
<cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepLanguage}" Icon="{shcm:FontIcon Glyph=&#xF2B7;}"/> <cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepLanguage}" Icon="{shcm:FontIcon Glyph=&#xF2B7;}"/>
<cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepDocument}" Icon="{shcm:FontIcon Glyph=&#xF28B;}"/> <cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepDocument}" Icon="{shcm:FontIcon Glyph=&#xF28B;}"/>
<cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepEnvironment}" Icon="{shcm:FontIcon Glyph=&#xE81E;}"/> <cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepEnvironment}" Icon="{shcm:FontIcon Glyph=&#xE81E;}"/>
<cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepCommonSetting}" Icon="{shcm:FontIcon Glyph=&#xE713;}"/> <cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepCommonSetting}" Icon="{shcm:FontIcon Glyph=&#xE713;}"/>
<cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepStaticResourceSetting}" Icon="{shcm:FontIcon Glyph=&#xE8BA;}"/> <cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepStaticResourceSetting}" Icon="{shcm:FontIcon Glyph=&#xE8BA;}"/>
<cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepStaticResource}" Icon="{shcm:FontIcon Glyph=&#xE8B9;}"/> <cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepStaticResource}" Icon="{shcm:FontIcon Glyph=&#xE8B9;}"/>
-->
<cwc:SegmentedItem Icon="{shcm:FontIcon Glyph=&#xF2B7;}"/>
<cwc:SegmentedItem Icon="{shcm:FontIcon Glyph=&#xF28B;}"/>
<cwc:SegmentedItem Icon="{shcm:FontIcon Glyph=&#xE81E;}"/>
<cwc:SegmentedItem Icon="{shcm:FontIcon Glyph=&#xE713;}"/>
<cwc:SegmentedItem Icon="{shcm:FontIcon Glyph=&#xE8BA;}"/>
<cwc:SegmentedItem Icon="{shcm:FontIcon Glyph=&#xE8B9;}"/>
</cwc:Segmented> </cwc:Segmented>
<Button <Button
Command="{Binding NextOrCompleteCommand}" Command="{Binding NextOrCompleteCommand}"

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license. // Licensed under the MIT license.
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control.Extension;
using Snap.Hutao.ViewModel.Guide; using Snap.Hutao.ViewModel.Guide;
namespace Snap.Hutao.View.Guide; namespace Snap.Hutao.View.Guide;
@@ -14,6 +15,6 @@ internal sealed partial class GuideView : UserControl
public GuideView() public GuideView()
{ {
InitializeComponent(); InitializeComponent();
DataContext = Ioc.Default.GetRequiredService<GuideViewModel>(); DataContext = this.ServiceProvider().GetRequiredService<GuideViewModel>();
} }
} }

View File

@@ -7,6 +7,8 @@ using Snap.Hutao.Core.Setting;
using Snap.Hutao.Model; using Snap.Hutao.Model;
using Snap.Hutao.Service; using Snap.Hutao.Service;
using Snap.Hutao.Web.Hoyolab; using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Hutao;
using Snap.Hutao.Web.Hutao.Response;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Globalization; using System.Globalization;
@@ -164,6 +166,19 @@ internal sealed partial class GuideViewModel : Abstraction.ViewModel
set => SetProperty(ref downloadSummaries, value); set => SetProperty(ref downloadSummaries, value);
} }
protected override async ValueTask<bool> InitializeUIAsync()
{
HutaoInfrastructureClient hutaoInfrastructureClient = serviceProvider.GetRequiredService<HutaoInfrastructureClient>();
HutaoResponse<StaticResourceSizeInformation> response = await hutaoInfrastructureClient.GetStaticSizeAsync().ConfigureAwait(false);
if (response.IsOk())
{
await taskContext.SwitchToMainThreadAsync();
StaticResourceOptions.SizeInformation = response.Data;
}
return true;
}
[Command("NextOrCompleteCommand")] [Command("NextOrCompleteCommand")]
private void NextOrComplete() private void NextOrComplete()
{ {

View File

@@ -46,31 +46,31 @@ internal static class StaticResource
private static readonly ApplicationDataCompositeValue LatestResourceVersionMap = new() private static readonly ApplicationDataCompositeValue LatestResourceVersionMap = new()
{ {
{ "AchievementIcon", 1 }, { "AchievementIcon", 2 },
{ "AvatarCard", 1 }, { "AvatarCard", 2 },
{ "AvatarIcon", 4 }, { "AvatarIcon", 5 },
{ "Bg", 2 }, { "Bg", 3 },
{ "ChapterIcon", 2 }, { "ChapterIcon", 3 },
{ "CodexMonster", 0 }, { "CodexMonster", 0 },
{ "Costume", 1 }, { "Costume", 2 },
{ "EmotionIcon", 2 }, { "EmotionIcon", 3 },
{ "EquipIcon", 3 }, { "EquipIcon", 4 },
{ "GachaAvatarIcon", 3 }, { "GachaAvatarIcon", 4 },
{ "GachaAvatarImg", 3 }, { "GachaAvatarImg", 4 },
{ "GachaEquipIcon", 3 }, { "GachaEquipIcon", 4 },
{ "GcgCharAvatarIcon", 0 }, { "GcgCharAvatarIcon", 0 },
{ "IconElement", 2 }, { "IconElement", 3 },
{ "ItemIcon", 3 }, { "ItemIcon", 4 },
{ "LoadingPic", 1 }, { "LoadingPic", 2 },
{ "MonsterIcon", 2 }, { "MonsterIcon", 3 },
{ "MonsterSmallIcon", 1 }, { "MonsterSmallIcon", 2 },
{ "NameCardIcon", 2 }, { "NameCardIcon", 3 },
{ "NameCardPic", 3 }, { "NameCardPic", 4 },
{ "NameCardPicAlpha", 0 }, { "NameCardPicAlpha", 0 },
{ "Property", 1 }, { "Property", 2 },
{ "RelicIcon", 3 }, { "RelicIcon", 4 },
{ "Skill", 3 }, { "Skill", 4 },
{ "Talent", 3 }, { "Talent", 4 },
}; };
public static void FulfillAll() public static void FulfillAll()

View File

@@ -0,0 +1,14 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.ViewModel.Guide;
[Localization]
internal enum StaticResourceArchive
{
[LocalizationKey(nameof(SH.ViewGuideStepStaticResourceSettingMinimumOff))]
Full,
[LocalizationKey(nameof(SH.ViewGuideStepStaticResourceSettingMinimumOn))]
Minimum,
}

View File

@@ -15,6 +15,12 @@ internal static class StaticResourceHttpHeaderBuilderExtension
{ {
return builder return builder
.SetHeader("x-quality", $"{UnsafeLocalSetting.Get(SettingKeys.StaticResourceImageQuality, StaticResourceQuality.Raw)}") .SetHeader("x-quality", $"{UnsafeLocalSetting.Get(SettingKeys.StaticResourceImageQuality, StaticResourceQuality.Raw)}")
.SetHeader("x-minimum", $"{LocalSetting.Get(SettingKeys.StaticResourceUseTrimmedArchive, false)}"); .SetHeader("x-archive", $"{UnsafeLocalSetting.Get(SettingKeys.StaticResourceImageArchive, StaticResourceArchive.Full)}");
}
public static TBuilder SetStaticResourceControlHeadersIf<TBuilder>(this TBuilder builder, bool condition)
where TBuilder : IHttpHeadersBuilder<HttpHeaders>
{
return condition ? builder.SetStaticResourceControlHeaders() : builder;
} }
} }

View File

@@ -1,41 +1,82 @@
// Copyright (c) DGP Studio. All rights reserved. // Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license. // Licensed under the MIT license.
using CommunityToolkit.Common;
using CommunityToolkit.Mvvm.ComponentModel;
using Snap.Hutao.Core.Setting; using Snap.Hutao.Core.Setting;
using Snap.Hutao.Model; using Snap.Hutao.Model;
using Snap.Hutao.Web.Hutao;
namespace Snap.Hutao.ViewModel.Guide; namespace Snap.Hutao.ViewModel.Guide;
[Injection(InjectAs.Singleton)] [Injection(InjectAs.Singleton)]
internal sealed class StaticResourceOptions internal sealed class StaticResourceOptions : ObservableObject
{ {
private readonly List<NameValue<StaticResourceQuality>> imageQualities = CollectionsNameValue.FromEnum<StaticResourceQuality>(q => q.GetLocalizedDescription()); private readonly List<NameValue<StaticResourceQuality>> imageQualities = CollectionsNameValue.FromEnum<StaticResourceQuality>(q => q.GetLocalizedDescription());
private readonly List<NameValue<StaticResourceArchive>> imageArchives = CollectionsNameValue.FromEnum<StaticResourceArchive>(a => a.GetLocalizedDescription());
private NameValue<StaticResourceQuality>? imageQuality; private NameValue<StaticResourceQuality>? imageQuality;
private NameValue<StaticResourceArchive>? imageArchive;
private string? sizeInformationText;
public StaticResourceOptions() private StaticResourceSizeInformation? sizeInformation;
{
ImageQuality = ImageQualities.First(q => q.Value == UnsafeLocalSetting.Get(SettingKeys.StaticResourceImageQuality, StaticResourceQuality.Raw));
}
public List<NameValue<StaticResourceQuality>> ImageQualities { get => imageQualities; } public List<NameValue<StaticResourceQuality>> ImageQualities { get => imageQualities; }
public NameValue<StaticResourceQuality>? ImageQuality public NameValue<StaticResourceQuality>? ImageQuality
{ {
get => imageQuality; get => imageQuality ??= ImageQualities.First(q => q.Value == UnsafeLocalSetting.Get(SettingKeys.StaticResourceImageQuality, StaticResourceQuality.Raw));
set set
{ {
if (value is not null) if (SetProperty(ref imageQuality, value) && value is not null)
{ {
imageQuality = value;
UnsafeLocalSetting.Set(SettingKeys.StaticResourceImageQuality, value.Value); UnsafeLocalSetting.Set(SettingKeys.StaticResourceImageQuality, value.Value);
UpdateSizeInformationText();
} }
} }
} }
public bool UseTrimmedArchive public List<NameValue<StaticResourceArchive>> ImageArchives { get => imageArchives; }
public NameValue<StaticResourceArchive>? ImageArchive
{ {
get => LocalSetting.Get(SettingKeys.StaticResourceUseTrimmedArchive, false); get => imageArchive ??= ImageArchives.First(a => a.Value == UnsafeLocalSetting.Get(SettingKeys.StaticResourceImageArchive, StaticResourceArchive.Full));
set => LocalSetting.Set(SettingKeys.StaticResourceUseTrimmedArchive, value); set
{
if (SetProperty(ref imageArchive, value) && value is not null)
{
UnsafeLocalSetting.Set(SettingKeys.StaticResourceImageArchive, value.Value);
UpdateSizeInformationText();
}
}
}
public StaticResourceSizeInformation? SizeInformation
{
get => sizeInformation;
set
{
sizeInformation = value;
UpdateSizeInformationText();
}
}
public string? SizeInformationText { get => sizeInformationText; set => SetProperty(ref sizeInformationText, value); }
private void UpdateSizeInformationText()
{
if (SizeInformation is not null)
{
long result = (ImageQuality?.Value, ImageArchive?.Value) switch
{
(StaticResourceQuality.Raw, StaticResourceArchive.Full) => SizeInformation.RawFull,
(StaticResourceQuality.Raw, StaticResourceArchive.Minimum) => SizeInformation.RawMinimum,
(StaticResourceQuality.High, StaticResourceArchive.Full) => SizeInformation.HighFull,
(StaticResourceQuality.High, StaticResourceArchive.Minimum) => SizeInformation.HighMinimum,
_ => 0,
};
SizeInformationText = SH.FormatViewGuideStaticResourceDownloadSize(Converters.ToFileSizeString(result));
}
} }
} }

View File

@@ -19,12 +19,10 @@ internal sealed partial class PandaClient
public async ValueTask<Response<UrlWrapper>> QRCodeFetchAsync(CancellationToken token = default) public async ValueTask<Response<UrlWrapper>> QRCodeFetchAsync(CancellationToken token = default)
{ {
// Use 12 (zzz) instead of 4 (gi) temporarily to get legacy game token GameLoginRequest options = GameLoginRequest.Create(4, HoyolabOptions.DeviceId40);
GameLoginRequest options = GameLoginRequest.Create(12, HoyolabOptions.DeviceId40);
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(ApiEndpoints.QrCodeFetch) .SetRequestUri(ApiEndpoints.QrCodeFetch)
.SetHeader("x-rpc-device_id", HoyolabOptions.DeviceId40)
.PostJson(options); .PostJson(options);
Response<UrlWrapper>? resp = await builder Response<UrlWrapper>? resp = await builder
@@ -36,11 +34,10 @@ internal sealed partial class PandaClient
public async ValueTask<Response<GameLoginResult>> QRCodeQueryAsync(string ticket, CancellationToken token = default) public async ValueTask<Response<GameLoginResult>> QRCodeQueryAsync(string ticket, CancellationToken token = default)
{ {
GameLoginRequest options = GameLoginRequest.Create(12, HoyolabOptions.DeviceId40, ticket); GameLoginRequest options = GameLoginRequest.Create(4, HoyolabOptions.DeviceId40, ticket);
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(ApiEndpoints.QrCodeQuery) .SetRequestUri(ApiEndpoints.QrCodeQuery)
.SetHeader("x-rpc-device_id", HoyolabOptions.DeviceId40)
.PostJson(options); .PostJson(options);
Response<GameLoginResult>? resp = await builder Response<GameLoginResult>? resp = await builder

View File

@@ -79,7 +79,6 @@ internal sealed partial class PassportClient2
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(ApiEndpoints.AccountGetSTokenByGameToken) .SetRequestUri(ApiEndpoints.AccountGetSTokenByGameToken)
.SetHeader("x-rpc-device_id", HoyolabOptions.DeviceId40)
.PostJson(data); .PostJson(data);
Response<LoginResult>? resp = await builder Response<LoginResult>? resp = await builder

View File

@@ -17,6 +17,16 @@ internal sealed partial class HutaoInfrastructureClient
private readonly ILogger<HutaoInfrastructureClient> logger; private readonly ILogger<HutaoInfrastructureClient> logger;
private readonly HttpClient httpClient; private readonly HttpClient httpClient;
public async ValueTask<HutaoResponse<StaticResourceSizeInformation>> GetStaticSizeAsync(CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.StaticSize)
.Get();
HutaoResponse<StaticResourceSizeInformation>? resp = await builder.TryCatchSendAsync<HutaoResponse<StaticResourceSizeInformation>>(httpClient, logger, token).ConfigureAwait(false);
return Web.Response.Response.DefaultIfNull(resp);
}
public async ValueTask<HutaoResponse<IPInformation>> GetIPInformationAsync(CancellationToken token = default) public async ValueTask<HutaoResponse<IPInformation>> GetIPInformationAsync(CancellationToken token = default)
{ {
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()

View File

@@ -0,0 +1,19 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hutao;
internal sealed partial class StaticResourceSizeInformation
{
[JsonPropertyName("raw_full")]
public long RawFull { get; set; }
[JsonPropertyName("raw_minimum")]
public long RawMinimum { get; set; }
[JsonPropertyName("tiny_full")]
public long HighFull { get; set; }
[JsonPropertyName("tiny_minimum")]
public long HighMinimum { get; set; }
}

View File

@@ -270,6 +270,8 @@ internal static class HutaoEndpoints
{ {
return $"{ApiSnapGenshinStaticZip}/{fileName}.zip"; return $"{ApiSnapGenshinStaticZip}/{fileName}.zip";
} }
public const string StaticSize = $"{ApiSnapGenshin}/static/size";
#endregion #endregion
#region Wallpaper #region Wallpaper