fix(comp-fav): 修复收藏夹可能会存在重复项的问题 (#1822)

This commit is contained in:
tangge233
2025-11-02 09:53:54 +08:00
committed by GitHub
parent 7e1cfbd589
commit f54ff39a42
10 changed files with 95 additions and 108 deletions

View File

@@ -1,6 +1,7 @@
Imports System.Net.Http
Imports System.Net.Http
Imports PCL.Core.Net
Imports PCL.Core.Utils
Imports PCL.Core.Utils.Exts
Public Class MyImage
Inherits Image
@@ -104,8 +105,7 @@ Public Class MyImage
End Property
Private _ActualSource As String = Nothing
Private Sub Load() _
Handles Me.Initialized '属性读取顺序修正:在完成 XAML 属性读取后再触发图片加载(#4868
Private Async Sub Load() Handles Me.Initialized '属性读取顺序修正:在完成 XAML 属性读取后再触发图片加载(#4868
'空
If Source Is Nothing Then
ActualSource = Nothing
@@ -126,73 +126,63 @@ Public Class MyImage
ActualSource = TempPath
If (Date.Now - TempFile.LastWriteTime) < FileCacheExpiredTime Then Return '无需刷新缓存
End If
RunInNewThread(
Sub()
Dim TempDownloadingPath As String = Nothing
Try
RetryStart:
'下载
ActualSource = LoadingSource '显示加载中图片
TempDownloadingPath = TempPath & RandomUtils.NextInt(0, 10000000)
Directory.CreateDirectory(GetPathFromFullPath(TempPath)) '重新实现下载,以避免携带 Header#5072
Using request As New Net.Http.HttpRequestMessage(Http.HttpMethod.Get, Url)
Using fs As New FileStream(TempDownloadingPath, FileMode.Create)
Using response = NetworkService.GetClient().SendAsync(request).Result
response.EnsureSuccessStatusCode()
Dim res = response.Content.ReadAsByteArrayAsync().Result
fs.Write(res, 0, res.Length)
Dim TempDownloadingPath As String
Try
'下载
ActualSource = LoadingSource '显示加载中图片
TempDownloadingPath = TempPath & RandomUtils.NextInt(0, 10000000)
Directory.CreateDirectory(GetPathFromFullPath(TempPath)) '重新实现下载,以避免携带 Header#5072
Using fs As New FileStream(TempDownloadingPath, FileMode.Create)
Using response = Await HttpRequestBuilder.Create(Url, HttpMethod.Get).
WithHttpVersionOption(HttpVersion.Version30).
SendAsync()
If response.IsSuccess Then
Using nfs = Await response.AsStreamAsync()
Await nfs.CopyToAsync(fs)
End Using
End Using
ElseIf Not FallbackSource.IsNullOrWhiteSpace() Then
Using fallbackResponse = Await HttpRequestBuilder.
Create(FallbackSource, HttpMethod.Get).
WithHttpVersionOption(HttpVersion.Version30).
SendAsync(True)
fs.SetLength(0)
Using fallbackNfs = Await fallbackResponse.AsStreamAsync()
Await fallbackNfs.CopyToAsync(fs)
End Using
End Using
End If
End Using
If Url <> Source AndAlso Url <> FallbackSource Then
'已经更换了地址
File.Delete(TempDownloadingPath)
ElseIf EnableCache Then
'保存缓存并显示
If File.Exists(TempPath) Then File.Delete(TempPath)
FileSystem.Rename(TempDownloadingPath, TempPath)
RunInUi(Sub() ActualSource = TempPath)
Else
'直接显示
RunInUiWait(Sub() ActualSource = TempDownloadingPath)
File.Delete(TempDownloadingPath)
End If
Catch ex As Exception
Try
If TempPath IsNot Nothing Then File.Delete(TempPath)
If TempDownloadingPath IsNot Nothing Then File.Delete(TempDownloadingPath)
Catch
End Try
If Not Retried Then
'更换备用地址
Log(ex, $"下载图片可重试地失败({Url}", LogLevel.Developer)
Retried = True
Url = If(FallbackSource, Source)
'空
If Url Is Nothing Then
ActualSource = Nothing
Return
End If
'本地图片
If Not Url.StartsWithF("http") Then
ActualSource = Url
Return
End If
'从缓存加载网络图片
TempPath = GetTempPath(Url)
TempFile = New FileInfo(TempPath)
If EnableCache AndAlso TempFile.Exists() Then
ActualSource = TempPath
If (Date.Now - TempFile.CreationTime) < FileCacheExpiredTime Then Return '无需刷新缓存
End If
'下载
If Source = Url Then Thread.Sleep(1000) '延迟 1s 重试
GoTo RetryStart
Else
Log(ex, $"下载图片失败({Url}", LogLevel.Hint)
End If
End Using
If Url <> Source AndAlso Url <> FallbackSource Then
'已经更换了地址
File.Delete(TempDownloadingPath)
ElseIf EnableCache Then
'保存缓存并显示
If File.Exists(TempPath) Then File.Delete(TempPath)
FileSystem.Rename(TempDownloadingPath, TempPath)
ActualSource = TempPath
Else
'直接显示
ActualSource = TempDownloadingPath
File.Delete(TempDownloadingPath)
End If
Catch ex As Exception
Try
If TempPath IsNot Nothing AndAlso File.Exists(TempPath) Then File.Delete(TempPath)
If TempDownloadingPath IsNot Nothing AndAlso File.Exists(TempDownloadingPath) Then File.Delete(TempDownloadingPath)
Catch
End Try
End Sub, "MyImage PicLoader " & GetUuid() & "#", ThreadPriority.BelowNormal)
'更换备用地址
Log(ex, $"下载图片失败Base = {Url}, Fallback = {FallbackSource}", LogLevel.Developer)
'从缓存加载网络图片
TempPath = GetTempPath(Url)
TempFile = New FileInfo(TempPath)
If EnableCache AndAlso TempFile.Exists() Then
ActualSource = TempPath
If (Date.Now - TempFile.CreationTime) < FileCacheExpiredTime Then Return '无需刷新缓存
End If
End Try
End Sub
Public Shared Function GetTempPath(Url As String) As String
Return $"{PathTemp}MyImage\{GetHash(Url)}.png"

View File

@@ -136,10 +136,10 @@ Public Module ModNet
Using request As New HttpRequestMessage(HttpMethod.Get, Url)
request.Headers.Accept.ParseAdd(Accept)
SecretHeadersSign(Url, request, UseBrowserUserAgent)
Using response = NetworkService.GetClient().SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cts.Token).Result
Using response = NetworkService.GetClient().SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cts.Token).GetAwaiter().GetResult()
EnsureSuccessStatusCode(response)
If Encode Is Nothing Then Encode = Encoding.UTF8
Using responseStream As Stream = response.Content.ReadAsStreamAsync().Result
Using responseStream As Stream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult()
'读取流并转换为字符串
Using reader As New StreamReader(responseStream, Encode)
Dim content As String = reader.ReadToEnd()
@@ -353,9 +353,9 @@ Public Module ModNet
request.Headers.Add(Pair.Key, Pair.Value)
Next
End If
Using response = NetworkService.GetClient().SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cts.Token).Result
Using response = NetworkService.GetClient().SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cts.Token).GetAwaiter().GetResult()
EnsureSuccessStatusCode(response)
Using responseStream = response.Content.ReadAsStreamAsync().Result
Using responseStream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult()
Using reader As New StreamReader(responseStream, Encoding.UTF8)
Return reader.ReadToEnd()
End Using
@@ -986,7 +986,7 @@ StartThread:
If Not Info.IsFirstThread OrElse Info.DownloadStart <> 0 Then request.Headers.Range = New Headers.RangeHeaderValue(Info.DownloadStart, Nothing)
Using cts As New CancellationTokenSource
cts.CancelAfter(Timeout)
Using response = NetworkService.GetClient().SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cts.Token).Result
Using response = NetworkService.GetClient().SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cts.Token).GetAwaiter().GetResult()
EnsureSuccessStatusCode(response)
If State = NetState.Error Then GoTo SourceBreak '快速中断
Dim Redirected = response.RequestMessage.RequestUri.OriginalString
@@ -1071,7 +1071,7 @@ NotSupportRange:
ResultStream = New FileStream(Info.Temp, FileMode.Create, FileAccess.Write, FileShare.Read)
End If
'开始下载
Using HttpStream = response.Content.ReadAsStreamAsync().Result
Using HttpStream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult()
If Setup.Get("SystemDebugDelay") Then Threading.Thread.Sleep(RandomUtils.NextInt(50, 3000))
Const bufferSize As Integer = 16384
Dim HttpData As Byte() = New Byte(bufferSize) {}

