diff --git a/src/Snap.Hutao/Snap.Hutao/Web/ApiEndpoints.cs b/src/Snap.Hutao/Snap.Hutao/Web/ApiEndpoints.cs index 4b16269a..058ba859 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/ApiEndpoints.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/ApiEndpoints.cs @@ -4,6 +4,7 @@ using Snap.Hutao.Model.Primitive; using Snap.Hutao.Service.Game.Scheme; using Snap.Hutao.Web.Hoyolab; +using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Branch; namespace Snap.Hutao.Web; @@ -157,6 +158,15 @@ internal static class ApiEndpoints public const string GameRecordRoleCombatPath = $"{ApiTakumiRecordApi}/role_combat"; #endregion + #region ApiTakumiDownloaderApi + + public static string SophonChunkGetBuild(BranchWrapper branch) + { + return $"{ApiTakumiDownloaderApi}/sophon_chunk/getBuild?branch={branch.Branch}&package_id={branch.PackageId}&password={branch.Password}"; + } + + #endregion + #region ApiTakumiEventCalculate #region V1 @@ -307,6 +317,11 @@ internal static class ApiEndpoints return $"{HoyoPlayApiConnectApi}/getGameDeprecatedFileConfigs?channel={scheme.Channel:D}&game_ids[]={scheme.GameId}&launcher_id={scheme.LauncherId}&sub_channel={scheme.SubChannel:D}"; } + public static string HoyoPlayConnectGameBranches(LaunchScheme scheme) + { + return $"{HoyoPlayApiConnectApi}/getGameBranches?game_ids[]={scheme.GameId}&launcher_id={scheme.LauncherId}"; + } + #endregion #region PassportApi | PassportApiV4 @@ -381,6 +396,8 @@ internal static class ApiEndpoints private const string ApiTakumiCardApi = $"{ApiTakumiRecord}/game_record/app/card/api"; private const string ApiTakumiCardWApi = $"{ApiTakumiRecord}/game_record/app/card/wapi"; + private const string ApiTakumiDownloaderApi = $"{ApiTakumi}/downloader"; + private const string ApiTakumiEvent = $"{ApiTakumi}/event"; private const string ApiTakumiEventCalculate = $"{ApiTakumiEvent}/e20200928calculate"; private const string ApiTakumiEventLuna = $"{ApiTakumiEvent}/luna"; @@ -423,4 +440,4 @@ internal static class ApiEndpoints return $"game=hk4e&game_biz=hk4e_cn&lang={languageCode}&bundle_id=hk4e_cn&platform=pc®ion={region}&level=55&uid=100000000"; } #endregion -} \ No newline at end of file +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/ApiOsEndpoints.cs b/src/Snap.Hutao/Snap.Hutao/Web/ApiOsEndpoints.cs index d14a3662..05f00619 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/ApiOsEndpoints.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/ApiOsEndpoints.cs @@ -4,6 +4,7 @@ using Snap.Hutao.Model.Primitive; using Snap.Hutao.Service.Game.Scheme; using Snap.Hutao.Web.Hoyolab; +using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Branch; namespace Snap.Hutao.Web; @@ -254,6 +255,11 @@ internal static class ApiOsEndpoints /// public const string CalculateSyncAvatarList = $"{SgPublicApi}/event/calculateos/sync/avatar/list"; + public static string SophonChunkGetBuild(BranchWrapper branch) + { + return $"{SgPublicApi}/sophon_chunk/getBuild?branch={branch.Branch}&package_id={branch.PackageId}&password={branch.Password}"; + } + #endregion #region SgHk4eApi @@ -316,6 +322,11 @@ internal static class ApiOsEndpoints return $"{SgHoyoPlayApiConnectApi}/getGameDeprecatedFileConfigs?channel={scheme.Channel:D}&game_ids[]={scheme.GameId}&launcher_id={scheme.LauncherId}&sub_channel={scheme.SubChannel:D}"; } + public static string HoyoPlayConnectGameBranches(LaunchScheme scheme) + { + return $"{SgHoyoPlayApiConnectApi}/getGameBranches?game_ids[]={scheme.GameId}&launcher_id={scheme.LauncherId}"; + } + #endregion #region WebApiOsAccountApi @@ -373,4 +384,4 @@ internal static class ApiOsEndpoints } #endregion -} \ No newline at end of file +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyoPlay/Connect/Branch/BranchWrapper.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyoPlay/Connect/Branch/BranchWrapper.cs new file mode 100644 index 00000000..a2874338 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyoPlay/Connect/Branch/BranchWrapper.cs @@ -0,0 +1,19 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Branch; + +internal sealed class BranchWrapper +{ + [JsonPropertyName("package_id")] + public string PackageId { get; set; } = default!; + + [JsonPropertyName("branch")] + public string Branch { get; set; } = default!; + + [JsonPropertyName("password")] + public string Password { get; set; } = default!; + + [JsonPropertyName("tag")] + public string Tag { get; set; } = default!; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyoPlay/Connect/Branch/GameBranch.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyoPlay/Connect/Branch/GameBranch.cs new file mode 100644 index 00000000..17498494 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyoPlay/Connect/Branch/GameBranch.cs @@ -0,0 +1,13 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Branch; + +internal sealed class GameBranch : GameIndexedObject +{ + [JsonPropertyName("main")] + public BranchWrapper Main { get; set; } = default!; + + [JsonPropertyName("pre_download")] + public BranchWrapper PreDownload { get; set; } = default!; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyoPlay/Connect/Branch/GameBranchesWrapper.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyoPlay/Connect/Branch/GameBranchesWrapper.cs new file mode 100644 index 00000000..e98f6df0 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyoPlay/Connect/Branch/GameBranchesWrapper.cs @@ -0,0 +1,10 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Branch; + +internal sealed class GameBranchesWrapper +{ + [JsonPropertyName("game_branches")] + public List GameBranches { get; set; } = default!; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyoPlay/Connect/HoyoPlayClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyoPlay/Connect/HoyoPlayClient.cs index 47db1612..c6204159 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyoPlay/Connect/HoyoPlayClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyoPlay/Connect/HoyoPlayClient.cs @@ -3,6 +3,7 @@ using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; using Snap.Hutao.Service.Game.Scheme; +using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Branch; using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.ChannelSDK; using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.DeprecatedFile; using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Package; @@ -71,4 +72,21 @@ internal sealed partial class HoyoPlayClient return Response.Response.DefaultIfNull(resp); } + + public async ValueTask> GetBranchesAsync(LaunchScheme scheme, CancellationToken token = default) + { + string url = scheme.IsOversea + ? ApiOsEndpoints.HoyoPlayConnectGameBranches(scheme) + : ApiEndpoints.HoyoPlayConnectGameBranches(scheme); + + HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() + .SetRequestUri(url) + .Get(); + + Response? resp = await builder + .SendAsync>(httpClient, logger, token) + .ConfigureAwait(false); + + return Response.Response.DefaultIfNull(resp); + } } diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/ISophonClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/ISophonClient.cs new file mode 100644 index 00000000..cbeaedd8 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/ISophonClient.cs @@ -0,0 +1,12 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Branch; +using Snap.Hutao.Web.Response; + +namespace Snap.Hutao.Web.Hoyolab.Takumi.Downloader; + +internal interface ISophonClient +{ + ValueTask> GetBuildAsync(BranchWrapper branch, CancellationToken token = default); +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/Manifest.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/Manifest.cs new file mode 100644 index 00000000..2eb66ac6 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/Manifest.cs @@ -0,0 +1,19 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hoyolab.Takumi.Downloader; + +internal sealed class Manifest +{ + [JsonPropertyName("id")] + public string Id { get; set; } = default!; + + [JsonPropertyName("checksum")] + public string Checksum { get; set; } = default!; + + [JsonPropertyName("compressed_size")] + public string CompressedSize { get; set; } = default!; + + [JsonPropertyName("uncompressed_size")] + public string UncompressedSize { get; set; } = default!; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/ManifestDownloadInfo.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/ManifestDownloadInfo.cs new file mode 100644 index 00000000..78c5f519 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/ManifestDownloadInfo.cs @@ -0,0 +1,22 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hoyolab.Takumi.Downloader; + +internal sealed class ManifestDownloadInfo +{ + [JsonPropertyName("encryption")] + public uint Encryption { get; set; } + + [JsonPropertyName("password")] + public string Password { get; set; } = default!; + + [JsonPropertyName("compression")] + public uint Compression { get; set; } + + [JsonPropertyName("url_prefix")] + public string UrlPrefix { get; set; } = default!; + + [JsonPropertyName("url_suffix")] + public string UrlSuffix { get; set; } = default!; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/ManifestStats.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/ManifestStats.cs new file mode 100644 index 00000000..3da14dc0 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/ManifestStats.cs @@ -0,0 +1,19 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hoyolab.Takumi.Downloader; + +internal sealed class ManifestStats +{ + [JsonPropertyName("compressed_size")] + public string CompressedSize { get; set; } = default!; + + [JsonPropertyName("uncompressed_size")] + public string UncompressedSize { get; set; } = default!; + + [JsonPropertyName("file_count")] + public string FileCount { get; set; } = default!; + + [JsonPropertyName("chunk_count")] + public string ChunkCount { get; set; } = default!; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/SophonBuild.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/SophonBuild.cs new file mode 100644 index 00000000..993c60f6 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/SophonBuild.cs @@ -0,0 +1,16 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hoyolab.Takumi.Downloader; + +internal sealed class SophonBuild +{ + [JsonPropertyName("build_id")] + public string BuildId { get; set; } = default!; + + [JsonPropertyName("tag")] + public string Tag { get; set; } = default!; + + [JsonPropertyName("manifests")] + public List Manifests { get; set; } = default!; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/SophonClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/SophonClient.cs new file mode 100644 index 00000000..4a527fb8 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/SophonClient.cs @@ -0,0 +1,33 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; +using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Branch; +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.Takumi.Downloader; + +[ConstructorGenerated(ResolveHttpClient = true)] +[HttpClient(HttpClientConfiguration.Default)] +internal sealed partial class SophonClient : ISophonClient +{ + private readonly IHttpRequestMessageBuilderFactory httpRequestMessageBuilderFactory; + private readonly ILogger logger; + private readonly HttpClient httpClient; + + public async ValueTask> GetBuildAsync(BranchWrapper branch, CancellationToken token = default) + { + HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() + .SetRequestUri(ApiEndpoints.SophonChunkGetBuild(branch)) + .Get(); + + Response? resp = await builder + .SendAsync>(httpClient, logger, token) + .ConfigureAwait(false); + + return Response.Response.DefaultIfNull(resp); + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/SophonClientFactory.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/SophonClientFactory.cs new file mode 100644 index 00000000..26975316 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/SophonClientFactory.cs @@ -0,0 +1,12 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Core.DependencyInjection.Abstraction; + +namespace Snap.Hutao.Web.Hoyolab.Takumi.Downloader; + +[Injection(InjectAs.Transient, typeof(IOverseaSupportFactory))] +[ConstructorGenerated(CallBaseConstructor = true)] +internal sealed partial class SophonClientFactory : OverseaSupportFactory +{ +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/SophonClientOversea.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/SophonClientOversea.cs new file mode 100644 index 00000000..11eba09f --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/SophonClientOversea.cs @@ -0,0 +1,33 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; +using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Branch; +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.Takumi.Downloader; + +[ConstructorGenerated(ResolveHttpClient = true)] +[HttpClient(HttpClientConfiguration.Default)] +internal sealed partial class SophonClientOversea : ISophonClient +{ + private readonly IHttpRequestMessageBuilderFactory httpRequestMessageBuilderFactory; + private readonly ILogger logger; + private readonly HttpClient httpClient; + + public async ValueTask> GetBuildAsync(BranchWrapper branch, CancellationToken token = default) + { + HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() + .SetRequestUri(ApiOsEndpoints.SophonChunkGetBuild(branch)) + .Get(); + + Response? resp = await builder + .SendAsync>(httpClient, logger, token) + .ConfigureAwait(false); + + return Response.Response.DefaultIfNull(resp); + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/SophonManifest.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/SophonManifest.cs new file mode 100644 index 00000000..73314fe4 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Downloader/SophonManifest.cs @@ -0,0 +1,31 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hoyolab.Takumi.Downloader; + +internal sealed class SophonManifest +{ + [JsonPropertyName("category_id")] + public string CategoryId { get; set; } = default!; + + [JsonPropertyName("category_name")] + public string CategoryName { get; set; } = default!; + + [JsonPropertyName("manifest")] + public Manifest Manifest { get; set; } = default!; + + [JsonPropertyName("chunk_download")] + public ManifestDownloadInfo ChunkDownload { get; set; } = default!; + + [JsonPropertyName("manifest_download")] + public ManifestDownloadInfo ManifestDownload { get; set; } = default!; + + [JsonPropertyName("matching_field")] + public string MatchingField { get; set; } = default!; + + [JsonPropertyName("stats")] + public ManifestStats Stats { get; set; } = default!; + + [JsonPropertyName("deduplicated_stats")] + public ManifestStats DeduplicatedStats { get; set; } = default!; +}