PageLaunchRight.xaml.vb 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. Imports System.Windows.Threading
  2. Public Class PageLaunchRight
  3. Implements IRefreshable, IDispatcherUnhandledException
  4. Private Sub Init() Handles Me.Loaded
  5. PanBack.ScrollToHome()
  6. PanLog.Visibility = ModeDebug.ToVisibility
  7. '快照版提示
  8. PanHint.Visibility =
  9. (BuildType <> BuildTypes.Release AndAlso VersionBranchMain = "Official" AndAlso
  10. CurrentRank < DonationRank.Rank23 AndAlso Not Settings.Get(Of Boolean)("HintSnapshot")).ToVisibility
  11. End Sub
  12. #Region "主页"
  13. ''' <summary>
  14. ''' 刷新主页。
  15. ''' </summary>
  16. Private Sub Refresh() Handles Me.Loaded
  17. RunInThread(
  18. Sub()
  19. Try
  20. SyncLock RefreshLock
  21. RefreshReal()
  22. End SyncLock
  23. Catch ex As Exception
  24. Logger.Error(ex, "加载 PCL 主页自定义信息失败", If(ModeDebug, LogBehavior.Alert, LogBehavior.Toast))
  25. End Try
  26. End Sub)
  27. End Sub
  28. Private Sub RefreshReal()
  29. Dim Content As String = Nothing, Url As String = Nothing
  30. Select Case Settings.Get(Of Integer)("UiCustomType")
  31. Case 1
  32. '加载本地文件
  33. Logger.Info("主页自定义数据来源:本地文件")
  34. Content = FileUtils.TryReadAsString(Paths.Base & "PCL\Custom.xaml")
  35. Case 2
  36. '联网下载
  37. Url = Settings.Get(Of String)("UiCustomNet")
  38. Case 3
  39. '预设
  40. Select Case Settings.Get(Of Integer)("UiCustomPreset")
  41. Case 0
  42. Logger.Info("主页预设:你知道吗")
  43. Content = "
  44. <local:MyCard Title=""你知道吗?"" Margin=""0,0,0,15"">
  45. <TextBlock Margin=""25,38,23,15"" FontSize=""13.5"" IsHitTestVisible=""False"" Text=""{hint}"" TextWrapping=""Wrap"" Foreground=""{DynamicResource ColorBrush1}"" />
  46. <local:MyIconButton Height=""22"" Width=""22"" Margin=""9"" VerticalAlignment=""Top"" HorizontalAlignment=""Right""
  47. EventType=""刷新主页"" EventData=""/""
  48. Logo=""M512.0 838.3c-80.2 0-153.4-29.3-210.2-77.4l75.5-75.5c11.5-11.5 25.8-22.0 25.8-37.0a27.2 27.2 0 0 0-27.1-27.1H104.0c-27.1 0-27.1 23.9-27.1 27.1v271.9a27.1 27.1 0 0 0 27.1 27.1c15.0 0 27.8-16.6 42.5-31.2l77.9-77.9c76.6 67.7 177.1 108.9 287.4 108.9 221.7 0 404.5-166.0 431.2-380.6h-109.8c-25.9 154.2-159.7 271.9-321.3 271.9zM919.9 76.6c-15.0 0-27.8 16.6-42.5 31.3L799.5 185.8c-76.5-67.7-177.1-108.9-287.4-108.9-221.8 0-404.5 166.1-431.3 380.6H190.6c25.9-154.2 159.7-271.9 321.4-271.9 80.2 0 153.4 29.3 210.1 77.4l-75.5 75.5c-11.6 11.5-25.8 22.0-25.8 37.1a27.2 27.2 0 0 0 27.1 27.1h271.9c27.1 0 27.1-23.9 27.1-27.1V103.8a27.1 27.1 0 0 0-27.1-27.1z"" />
  49. </local:MyCard>"
  50. Case 1
  51. Logger.Info("主页预设:回声洞")
  52. Content = "
  53. <local:MyCard Title=""回声洞"" Margin=""0,0,0,15"">
  54. <TextBlock Margin=""25,38,23,15"" FontSize=""13.5"" IsHitTestVisible=""False"" Text=""{cave}"" TextWrapping=""Wrap"" Foreground=""{DynamicResource ColorBrush1}"" />
  55. <local:MyIconButton Height=""22"" Width=""22"" Margin=""9"" VerticalAlignment=""Top"" HorizontalAlignment=""Right""
  56. EventType=""刷新主页"" EventData=""/""
  57. Logo=""M512.0 838.3c-80.2 0-153.4-29.3-210.2-77.4l75.5-75.5c11.5-11.5 25.8-22.0 25.8-37.0a27.2 27.2 0 0 0-27.1-27.1H104.0c-27.1 0-27.1 23.9-27.1 27.1v271.9a27.1 27.1 0 0 0 27.1 27.1c15.0 0 27.8-16.6 42.5-31.2l77.9-77.9c76.6 67.7 177.1 108.9 287.4 108.9 221.7 0 404.5-166.0 431.2-380.6h-109.8c-25.9 154.2-159.7 271.9-321.3 271.9zM919.9 76.6c-15.0 0-27.8 16.6-42.5 31.3L799.5 185.8c-76.5-67.7-177.1-108.9-287.4-108.9-221.8 0-404.5 166.1-431.3 380.6H190.6c25.9-154.2 159.7-271.9 321.4-271.9 80.2 0 153.4 29.3 210.1 77.4l-75.5 75.5c-11.6 11.5-25.8 22.0-25.8 37.1a27.2 27.2 0 0 0 27.1 27.1h271.9c27.1 0 27.1-23.9 27.1-27.1V103.8a27.1 27.1 0 0 0-27.1-27.1z"" />
  58. </local:MyCard>"
  59. Case 2
  60. Logger.Info("主页预设:Minecraft 新闻")
  61. Url = "https://mcnews.meloong.com"
  62. Case 3
  63. Logger.Info("主页预设:简单主页")
  64. Url = "https://pclhomeplazaoss.lingyunawa.top:26994/d/Homepages/MFn233/Custom.xaml"
  65. Case 4
  66. Logger.Info("主页预设:每日整合包推荐")
  67. Url = "https://pclsub.sodamc.com/"
  68. Case 5
  69. Logger.Info("主页预设:Minecraft 皮肤推荐")
  70. Url = "https://forgepixel.com/pcl_sub_file"
  71. Case 6
  72. Logger.Info("主页预设:OpenBMCLAPI 仪表盘 Lite")
  73. Url = "https://pcl-bmcl.milu.ink/"
  74. Case 7
  75. Logger.Info("主页预设:主页市场")
  76. Url = "https://pclhomeplazaoss.lingyunawa.top:26994/p/Homepages/Homepage.Market/Custom.xaml"
  77. Case 8
  78. Logger.Info("主页预设:更新日志")
  79. Url = "https://pclhomeplazaoss.lingyunawa.top:26994/d/Homepages/Joker2184/UpdateHomepage.xaml"
  80. Case 9
  81. Logger.Info("主页预设:PCL 新功能说明书")
  82. Url = "https://raw.gitcode.com/WForst-Breeze/WhatsNewPCL/raw/main/Custom.xaml"
  83. Case 10
  84. Logger.Info("主页预设:OpenMCIM Dashboard")
  85. Url = "https://files.mcimirror.top/PCL"
  86. Case 11
  87. Logger.Info("主页预设:杂志主页")
  88. Url = "https://gh-proxy.com/https://github.com/Neclyon/Magazine-Homepage-PCL/raw/main/output/Custom.xaml"
  89. Case 12
  90. Logger.Info("主页预设:PCL GitHub 仪表盘")
  91. Url = "https://ddf.pcl-community.top/Custom.xaml"
  92. Case 13
  93. Logger.Info("主页预设:PCL 更新摘要")
  94. Url = "https://raw.gitcode.com/ENC_Euphony/PCL-AI-Summary-HomePage/raw/master/Custom.xaml"
  95. Case 14
  96. Logger.Info("主页预设:今日新闻热点")
  97. Url = "https://pcl.wyc-w.top/index.xaml"
  98. Case 15
  99. Logger.Info("主页预设:Minecraft 芝士站")
  100. Url = "https://mkss-services.awaeric.xyz"
  101. Case 16
  102. Logger.Info("主页预设:整合包推荐引擎")
  103. Url = "https://qawsedrftgyhujiko.fun/pcl2/Custom.xaml"
  104. Case 17
  105. Logger.Info("主页预设:Bangumi 番剧主页")
  106. Url = "https://bangumi.p.kaphia.top"
  107. Case 18
  108. Logger.Info("主页预设:Bilibili 热门")
  109. Url = "https://bilibili.p.kaphia.top"
  110. Case 19
  111. Logger.Info("主页预设:Music 云音乐热门")
  112. Url = "https://cloudmusic.p.kaphia.top"
  113. Case 20
  114. Logger.Info("主页预设:模组推荐")
  115. Url = "https://g-fish.dpdns.org/download/ModPage.xaml"
  116. Case 21
  117. Logger.Info("主页预设:每日游戏推荐")
  118. Url = "https://pcl.xn--9iq209asnop94b.top/download/GamePage.xaml"
  119. Case 22
  120. Logger.Info("主页预设:Wallpaper 每日壁纸")
  121. Url = "https://wallpaper.p.kaphia.top"
  122. Case 23
  123. Logger.Info("主页预设:Weather 天气预报")
  124. Url = "https://weather-p.kaphia.top/{variable:PCLDailyWeatherPage:choose}.xaml"
  125. Case 24
  126. Logger.Info("主页预设:新一代狗屁王大杂烩主页")
  127. Url = "https://end.g-fish.dpdns.org/Custom.xaml"
  128. End Select
  129. End Select
  130. '联网下载
  131. If Not String.IsNullOrWhiteSpace(Url) Then
  132. '替换自定义变量与设置
  133. Url = ArgumentReplace(Url, AddressOf WebUtility.HtmlEncode)
  134. If Url = Settings.Get(Of String)("CacheSavedPageUrl") AndAlso FileUtils.Exists(PathTemp & "Cache\Custom.xaml") Then
  135. '缓存可用
  136. Logger.Info("主页自定义数据来源:联网缓存文件")
  137. Content = FileUtils.TryReadAsString(PathTemp & "Cache\Custom.xaml")
  138. '后台更新缓存
  139. OnlineLoader.Start((Url, False))
  140. Else
  141. '缓存不可用
  142. Logger.Info("主页自定义数据来源:联网全新下载")
  143. Hint("正在加载主页……")
  144. RunInUiWait(Sub() LoadContent(Nothing)) '在加载结束前清空页面
  145. Settings.Set("CacheSavedPageVersion", "")
  146. OnlineLoader.Start((Url, True)) '下载完成后将会再次触发更新
  147. Return
  148. End If
  149. End If
  150. '同步到 UI
  151. RunInUi(Sub() LoadContent(Content))
  152. End Sub
  153. Private RefreshLock As New Object
  154. '联网获取主页文件
  155. Private OnlineLoader As New LoaderTask(Of (Address As String, ShouldRefresh As Boolean), Integer)("下载主页", AddressOf OnlineLoaderSub) With {.ReloadTimeout = 10 * 60 * 1000}
  156. Private Sub OnlineLoaderSub(Task As LoaderTask(Of (Address As String, ShouldRefresh As Boolean), Integer))
  157. Dim Address As String = Task.Input.Address '#3721 中连续触发两次导致内容变化
  158. Dim ShouldRefresh As Boolean = Task.Input.ShouldRefresh
  159. Try
  160. '获取版本校验地址
  161. Dim VersionAddress As String
  162. If Address.Contains(".xaml") Then
  163. VersionAddress = Address.Replace(".xaml", ".xaml.ini")
  164. Else
  165. VersionAddress = Address.BeforeFirst("?")
  166. If Not VersionAddress.EndsWithF("/") Then VersionAddress += "/"
  167. VersionAddress += "version"
  168. If Address.Contains("?") Then VersionAddress += "?" & Address.AfterFirst("?")
  169. End If
  170. '校验版本
  171. Dim Version As String = ""
  172. Try
  173. Version = NetRequestByClientRetry(VersionAddress)
  174. If Version.Length > 1000 Then Throw New Exception($"获取的主页版本过长({Version.Length} 字符)")
  175. Dim CurrentVersion As String = Settings.Get(Of String)("CacheSavedPageVersion")
  176. If Version <> "" AndAlso CurrentVersion <> "" AndAlso Version = CurrentVersion Then
  177. Logger.Info($"当前缓存的主页已为最新,当前版本:{Version},检查源:{VersionAddress}")
  178. Return
  179. End If
  180. Logger.Info($"需要下载联网主页,当前版本:{Version},检查源:{VersionAddress}")
  181. Catch exx As Exception
  182. Logger.Warn(exx, $"联网获取主页版本失败")
  183. Logger.Info($"无法检查联网主页版本,将直接下载,检查源:{VersionAddress}")
  184. End Try
  185. '实际下载
  186. Dim FileContent As String = NetRequestByClientRetry(Address)
  187. Logger.Info($"已联网下载主页,内容长度:{FileContent.Length},来源:{Address}")
  188. Settings.Set("CacheSavedPageUrl", Address)
  189. Settings.Set("CacheSavedPageVersion", Version)
  190. FileUtils.Write(PathTemp & "Cache\Custom.xaml", FileContent)
  191. '若内容变更则要求刷新
  192. If LoadedContentHash <> FileContent.GetStableHashCode() AndAlso ShouldRefresh Then Refresh()
  193. Catch ex As Exception
  194. Logger.Error(ex, $"下载主页失败({Address})", If(ModeDebug, LogBehavior.Alert, LogBehavior.Toast))
  195. End Try
  196. End Sub
  197. ''' <summary>
  198. ''' 立即强制刷新主页。
  199. ''' 必须在 UI 线程调用。
  200. ''' </summary>
  201. Public Sub ForceRefresh() Implements IRefreshable.Refresh
  202. Logger.Info("要求强制刷新主页")
  203. ClearCache()
  204. '实际的刷新
  205. If FrmMain.PageCurrent.Page = FormMain.PageType.Launch Then
  206. PanBack.ScrollToHome()
  207. Refresh()
  208. Else
  209. FrmMain.PageChange(FormMain.PageType.Launch)
  210. End If
  211. End Sub
  212. ''' <summary>
  213. ''' 清空主页缓存信息。
  214. ''' </summary>
  215. Private Sub ClearCache()
  216. LoadedContentHash = Nothing
  217. OnlineLoader.Input = ("", True)
  218. Settings.Set("CacheSavedPageUrl", "")
  219. Settings.Set("CacheSavedPageVersion", "")
  220. Logger.Info("已清空主页缓存")
  221. End Sub
  222. ''' <summary>
  223. ''' 从文本内容中加载主页。
  224. ''' 必须在 UI 线程调用。
  225. ''' </summary>
  226. Private Sub LoadContent(Content As String)
  227. Try
  228. SyncLock LoadContentLock
  229. '如果加载目标内容一致则不加载
  230. Dim Hash = If(Content, "").GetStableHashCode()
  231. If Hash = LoadedContentHash Then Return
  232. LoadedContentHash = Hash
  233. '实际加载内容
  234. PanCustom.Children.Clear()
  235. If String.IsNullOrWhiteSpace(Content) Then
  236. Logger.Info($"实例化:清空主页 UI,来源为空")
  237. Return
  238. End If
  239. Dim LoadStartTime As Date = Date.Now
  240. '修改时应同时修改 PageOtherHelpDetail.Init
  241. Content = ArgumentReplace(Content, AddressOf StringUtils.XmlEscape)
  242. Do While Content.Contains("xmlns")
  243. Content = Content.RegexReplace("xmlns[^""']*(""|')[^""']*(""|')", "").Replace("xmlns", "") '禁止声明命名空间
  244. Loop
  245. Content = "<StackPanel xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:sys=""clr-namespace:System;assembly=mscorlib"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" xmlns:local=""clr-namespace:PCL;assembly=Plain Craft Launcher 2"" xmlns:core=""clr-namespace:MeloongCore;assembly=MeloongCore"" xmlns:corewpf=""clr-namespace:MeloongCore.Wpf;assembly=MeloongCore.Wpf"">" & Content & "</StackPanel>"
  246. Logger.Info($"实例化:加载主页 UI 开始,最终内容长度:{Content.Count}")
  247. PanCustom.Children.Add(GetObjectFromXML(Content))
  248. '加载计时
  249. Dim LoadCostTime = (Date.Now - LoadStartTime).Milliseconds
  250. Logger.Info($"实例化:加载主页 UI 完成,耗时 {LoadCostTime}ms")
  251. If LoadCostTime > 3000 Then Hint($"主页加载过于缓慢(花费了 {Math.Round(LoadCostTime / 1000, 1)} 秒),请向主页作者反馈此问题,或暂时停止使用该主页")
  252. End SyncLock
  253. Catch ex As Exception
  254. Logger.Warn(ex, $"加载失败的主页内容:{vbCrLf}{Content}")
  255. OnLoadContentFailed(ex)
  256. End Try
  257. End Sub
  258. ''' <summary>
  259. ''' 加载主页失败时调用。
  260. ''' </summary>
  261. Private Sub OnLoadContentFailed(ex As Exception)
  262. If ModeDebug OrElse Settings.Get(Of Integer)("UiCustomType") = 1 Then
  263. Logger.Warn(ex, "加载主页失败")
  264. If MyMsgBox(If(TypeOf ex Is UnauthorizedAccessException, ex.Message, $"主页内容编写有误,请根据下列错误信息进行检查:{vbCrLf}{ex.GetDisplay(False)}"),
  265. "加载主页失败", "重试", "取消") = 1 Then ForceRefresh()
  266. Else
  267. Logger.Error(ex, "加载主页失败", LogBehavior.Toast)
  268. End If
  269. End Sub
  270. ''' <summary>
  271. ''' 捕获主页在 Measure 和 Arrange 阶段抛出的异常。
  272. ''' </summary>
  273. Private Sub DispatcherUnhandledException(sender As Object, e As DispatcherUnhandledExceptionEventArgs) Implements IDispatcherUnhandledException.DispatcherUnhandledException
  274. If TypeOf e.Exception IsNot Markup.XamlParseException Then Return
  275. e.Handled = True
  276. LoadContent(Nothing)
  277. OnLoadContentFailed(e.Exception)
  278. End Sub
  279. Private LoadedContentHash As ULong? = Nothing
  280. Private LoadContentLock As New Object
  281. #End Region
  282. End Class