View File

@@ -1,5 +1,6 @@
Imports System.Threading.Tasks
Imports System.Net.Http
Imports System.Collections.Concurrent
Imports LiteDB
Imports PCL.Core.Utils
@@ -1114,7 +1115,7 @@ NoSubtitle:
''' <summary>
''' 已知工程信息的缓存。
''' </summary>
Public CompProjectCache As New Dictionary(Of String, CompProject)
Public CompProjectCache As New ConcurrentDictionary(Of String, CompProject)
''' <summary>
''' 根据搜索请求获取一系列的工程列表。需要基于加载器运行。
''' </summary>
@@ -1922,7 +1923,7 @@ Retry:
#Region "CompFavorites | 收藏"
Class CompFavorites
Public Shared Function GetShareCode(Data As List(Of String)) As String
Public Shared Function GetShareCode(Data As HashSet(Of String)) As String
Try
Return New JArray(Data).ToString(Newtonsoft.Json.Formatting.None)
Catch ex As Exception
@@ -1931,13 +1932,13 @@ Retry:
Return ""
End Function
Public Shared Function GetIdsByShareCode(Code As String) As List(Of String)
Public Shared Function GetIdsByShareCode(Code As String) As HashSet(Of String)
Try
Return JArray.Parse(Code).ToObject(Of List(Of String))()
Return JArray.Parse(Code).ToObject(Of HashSet(Of String))()
Catch ex As Exception
Log(ex, "[CompFavorites] 通过分享获取 ID 出错")
End Try
Return New List(Of String)
Return New HashSet(Of String)
End Function
''' <summary>
@@ -1965,7 +1966,6 @@ Retry:
Hint($"已将 {Project.TranslatedName} 从 {i.Name} 中删除", HintType.Finish)
Else
i.Favs.Add(Project.Id)
i.Favs = i.Favs.Distinct().ToList()
Hint($"已将 {Project.TranslatedName} 添加到 {i.Name} 中", HintType.Finish)
End If
Save()
@@ -1995,8 +1995,7 @@ Retry:
AddHandler Item.Click, Sub()
Try
Dim Count As Integer = i.Favs.Count
i.Favs.AddRange(Project.Select(Function(p) p.Id).AsEnumerable)
i.Favs = i.Favs.Distinct.ToList()
Project.Select(Function(p) p.Id).ToList().ForEach(Function(x) i.Favs.Add(x))
Save()
Dim SuccessCount As Integer = i.Favs.Count - Count
Dim FailedCount As Integer = Project.Count - SuccessCount
@@ -2030,7 +2029,7 @@ Retry:
''' 收藏的工程 ID 列表
''' </summary>
''' <returns></returns>
Property Favs As New List(Of String)
Property Favs As New HashSet(Of String)
''' <summary>
''' 备注
''' </summary>
@@ -2047,9 +2046,9 @@ Retry:
If _FavoritesList Is Nothing Then
Dim RawData As String = Setup.Get("CompFavorites")
Dim RawList As List(Of FavData) = Nothing
Dim Migrate As List(Of String) = Nothing
Dim Migrate As HashSet(Of String) = Nothing
Try
Migrate = JArray.Parse(RawData).ToObject(Of List(Of String)) ' 从旧版本迁移
Migrate = JArray.Parse(RawData).ToObject(Of HashSet(Of String)) ' 从旧版本迁移
Catch ex As Exception
End Try
If Migrate IsNot Nothing Then
@@ -2089,16 +2088,16 @@ Retry:
''' <param name="Name"></param>
''' <param name="FavList">没有传 Nothing</param>
''' <returns></returns>
Public Shared Function GetNewFav(Name As String, FavList As List(Of String)) As FavData
Public Shared Function GetNewFav(Name As String, FavList As HashSet(Of String)) As FavData
Dim res As New FavData With {.Name = Name, .Id = Guid.NewGuid.ToString()}
If FavList Is Nothing Then
res.Favs = New List(Of String)
res.Favs = New HashSet(Of String)
Else
res.Favs = FavList
End If
Return res
End Function
Public Shared Function IsFavourite(Id As String) As Boolean
If FavoritesList Is Nothing Then Return False
For Each i In FavoritesList

View File

@@ -422,7 +422,8 @@ Public Module ModDownload
WithHeader("Accept-Language", "en-US,en;q=0.5").
WithHeader("X-Requested-With", "XMLHttpRequest").
SendAsync(True).
Result.
GetAwaiter().
GetResult().
AsStringContent()
If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & "")
Try
@@ -1371,13 +1372,13 @@ Public Module ModDownload
Dim ResultProduction As JObject
Using productionResponse = HttpRequestBuilder.Create("https://releases.r2.labymod.net/api/v1/manifest/production/latest.json", HttpMethod.Get).
WithHttpVersionOption(HttpVersion.Version20).
SendAsync(True).Result
SendAsync(True).GetAwaiter().GetResult()
ResultProduction = GetJson(productionResponse.AsStringContent())
End Using
Dim ResultSnapshot As JObject
Using snapshotResponse = HttpRequestBuilder.Create("https://releases.r2.labymod.net/api/v1/manifest/snapshot/latest.json", HttpMethod.Get).
WithHttpVersionOption(HttpVersion.Version20).
SendAsync(True).Result
SendAsync(True).GetAwaiter().GetResult()
ResultSnapshot = GetJson(snapshotResponse.AsStringContent())
End Using
Dim Result As New JObject

View File

@@ -50,11 +50,11 @@ Public Module ModJava
Javas.CheckJavaAvailability()
Dim reqMin = If(MinVersion, New Version(1, 0, 0))
Dim reqMax = If(MaxVersion, New Version(999, 999, 999))
Dim ret = Javas.SelectSuitableJava(reqMin, reqMax).Result.FirstOrDefault()
Dim ret = Javas.SelectSuitableJava(reqMin, reqMax).GetAwaiter().GetResult().FirstOrDefault()
If ret Is Nothing Then
Log("[Java] 没有找到合适的 Java 开始尝试重新搜索后选择")
Javas.ScanJavaAsync().GetAwaiter().GetResult()
ret = Javas.SelectSuitableJava(reqMin, reqMax).Result.FirstOrDefault()
ret = Javas.SelectSuitableJava(reqMin, reqMax).GetAwaiter().GetResult().FirstOrDefault()
End If
Log($"[Java] 返回自动选择的 Java {If(ret IsNot Nothing, ret.ToString(), "无结果")}")
Return ret

