mirror of
https://github.com/2dust/v2rayN.git
synced 2025-08-23 19:36:55 +00:00
Compare commits
5 commits
11c0de87e5
...
dbf12af3a0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
dbf12af3a0 | ||
![]() |
120e8d0686 | ||
![]() |
186b56aed9 | ||
![]() |
c560fe13fe | ||
![]() |
95e3ebd815 |
6 changed files with 206 additions and 114 deletions
|
@ -1,7 +1,7 @@
|
||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>7.13.7</Version>
|
<Version>7.14.0</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|
|
@ -464,9 +464,11 @@ public class Global
|
||||||
public static readonly List<int> TunMtus =
|
public static readonly List<int> TunMtus =
|
||||||
[
|
[
|
||||||
1280,
|
1280,
|
||||||
1408,
|
1408,
|
||||||
1500,
|
1500,
|
||||||
9000
|
4064,
|
||||||
|
9000,
|
||||||
|
65535
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> TunStacks =
|
public static readonly List<string> TunStacks =
|
||||||
|
|
|
@ -1412,6 +1412,11 @@ public class ConfigHandler
|
||||||
{
|
{
|
||||||
profileItem = V2rayFmt.ResolveFull(strData, subRemarks);
|
profileItem = V2rayFmt.ResolveFull(strData, subRemarks);
|
||||||
}
|
}
|
||||||
|
//Is Html Page
|
||||||
|
if (profileItem is null && HtmlPageFmt.IsHtmlPage(strData))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
//Is Clash configuration
|
//Is Clash configuration
|
||||||
if (profileItem is null)
|
if (profileItem is null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -220,14 +220,7 @@ public class BaseFmt
|
||||||
|
|
||||||
protected static bool Contains(string str, params string[] s)
|
protected static bool Contains(string str, params string[] s)
|
||||||
{
|
{
|
||||||
foreach (var item in s)
|
return s.All(item => str.Contains(item, StringComparison.OrdinalIgnoreCase));
|
||||||
{
|
|
||||||
if (str.Contains(item, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static string WriteAllText(string strData, string ext = "json")
|
protected static string WriteAllText(string strData, string ext = "json")
|
||||||
|
|
11
v2rayN/ServiceLib/Handler/Fmt/HtmlPageFmt.cs
Normal file
11
v2rayN/ServiceLib/Handler/Fmt/HtmlPageFmt.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace ServiceLib.Handler.Fmt;
|
||||||
|
|
||||||
|
public class HtmlPageFmt : BaseFmt
|
||||||
|
{
|
||||||
|
public static bool IsHtmlPage(string strData)
|
||||||
|
{
|
||||||
|
return Contains(strData, "<html", "<!doctype html", "<head");
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,119 +15,200 @@ public class SubscriptionHandler
|
||||||
|
|
||||||
foreach (var item in subItem)
|
foreach (var item in subItem)
|
||||||
{
|
{
|
||||||
var id = item.Id.TrimEx();
|
try
|
||||||
var url = item.Url.TrimEx();
|
|
||||||
var userAgent = item.UserAgent.TrimEx();
|
|
||||||
var hashCode = $"{item.Remarks}->";
|
|
||||||
if (id.IsNullOrEmpty() || url.IsNullOrEmpty() || (subId.IsNotEmpty() && item.Id != subId))
|
|
||||||
{
|
{
|
||||||
//_updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgNoValidSubscription}");
|
if (!IsValidSubscription(item, subId))
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!url.StartsWith(Global.HttpsProtocol) && !url.StartsWith(Global.HttpProtocol))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (item.Enabled == false)
|
|
||||||
{
|
|
||||||
updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgSkipSubscriptionUpdate}");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var downloadHandle = new DownloadService();
|
|
||||||
downloadHandle.Error += (sender2, args) =>
|
|
||||||
{
|
|
||||||
updateFunc?.Invoke(false, $"{hashCode}{args.GetException().Message}");
|
|
||||||
};
|
|
||||||
|
|
||||||
updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgStartGettingSubscriptions}");
|
|
||||||
|
|
||||||
//one url
|
|
||||||
url = Utils.GetPunycode(url);
|
|
||||||
//convert
|
|
||||||
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);
|
continue;
|
||||||
}
|
|
||||||
if (!url.Contains("config="))
|
|
||||||
{
|
|
||||||
url += string.Format("&config={0}", Global.SubConvertConfig.FirstOrDefault());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var result = await downloadHandle.TryDownloadString(url, blProxy, userAgent);
|
|
||||||
if (blProxy && result.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
result = await downloadHandle.TryDownloadString(url, false, userAgent);
|
|
||||||
}
|
|
||||||
|
|
||||||
//more url
|
|
||||||
if (item.ConvertTarget.IsNullOrEmpty() && item.MoreUrl.TrimEx().IsNotEmpty())
|
|
||||||
{
|
|
||||||
if (result.IsNotEmpty() && Utils.IsBase64String(result))
|
|
||||||
{
|
|
||||||
result = Utils.Base64Decode(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var lstUrl = item.MoreUrl.TrimEx().Split(",") ?? [];
|
var hashCode = $"{item.Remarks}->";
|
||||||
foreach (var it in lstUrl)
|
if (item.Enabled == false)
|
||||||
{
|
{
|
||||||
var url2 = Utils.GetPunycode(it);
|
updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgSkipSubscriptionUpdate}");
|
||||||
if (url2.IsNullOrEmpty())
|
continue;
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result2 = await downloadHandle.TryDownloadString(url2, blProxy, userAgent);
|
|
||||||
if (blProxy && result2.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
result2 = await downloadHandle.TryDownloadString(url2, false, userAgent);
|
|
||||||
}
|
|
||||||
if (result2.IsNotEmpty())
|
|
||||||
{
|
|
||||||
if (Utils.IsBase64String(result2))
|
|
||||||
{
|
|
||||||
result += Environment.NewLine + Utils.Base64Decode(result2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result += Environment.NewLine + result2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (result.IsNullOrEmpty())
|
// Create download handler
|
||||||
|
var downloadHandle = CreateDownloadHandler(hashCode, updateFunc);
|
||||||
|
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
|
||||||
|
await ProcessDownloadResult(config, item.Id, result, hashCode, updateFunc);
|
||||||
|
|
||||||
|
updateFunc?.Invoke(false, "-------------------------------------------------------");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgSubscriptionDecodingFailed}");
|
var hashCode = $"{item.Remarks}->";
|
||||||
|
Logging.SaveLog("UpdateSubscription", ex);
|
||||||
|
updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgFailedImportSubscription}: {ex.Message}");
|
||||||
|
updateFunc?.Invoke(false, "-------------------------------------------------------");
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgGetSubscriptionSuccessfully}");
|
|
||||||
if (result?.Length < 99)
|
|
||||||
{
|
|
||||||
updateFunc?.Invoke(false, $"{hashCode}{result}");
|
|
||||||
}
|
|
||||||
|
|
||||||
var ret = await ConfigHandler.AddBatchServers(config, result, id, true);
|
|
||||||
if (ret <= 0)
|
|
||||||
{
|
|
||||||
Logging.SaveLog("FailedImportSubscription");
|
|
||||||
Logging.SaveLog(result);
|
|
||||||
}
|
|
||||||
updateFunc?.Invoke(false,
|
|
||||||
ret > 0
|
|
||||||
? $"{hashCode}{ResUI.MsgUpdateSubscriptionEnd}"
|
|
||||||
: $"{hashCode}{ResUI.MsgFailedImportSubscription}");
|
|
||||||
}
|
|
||||||
updateFunc?.Invoke(false, "-------------------------------------------------------");
|
|
||||||
|
|
||||||
//await ConfigHandler.DedupServerList(config, id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFunc?.Invoke(true, $"{ResUI.MsgUpdateSubscriptionEnd}");
|
updateFunc?.Invoke(true, $"{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, Action<bool, string> updateFunc)
|
||||||
|
{
|
||||||
|
var downloadHandle = new DownloadService();
|
||||||
|
downloadHandle.Error += (sender2, args) =>
|
||||||
|
{
|
||||||
|
updateFunc?.Invoke(false, $"{hashCode}{args.GetException().Message}");
|
||||||
|
};
|
||||||
|
return downloadHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<string> 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<string> 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<string> 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<string> 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, Action<bool, string> updateFunc)
|
||||||
|
{
|
||||||
|
if (result.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgSubscriptionDecodingFailed}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgGetSubscriptionSuccessfully}");
|
||||||
|
|
||||||
|
// If result is too short, display content directly
|
||||||
|
if (result.Length < 99)
|
||||||
|
{
|
||||||
|
updateFunc?.Invoke(false, $"{hashCode}{result}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
updateFunc?.Invoke(false,
|
||||||
|
ret > 0
|
||||||
|
? $"{hashCode}{ResUI.MsgUpdateSubscriptionEnd}"
|
||||||
|
: $"{hashCode}{ResUI.MsgFailedImportSubscription}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue