Skip to content

Commit

Permalink
improve CurseForge package installer
Browse files Browse the repository at this point in the history
  • Loading branch information
laolarou726 committed Oct 8, 2023
1 parent a9e8890 commit 81beaf9
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 26 deletions.
13 changes: 8 additions & 5 deletions ProjBobcat/ProjBobcat/Class/Helper/CurseForgeAPIHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
using System.Threading.Tasks;
using ProjBobcat.Class.Model.CurseForge;
using ProjBobcat.Class.Model.CurseForge.API;
using ProjBobcat.Exceptions;

namespace ProjBobcat.Class.Helper;

#region Temp Models

record AddonInfoReqModel(IEnumerable<int> modIds);
record AddonInfoReqModel(IEnumerable<long> modIds);

Check warning on line 16 in ProjBobcat/ProjBobcat/Class/Helper/CurseForgeAPIHelper.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The type 'AddonInfoReqModel' defines init-only properties, deserialization of which is currently not supported in source generation mode.

Check warning on line 16 in ProjBobcat/ProjBobcat/Class/Helper/CurseForgeAPIHelper.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The type 'AddonInfoReqModel' defines init-only properties, deserialization of which is currently not supported in source generation mode.

[JsonSerializable(typeof(AddonInfoReqModel))]
partial class AddonInfoReqModelContext : JsonSerializerContext
Expand Down Expand Up @@ -88,7 +89,7 @@ public static void SetApiKey(string apiKey)
.DataModelWithPaginationCurseForgeAddonInfoArray);
}

public static async Task<CurseForgeAddonInfo?> GetAddon(int addonId)
public static async Task<CurseForgeAddonInfo?> GetAddon(long addonId)
{
var reqUrl = $"{BaseUrl}/mods/{addonId}";

Expand All @@ -99,7 +100,7 @@ public static void SetApiKey(string apiKey)
?.Data;
}

public static async Task<CurseForgeAddonInfo[]?> GetAddons(IEnumerable<int> addonIds)
public static async Task<CurseForgeAddonInfo[]?> GetAddons(IEnumerable<long> addonIds)
{
const string reqUrl = $"{BaseUrl}/mods";
var data = JsonSerializer.Serialize(new AddonInfoReqModel(addonIds),
Expand All @@ -116,7 +117,7 @@ public static void SetApiKey(string apiKey)
.DataModelCurseForgeAddonInfoArray))?.Data;
}

public static async Task<CurseForgeLatestFileModel[]?> GetAddonFiles(int addonId)
public static async Task<CurseForgeLatestFileModel[]?> GetAddonFiles(long addonId)
{
var reqUrl = $"{BaseUrl}/mods/{addonId}/files";

Expand Down Expand Up @@ -159,7 +160,9 @@ public static void SetApiKey(string apiKey)

using var req = Req(HttpMethod.Get, reqUrl);
using var res = await Client.SendAsync(req);
res.EnsureSuccessStatusCode();

if (!res.IsSuccessStatusCode)
throw new CurseForgeModResolveException(addonId, fileId);

return (await res.Content.ReadFromJsonAsync(DataModelStringResultContext.Default.DataModelString))?.Data;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ public int GetHashCode(CurseForgeDependencyModel obj)

public class CurseForgeDependencyModel
{
[JsonPropertyName("modId")] public int ModId { get; set; }
[JsonPropertyName("modId")] public long ModId { get; set; }
[JsonPropertyName("relationType")] public int RelationType { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using ProjBobcat.Class.Helper;
using ProjBobcat.Class.Model;
using ProjBobcat.Class.Model.CurseForge;
using ProjBobcat.Exceptions;
using ProjBobcat.Interface;
using SharpCompress.Archives;

Expand Down Expand Up @@ -47,30 +48,68 @@ public async Task InstallTaskAsync()
{
return urls.Select(file => (file.ProjectId, file.FileId));
});

var urlBags = new ConcurrentBag<DownloadFile>();
var urlReqExceptions = new ConcurrentBag<CurseForgeModResolveException>();
var actionBlock = new ActionBlock<(long, long)>(async t =>
{
var downloadUrlRes = await CurseForgeAPIHelper.GetAddonDownloadUrl(t.Item1, t.Item2);
var d = downloadUrlRes.Trim('"');
var fn = Path.GetFileName(d);
var downloadFile = new DownloadFile
var retryCount = 0;
do
{
DownloadPath = di.FullName,
DownloadUri = d,
FileName = fn
};
downloadFile.Completed += WhenCompleted;
urlBags.Add(downloadFile);
TotalDownloaded++;
var progress = (double)TotalDownloaded / NeedToDownload * 100;
InvokeStatusChangedEvent($"成功解析 MOD [{t.Item1}] 的下载地址",
progress);
retryCount++;
try
{
var downloadUrlRes = await CurseForgeAPIHelper.GetAddonDownloadUrl(t.Item1, t.Item2);
var d = downloadUrlRes.Trim('"');
var fn = Path.GetFileName(d);
var downloadFile = new DownloadFile
{
DownloadPath = di.FullName,
DownloadUri = d,
FileName = fn
};
downloadFile.Completed += WhenCompleted;
urlBags.Add(downloadFile);
TotalDownloaded++;
NeedToDownload++;
var progress = (double)TotalDownloaded / NeedToDownload * 100;
InvokeStatusChangedEvent($"成功解析 MOD [{t.Item1}] 的下载地址",
progress);
}
catch (CurseForgeModResolveException e)
{
InvokeStatusChangedEvent($"MOD [{t.Item1}] 的下载地址解析失败,即将重试",
114514);
CurseForgeAddonInfo? info = null;
try
{
info = await CurseForgeAPIHelper.GetAddon(t.Item1);
}
catch (Exception) { }
if (info == null)
{
urlReqExceptions.Add(e);
return;
}
var moreInfo = $$"""
模组名称:{{info.Name}}
模组链接:{{((info.Links?.TryGetValue("websiteUrl", out var link) ?? false) ? link : "-")}}
""";
var ex = new CurseForgeModResolveException(t.Item1, t.Item2, moreInfo);
urlReqExceptions.Add(ex);
}
} while (retryCount < 3);
}, new ExecutionDataflowBlockOptions
{
BoundedCapacity = 32,
Expand All @@ -84,6 +123,9 @@ public async Task InstallTaskAsync()

await actionBlock.Completion;

if (!urlBags.IsEmpty)
throw new AggregateException(urlReqExceptions);

TotalDownloaded = 0;
await DownloadHelper.AdvancedDownloadListFile(urlBags, new DownloadSettings
{
Expand Down
32 changes: 32 additions & 0 deletions ProjBobcat/ProjBobcat/Exceptions/CurseForgeModResolveException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;

namespace ProjBobcat.Exceptions;

public class CurseForgeModResolveException : Exception
{
readonly long _addonId, _fileId;
readonly string? _moreInfo;

public CurseForgeModResolveException(long addonId, long fileId)
{
_addonId = addonId;
_fileId = fileId;
}

public CurseForgeModResolveException(long addonId, long fileId, string moreInfo)
{
_addonId = addonId;
_fileId = fileId;
_moreInfo = moreInfo;
}

public override string ToString()
{
return $$"""

无法解析一个或多个 CurseForge 模组,可能的原因是因为该模组已近被作者删除或是该整合包所需的该模组的文件已经被删除。
模组文件下载链接:https://api.curseforge.com/v1/mods/{{_addonId}}/files/{{_fileId}}/download-url
{{_moreInfo}}
""";
}
}

0 comments on commit 81beaf9

Please sign in to comment.