View File

@@ -616,7 +616,7 @@ Retry:
Dim PrepareJson As JObject
Using response = HttpRequestBuilder.Create("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode", HttpMethod.Post).
WithContent(New ByteArrayContent(Encoding.UTF8.GetBytes($"client_id={OAuthClientId}&tenant=/consumers&scope=XboxLive.signin%20offline_access")), "application/x-www-form-urlencoded").
SendAsync(True).Result
SendAsync(True).GetAwaiter().GetResult()
PrepareJson = GetJson(response.AsStringContent())
End Using
@@ -654,7 +654,7 @@ Retry:
Try
Using response = HttpRequestBuilder.Create("https://login.live.com/oauth20_token.srf", HttpMethod.Post).
WithContent($"client_id={OAuthClientId}&refresh_token={Uri.EscapeDataString(Code)}&grant_type=refresh_token&scope=XboxLive.signin%20offline_access", "application/x-www-form-urlencoded").
SendAsync(True).Result
SendAsync(True).GetAwaiter().GetResult()
Result = response.AsStringContent()
End Using
Catch ex As ThreadInterruptedException
@@ -717,7 +717,7 @@ Retry:
Dim contentData = JsonSerializer.Serialize(requestData)
Using response = HttpRequestBuilder.Create("https://user.auth.xboxlive.com/user/authenticate", HttpMethod.Post).
WithContent(contentData, "application/json").
SendAsync(True).Result
SendAsync(True).GetAwaiter().GetResult()
Result = response.AsStringContent()
End Using
Catch ex As Exception
@@ -767,7 +767,7 @@ Retry:
Dim contentData = JsonSerializer.Serialize(requestData)
Using response = HttpRequestBuilder.Create("https://xsts.auth.xboxlive.com/xsts/authorize", HttpMethod.Post).
WithContent(contentData, "application/json").
SendAsync(True).Result
SendAsync(True).GetAwaiter().GetResult()
Result = response.AsStringContent()
End Using
Catch ex As HttpRequestException
@@ -831,7 +831,7 @@ Retry:
Dim contentData = JsonSerializer.Serialize(requestData)
Using response = HttpRequestBuilder.Create("https://api.minecraftservices.com/authentication/login_with_xbox", HttpMethod.Post).
WithContent(contentData, "application/json").
SendAsync(True).Result
SendAsync(True).GetAwaiter().GetResult()
Result = response.AsStringContent()
End Using
Catch ex As HttpRequestException
@@ -873,7 +873,7 @@ Retry:
Try
Using response = HttpRequestBuilder.Create("https://api.minecraftservices.com/entitlements", HttpMethod.Get).
WithBearerToken(accessToken).
SendAsync(True).Result
SendAsync(True).GetAwaiter().GetResult()
result = response.AsStringContent()
End Using
Dim ResultJson As JObject = GetJson(result)
@@ -901,7 +901,7 @@ Retry:
Try
Using response = HttpRequestBuilder.Create("https://api.minecraftservices.com/minecraft/profile", HttpMethod.Get).
WithBearerToken(AccessToken).
SendAsync(True).Result
SendAsync(True).GetAwaiter().GetResult()
Result = response.AsStringContent()
End Using
Catch ex As HttpRequestException

