namespace ServiceLib.Handler; public static class SubscriptionHandler { public static async Task UpdateProcess(Config config, string subId, bool blProxy, Func updateFunc) { await updateFunc?.Invoke(false, ResUI.MsgUpdateSubscriptionStart); var subItem = await AppManager.Instance.SubItems(); if (subItem is not { Count: > 0 }) { await updateFunc?.Invoke(false, ResUI.MsgNoValidSubscription); return; } var successCount = 0; foreach (var item in subItem) { try { if (!IsValidSubscription(item, subId)) { continue; } var hashCode = $"{item.Remarks}->"; if (item.Enabled == false) { await updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgSkipSubscriptionUpdate}"); continue; } // Create download handler var downloadHandle = CreateDownloadHandler(hashCode, updateFunc); await updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgStartGettingSubscriptions}"); // Get all subscription content (main subscription + additional subscriptions) var result = await DownloadAllSubscriptions(config, item, blProxy, downloadHandle); // Process download result if (await ProcessDownloadResult(config, item.Id, result, hashCode, updateFunc)) { successCount++; } await updateFunc?.Invoke(false, "-------------------------------------------------------"); } catch (Exception ex) { var hashCode = $"{item.Remarks}->"; Logging.SaveLog("UpdateSubscription", ex); await updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgFailedImportSubscription}: {ex.Message}"); await updateFunc?.Invoke(false, "-------------------------------------------------------"); } } await updateFunc?.Invoke(successCount > 0, $"{ResUI.MsgUpdateSubscriptionEnd}"); } private static bool IsValidSubscription(SubItem item, string subId) { var id = item.Id.TrimEx(); var url = item.Url.TrimEx(); if (id.IsNullOrEmpty() || url.IsNullOrEmpty()) { return false; } if (subId.IsNotEmpty() && item.Id != subId) { return false; } if (!url.StartsWith(Global.HttpsProtocol) && !url.StartsWith(Global.HttpProtocol)) { return false; } return true; } private static DownloadService CreateDownloadHandler(string hashCode, Func updateFunc) { var downloadHandle = new DownloadService(); downloadHandle.Error += (sender2, args) => { updateFunc?.Invoke(false, $"{hashCode}{args.GetException().Message}"); }; return downloadHandle; } private static async Task DownloadSubscriptionContent(DownloadService downloadHandle, string url, bool blProxy, string userAgent) { var result = await downloadHandle.TryDownloadString(url, blProxy, userAgent); // If download with proxy fails, try direct connection if (blProxy && result.IsNullOrEmpty()) { result = await downloadHandle.TryDownloadString(url, false, userAgent); } return result ?? string.Empty; } private static async Task DownloadAllSubscriptions(Config config, SubItem item, bool blProxy, DownloadService downloadHandle) { // Download main subscription content var result = await DownloadMainSubscription(config, item, blProxy, downloadHandle); // Process additional subscription links (if any) if (item.ConvertTarget.IsNullOrEmpty() && item.MoreUrl.TrimEx().IsNotEmpty()) { result = await DownloadAdditionalSubscriptions(item, result, blProxy, downloadHandle); } return result; } private static async Task DownloadMainSubscription(Config config, SubItem item, bool blProxy, DownloadService downloadHandle) { // Prepare subscription URL and download directly var url = Utils.GetPunycode(item.Url.TrimEx()); // If conversion is needed if (item.ConvertTarget.IsNotEmpty()) { var subConvertUrl = config.ConstItem.SubConvertUrl.IsNullOrEmpty() ? Global.SubConvertUrls.FirstOrDefault() : config.ConstItem.SubConvertUrl; url = string.Format(subConvertUrl!, Utils.UrlEncode(url)); if (!url.Contains("target=")) { url += string.Format("&target={0}", item.ConvertTarget); } if (!url.Contains("config=")) { url += string.Format("&config={0}", Global.SubConvertConfig.FirstOrDefault()); } } // Download and return result directly return await DownloadSubscriptionContent(downloadHandle, url, blProxy, item.UserAgent); } private static async Task DownloadAdditionalSubscriptions(SubItem item, string mainResult, bool blProxy, DownloadService downloadHandle) { var result = mainResult; // If main subscription result is Base64 encoded, decode it first if (result.IsNotEmpty() && Utils.IsBase64String(result)) { result = Utils.Base64Decode(result); } // Process additional URL list var lstUrl = item.MoreUrl.TrimEx().Split(",") ?? []; foreach (var it in lstUrl) { var url2 = Utils.GetPunycode(it); if (url2.IsNullOrEmpty()) { continue; } var additionalResult = await DownloadSubscriptionContent(downloadHandle, url2, blProxy, item.UserAgent); if (additionalResult.IsNotEmpty()) { // Process additional subscription results, add to main result if (Utils.IsBase64String(additionalResult)) { result += Environment.NewLine + Utils.Base64Decode(additionalResult); } else { result += Environment.NewLine + additionalResult; } } } return result; } private static async Task ProcessDownloadResult(Config config, string id, string result, string hashCode, Func updateFunc) { if (result.IsNullOrEmpty()) { await updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgSubscriptionDecodingFailed}"); return false; } await updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgGetSubscriptionSuccessfully}"); // If result is too short, display content directly if (result.Length < 99) { await updateFunc?.Invoke(false, $"{hashCode}{result}"); } await updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgStartParsingSubscription}"); // Add servers to configuration var ret = await ConfigHandler.AddBatchServers(config, result, id, true); if (ret <= 0) { Logging.SaveLog("FailedImportSubscription"); Logging.SaveLog(result); } // Update completion message await updateFunc?.Invoke(false, ret > 0 ? $"{hashCode}{ResUI.MsgUpdateSubscriptionEnd}" : $"{hashCode}{ResUI.MsgFailedImportSubscription}"); return ret > 0; } }