✨ 为 Mod 列表、下载添加 MCLM 下载源
This commit is contained in:
@@ -1,7 +1,47 @@
|
||||
Imports System.Net
|
||||
Imports PCL.ModNet
|
||||
|
||||
Public Module ModNet
|
||||
|
||||
#Region "Mod"
|
||||
Public Function ModMirrorUrl(Url As String) As String
|
||||
Select Case Setup.Get("ToolDownloadMod")
|
||||
Case 0
|
||||
Return Url
|
||||
Case 1
|
||||
Return Url.Replace("api.modrinth.com/v2", "mod.mcmirror.top/modrinth").
|
||||
Replace("staging-api.modrinth.com/v2", "mod.mcmirror.top/modrinth").
|
||||
Replace("cdn.modrinth.com", "mod.mcimirror.top").
|
||||
Replace("api.curseforge.com", "mod.mcimirror.top/curseforge").
|
||||
Replace("edge.forgecdn.net", "mod.mcimirror.top").
|
||||
Replace("mediafilez.forgecdn.net", "mod.mcimirror.top").
|
||||
Replace("media.forgecdn.net", "mod.mcimirror.top")
|
||||
Case Else
|
||||
Return Url
|
||||
End Select
|
||||
End Function
|
||||
Public Function AllowModMirror(IsModFile As Boolean)
|
||||
Return Setup.Get("ToolDownloadMod") <> 0 AndAlso (Setup.Get("ToolDownloadModMirror") OrElse Not IsModFile)
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 请求 CurseForge 或者 Modrinth。会抛出异常。
|
||||
''' </summary>
|
||||
Public Function NetRequestMod(Url As String, Method As String, Data As Object, ContentType As String,
|
||||
Optional Headers As Dictionary(Of String, String) = Nothing,
|
||||
Optional IsModFile As Boolean = False) As String
|
||||
Try
|
||||
If AllowModMirror(IsModFile) Then
|
||||
Return NetRequestOnce(Url, Method, Data, ContentType, Timeout:=5000, Headers:=Headers)
|
||||
Else
|
||||
Return NetRequestRetry(Url, Method, Data, ContentType, Headers:=Headers)
|
||||
End If
|
||||
Catch ex1 As Exception
|
||||
If Not AllowModMirror(IsModFile) Then Throw ex1 '没机会了,45s 都没有加载完
|
||||
Dim retry As Integer = 0
|
||||
Return NetRequestOnce(ModMirrorUrl(Url), Method, Data, ContentType, Timeout:=30000, Headers:=Headers)
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
|
||||
Public Const NetDownloadEnd As String = ".PCLDownloading"
|
||||
|
||||
''' <summary>
|
||||
@@ -103,6 +143,9 @@ Retry:
|
||||
Dim RetryCount As Integer = 0
|
||||
Dim RetryException As Exception = Nothing
|
||||
Dim StartTime As Long = GetTimeTick()
|
||||
If BackupUrl Is Nothing Then
|
||||
If ModMirrorUrl(Url) <> Url Then BackupUrl = ModMirrorUrl(Url)
|
||||
End If
|
||||
Try
|
||||
Retry:
|
||||
Select Case RetryCount
|
||||
|
||||
@@ -947,7 +947,7 @@ Retry:
|
||||
Try
|
||||
'获取工程列表
|
||||
Log("[Comp] 开始从 CurseForge 获取工程列表:" & CurseForgeUrl)
|
||||
Dim RequestResult As JObject = NetGetCodeByRequestRetry(CurseForgeUrl, IsJson:=True, Encode:=Encoding.UTF8)
|
||||
Dim RequestResult As JObject = NetGetCodeByRequestRetry(CurseForgeUrl, IsJson:=True, Encode:=Encoding.UTF8, BackupUrl:=ModMirrorUrl(CurseForgeUrl))
|
||||
Task.Progress += 0.2
|
||||
Dim ProjectList As New List(Of CompProject)
|
||||
For Each JsonEntry As JObject In RequestResult("data")
|
||||
@@ -977,7 +977,7 @@ Retry:
|
||||
Sub()
|
||||
Try
|
||||
Log("[Comp] 开始从 Modrinth 获取工程列表:" & ModrinthUrl)
|
||||
Dim RequestResult As JObject = NetGetCodeByRequestRetry(ModrinthUrl, IsJson:=True, Encode:=Encoding.UTF8)
|
||||
Dim RequestResult As JObject = NetGetCodeByRequestRetry(ModrinthUrl, IsJson:=True, Encode:=Encoding.UTF8, BackupUrl:=ModMirrorUrl(ModrinthUrl))
|
||||
Task.Progress += 0.2
|
||||
Dim ProjectList As New List(Of CompProject)
|
||||
For Each JsonEntry As JObject In RequestResult("hits")
|
||||
@@ -1190,6 +1190,10 @@ Retry:
|
||||
''' </summary>
|
||||
Public DownloadUrls As List(Of String)
|
||||
''' <summary>
|
||||
''' 文件所有可能的镜像源。
|
||||
''' </summary>
|
||||
Public MirrorUrls As List(Of String)
|
||||
''' <summary>
|
||||
''' 文件的 SHA1 或 MD5。
|
||||
''' </summary>
|
||||
Public ReadOnly Hash As String = Nothing
|
||||
@@ -1207,7 +1211,9 @@ Retry:
|
||||
''' </summary>
|
||||
''' <param name="LocalAddress">目标本地文件夹,或完整的文件路径。会自动判断类型。</param>
|
||||
Public Function ToNetFile(LocalAddress As String) As NetFile
|
||||
Return New NetFile(DownloadUrls, LocalAddress & If(LocalAddress.EndsWithF("\"), FileName, ""), New FileChecker(Hash:=Hash), UseBrowserUserAgent:=True)
|
||||
Dim urls As List(Of String) = DownloadUrls
|
||||
If AllowModMirror(True) Then urls.AddRange(MirrorUrls)
|
||||
Return New NetFile(urls, LocalAddress & If(LocalAddress.EndsWithF("\"), FileName, ""), New FileChecker(Hash:=Hash), UseBrowserUserAgent:=True)
|
||||
End Function
|
||||
|
||||
'实例化
|
||||
@@ -1325,6 +1331,11 @@ Retry:
|
||||
If RawLoaders.Contains("quilt") Then ModLoaders.Add(CompModLoaderType.Quilt)
|
||||
#End Region
|
||||
End If
|
||||
'末尾添加备用镜像 URL
|
||||
MirrorUrls = New List(Of String)
|
||||
For Each u In DownloadUrls
|
||||
MirrorUrls.Add(ModMirrorUrl(u))
|
||||
Next
|
||||
End If
|
||||
End Sub
|
||||
''' <summary>
|
||||
@@ -1426,10 +1437,24 @@ Retry:
|
||||
Dim TargetProject As CompProject
|
||||
If CompProjectCache.ContainsKey(ProjectId) Then '存在缓存
|
||||
TargetProject = CompProjectCache(ProjectId)
|
||||
ElseIf FromCurseForge Then 'CurseForge
|
||||
TargetProject = New CompProject(NetGetCodeByRequestRetry("https://api.curseforge.com/v1/mods/" & ProjectId, IsJson:=True, Encode:=Encoding.UTF8)("data"))
|
||||
Else 'Modrinth
|
||||
TargetProject = New CompProject(NetGetCodeByRequestRetry("https://api.modrinth.com/v2/project/" & ProjectId, IsJson:=True, Encode:=Encoding.UTF8))
|
||||
Else
|
||||
Dim retryCount As Integer = 0
|
||||
Retry:
|
||||
Dim comp As Object
|
||||
If FromCurseForge Then 'CurseForge
|
||||
comp = NetGetCodeByRequestRetry("https://api.curseforge.com/v1/mods/" & ProjectId, IsJson:=True, Encode:=Encoding.UTF8)
|
||||
TargetProject = New CompProject(NetGetCodeByRequestRetry("https://api.curseforge.com/v1/mods/" & ProjectId, IsJson:=True, Encode:=Encoding.UTF8)("data"))
|
||||
Else 'Modrinth
|
||||
comp = NetGetCodeByRequestRetry("https://api.modrinth.com/v2/project/" & ProjectId, IsJson:=True, Encode:=Encoding.UTF8)
|
||||
TargetProject = New CompProject(NetGetCodeByRequestRetry("https://api.modrinth.com/v2/project/" & ProjectId, IsJson:=True, Encode:=Encoding.UTF8))
|
||||
End If
|
||||
If comp Is Nothing Then '获取 ("data") 时会报值不能为 null
|
||||
If retryCount > 2 Then Throw New Exception("工程对象获取失败,请等待一段时间后刷新再试!")
|
||||
retryCount += 1
|
||||
Thread.Sleep(1000)
|
||||
GoTo Retry
|
||||
End If
|
||||
TargetProject = New CompProject(If(FromCurseForge, comp("data"), comp))
|
||||
End If
|
||||
'获取工程对象的文件列表
|
||||
Log("[Comp] 开始获取文件列表:" & ProjectId)
|
||||
@@ -1437,7 +1462,7 @@ Retry:
|
||||
If FromCurseForge Then
|
||||
'CurseForge
|
||||
If TargetProject.Type = CompType.Mod Then 'Mod 使用每个版本最新的文件
|
||||
ResultJsonArray = GetJson(NetRequestRetry("https://api.curseforge.com/v1/mods/files", "POST", "{""fileIds"": [" & Join(TargetProject.CurseForgeFileIds, ",") & "]}", "application/json"))("data")
|
||||
ResultJsonArray = GetJson(NetRequestMod("https://api.curseforge.com/v1/mods/files", "POST", "{""fileIds"": [" & Join(TargetProject.CurseForgeFileIds, ",") & "]}", "application/json"))("data")
|
||||
Else '否则使用全部文件
|
||||
ResultJsonArray = NetGetCodeByRequestRetry($"https://api.curseforge.com/v1/mods/{ProjectId}/files?pageSize=999", Accept:="application/json", IsJson:=True)("data")
|
||||
End If
|
||||
@@ -1457,7 +1482,7 @@ Retry:
|
||||
Log($"[Comp] {ProjectId} 文件列表中还需要获取信息的前置 Mod:{Join(UndoneDeps, ",")}")
|
||||
Dim Projects As JArray
|
||||
If TargetProject.FromCurseForge Then
|
||||
Projects = GetJson(NetRequestRetry("https://api.curseforge.com/v1/mods",
|
||||
Projects = GetJson(NetRequestMod("https://api.curseforge.com/v1/mods",
|
||||
"POST", "{""modIds"": [" & Join(UndoneDeps, ",") & "]}", "application/json"))("data")
|
||||
Else
|
||||
Projects = NetGetCodeByRequestRetry($"https://api.modrinth.com/v2/projects?ids=[""{Join(UndoneDeps, """,""")}""]",
|
||||
|
||||
@@ -952,7 +952,7 @@ Finished:
|
||||
Try
|
||||
'步骤 1:获取 Hash 与对应的工程 ID
|
||||
Dim ModrinthHashes = Mods.Select(Function(m) m.ModrinthHash).ToList()
|
||||
Dim ModrinthVersion = CType(GetJson(NetRequestRetry("https://api.modrinth.com/v2/version_files", "POST",
|
||||
Dim ModrinthVersion = CType(GetJson(NetRequestMod("https://api.modrinth.com/v2/version_files", "POST",
|
||||
$"{{""hashes"": [""{ModrinthHashes.Join(""",""")}""], ""algorithm"": ""sha1""}}", "application/json")), JObject)
|
||||
Log($"[Mod] 从 Modrinth 获取到 {ModrinthVersion.Count} 个本地 Mod 的对应信息")
|
||||
'步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
|
||||
@@ -985,7 +985,7 @@ Finished:
|
||||
Next
|
||||
Log($"[Mod] 已从 Modrinth 获取本地 Mod 信息,继续获取更新信息")
|
||||
'步骤 4:获取更新信息
|
||||
Dim ModrinthUpdate = CType(GetJson(NetRequestRetry("https://api.modrinth.com/v2/version_files/update", "POST",
|
||||
Dim ModrinthUpdate = CType(GetJson(NetRequestMod("https://api.modrinth.com/v2/version_files/update", "POST",
|
||||
$"{{""hashes"": [""{ModrinthMapping.SelectMany(Function(l) l.Value.Select(Function(m) m.ModrinthHash)).Join(""",""")}""], ""algorithm"": ""sha1"",
|
||||
""loaders"": [""{ModLoaders.Join(""",""").ToLower}""],""game_versions"": [""{McVersion}""]}}", "application/json")), JObject)
|
||||
For Each Entry In Mods
|
||||
@@ -1022,7 +1022,7 @@ Finished:
|
||||
CurseForgeHashes.Add(Entry.CurseForgeHash)
|
||||
If Loader.IsAbortedWithThread(MainThread) Then Exit Sub
|
||||
Next
|
||||
Dim CurseForgeRaw = CType(CType(GetJson(NetRequestRetry("https://api.curseforge.com/v1/fingerprints/432/", "POST",
|
||||
Dim CurseForgeRaw = CType(CType(GetJson(NetRequestMod("https://api.curseforge.com/v1/fingerprints/432/", "POST",
|
||||
$"{{""fingerprints"": [{CurseForgeHashes.Join(",")}]}}", "application/json")), JObject)("data")("exactMatches"), JContainer)
|
||||
Log($"[Mod] 从 CurseForge 获取到 {CurseForgeRaw.Count} 个本地 Mod 的对应信息")
|
||||
'步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
|
||||
@@ -1045,7 +1045,7 @@ Finished:
|
||||
Log($"[Mod] 需要从 CurseForge 获取 {CurseForgeMapping.Count} 个本地 Mod 的工程信息")
|
||||
'步骤 3:获取工程信息
|
||||
If Not CurseForgeMapping.Any() Then Exit Sub
|
||||
Dim CurseForgeProject = CType(GetJson(NetRequestRetry("https://api.curseforge.com/v1/mods", "POST",
|
||||
Dim CurseForgeProject = CType(GetJson(NetRequestMod("https://api.curseforge.com/v1/mods", "POST",
|
||||
$"{{""modIds"": [{CurseForgeMapping.Keys.Join(",")}]}}", "application/json")), JObject)("data")
|
||||
Dim UpdateFileIds As New Dictionary(Of Integer, List(Of McMod)) 'FileId -> 本地 Mod 文件列表
|
||||
Dim FileIdToProjectSlug As New Dictionary(Of Integer, String)
|
||||
@@ -1087,7 +1087,7 @@ Finished:
|
||||
Log($"[Mod] 已从 CurseForge 获取本地 Mod 信息,需要获取 {UpdateFileIds.Count} 个用于检查更新的文件信息")
|
||||
'步骤 4:获取更新文件信息
|
||||
If Not UpdateFileIds.Any() Then Exit Sub
|
||||
Dim CurseForgeFiles = CType(GetJson(NetRequestRetry("https://api.curseforge.com/v1/mods/files", "POST",
|
||||
Dim CurseForgeFiles = CType(GetJson(NetRequestMod("https://api.curseforge.com/v1/mods/files", "POST",
|
||||
$"{{""fileIds"": [{UpdateFileIds.Keys.Join(",")}]}}", "application/json")), JObject)("data")
|
||||
Dim UpdateFiles As New Dictionary(Of McMod, CompFile)
|
||||
For Each FileJson In CurseForgeFiles
|
||||
|
||||
Reference in New Issue
Block a user