View File

@@ -622,8 +622,7 @@ pause"
WithHeader("Accept", "text/html").
WithHeader("Accept-Language", "en-US,en;q=0.5").
WithHeader("X-Requested-With", "XMLHttpRequest").
SendAsync(True).
Result.
SendAsync(True).GetAwaiter().GetResult().
AsStringContent()
Task.Progress = 0.8
Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
@@ -792,8 +791,7 @@ Retry:
WithHeader("Accept", "text/html").
WithHeader("Accept-Language", "en-US,en;q=0.5").
WithHeader("X-Requested-With", "XMLHttpRequest").
SendAsync(True).
Result.
SendAsync(True).GetAwaiter().GetResult().
AsStringContent()
Task.Progress = 0.8
Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))

View File

@@ -362,7 +362,7 @@ Public Class PageDownloadCompFavorites
Private Sub Btn_FavoritesShare_Clicked(sender As Object, e As RouteEventArgs) Handles Btn_FavoritesShare.Click
Try
ClipboardSet(CompFavorites.GetShareCode(SelectedItemList.Select(Function(i) CType(i.Tag, CompProject).Id).ToList()))
ClipboardSet(CompFavorites.GetShareCode(SelectedItemList.Select(Function(i) CType(i.Tag, CompProject).Id).ToHashSet()))
Items_SetSelectAll(False)
Catch ex As Exception
Log(ex, "[CompFavourites] 分享收藏时发生错误", LogLevel.Hint)
@@ -576,8 +576,7 @@ Public Class PageDownloadCompFavorites
RefreshFavTargets()
ComboTargetFav.SelectedIndex = ComboTargetFav.Items.Count - 1
Case 2
CurrentFavTarget.Favs.AddRange(NewFavs)
CurrentFavTarget.Favs.Distinct()
NewFavs.ToList().ForEach(Function(x) CurrentFavTarget.Favs.Add(x))
CompFavorites.Save()
Loader.Start(IsForceRestart:=True)
End Select

View File

@@ -1561,7 +1561,7 @@ Install:
'分享
Private Sub BtnSelectShare_Click() Handles BtnSelectShare.Click
Dim ShareList As List(Of String) = CompResourceListLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawPath) AndAlso m.Comp IsNot Nothing).Select(Function(i) i.Comp.Id).ToList()
Dim ShareList As HashSet(Of String) = CompResourceListLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawPath) AndAlso m.Comp IsNot Nothing).Select(Function(i) i.Comp.Id).ToHashSet()
ClipboardSet(CompFavorites.GetShareCode(ShareList))
ChangeAllSelected(False)
End Sub

View File

@@ -30,7 +30,7 @@ Public Class PageOtherVote
Public Sub VoteListGet(Task As LoaderTask(Of Integer, List(Of Vote)))
Dim content As String
Using response = HttpRequestBuilder.Create("https://github.com/Meloong-Git/PCL/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8?discussions_q=is%3Aopen+category%3A%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8+sort%3Atop", Http.HttpMethod.Get).
SendAsync(True).Result
SendAsync(True).GetAwaiter().GetResult()
content = response.AsStringContent()
End Using
If content Is Nothing Then Throw New Exception("空内容")