Compare commits

...

3 Commits

Author SHA1 Message Date
qhy040404
702a3d0e62 refactor launchgame resources 2024-06-17 15:01:36 +08:00
qhy040404
176535ca98 minor change 2024-06-17 13:14:03 +08:00
qhy040404
36eab42692 Add HoyoPlay API 2024-06-17 12:55:17 +08:00
23 changed files with 341 additions and 64 deletions

View File

@@ -45,6 +45,10 @@ internal class LaunchScheme : IEquatable<ChannelOptions>
/// </summary>
public int LauncherId { get; private protected set; }
public string HoyoPlayLauncherId { get; private protected set; } = default!;
public string HoyoPlayGameId { get; private protected set; } = default!;
/// <summary>
/// API Key
/// </summary>

View File

@@ -9,11 +9,15 @@ internal sealed class LaunchSchemeBilibili : LaunchScheme
{
private const int SdkStaticLauncherBilibiliId = 17;
private const string SdkStaticLauncherBilibiliKey = "KAtdSsoQ";
private const string HoyoPlayLauncherBilibiliId = "umfgRO5gh5";
private const string HoyoPlayGameBilibiliId = "T2S0Gz4Dr2";
public LaunchSchemeBilibili(SubChannelType subChannel, bool isNotCompatOnly = true)
{
LauncherId = SdkStaticLauncherBilibiliId;
Key = SdkStaticLauncherBilibiliKey;
HoyoPlayLauncherId = HoyoPlayLauncherBilibiliId;
HoyoPlayGameId = HoyoPlayGameBilibiliId;
Channel = ChannelType.Bili;
SubChannel = subChannel;
IsOversea = false;

View File

@@ -9,11 +9,15 @@ internal sealed class LaunchSchemeChinese : LaunchScheme
{
private const int SdkStaticLauncherChineseId = 18;
private const string SdkStaticLauncherChineseKey = "eYd89JmJ";
private const string HoyoPlayLauncherChineseId = "jGHBHlcOq1";
private const string HoyoPlayGameChineseId = "1Z8W5NHUQb";
public LaunchSchemeChinese(ChannelType channel, SubChannelType subChannel, bool isNotCompatOnly = true)
{
LauncherId = SdkStaticLauncherChineseId;
Key = SdkStaticLauncherChineseKey;
HoyoPlayLauncherId = HoyoPlayLauncherChineseId;
HoyoPlayGameId = HoyoPlayGameChineseId;
Channel = channel;
SubChannel = subChannel;
IsOversea = false;

View File

@@ -9,11 +9,15 @@ internal sealed class LaunchSchemeOversea : LaunchScheme
{
private const int SdkStaticLauncherOverseaId = 10;
private const string SdkStaticLauncherOverseaKey = "gcStgarh";
private const string HoyoPlayLauncherOverseaId = "VYTpXlbWo8";
private const string HoyoPlayGameOverseaId = "gopR6Cufr3";
public LaunchSchemeOversea(ChannelType channel, SubChannelType subChannel, bool isNotCompatOnly = true)
{
LauncherId = SdkStaticLauncherOverseaId;
Key = SdkStaticLauncherOverseaKey;
HoyoPlayLauncherId = HoyoPlayLauncherOverseaId;
HoyoPlayGameId = HoyoPlayGameOverseaId;
Channel = channel;
SubChannel = subChannel;
IsOversea = true;

View File

@@ -6,12 +6,13 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:shcm="using:Snap.Hutao.Control.Markup"
xmlns:shwhshlr="using:Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher.Resource"
xmlns:shwhhpc="using:Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect"
xmlns:shwhhpcp="using:Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Package"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
d:DataContext="{d:DesignInstance shwhshlr:Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher.Resource.Package}"
d:DataContext="{d:DesignInstance shwhhpcp:Package}"
IsExpanded="True"
ItemsSource="{Binding VoicePacks}"
ItemsSource="{Binding AllPackages}"
mc:Ignorable="d">
<cwc:SettingsExpander.Resources>
@@ -24,7 +25,7 @@
</cwc:SettingsExpander.Resources>
<cwc:SettingsExpander.ItemTemplate>
<DataTemplate x:DataType="shwhshlr:VoicePackage">
<DataTemplate x:DataType="shwhhpc:PackageSegment">
<cwc:SettingsCard
Padding="{ThemeResource SettingsExpanderItemHasIconPadding}"
ActionIcon="{shcm:FontIcon Glyph={StaticResource FontIconContentCopy}}"
@@ -42,13 +43,13 @@
Margin="8,0,0,0"
HorizontalAlignment="Left"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding PackageSize, Converter={StaticResource FileSizeToFriendlyStringConverter}}"/>
Text="{Binding Size, Converter={StaticResource FileSizeToFriendlyStringConverter}}"/>
<FontIcon FontSize="{StaticResource CaptionTextBlockFontSize}" Glyph="{StaticResource FontIconContentFolder}"/>
<TextBlock
Width="80"
Margin="8,0,0,0"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding Size, Converter={StaticResource FileSizeToFriendlyStringConverter}}"/>
Text="{Binding DecompressedSize, Converter={StaticResource FileSizeToFriendlyStringConverter}}"/>
<FontIcon FontSize="{StaticResource CaptionTextBlockFontSize}" Glyph="{StaticResource FontIconContentAsteriskBadge12}"/>
<TextBlock
Margin="8,0,0,0"
@@ -60,45 +61,4 @@
</cwc:SettingsCard>
</DataTemplate>
</cwc:SettingsExpander.ItemTemplate>
<cwc:SettingsExpander.ItemsHeader>
<cwc:SettingsCard
MinHeight="52"
Padding="{ThemeResource SettingsExpanderItemHasIconPadding}"
ActionIcon="{shcm:FontIcon Glyph={StaticResource FontIconContentCopy}}"
Background="{ThemeResource InfoBarInformationalSeverityBackgroundBrush}"
BorderThickness="0"
Command="{Binding CopyPathCommand}"
CornerRadius="0"
Header="{Binding DisplayName}"
IsClickEnabled="True">
<cwc:SettingsCard.Description>
<StackPanel
Grid.Row="1"
Margin="0,4,0,0"
Orientation="Horizontal">
<FontIcon FontSize="{StaticResource CaptionTextBlockFontSize}" Glyph="{StaticResource FontIconContentZipFolder}"/>
<TextBlock
Width="80"
Margin="8,0,0,0"
HorizontalAlignment="Left"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding PackageSize, Converter={StaticResource FileSizeToFriendlyStringConverter}}"/>
<FontIcon FontSize="{StaticResource CaptionTextBlockFontSize}" Glyph="{StaticResource FontIconContentFolder}"/>
<TextBlock
Width="80"
Margin="8,0,0,0"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding Size, Converter={StaticResource FileSizeToFriendlyStringConverter}}"/>
<FontIcon FontSize="{StaticResource CaptionTextBlockFontSize}" Glyph="{StaticResource FontIconContentAsteriskBadge12}"/>
<TextBlock
Margin="8,0,0,0"
IsTextSelectionEnabled="True"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding Md5}"/>
</StackPanel>
</cwc:SettingsCard.Description>
</cwc:SettingsCard>
</cwc:SettingsExpander.ItemsHeader>
</cwc:SettingsExpander>

View File

@@ -366,13 +366,13 @@
</PivotItem>
<PivotItem Header="{shcm:ResourceString Name=ViewPageLaunchGameResourceHeader}">
<Grid>
<ScrollViewer Visibility="{Binding GameResource, Converter={StaticResource EmptyObjectToBoolConverter}}">
<ScrollViewer Visibility="{Binding GamePackage, Converter={StaticResource EmptyObjectToBoolConverter}}">
<StackPanel>
<Border Margin="16,16,16,0" cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
<Border Style="{ThemeResource AcrylicBorderCardStyle}">
<shvc:LaunchGameResourceExpander
cw:Effects.Shadow="{ThemeResource CompatCardShadow}"
DataContext="{Binding GameResource.PreDownloadGame.Latest, Mode=OneWay}"
DataContext="{Binding GamePackage.PreDownload.Major, Mode=OneWay}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameResourcePreDownloadHeader}"
IsEnabled="{Binding FallbackValue=False, Converter={StaticResource EmptyObjectToBoolConverter}}"
IsExpanded="{Binding FallbackValue=False, Converter={StaticResource EmptyObjectToBoolConverter}, Mode=OneTime}"/>
@@ -382,22 +382,22 @@
<ItemsControl
Margin="0,0,0,0"
ItemTemplate="{StaticResource GameResourceTemplate}"
ItemsSource="{Binding GameResource.PreDownloadGame.Diffs, Mode=OneWay}"/>
ItemsSource="{Binding GamePackage.PreDownload.Patches, Mode=OneWay}"/>
<Border Margin="16,16,16,0" cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
<Border Style="{ThemeResource AcrylicBorderCardStyle}">
<shvc:LaunchGameResourceExpander
cw:Effects.Shadow="{ThemeResource CompatCardShadow}"
DataContext="{Binding GameResource.Game.Latest, Mode=OneWay}"
DataContext="{Binding GamePackage.Main.Major, Mode=OneWay}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameResourceLatestHeader}"/>
</Border>
</Border>
<ItemsControl
Margin="0,0,0,16"
ItemTemplate="{StaticResource GameResourceTemplate}"
ItemsSource="{Binding GameResource.Game.Diffs, Mode=OneWay}"/>
ItemsSource="{Binding GamePackage.Main.Patches, Mode=OneWay}"/>
</StackPanel>
</ScrollViewer>
<shvc:LoadingView IsLoading="{Binding GameResource, Converter={StaticResource EmptyObjectToBoolRevertConverter}}"/>
<shvc:LoadingView IsLoading="{Binding GamePackage, Converter={StaticResource EmptyObjectToBoolRevertConverter}}"/>
</Grid>
</PivotItem>
</Pivot>

View File

@@ -15,8 +15,8 @@ using Snap.Hutao.Service.Game.PathAbstraction;
using Snap.Hutao.Service.Game.Scheme;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.Service.User;
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher.Resource;
using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect;
using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Package;
using System.Collections.Immutable;
using System.IO;
@@ -39,7 +39,6 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel, IView
private readonly IGameLocatorFactory gameLocatorFactory;
private readonly LaunchGameShared launchGameShared;
private readonly IInfoBarService infoBarService;
private readonly ResourceClient resourceClient;
private readonly RuntimeOptions runtimeOptions;
private readonly LaunchOptions launchOptions;
private readonly IUserService userService;
@@ -47,11 +46,12 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel, IView
private readonly IGameServiceFacade gameService;
private readonly IMemoryCache memoryCache;
private readonly AppOptions appOptions;
private readonly HoyoPlayClient hoyoPlayClient;
private LaunchScheme? selectedScheme;
private AdvancedCollectionView<GameAccount>? gameAccountsView;
private GameAccount? selectedGameAccount;
private GameResource? gameResource;
private GamePackage? gamePackage;
private bool gamePathSelectedAndValid;
private ImmutableList<GamePathEntry> gamePathEntries;
private GamePathEntry? selectedGamePathEntry;
@@ -69,7 +69,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel, IView
public List<LaunchScheme> KnownSchemes { get; } = KnownLaunchSchemes.Get();
[AlsoAsyncSets(nameof(GameResource), nameof(GameAccountsView))]
[AlsoAsyncSets(nameof(GameAccountsView), nameof(GamePackage))]
public LaunchScheme? SelectedScheme
{
get => selectedScheme;
@@ -80,7 +80,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel, IView
public GameAccount? SelectedGameAccount { get => selectedGameAccount; set => SetProperty(ref selectedGameAccount, value); }
public GameResource? GameResource { get => gameResource; set => SetProperty(ref gameResource, value); }
public GamePackage? GamePackage { get => gamePackage; set => SetProperty(ref gamePackage, value); }
public bool GamePathSelectedAndValid
{
@@ -301,10 +301,10 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel, IView
SelectedGameAccount = default;
await UpdateGameAccountsViewAsync().ConfigureAwait(false);
UpdateGameResourceAsync(value).SafeForget();
UpdateGamePackageAsync(value).SafeForget();
}
async ValueTask UpdateGameResourceAsync(LaunchScheme? scheme)
async ValueTask UpdateGamePackageAsync(LaunchScheme? scheme)
{
if (scheme is null)
{
@@ -312,14 +312,14 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel, IView
}
await taskContext.SwitchToBackgroundAsync();
Web.Response.Response<GameResource> response = await resourceClient
.GetResourceAsync(scheme)
Web.Response.Response<GamePackages> response = await hoyoPlayClient
.GetPackagesAsync(scheme)
.ConfigureAwait(false);
if (response.IsOk())
{
await taskContext.SwitchToMainThreadAsync();
GameResource = response.Data;
GamePackage = response.Data.Packages.Single();
}
}

View File

@@ -284,6 +284,25 @@ internal static class ApiEndpoints
#endregion
#region HoyoPlayApi
public static string HoyoPlayConnectGamePackages(LaunchScheme scheme)
{
return $"{HoyoPlayApiConnectApi}/getGamePackages?game_ids[]={scheme.HoyoPlayGameId}&launcher_id={scheme.HoyoPlayLauncherId}";
}
public static string HoyoPlayConnectGameChannelSDKs(LaunchScheme scheme)
{
return $"{HoyoPlayApiConnectApi}/getGameChannelSDKs?channel={scheme.Channel:D}&game_ids[]={scheme.HoyoPlayGameId}&launcher_id={scheme.HoyoPlayLauncherId}&sub_channel={scheme.SubChannel:D}";
}
public static string HoyoPlayConnectDeprecatedFileConfigs(LaunchScheme scheme)
{
return $"{HoyoPlayApiConnectApi}/getGameDeprecatedFileConfigs?channel={scheme.Channel:D}&game_ids[]={scheme.HoyoPlayGameId}&launcher_id={scheme.HoyoPlayLauncherId}&sub_channel={scheme.SubChannel:D}";
}
#endregion
#region PassportApi | PassportApiV4
/// <summary>
@@ -395,6 +414,9 @@ internal static class ApiEndpoints
private const string Hk4eSdk = "https://hk4e-sdk.mihoyo.com";
private const string HoyoPlayApi = "https://hyp-api.mihoyo.com";
private const string HoyoPlayApiConnectApi = $"{HoyoPlayApi}/hyp/hyp-connect/api";
private const string PassportApi = "https://passport-api.mihoyo.com";
private const string PassportApiAuthApi = $"{PassportApi}/account/auth/api";
private const string PassportApiV4 = "https://passport-api-v4.mihoyo.com";

View File

@@ -298,6 +298,25 @@ internal static class ApiOsEndpoints
}
#endregion
#region SgHoyoPlayApi
public static string SgHoyoPlayConnectGamePackages(LaunchScheme scheme)
{
return $"{SgHoyoPlayApiConnectApi}/getGamePackages?game_ids[]={scheme.HoyoPlayGameId}&launcher_id={scheme.HoyoPlayLauncherId}";
}
public static string SgHoyoPlayConnectGameChannelSDKs(LaunchScheme scheme)
{
return $"{SgHoyoPlayApiConnectApi}/getGameChannelSDKs?channel={scheme.Channel:D}&game_ids[]={scheme.HoyoPlayGameId}&launcher_id={scheme.HoyoPlayLauncherId}&sub_channel={scheme.SubChannel:D}";
}
public static string SgHoyoPlayConnectDeprecatedFileConfigs(LaunchScheme scheme)
{
return $"{SgHoyoPlayApiConnectApi}/getGameDeprecatedFileConfigs?channel={scheme.Channel:D}&game_ids[]={scheme.HoyoPlayGameId}&launcher_id={scheme.HoyoPlayLauncherId}&sub_channel={scheme.SubChannel:D}";
}
#endregion
#region SdkOsStaticLauncherApi
public static string SdkOsStaticLauncherResource(LaunchScheme scheme)
@@ -344,6 +363,9 @@ internal static class ApiOsEndpoints
private const string SgPublicApi = "https://sg-public-api.hoyoverse.com";
private const string SgHk4eApi = "https://sg-hk4e-api.hoyoverse.com";
private const string SgHoyoPlayApi = "https://sg-hyp-api.hoyoverse.com";
private const string SgHoyoPlayApiConnectApi = $"{SgHoyoPlayApi}/hyp/hyp-connect/api";
private const string WebApiOs = "https://webapi-os.account.hoyoverse.com";
private const string WebApiOsAccountApi = $"{WebApiOs}/Api";

View File

@@ -0,0 +1,16 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.ChannelSDK;
internal sealed class ChannelSDK : GameSpecified
{
[JsonPropertyName("channel_sdk_pkg")]
public PackageSegment ChannelSdkPackage { get; set; } = default!;
[JsonPropertyName("pkg_version_file_name")]
public string PackageVersionFileName { get; set; } = default!;
[JsonPropertyName("version")]
public string Version { get; set; } = default!;
}

View File

@@ -0,0 +1,10 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.ChannelSDK;
internal sealed class ChannelSDKs
{
[JsonPropertyName("game_channel_sdks")]
public List<ChannelSDK> GameChannelSDKs { get; set; } = default!;
}

View File

@@ -0,0 +1,10 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.DeprecatedFile;
internal sealed class DeprecatedFile
{
[JsonPropertyName("name")]
public string Name { get; set; } = default!;
}

View File

@@ -0,0 +1,10 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.DeprecatedFile;
internal sealed class DeprecatedFileConfig : GameSpecified
{
[JsonPropertyName("deprecated_files")]
public List<DeprecatedFile> DeprecatedFiles { get; set; } = default!;
}

View File

@@ -0,0 +1,10 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.DeprecatedFile;
internal sealed class DeprecatedFileConfigs
{
[JsonPropertyName("deprecated_file_configs")]
public List<DeprecatedFileConfig> Configs { get; set; } = default!;
}

View File

@@ -0,0 +1,13 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect;
internal sealed class Game
{
[JsonPropertyName("id")]
public string Id { get; set; } = default!;
[JsonPropertyName("biz")]
public string Biz { get; set; } = default!;
}

View File

@@ -0,0 +1,10 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect;
internal class GameSpecified
{
[JsonPropertyName("game")]
public Game Game { get; set; } = default!;
}

View File

@@ -0,0 +1,74 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.Service.Game.Scheme;
using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.ChannelSDK;
using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.DeprecatedFile;
using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Package;
using Snap.Hutao.Web.Request.Builder;
using Snap.Hutao.Web.Request.Builder.Abstraction;
using Snap.Hutao.Web.Response;
using System.Net.Http;
namespace Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect;
[ConstructorGenerated(ResolveHttpClient = true)]
[HttpClient(HttpClientConfiguration.Default)]
internal sealed partial class HoyoPlayClient
{
private readonly IHttpRequestMessageBuilderFactory httpRequestMessageBuilderFactory;
private readonly HttpClient httpClient;
private readonly ILogger<HoyoPlayClient> logger;
public async ValueTask<Response<GamePackages>> GetPackagesAsync(LaunchScheme scheme, CancellationToken token = default)
{
string url = scheme.IsOversea
? ApiOsEndpoints.SgHoyoPlayConnectGamePackages(scheme)
: ApiEndpoints.HoyoPlayConnectGamePackages(scheme);
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(url)
.Get();
Response<GamePackages>? resp = await builder
.SendAsync<Response<GamePackages>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
}
public async ValueTask<Response<ChannelSDKs>> GetChannelSDKAsync(LaunchScheme scheme, CancellationToken token = default)
{
string url = scheme.IsOversea
? ApiOsEndpoints.SgHoyoPlayConnectGameChannelSDKs(scheme)
: ApiEndpoints.HoyoPlayConnectGameChannelSDKs(scheme);
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(url)
.Get();
Response<ChannelSDKs>? resp = await builder
.SendAsync<Response<ChannelSDKs>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
}
public async ValueTask<Response<DeprecatedFileConfigs>> GetDeprecatedFilesAsync(LaunchScheme scheme, CancellationToken token = default)
{
string url = scheme.IsOversea
? ApiOsEndpoints.SgHoyoPlayConnectDeprecatedFileConfigs(scheme)
: ApiEndpoints.HoyoPlayConnectDeprecatedFileConfigs(scheme);
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(url)
.Get();
Response<DeprecatedFileConfigs>? resp = await builder
.SendAsync<Response<DeprecatedFileConfigs>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
}
}

View File

@@ -0,0 +1,10 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Package;
internal sealed class AudioPackageSegment : PackageSegment
{
[JsonPropertyName("language")]
public string Language { get; set; } = default!;
}

View File

@@ -0,0 +1,13 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Package;
internal sealed class GameBranch
{
[JsonPropertyName("major")]
public Package? Major { get; set; }
[JsonPropertyName("patches")]
public List<Package> Patches { get; set; } = default!;
}

View File

@@ -0,0 +1,13 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Package;
internal sealed class GamePackage : GameSpecified
{
[JsonPropertyName("main")]
public GameBranch Main { get; set; } = default!;
[JsonPropertyName("pre_download")]
public GameBranch PreDownload { get; set; } = default!;
}

View File

@@ -0,0 +1,10 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Package;
internal sealed class GamePackages
{
[JsonPropertyName("game_packages")]
public List<GamePackage> Packages { get; set; } = default!;
}

View File

@@ -0,0 +1,25 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Package;
internal sealed class Package
{
[JsonPropertyName("version")]
public string Version { get; set; } = default!;
[JsonPropertyName("game_pkgs")]
public List<PackageSegment> GamePackages { get; set; } = default!;
[JsonPropertyName("audio_pkgs")]
public List<AudioPackageSegment> AudioPackages { get; set; } = default!;
[JsonPropertyName("res_list_url")]
public string ResListUrl { get; set; } = default!;
[JsonIgnore]
public List<PackageSegment> AllPackages
{
get => [.. GamePackages, .. AudioPackages];
}
}

View File

@@ -0,0 +1,33 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.IO.DataTransfer;
using Snap.Hutao.Service.Notification;
namespace Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect;
internal partial class PackageSegment
{
[JsonPropertyName("url")]
public string Url { get; set; } = default!;
[JsonPropertyName("md5")]
public string Md5 { get; set; } = default!;
[JsonPropertyName("size")]
public long Size { get; set; } = default!;
[JsonPropertyName("decompressed_size")]
public long DecompressedSize { get; set; } = default!;
[JsonIgnore]
public string DisplayName { get => System.IO.Path.GetFileName(Url); }
[Command("CopyPathCommand")]
private void CopyPathToClipboard()
{
IServiceProvider serviceProvider = Ioc.Default;
serviceProvider.GetRequiredService<IClipboardProvider>().SetText(Url);
serviceProvider.GetRequiredService<IInfoBarService>().Success(SH.WebGameResourcePathCopySucceed);
}
}