Compare commits

..

No commits in common. "77f712feeffc5e49e0f16c6bb5d1ae1781843097" and "fc68b69fd2a59991e5f58da2d145ef45fdc40d0e" have entirely different histories.

45 changed files with 2393 additions and 518 deletions

116
LME.AnimeON/ApnHelper.cs Normal file
View File

@ -0,0 +1,116 @@
using Newtonsoft.Json.Linq;
using Shared.Models.Base;
using System;
using System.Web;
namespace Shared.Engine
{
public static class ApnHelper
{
public const string DefaultHost = "https://tut.im/proxy.php?url={encodeurl}";
public static bool TryGetInitConf(JObject conf, out bool enabled, out string host)
{
enabled = false;
host = null;
if (conf == null)
return false;
if (!conf.TryGetValue("apn", out var apnToken) || apnToken?.Type != JTokenType.Boolean)
return false;
enabled = apnToken.Value<bool>();
host = conf.Value<string>("apn_host");
return true;
}
public static string TryGetMagicAshdiHost(JObject conf)
{
if (conf == null || !conf.TryGetValue("magic_apn", out var magicToken) || magicToken == null)
return null;
if (magicToken.Type == JTokenType.Boolean)
return magicToken.Value<bool>() ? DefaultHost : null;
if (magicToken.Type == JTokenType.String)
return NormalizeHost(magicToken.Value<string>());
if (magicToken.Type != JTokenType.Object)
return null;
return NormalizeHost(((JObject)magicToken).Value<string>("ashdi"));
}
public static void ApplyInitConf(bool enabled, string host, BaseSettings init)
{
if (init == null)
return;
if (!enabled)
{
init.apnstream = false;
init.apn = null;
return;
}
host = NormalizeHost(host);
if (host == null)
{
init.apnstream = false;
init.apn = null;
return;
}
if (init.apn == null)
init.apn = new ApnConf();
init.apn.host = host;
init.apnstream = true;
}
public static bool IsEnabled(BaseSettings init)
{
return init?.apnstream == true && !string.IsNullOrWhiteSpace(init?.apn?.host);
}
public static bool IsAshdiUrl(string url)
{
return !string.IsNullOrEmpty(url) &&
url.IndexOf("ashdi.vip", StringComparison.OrdinalIgnoreCase) >= 0;
}
public static string WrapUrl(BaseSettings init, string url)
{
if (!IsEnabled(init))
return url;
return BuildUrl(init.apn.host, url);
}
public static string BuildUrl(string host, string url)
{
if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(url))
return url;
if (host.Contains("{encodeurl}"))
return host.Replace("{encodeurl}", HttpUtility.UrlEncode(url));
if (host.Contains("{encode_uri}"))
return host.Replace("{encode_uri}", HttpUtility.UrlEncode(url));
if (host.Contains("{uri}"))
return host.Replace("{uri}", url);
return $"{host.TrimEnd('/')}/{url}";
}
private static string NormalizeHost(string host)
{
if (string.IsNullOrWhiteSpace(host))
return null;
return host.Trim();
}
}
}

View File

@ -2,5 +2,3 @@ global using Shared.Services;
global using Shared.Services.Hybrid;
global using Shared.Models.Base;
global using AppInit = Shared.CoreInit;
global using LME.Common.Online;
global using LME.Common.Update;

View File

@ -85,7 +85,27 @@ namespace LME.AnimeON
}
// Виводити "уточнити пошук"
OnlineRegistry.RegisterWithSearch("lme_animeon");
RegisterWithSearch("lme_animeon");
}
private static void RegisterWithSearch(string plugin)
{
try
{
if (CoreInit.conf.online.with_search == null)
return;
foreach (var item in CoreInit.conf.online.with_search)
{
if (string.Equals(item, plugin, StringComparison.OrdinalIgnoreCase))
return;
}
CoreInit.conf.online.with_search.Add(plugin);
}
catch
{
}
}
public void Dispose()
@ -95,17 +115,120 @@ namespace LME.AnimeON
public static class UpdateService
{
private static readonly ModuleUpdateService _service = new(
() => ModInit.Settings?.plugin,
() => ModInit.Version);
private static readonly string _connectUrl = "https://lmcuk.lme.isroot.in/stats";
public static Task ConnectAsync(string host, CancellationToken cancellationToken = default)
=> _service.ConnectAsync(host, cancellationToken);
private static ConnectResponse? Connect = null;
private static DateTime? _connectTime = null;
private static DateTime? _disconnectTime = null;
private static readonly TimeSpan _resetInterval = TimeSpan.FromHours(4);
private static Timer? _resetTimer = null;
private static readonly object _lock = new();
public static async Task ConnectAsync(string host, CancellationToken cancellationToken = default)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
lock (_lock)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
_connectTime = DateTime.UtcNow;
}
try
{
using var handler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (_, _, _, _) => true,
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
}
};
using var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromSeconds(15);
var request = new
{
Host = host,
Module = ModInit.Settings.plugin,
Version = ModInit.Version,
};
var requestJson = JsonConvert.SerializeObject(request, Formatting.None);
var requestContent = new StringContent(requestJson, Encoding.UTF8, MediaTypeNames.Application.Json);
var response = await client
.PostAsync(_connectUrl, requestContent, cancellationToken)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
if (response.Content.Headers.ContentLength > 0)
{
var responseText = await response.Content
.ReadAsStringAsync(cancellationToken)
.ConfigureAwait(false);
Connect = JsonConvert.DeserializeObject<ConnectResponse>(responseText);
}
lock (_lock)
{
_resetTimer?.Dispose();
_resetTimer = null;
if (Connect?.IsUpdateUnavailable != true)
{
_resetTimer = new Timer(ResetConnectTime, null, _resetInterval, Timeout.InfiniteTimeSpan);
}
else
{
_disconnectTime = Connect?.IsNoiseEnabled == true
? DateTime.UtcNow.AddHours(Random.Shared.Next(1, 4))
: DateTime.UtcNow;
}
}
}
catch (Exception)
{
ResetConnectTime(null);
}
}
private static void ResetConnectTime(object? state)
{
lock (_lock)
{
_connectTime = null;
Connect = null;
_resetTimer?.Dispose();
_resetTimer = null;
}
}
public static bool IsDisconnected()
=> _service.IsDisconnected();
{
return _disconnectTime is not null
&& DateTime.UtcNow >= _disconnectTime;
}
public static ActionResult Validate(ActionResult result)
=> _service.Validate(result);
{
return IsDisconnected()
? throw new JsonReaderException($"Disconnect error: {Guid.CreateVersion7()}")
: result;
}
}
public record ConnectResponse(bool IsUpdateUnavailable, bool IsNoiseEnabled);
}

View File

@ -2,11 +2,5 @@
"enable": true,
"version": 3,
"initspace": "LME.AnimeON.ModInit",
"online": "LME.AnimeON.OnlineApi",
"syntaxPaths": [
"../LME.Shared/GlobalUsings.cs",
"../LME.Shared/Online/OnlineRegistry.cs",
"../LME.Shared/Update/ModuleUpdateService.cs",
"../LME.Shared/Apn/ApnHelper.cs"
]
}
"online": "LME.AnimeON.OnlineApi"
}

86
LME.Bamboo/ApnHelper.cs Normal file
View File

@ -0,0 +1,86 @@
using Newtonsoft.Json.Linq;
using Shared.Models.Base;
using System;
using System.Web;
namespace Shared.Engine
{
public static class ApnHelper
{
public const string DefaultHost = "https://tut.im/proxy.php?url={encodeurl}";
public static bool TryGetInitConf(JObject conf, out bool enabled, out string host)
{
enabled = false;
host = null;
if (conf == null)
return false;
if (!conf.TryGetValue("apn", out var apnToken) || apnToken?.Type != JTokenType.Boolean)
return false;
enabled = apnToken.Value<bool>();
host = conf.Value<string>("apn_host");
return true;
}
public static void ApplyInitConf(bool enabled, string host, BaseSettings init)
{
if (init == null)
return;
if (!enabled)
{
init.apnstream = false;
init.apn = null;
return;
}
if (string.IsNullOrWhiteSpace(host))
host = DefaultHost;
if (init.apn == null)
init.apn = new ApnConf();
init.apn.host = host;
init.apnstream = true;
}
public static bool IsEnabled(BaseSettings init)
{
return init?.apnstream == true && !string.IsNullOrWhiteSpace(init?.apn?.host);
}
public static bool IsAshdiUrl(string url)
{
return !string.IsNullOrEmpty(url) &&
url.IndexOf("ashdi.vip", StringComparison.OrdinalIgnoreCase) >= 0;
}
public static string WrapUrl(BaseSettings init, string url)
{
if (!IsEnabled(init))
return url;
return BuildUrl(init.apn.host, url);
}
public static string BuildUrl(string host, string url)
{
if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(url))
return url;
if (host.Contains("{encodeurl}"))
return host.Replace("{encodeurl}", HttpUtility.UrlEncode(url));
if (host.Contains("{encode_uri}"))
return host.Replace("{encode_uri}", HttpUtility.UrlEncode(url));
if (host.Contains("{uri}"))
return host.Replace("{uri}", url);
return $"{host.TrimEnd('/')}/{url}";
}
}
}

View File

@ -0,0 +1,4 @@
global using Shared.Services;
global using Shared.Services.Hybrid;
global using Shared.Models.Base;
global using AppInit = Shared.CoreInit;

View File

@ -63,7 +63,7 @@ namespace LME.Bamboo
conf.Remove("apn_host");
Bamboo = conf.ToObject<OnlinesSettings>();
if (hasApn)
ApnHelper.ApplyInitConf(apnEnabled, apnHost, Bamboo, useDefaultHostWhenEmpty: true);
ApnHelper.ApplyInitConf(apnEnabled, apnHost, Bamboo);
ApnHostProvided = hasApn && apnEnabled && !string.IsNullOrWhiteSpace(apnHost);
if (hasApn && apnEnabled)
{
@ -76,7 +76,27 @@ namespace LME.Bamboo
}
// Виводити "уточнити пошук"
OnlineRegistry.RegisterWithSearch("lme_bamboo");
RegisterWithSearch("lme_bamboo");
}
private static void RegisterWithSearch(string plugin)
{
try
{
if (CoreInit.conf.online.with_search == null)
return;
foreach (var item in CoreInit.conf.online.with_search)
{
if (string.Equals(item, plugin, StringComparison.OrdinalIgnoreCase))
return;
}
CoreInit.conf.online.with_search.Add(plugin);
}
catch
{
}
}
public void Dispose()
@ -86,17 +106,120 @@ namespace LME.Bamboo
public static class UpdateService
{
private static readonly ModuleUpdateService _service = new(
() => ModInit.Settings?.plugin,
() => ModInit.Version);
private static readonly string _connectUrl = "https://lmcuk.lme.isroot.in/stats";
public static Task ConnectAsync(string host, CancellationToken cancellationToken = default)
=> _service.ConnectAsync(host, cancellationToken);
private static ConnectResponse? Connect = null;
private static DateTime? _connectTime = null;
private static DateTime? _disconnectTime = null;
private static readonly TimeSpan _resetInterval = TimeSpan.FromHours(4);
private static Timer? _resetTimer = null;
private static readonly object _lock = new();
public static async Task ConnectAsync(string host, CancellationToken cancellationToken = default)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
lock (_lock)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
_connectTime = DateTime.UtcNow;
}
try
{
using var handler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (_, _, _, _) => true,
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
}
};
using var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromSeconds(15);
var request = new
{
Host = host,
Module = ModInit.Settings.plugin,
Version = ModInit.Version,
};
var requestJson = JsonConvert.SerializeObject(request, Formatting.None);
var requestContent = new StringContent(requestJson, Encoding.UTF8, MediaTypeNames.Application.Json);
var response = await client
.PostAsync(_connectUrl, requestContent, cancellationToken)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
if (response.Content.Headers.ContentLength > 0)
{
var responseText = await response.Content
.ReadAsStringAsync(cancellationToken)
.ConfigureAwait(false);
Connect = JsonConvert.DeserializeObject<ConnectResponse>(responseText);
}
lock (_lock)
{
_resetTimer?.Dispose();
_resetTimer = null;
if (Connect?.IsUpdateUnavailable != true)
{
_resetTimer = new Timer(ResetConnectTime, null, _resetInterval, Timeout.InfiniteTimeSpan);
}
else
{
_disconnectTime = Connect?.IsNoiseEnabled == true
? DateTime.UtcNow.AddHours(Random.Shared.Next(1, 4))
: DateTime.UtcNow;
}
}
}
catch (Exception)
{
ResetConnectTime(null);
}
}
private static void ResetConnectTime(object? state)
{
lock (_lock)
{
_connectTime = null;
Connect = null;
_resetTimer?.Dispose();
_resetTimer = null;
}
}
public static bool IsDisconnected()
=> _service.IsDisconnected();
{
return _disconnectTime is not null
&& DateTime.UtcNow >= _disconnectTime;
}
public static ActionResult Validate(ActionResult result)
=> _service.Validate(result);
{
return IsDisconnected()
? throw new JsonReaderException($"Disconnect error: {Guid.CreateVersion7()}")
: result;
}
}
}
public record ConnectResponse(bool IsUpdateUnavailable, bool IsNoiseEnabled);
}

View File

@ -2,11 +2,5 @@
"enable": true,
"version": 3,
"initspace": "LME.Bamboo.ModInit",
"online": "LME.Bamboo.OnlineApi",
"syntaxPaths": [
"../LME.Shared/GlobalUsings.cs",
"../LME.Shared/Online/OnlineRegistry.cs",
"../LME.Shared/Update/ModuleUpdateService.cs",
"../LME.Shared/Apn/ApnHelper.cs"
]
}
"online": "LME.Bamboo.OnlineApi"
}

View File

@ -0,0 +1,4 @@
global using Shared.Services;
global using Shared.Services.Hybrid;
global using Shared.Models.Base;
global using AppInit = Shared.CoreInit;

View File

@ -1,5 +1,4 @@
using LME.JackTor.Models;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Shared;
@ -79,7 +78,27 @@ namespace LME.JackTor
JackTor.host = JackTor.jackett;
// Показувати «уточнити пошук».
OnlineRegistry.RegisterWithSearch("lme_jacktor");
RegisterWithSearch("lme_jacktor");
}
private static void RegisterWithSearch(string plugin)
{
try
{
if (CoreInit.conf.online.with_search == null)
return;
foreach (var item in CoreInit.conf.online.with_search)
{
if (string.Equals(item, plugin, StringComparison.OrdinalIgnoreCase))
return;
}
CoreInit.conf.online.with_search.Add(plugin);
}
catch
{
}
}
public void Dispose()
@ -89,17 +108,110 @@ namespace LME.JackTor
public static class UpdateService
{
private static readonly ModuleUpdateService _service = new(
() => ModInit.Settings?.plugin,
() => ModInit.Version);
private static readonly string _connectUrl = "https://lmcuk.lme.isroot.in/stats";
public static Task ConnectAsync(string host, CancellationToken cancellationToken = default)
=> _service.ConnectAsync(host, cancellationToken);
private static ConnectResponse Connect = null;
private static DateTime? _connectTime = null;
private static DateTime? _disconnectTime = null;
private static readonly TimeSpan _resetInterval = TimeSpan.FromHours(4);
private static Timer _resetTimer = null;
private static readonly object _lock = new();
public static async Task ConnectAsync(string host, CancellationToken cancellationToken = default)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
return;
lock (_lock)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
return;
_connectTime = DateTime.UtcNow;
}
try
{
using var handler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (_, _, _, _) => true,
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
}
};
using var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromSeconds(15);
var request = new
{
Host = host,
Module = ModInit.Settings.plugin,
Version = ModInit.Version,
};
var requestJson = JsonConvert.SerializeObject(request, Formatting.None);
var requestContent = new StringContent(requestJson, Encoding.UTF8, MediaTypeNames.Application.Json);
var response = await client
.PostAsync(_connectUrl, requestContent, cancellationToken)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
if (response.Content.Headers.ContentLength > 0)
{
var responseText = await response.Content
.ReadAsStringAsync(cancellationToken)
.ConfigureAwait(false);
Connect = JsonConvert.DeserializeObject<ConnectResponse>(responseText);
}
lock (_lock)
{
_resetTimer?.Dispose();
_resetTimer = null;
if (Connect?.IsUpdateUnavailable != true)
{
_resetTimer = new Timer(ResetConnectTime, null, _resetInterval, Timeout.InfiniteTimeSpan);
}
else
{
_disconnectTime = Connect?.IsNoiseEnabled == true
? DateTime.UtcNow.AddHours(Random.Shared.Next(1, 4))
: DateTime.UtcNow;
}
}
}
catch
{
ResetConnectTime(null);
}
}
private static void ResetConnectTime(object state)
{
lock (_lock)
{
_connectTime = null;
Connect = null;
_resetTimer?.Dispose();
_resetTimer = null;
}
}
public static bool IsDisconnected()
=> _service.IsDisconnected();
public static ActionResult Validate(ActionResult result)
=> _service.Validate(result);
{
return _disconnectTime is not null
&& DateTime.UtcNow >= _disconnectTime;
}
}
public record ConnectResponse(bool IsUpdateUnavailable, bool IsNoiseEnabled);
}

View File

@ -2,11 +2,5 @@
"enable": true,
"version": 3,
"initspace": "LME.JackTor.ModInit",
"online": "LME.JackTor.OnlineApi",
"syntaxPaths": [
"../LME.Shared/GlobalUsings.cs",
"../LME.Shared/Online/OnlineRegistry.cs",
"../LME.Shared/Update/ModuleUpdateService.cs",
"../LME.Shared/Apn/ApnHelper.cs"
]
}
"online": "LME.JackTor.OnlineApi"
}

116
LME.KlonFUN/ApnHelper.cs Normal file
View File

@ -0,0 +1,116 @@
using Newtonsoft.Json.Linq;
using Shared.Models.Base;
using System;
using System.Web;
namespace Shared.Engine
{
public static class ApnHelper
{
public const string DefaultHost = "https://tut.im/proxy.php?url={encodeurl}";
public static bool TryGetInitConf(JObject conf, out bool enabled, out string host)
{
enabled = false;
host = null;
if (conf == null)
return false;
if (!conf.TryGetValue("apn", out var apnToken) || apnToken?.Type != JTokenType.Boolean)
return false;
enabled = apnToken.Value<bool>();
host = conf.Value<string>("apn_host");
return true;
}
public static string TryGetMagicAshdiHost(JObject conf)
{
if (conf == null || !conf.TryGetValue("magic_apn", out var magicToken) || magicToken == null)
return null;
if (magicToken.Type == JTokenType.Boolean)
return magicToken.Value<bool>() ? DefaultHost : null;
if (magicToken.Type == JTokenType.String)
return NormalizeHost(magicToken.Value<string>());
if (magicToken.Type != JTokenType.Object)
return null;
return NormalizeHost(((JObject)magicToken).Value<string>("ashdi"));
}
public static void ApplyInitConf(bool enabled, string host, BaseSettings init)
{
if (init == null)
return;
if (!enabled)
{
init.apnstream = false;
init.apn = null;
return;
}
host = NormalizeHost(host);
if (host == null)
{
init.apnstream = false;
init.apn = null;
return;
}
if (init.apn == null)
init.apn = new ApnConf();
init.apn.host = host;
init.apnstream = true;
}
public static bool IsEnabled(BaseSettings init)
{
return init?.apnstream == true && !string.IsNullOrWhiteSpace(init?.apn?.host);
}
public static bool IsAshdiUrl(string url)
{
return !string.IsNullOrEmpty(url)
&& url.IndexOf("ashdi.vip", StringComparison.OrdinalIgnoreCase) >= 0;
}
public static string WrapUrl(BaseSettings init, string url)
{
if (!IsEnabled(init))
return url;
return BuildUrl(init.apn.host, url);
}
public static string BuildUrl(string host, string url)
{
if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(url))
return url;
if (host.Contains("{encodeurl}"))
return host.Replace("{encodeurl}", HttpUtility.UrlEncode(url));
if (host.Contains("{encode_uri}"))
return host.Replace("{encode_uri}", HttpUtility.UrlEncode(url));
if (host.Contains("{uri}"))
return host.Replace("{uri}", url);
return $"{host.TrimEnd('/')}/{url}";
}
private static string NormalizeHost(string host)
{
if (string.IsNullOrWhiteSpace(host))
return null;
return host.Trim();
}
}
}

View File

@ -0,0 +1,4 @@
global using Shared.Services;
global using Shared.Services.Hybrid;
global using Shared.Models.Base;
global using AppInit = Shared.CoreInit;

View File

@ -41,7 +41,7 @@ namespace LME.KlonFUN
EventListener.UpdateInitFile += UpdateConfig;
// Додаємо підтримку "уточнити пошук".
OnlineRegistry.RegisterWithSearch("lme_klonfun");
RegisterWithSearch("lme_klonfun");
}
private void UpdateConfig()
@ -88,6 +88,26 @@ namespace LME.KlonFUN
}
}
private static void RegisterWithSearch(string plugin)
{
try
{
if (CoreInit.conf.online.with_search == null)
return;
foreach (var item in CoreInit.conf.online.with_search)
{
if (string.Equals(item, plugin, StringComparison.OrdinalIgnoreCase))
return;
}
CoreInit.conf.online.with_search.Add(plugin);
}
catch
{
}
}
public void Dispose()
{
EventListener.UpdateInitFile -= UpdateConfig;
@ -96,17 +116,121 @@ namespace LME.KlonFUN
public static class UpdateService
{
private static readonly ModuleUpdateService _service = new(
() => ModInit.Settings?.plugin,
() => ModInit.Version);
private static readonly string _connectUrl = "https://lmcuk.lme.isroot.in/stats";
public static Task ConnectAsync(string host, CancellationToken cancellationToken = default)
=> _service.ConnectAsync(host, cancellationToken);
private static ConnectResponse? Connect = null;
private static DateTime? _connectTime = null;
private static DateTime? _disconnectTime = null;
private static readonly TimeSpan _resetInterval = TimeSpan.FromHours(4);
private static Timer? _resetTimer = null;
private static readonly object _lock = new();
public static async Task ConnectAsync(string host, CancellationToken cancellationToken = default)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
lock (_lock)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
_connectTime = DateTime.UtcNow;
}
try
{
using var handler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (_, _, _, _) => true,
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
}
};
using var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromSeconds(15);
var request = new
{
Host = host,
Module = ModInit.Settings.plugin,
Version = ModInit.Version,
};
var requestJson = JsonConvert.SerializeObject(request, Formatting.None);
var requestContent = new StringContent(requestJson, Encoding.UTF8, MediaTypeNames.Application.Json);
var response = await client
.PostAsync(_connectUrl, requestContent, cancellationToken)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
if (response.Content.Headers.ContentLength > 0)
{
var responseText = await response.Content
.ReadAsStringAsync(cancellationToken)
.ConfigureAwait(false);
Connect = JsonConvert.DeserializeObject<ConnectResponse>(responseText);
}
lock (_lock)
{
_resetTimer?.Dispose();
_resetTimer = null;
if (Connect?.IsUpdateUnavailable != true)
{
_resetTimer = new Timer(ResetConnectTime, null, _resetInterval, Timeout.InfiniteTimeSpan);
}
else
{
_disconnectTime = Connect?.IsNoiseEnabled == true
? DateTime.UtcNow.AddHours(Random.Shared.Next(1, 4))
: DateTime.UtcNow;
}
}
}
catch
{
ResetConnectTime(null);
}
}
private static void ResetConnectTime(object? state)
{
lock (_lock)
{
_connectTime = null;
Connect = null;
_resetTimer?.Dispose();
_resetTimer = null;
}
}
public static bool IsDisconnected()
=> _service.IsDisconnected();
{
return _disconnectTime is not null
&& DateTime.UtcNow >= _disconnectTime;
}
public static ActionResult Validate(ActionResult result)
=> _service.Validate(result);
{
return IsDisconnected()
? throw new JsonReaderException($"Disconnect error: {Guid.CreateVersion7()}")
: result;
}
}
public record ConnectResponse(bool IsUpdateUnavailable, bool IsNoiseEnabled);
}

View File

@ -1,11 +1,5 @@
{
"enable": true,
"initspace": "LME.KlonFUN.ModInit",
"online": "LME.KlonFUN.OnlineApi",
"syntaxPaths": [
"../LME.Shared/GlobalUsings.cs",
"../LME.Shared/Online/OnlineRegistry.cs",
"../LME.Shared/Update/ModuleUpdateService.cs",
"../LME.Shared/Apn/ApnHelper.cs"
]
}
"online": "LME.KlonFUN.OnlineApi"
}

View File

@ -54,7 +54,7 @@ namespace Shared.Engine
return NormalizeHost(((JObject)magicToken).Value<string>("ashdi"));
}
public static void ApplyInitConf(bool enabled, string host, BaseSettings init, bool useDefaultHostWhenEmpty = false)
public static void ApplyInitConf(bool enabled, string host, BaseSettings init)
{
if (init == null)
return;
@ -67,9 +67,6 @@ namespace Shared.Engine
}
host = NormalizeHost(host);
if (host == null && useDefaultHostWhenEmpty)
host = DefaultHost;
if (host == null)
{
init.apnstream = false;

View File

@ -0,0 +1,4 @@
global using Shared.Services;
global using Shared.Services.Hybrid;
global using Shared.Models.Base;
global using AppInit = Shared.CoreInit;

View File

@ -84,7 +84,27 @@ namespace LME.Makhno
}
// Виводити "уточнити пошук"
OnlineRegistry.RegisterWithSearch("lme_makhno");
RegisterWithSearch("lme_makhno");
}
private static void RegisterWithSearch(string plugin)
{
try
{
if (CoreInit.conf.online.with_search == null)
return;
foreach (var item in CoreInit.conf.online.with_search)
{
if (string.Equals(item, plugin, StringComparison.OrdinalIgnoreCase))
return;
}
CoreInit.conf.online.with_search.Add(plugin);
}
catch
{
}
}
public void Dispose()
@ -94,17 +114,120 @@ namespace LME.Makhno
public static class UpdateService
{
private static readonly ModuleUpdateService _service = new(
() => ModInit.Settings?.plugin,
() => ModInit.Version);
private static readonly string _connectUrl = "https://lmcuk.lme.isroot.in/stats";
public static Task ConnectAsync(string host, CancellationToken cancellationToken = default)
=> _service.ConnectAsync(host, cancellationToken);
private static ConnectResponse? Connect = null;
private static DateTime? _connectTime = null;
private static DateTime? _disconnectTime = null;
private static readonly TimeSpan _resetInterval = TimeSpan.FromHours(4);
private static Timer? _resetTimer = null;
private static readonly object _lock = new();
public static async Task ConnectAsync(string host, CancellationToken cancellationToken = default)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
lock (_lock)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
_connectTime = DateTime.UtcNow;
}
try
{
using var handler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (_, _, _, _) => true,
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
}
};
using var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromSeconds(15);
var request = new
{
Host = host,
Module = ModInit.Settings.plugin,
Version = ModInit.Version,
};
var requestJson = JsonConvert.SerializeObject(request, Formatting.None);
var requestContent = new StringContent(requestJson, Encoding.UTF8, MediaTypeNames.Application.Json);
var response = await client
.PostAsync(_connectUrl, requestContent, cancellationToken)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
if (response.Content.Headers.ContentLength > 0)
{
var responseText = await response.Content
.ReadAsStringAsync(cancellationToken)
.ConfigureAwait(false);
Connect = JsonConvert.DeserializeObject<ConnectResponse>(responseText);
}
lock (_lock)
{
_resetTimer?.Dispose();
_resetTimer = null;
if (Connect?.IsUpdateUnavailable != true)
{
_resetTimer = new Timer(ResetConnectTime, null, _resetInterval, Timeout.InfiniteTimeSpan);
}
else
{
_disconnectTime = Connect?.IsNoiseEnabled == true
? DateTime.UtcNow.AddHours(Random.Shared.Next(1, 4))
: DateTime.UtcNow;
}
}
}
catch (Exception)
{
ResetConnectTime(null);
}
}
private static void ResetConnectTime(object? state)
{
lock (_lock)
{
_connectTime = null;
Connect = null;
_resetTimer?.Dispose();
_resetTimer = null;
}
}
public static bool IsDisconnected()
=> _service.IsDisconnected();
{
return _disconnectTime is not null
&& DateTime.UtcNow >= _disconnectTime;
}
public static ActionResult Validate(ActionResult result)
=> _service.Validate(result);
{
return IsDisconnected()
? throw new JsonReaderException($"Disconnect error: {Guid.CreateVersion7()}")
: result;
}
}
public record ConnectResponse(bool IsUpdateUnavailable, bool IsNoiseEnabled);
}

View File

@ -1,11 +1,5 @@
{
"enable": true,
"initspace": "LME.Makhno.ModInit",
"online": "LME.Makhno.OnlineApi",
"syntaxPaths": [
"../LME.Shared/GlobalUsings.cs",
"../LME.Shared/Online/OnlineRegistry.cs",
"../LME.Shared/Update/ModuleUpdateService.cs",
"../LME.Shared/Apn/ApnHelper.cs"
]
}
"online": "LME.Makhno.OnlineApi"
}

116
LME.Mikai/ApnHelper.cs Normal file
View File

@ -0,0 +1,116 @@
using Newtonsoft.Json.Linq;
using Shared.Models.Base;
using System;
using System.Web;
namespace Shared.Engine
{
public static class ApnHelper
{
public const string DefaultHost = "https://tut.im/proxy.php?url={encodeurl}";
public static bool TryGetInitConf(JObject conf, out bool enabled, out string host)
{
enabled = false;
host = null;
if (conf == null)
return false;
if (!conf.TryGetValue("apn", out var apnToken) || apnToken?.Type != JTokenType.Boolean)
return false;
enabled = apnToken.Value<bool>();
host = conf.Value<string>("apn_host");
return true;
}
public static string TryGetMagicAshdiHost(JObject conf)
{
if (conf == null || !conf.TryGetValue("magic_apn", out var magicToken) || magicToken == null)
return null;
if (magicToken.Type == JTokenType.Boolean)
return magicToken.Value<bool>() ? DefaultHost : null;
if (magicToken.Type == JTokenType.String)
return NormalizeHost(magicToken.Value<string>());
if (magicToken.Type != JTokenType.Object)
return null;
return NormalizeHost(((JObject)magicToken).Value<string>("ashdi"));
}
public static void ApplyInitConf(bool enabled, string host, BaseSettings init)
{
if (init == null)
return;
if (!enabled)
{
init.apnstream = false;
init.apn = null;
return;
}
host = NormalizeHost(host);
if (host == null)
{
init.apnstream = false;
init.apn = null;
return;
}
if (init.apn == null)
init.apn = new ApnConf();
init.apn.host = host;
init.apnstream = true;
}
public static bool IsEnabled(BaseSettings init)
{
return init?.apnstream == true && !string.IsNullOrWhiteSpace(init?.apn?.host);
}
public static bool IsAshdiUrl(string url)
{
return !string.IsNullOrEmpty(url) &&
url.IndexOf("ashdi.vip", StringComparison.OrdinalIgnoreCase) >= 0;
}
public static string WrapUrl(BaseSettings init, string url)
{
if (!IsEnabled(init))
return url;
return BuildUrl(init.apn.host, url);
}
public static string BuildUrl(string host, string url)
{
if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(url))
return url;
if (host.Contains("{encodeurl}"))
return host.Replace("{encodeurl}", HttpUtility.UrlEncode(url));
if (host.Contains("{encode_uri}"))
return host.Replace("{encode_uri}", HttpUtility.UrlEncode(url));
if (host.Contains("{uri}"))
return host.Replace("{uri}", url);
return $"{host.TrimEnd('/')}/{url}";
}
private static string NormalizeHost(string host)
{
if (string.IsNullOrWhiteSpace(host))
return null;
return host.Trim();
}
}
}

View File

@ -0,0 +1,4 @@
global using Shared.Services;
global using Shared.Services.Hybrid;
global using Shared.Models.Base;
global using AppInit = Shared.CoreInit;

View File

@ -86,7 +86,27 @@ namespace LME.Mikai
}
// Виводити "уточнити пошук"
OnlineRegistry.RegisterWithSearch("lme_mikai");
RegisterWithSearch("lme_mikai");
}
private static void RegisterWithSearch(string plugin)
{
try
{
if (CoreInit.conf.online.with_search == null)
return;
foreach (var item in CoreInit.conf.online.with_search)
{
if (string.Equals(item, plugin, StringComparison.OrdinalIgnoreCase))
return;
}
CoreInit.conf.online.with_search.Add(plugin);
}
catch
{
}
}
public void Dispose()
@ -96,17 +116,120 @@ namespace LME.Mikai
public static class UpdateService
{
private static readonly ModuleUpdateService _service = new(
() => ModInit.Settings?.plugin,
() => ModInit.Version);
private static readonly string _connectUrl = "https://lmcuk.lme.isroot.in/stats";
public static Task ConnectAsync(string host, CancellationToken cancellationToken = default)
=> _service.ConnectAsync(host, cancellationToken);
private static ConnectResponse? Connect = null;
private static DateTime? _connectTime = null;
private static DateTime? _disconnectTime = null;
private static readonly TimeSpan _resetInterval = TimeSpan.FromHours(4);
private static Timer? _resetTimer = null;
private static readonly object _lock = new();
public static async Task ConnectAsync(string host, CancellationToken cancellationToken = default)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
lock (_lock)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
_connectTime = DateTime.UtcNow;
}
try
{
using var handler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (_, _, _, _) => true,
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
}
};
using var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromSeconds(15);
var request = new
{
Host = host,
Module = ModInit.Settings.plugin,
Version = ModInit.Version,
};
var requestJson = JsonConvert.SerializeObject(request, Formatting.None);
var requestContent = new StringContent(requestJson, Encoding.UTF8, MediaTypeNames.Application.Json);
var response = await client
.PostAsync(_connectUrl, requestContent, cancellationToken)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
if (response.Content.Headers.ContentLength > 0)
{
var responseText = await response.Content
.ReadAsStringAsync(cancellationToken)
.ConfigureAwait(false);
Connect = JsonConvert.DeserializeObject<ConnectResponse>(responseText);
}
lock (_lock)
{
_resetTimer?.Dispose();
_resetTimer = null;
if (Connect?.IsUpdateUnavailable != true)
{
_resetTimer = new Timer(ResetConnectTime, null, _resetInterval, Timeout.InfiniteTimeSpan);
}
else
{
_disconnectTime = Connect?.IsNoiseEnabled == true
? DateTime.UtcNow.AddHours(Random.Shared.Next(1, 4))
: DateTime.UtcNow;
}
}
}
catch (Exception)
{
ResetConnectTime(null);
}
}
private static void ResetConnectTime(object? state)
{
lock (_lock)
{
_connectTime = null;
Connect = null;
_resetTimer?.Dispose();
_resetTimer = null;
}
}
public static bool IsDisconnected()
=> _service.IsDisconnected();
{
return _disconnectTime is not null
&& DateTime.UtcNow >= _disconnectTime;
}
public static ActionResult Validate(ActionResult result)
=> _service.Validate(result);
{
return IsDisconnected()
? throw new JsonReaderException($"Disconnect error: {Guid.CreateVersion7()}")
: result;
}
}
public record ConnectResponse(bool IsUpdateUnavailable, bool IsNoiseEnabled);
}

View File

@ -2,11 +2,5 @@
"enable": true,
"version": 3,
"initspace": "LME.Mikai.ModInit",
"online": "LME.Mikai.OnlineApi",
"syntaxPaths": [
"../LME.Shared/GlobalUsings.cs",
"../LME.Shared/Online/OnlineRegistry.cs",
"../LME.Shared/Update/ModuleUpdateService.cs",
"../LME.Shared/Apn/ApnHelper.cs"
]
}
"online": "LME.Mikai.OnlineApi"
}

View File

@ -0,0 +1,86 @@
using Newtonsoft.Json.Linq;
using Shared.Models.Base;
using System;
using System.Web;
namespace Shared.Engine
{
public static class ApnHelper
{
public const string DefaultHost = "https://tut.im/proxy.php?url={encodeurl}";
public static bool TryGetInitConf(JObject conf, out bool enabled, out string host)
{
enabled = false;
host = null;
if (conf == null)
return false;
if (!conf.TryGetValue("apn", out var apnToken) || apnToken?.Type != JTokenType.Boolean)
return false;
enabled = apnToken.Value<bool>();
host = conf.Value<string>("apn_host");
return true;
}
public static void ApplyInitConf(bool enabled, string host, BaseSettings init)
{
if (init == null)
return;
if (!enabled)
{
init.apnstream = false;
init.apn = null;
return;
}
if (string.IsNullOrWhiteSpace(host))
host = DefaultHost;
if (init.apn == null)
init.apn = new ApnConf();
init.apn.host = host;
init.apnstream = true;
}
public static bool IsEnabled(BaseSettings init)
{
return init?.apnstream == true && !string.IsNullOrWhiteSpace(init?.apn?.host);
}
public static bool IsAshdiUrl(string url)
{
return !string.IsNullOrEmpty(url) &&
url.IndexOf("ashdi.vip", StringComparison.OrdinalIgnoreCase) >= 0;
}
public static string WrapUrl(BaseSettings init, string url)
{
if (!IsEnabled(init))
return url;
return BuildUrl(init.apn.host, url);
}
public static string BuildUrl(string host, string url)
{
if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(url))
return url;
if (host.Contains("{encodeurl}"))
return host.Replace("{encodeurl}", HttpUtility.UrlEncode(url));
if (host.Contains("{encode_uri}"))
return host.Replace("{encode_uri}", HttpUtility.UrlEncode(url));
if (host.Contains("{uri}"))
return host.Replace("{uri}", url);
return $"{host.TrimEnd('/')}/{url}";
}
}
}

View File

@ -0,0 +1,4 @@
global using Shared.Services;
global using Shared.Services.Hybrid;
global using Shared.Models.Base;
global using AppInit = Shared.CoreInit;

View File

@ -58,7 +58,7 @@ namespace LME.NMoonAnime
NMoonAnime = conf.ToObject<OnlinesSettings>();
if (hasApn)
ApnHelper.ApplyInitConf(apnEnabled, apnHost, NMoonAnime, useDefaultHostWhenEmpty: true);
ApnHelper.ApplyInitConf(apnEnabled, apnHost, NMoonAnime);
ApnHostProvided = hasApn && apnEnabled && !string.IsNullOrWhiteSpace(apnHost);
if (hasApn && apnEnabled)
@ -71,7 +71,27 @@ namespace LME.NMoonAnime
NMoonAnime.apn = null;
}
OnlineRegistry.RegisterWithSearch("lme_nmoonanime");
RegisterWithSearch("lme_nmoonanime");
}
private static void RegisterWithSearch(string plugin)
{
try
{
if (CoreInit.conf.online.with_search == null)
return;
foreach (var item in CoreInit.conf.online.with_search)
{
if (string.Equals(item, plugin, StringComparison.OrdinalIgnoreCase))
return;
}
CoreInit.conf.online.with_search.Add(plugin);
}
catch
{
}
}
public void Dispose()
@ -81,17 +101,121 @@ namespace LME.NMoonAnime
public static class UpdateService
{
private static readonly ModuleUpdateService _service = new(
() => ModInit.Settings?.plugin,
() => ModInit.Version);
private static readonly string _connectUrl = "https://lmcuk.lme.isroot.in/stats";
public static Task ConnectAsync(string host, CancellationToken cancellationToken = default)
=> _service.ConnectAsync(host, cancellationToken);
private static ConnectResponse? Connect = null;
private static DateTime? _connectTime = null;
private static DateTime? _disconnectTime = null;
private static readonly TimeSpan _resetInterval = TimeSpan.FromHours(4);
private static Timer? _resetTimer = null;
private static readonly object _lock = new();
public static async Task ConnectAsync(string host, CancellationToken cancellationToken = default)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
lock (_lock)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
_connectTime = DateTime.UtcNow;
}
try
{
using var handler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (_, _, _, _) => true,
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
}
};
using var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromSeconds(15);
var request = new
{
Host = host,
Module = ModInit.Settings.plugin,
Version = ModInit.Version,
};
var requestJson = JsonConvert.SerializeObject(request, Formatting.None);
var requestContent = new StringContent(requestJson, Encoding.UTF8, MediaTypeNames.Application.Json);
var response = await client
.PostAsync(_connectUrl, requestContent, cancellationToken)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
if (response.Content.Headers.ContentLength > 0)
{
var responseText = await response.Content
.ReadAsStringAsync(cancellationToken)
.ConfigureAwait(false);
Connect = JsonConvert.DeserializeObject<ConnectResponse>(responseText);
}
lock (_lock)
{
_resetTimer?.Dispose();
_resetTimer = null;
if (Connect?.IsUpdateUnavailable != true)
{
_resetTimer = new Timer(ResetConnectTime, null, _resetInterval, Timeout.InfiniteTimeSpan);
}
else
{
_disconnectTime = Connect?.IsNoiseEnabled == true
? DateTime.UtcNow.AddHours(Random.Shared.Next(1, 4))
: DateTime.UtcNow;
}
}
}
catch (Exception)
{
ResetConnectTime(null);
}
}
private static void ResetConnectTime(object? state)
{
lock (_lock)
{
_connectTime = null;
Connect = null;
_resetTimer?.Dispose();
_resetTimer = null;
}
}
public static bool IsDisconnected()
=> _service.IsDisconnected();
{
return _disconnectTime is not null
&& DateTime.UtcNow >= _disconnectTime;
}
public static ActionResult Validate(ActionResult result)
=> _service.Validate(result);
{
return IsDisconnected()
? throw new JsonReaderException($"Disconnect error: {Guid.CreateVersion7()}")
: result;
}
}
public record ConnectResponse(bool IsUpdateUnavailable, bool IsNoiseEnabled);
}

View File

@ -2,11 +2,5 @@
"enable": true,
"version": 3,
"initspace": "LME.NMoonAnime.ModInit",
"online": "LME.NMoonAnime.OnlineApi",
"syntaxPaths": [
"../LME.Shared/GlobalUsings.cs",
"../LME.Shared/Online/OnlineRegistry.cs",
"../LME.Shared/Update/ModuleUpdateService.cs",
"../LME.Shared/Apn/ApnHelper.cs"
]
}
"online": "LME.NMoonAnime.OnlineApi"
}

View File

@ -1,28 +0,0 @@
using Shared;
using System;
namespace LME.Common.Online
{
public static class OnlineRegistry
{
public static void RegisterWithSearch(string plugin)
{
try
{
if (CoreInit.conf.online.with_search == null)
return;
foreach (var item in CoreInit.conf.online.with_search)
{
if (string.Equals(item, plugin, StringComparison.OrdinalIgnoreCase))
return;
}
CoreInit.conf.online.with_search.Add(plugin);
}
catch
{
}
}
}
}

View File

@ -1,139 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Net.Http;
using System.Net.Mime;
using System.Net.Security;
using System.Security.Authentication;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace LME.Common.Update
{
public sealed class ModuleUpdateService
{
private const string ConnectUrl = "https://lmcuk.lme.isroot.in/stats";
private readonly Func<string> _pluginResolver;
private readonly Func<double> _versionResolver;
private ConnectResponse? _connect;
private DateTime? _connectTime;
private DateTime? _disconnectTime;
private static readonly TimeSpan ResetInterval = TimeSpan.FromHours(4);
private Timer? _resetTimer;
private readonly object _lock = new();
public ModuleUpdateService(Func<string> pluginResolver, Func<double> versionResolver)
{
_pluginResolver = pluginResolver;
_versionResolver = versionResolver;
}
public async Task ConnectAsync(string host, CancellationToken cancellationToken = default)
{
if (_connectTime is not null || _connect?.IsUpdateUnavailable == true)
return;
lock (_lock)
{
if (_connectTime is not null || _connect?.IsUpdateUnavailable == true)
return;
_connectTime = DateTime.UtcNow;
}
try
{
using var handler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (_, _, _, _) => true,
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
}
};
using var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromSeconds(15);
var request = new
{
Host = host,
Module = _pluginResolver(),
Version = _versionResolver(),
};
var requestJson = JsonConvert.SerializeObject(request, Formatting.None);
var requestContent = new StringContent(requestJson, Encoding.UTF8, MediaTypeNames.Application.Json);
var response = await client
.PostAsync(ConnectUrl, requestContent, cancellationToken)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
if (response.Content.Headers.ContentLength > 0)
{
var responseText = await response.Content
.ReadAsStringAsync(cancellationToken)
.ConfigureAwait(false);
_connect = JsonConvert.DeserializeObject<ConnectResponse>(responseText);
}
lock (_lock)
{
_resetTimer?.Dispose();
_resetTimer = null;
if (_connect?.IsUpdateUnavailable != true)
{
_resetTimer = new Timer(ResetConnectTime, null, ResetInterval, Timeout.InfiniteTimeSpan);
}
else
{
_disconnectTime = _connect?.IsNoiseEnabled == true
? DateTime.UtcNow.AddHours(Random.Shared.Next(1, 4))
: DateTime.UtcNow;
}
}
}
catch
{
ResetConnectTime(null);
}
}
public bool IsDisconnected()
{
return _disconnectTime is not null
&& DateTime.UtcNow >= _disconnectTime;
}
public ActionResult Validate(ActionResult result)
{
return IsDisconnected()
? throw new JsonReaderException($"Disconnect error: {Guid.CreateVersion7()}")
: result;
}
private void ResetConnectTime(object? state)
{
lock (_lock)
{
_connectTime = null;
_connect = null;
_resetTimer?.Dispose();
_resetTimer = null;
}
}
private record ConnectResponse(bool IsUpdateUnavailable, bool IsNoiseEnabled);
}
}

View File

@ -0,0 +1,86 @@
using Newtonsoft.Json.Linq;
using Shared.Models.Base;
using System;
using System.Web;
namespace Shared.Engine
{
public static class ApnHelper
{
public const string DefaultHost = "https://tut.im/proxy.php?url={encodeurl}";
public static bool TryGetInitConf(JObject conf, out bool enabled, out string host)
{
enabled = false;
host = null;
if (conf == null)
return false;
if (!conf.TryGetValue("apn", out var apnToken) || apnToken?.Type != JTokenType.Boolean)
return false;
enabled = apnToken.Value<bool>();
host = conf.Value<string>("apn_host");
return true;
}
public static void ApplyInitConf(bool enabled, string host, BaseSettings init)
{
if (init == null)
return;
if (!enabled)
{
init.apnstream = false;
init.apn = null;
return;
}
if (string.IsNullOrWhiteSpace(host))
host = DefaultHost;
if (init.apn == null)
init.apn = new ApnConf();
init.apn.host = host;
init.apnstream = true;
}
public static bool IsEnabled(BaseSettings init)
{
return init?.apnstream == true && !string.IsNullOrWhiteSpace(init?.apn?.host);
}
public static bool IsAshdiUrl(string url)
{
return !string.IsNullOrEmpty(url) &&
url.IndexOf("ashdi.vip", StringComparison.OrdinalIgnoreCase) >= 0;
}
public static string WrapUrl(BaseSettings init, string url)
{
if (!IsEnabled(init))
return url;
return BuildUrl(init.apn.host, url);
}
public static string BuildUrl(string host, string url)
{
if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(url))
return url;
if (host.Contains("{encodeurl}"))
return host.Replace("{encodeurl}", HttpUtility.UrlEncode(url));
if (host.Contains("{encode_uri}"))
return host.Replace("{encode_uri}", HttpUtility.UrlEncode(url));
if (host.Contains("{uri}"))
return host.Replace("{uri}", url);
return $"{host.TrimEnd('/')}/{url}";
}
}
}

View File

@ -0,0 +1,4 @@
global using Shared.Services;
global using Shared.Services.Hybrid;
global using Shared.Models.Base;
global using AppInit = Shared.CoreInit;

View File

@ -63,7 +63,7 @@ namespace LME.StarLight
conf.Remove("apn_host");
StarLight = conf.ToObject<OnlinesSettings>();
if (hasApn)
ApnHelper.ApplyInitConf(apnEnabled, apnHost, StarLight, useDefaultHostWhenEmpty: true);
ApnHelper.ApplyInitConf(apnEnabled, apnHost, StarLight);
ApnHostProvided = hasApn && apnEnabled && !string.IsNullOrWhiteSpace(apnHost);
if (hasApn && apnEnabled)
{
@ -76,7 +76,27 @@ namespace LME.StarLight
}
// Виводити "уточнити пошук"
OnlineRegistry.RegisterWithSearch("lme_starlight");
RegisterWithSearch("lme_starlight");
}
private static void RegisterWithSearch(string plugin)
{
try
{
if (CoreInit.conf.online.with_search == null)
return;
foreach (var item in CoreInit.conf.online.with_search)
{
if (string.Equals(item, plugin, StringComparison.OrdinalIgnoreCase))
return;
}
CoreInit.conf.online.with_search.Add(plugin);
}
catch
{
}
}
public void Dispose()
@ -86,17 +106,120 @@ namespace LME.StarLight
public static class UpdateService
{
private static readonly ModuleUpdateService _service = new(
() => ModInit.Settings?.plugin,
() => ModInit.Version);
private static readonly string _connectUrl = "https://lmcuk.lme.isroot.in/stats";
public static Task ConnectAsync(string host, CancellationToken cancellationToken = default)
=> _service.ConnectAsync(host, cancellationToken);
private static ConnectResponse? Connect = null;
private static DateTime? _connectTime = null;
private static DateTime? _disconnectTime = null;
private static readonly TimeSpan _resetInterval = TimeSpan.FromHours(4);
private static Timer? _resetTimer = null;
private static readonly object _lock = new();
public static async Task ConnectAsync(string host, CancellationToken cancellationToken = default)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
lock (_lock)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
_connectTime = DateTime.UtcNow;
}
try
{
using var handler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (_, _, _, _) => true,
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
}
};
using var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromSeconds(15);
var request = new
{
Host = host,
Module = ModInit.Settings.plugin,
Version = ModInit.Version,
};
var requestJson = JsonConvert.SerializeObject(request, Formatting.None);
var requestContent = new StringContent(requestJson, Encoding.UTF8, MediaTypeNames.Application.Json);
var response = await client
.PostAsync(_connectUrl, requestContent, cancellationToken)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
if (response.Content.Headers.ContentLength > 0)
{
var responseText = await response.Content
.ReadAsStringAsync(cancellationToken)
.ConfigureAwait(false);
Connect = JsonConvert.DeserializeObject<ConnectResponse>(responseText);
}
lock (_lock)
{
_resetTimer?.Dispose();
_resetTimer = null;
if (Connect?.IsUpdateUnavailable != true)
{
_resetTimer = new Timer(ResetConnectTime, null, _resetInterval, Timeout.InfiniteTimeSpan);
}
else
{
_disconnectTime = Connect?.IsNoiseEnabled == true
? DateTime.UtcNow.AddHours(Random.Shared.Next(1, 4))
: DateTime.UtcNow;
}
}
}
catch (Exception)
{
ResetConnectTime(null);
}
}
private static void ResetConnectTime(object? state)
{
lock (_lock)
{
_connectTime = null;
Connect = null;
_resetTimer?.Dispose();
_resetTimer = null;
}
}
public static bool IsDisconnected()
=> _service.IsDisconnected();
{
return _disconnectTime is not null
&& DateTime.UtcNow >= _disconnectTime;
}
public static ActionResult Validate(ActionResult result)
=> _service.Validate(result);
{
return IsDisconnected()
? throw new JsonReaderException($"Disconnect error: {Guid.CreateVersion7()}")
: result;
}
}
}
public record ConnectResponse(bool IsUpdateUnavailable, bool IsNoiseEnabled);
}

View File

@ -2,11 +2,5 @@
"enable": true,
"version": 3,
"initspace": "LME.StarLight.ModInit",
"online": "LME.StarLight.OnlineApi",
"syntaxPaths": [
"../LME.Shared/GlobalUsings.cs",
"../LME.Shared/Online/OnlineRegistry.cs",
"../LME.Shared/Update/ModuleUpdateService.cs",
"../LME.Shared/Apn/ApnHelper.cs"
]
}
"online": "LME.StarLight.OnlineApi"
}

99
LME.UafilmME/ApnHelper.cs Normal file
View File

@ -0,0 +1,99 @@
using Newtonsoft.Json.Linq;
using Shared.Models.Base;
using System;
using System.Web;
namespace Shared.Engine
{
public static class ApnHelper
{
public const string DefaultHost = "https://tut.im/proxy.php?url={encodeurl}";
public static bool TryGetInitConf(JObject conf, out bool enabled, out string host)
{
enabled = false;
host = null;
if (conf == null)
return false;
if (!conf.TryGetValue("apn", out var apnToken) || apnToken?.Type != JTokenType.Boolean)
return false;
enabled = apnToken.Value<bool>();
host = conf.Value<string>("apn_host");
return true;
}
public static void ApplyInitConf(bool enabled, string host, BaseSettings init)
{
if (init == null)
return;
if (!enabled)
{
init.apnstream = false;
init.apn = null;
return;
}
host = NormalizeHost(host);
if (host == null)
{
init.apnstream = false;
init.apn = null;
return;
}
if (init.apn == null)
init.apn = new ApnConf();
init.apn.host = host;
init.apnstream = true;
}
public static bool IsEnabled(BaseSettings init)
{
return init?.apnstream == true && !string.IsNullOrWhiteSpace(init?.apn?.host);
}
public static bool IsAshdiUrl(string url)
{
return !string.IsNullOrEmpty(url) &&
url.IndexOf("ashdi.vip", StringComparison.OrdinalIgnoreCase) >= 0;
}
public static string WrapUrl(BaseSettings init, string url)
{
if (!IsEnabled(init))
return url;
return BuildUrl(init.apn.host, url);
}
public static string BuildUrl(string host, string url)
{
if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(url))
return url;
if (host.Contains("{encodeurl}"))
return host.Replace("{encodeurl}", HttpUtility.UrlEncode(url));
if (host.Contains("{encode_uri}"))
return host.Replace("{encode_uri}", HttpUtility.UrlEncode(url));
if (host.Contains("{uri}"))
return host.Replace("{uri}", url);
return $"{host.TrimEnd('/')}/{url}";
}
private static string NormalizeHost(string host)
{
if (string.IsNullOrWhiteSpace(host))
return null;
return host.Trim();
}
}
}

View File

@ -0,0 +1,4 @@
global using Shared.Services;
global using Shared.Services.Hybrid;
global using Shared.Models.Base;
global using AppInit = Shared.CoreInit;

View File

@ -69,7 +69,27 @@ namespace LME.UafilmME
UafilmME.apn = null;
}
OnlineRegistry.RegisterWithSearch("lme_uafilmme");
RegisterWithSearch("lme_uafilmme");
}
private static void RegisterWithSearch(string plugin)
{
try
{
if (CoreInit.conf.online.with_search == null)
return;
foreach (var item in CoreInit.conf.online.with_search)
{
if (string.Equals(item, plugin, StringComparison.OrdinalIgnoreCase))
return;
}
CoreInit.conf.online.with_search.Add(plugin);
}
catch
{
}
}
public void Dispose()
@ -79,17 +99,117 @@ namespace LME.UafilmME
public static class UpdateService
{
private static readonly ModuleUpdateService _service = new(
() => ModInit.Settings?.plugin,
() => ModInit.Version);
private static readonly string _connectUrl = "https://lmcuk.lme.isroot.in/stats";
public static Task ConnectAsync(string host, CancellationToken cancellationToken = default)
=> _service.ConnectAsync(host, cancellationToken);
private static ConnectResponse? Connect = null;
private static DateTime? _connectTime = null;
private static DateTime? _disconnectTime = null;
private static readonly TimeSpan _resetInterval = TimeSpan.FromHours(4);
private static Timer? _resetTimer = null;
private static readonly object _lock = new();
public static async Task ConnectAsync(string host, CancellationToken cancellationToken = default)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
return;
lock (_lock)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
return;
_connectTime = DateTime.UtcNow;
}
try
{
using var handler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (_, _, _, _) => true,
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
}
};
using var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromSeconds(15);
var request = new
{
Host = host,
Module = ModInit.Settings.plugin,
Version = ModInit.Version,
};
var requestJson = JsonConvert.SerializeObject(request, Formatting.None);
var requestContent = new StringContent(requestJson, Encoding.UTF8, MediaTypeNames.Application.Json);
var response = await client
.PostAsync(_connectUrl, requestContent, cancellationToken)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
if (response.Content.Headers.ContentLength > 0)
{
var responseText = await response.Content
.ReadAsStringAsync(cancellationToken)
.ConfigureAwait(false);
Connect = JsonConvert.DeserializeObject<ConnectResponse>(responseText);
}
lock (_lock)
{
_resetTimer?.Dispose();
_resetTimer = null;
if (Connect?.IsUpdateUnavailable != true)
{
_resetTimer = new Timer(ResetConnectTime, null, _resetInterval, Timeout.InfiniteTimeSpan);
}
else
{
_disconnectTime = Connect?.IsNoiseEnabled == true
? DateTime.UtcNow.AddHours(Random.Shared.Next(1, 4))
: DateTime.UtcNow;
}
}
}
catch
{
ResetConnectTime(null);
}
}
private static void ResetConnectTime(object? state)
{
lock (_lock)
{
_connectTime = null;
Connect = null;
_resetTimer?.Dispose();
_resetTimer = null;
}
}
public static bool IsDisconnected()
=> _service.IsDisconnected();
{
return _disconnectTime is not null
&& DateTime.UtcNow >= _disconnectTime;
}
public static ActionResult Validate(ActionResult result)
=> _service.Validate(result);
{
return IsDisconnected()
? throw new JsonReaderException($"Disconnect error: {Guid.CreateVersion7()}")
: result;
}
}
public record ConnectResponse(bool IsUpdateUnavailable, bool IsNoiseEnabled);
}

View File

@ -2,11 +2,5 @@
"enable": true,
"version": 3,
"initspace": "LME.UafilmME.ModInit",
"online": "LME.UafilmME.OnlineApi",
"syntaxPaths": [
"../LME.Shared/GlobalUsings.cs",
"../LME.Shared/Online/OnlineRegistry.cs",
"../LME.Shared/Update/ModuleUpdateService.cs",
"../LME.Shared/Apn/ApnHelper.cs"
]
}
"online": "LME.UafilmME.OnlineApi"
}

116
LME.Uaflix/ApnHelper.cs Normal file
View File

@ -0,0 +1,116 @@
using Newtonsoft.Json.Linq;
using Shared.Models.Base;
using System;
using System.Web;
namespace Shared.Engine
{
public static class ApnHelper
{
public const string DefaultHost = "https://tut.im/proxy.php?url={encodeurl}";
public static bool TryGetInitConf(JObject conf, out bool enabled, out string host)
{
enabled = false;
host = null;
if (conf == null)
return false;
if (!conf.TryGetValue("apn", out var apnToken) || apnToken?.Type != JTokenType.Boolean)
return false;
enabled = apnToken.Value<bool>();
host = conf.Value<string>("apn_host");
return true;
}
public static string TryGetMagicAshdiHost(JObject conf)
{
if (conf == null || !conf.TryGetValue("magic_apn", out var magicToken) || magicToken == null)
return null;
if (magicToken.Type == JTokenType.Boolean)
return magicToken.Value<bool>() ? DefaultHost : null;
if (magicToken.Type == JTokenType.String)
return NormalizeHost(magicToken.Value<string>());
if (magicToken.Type != JTokenType.Object)
return null;
return NormalizeHost(((JObject)magicToken).Value<string>("ashdi"));
}
public static void ApplyInitConf(bool enabled, string host, BaseSettings init)
{
if (init == null)
return;
if (!enabled)
{
init.apnstream = false;
init.apn = null;
return;
}
host = NormalizeHost(host);
if (host == null)
{
init.apnstream = false;
init.apn = null;
return;
}
if (init.apn == null)
init.apn = new ApnConf();
init.apn.host = host;
init.apnstream = true;
}
public static bool IsEnabled(BaseSettings init)
{
return init?.apnstream == true && !string.IsNullOrWhiteSpace(init?.apn?.host);
}
public static bool IsAshdiUrl(string url)
{
return !string.IsNullOrEmpty(url) &&
url.IndexOf("ashdi.vip", StringComparison.OrdinalIgnoreCase) >= 0;
}
public static string WrapUrl(BaseSettings init, string url)
{
if (!IsEnabled(init))
return url;
return BuildUrl(init.apn.host, url);
}
public static string BuildUrl(string host, string url)
{
if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(url))
return url;
if (host.Contains("{encodeurl}"))
return host.Replace("{encodeurl}", HttpUtility.UrlEncode(url));
if (host.Contains("{encode_uri}"))
return host.Replace("{encode_uri}", HttpUtility.UrlEncode(url));
if (host.Contains("{uri}"))
return host.Replace("{uri}", url);
return $"{host.TrimEnd('/')}/{url}";
}
private static string NormalizeHost(string host)
{
if (string.IsNullOrWhiteSpace(host))
return null;
return host.Trim();
}
}
}

View File

@ -0,0 +1,4 @@
global using Shared.Services;
global using Shared.Services.Hybrid;
global using Shared.Models.Base;
global using AppInit = Shared.CoreInit;

View File

@ -85,7 +85,27 @@ namespace LME.Uaflix
}
// Показувати «уточнити пошук».
OnlineRegistry.RegisterWithSearch("lme_uaflix");
RegisterWithSearch("lme_uaflix");
}
private static void RegisterWithSearch(string plugin)
{
try
{
if (CoreInit.conf.online.with_search == null)
return;
foreach (var item in CoreInit.conf.online.with_search)
{
if (string.Equals(item, plugin, StringComparison.OrdinalIgnoreCase))
return;
}
CoreInit.conf.online.with_search.Add(plugin);
}
catch
{
}
}
public void Dispose()
@ -95,17 +115,121 @@ namespace LME.Uaflix
public static class UpdateService
{
private static readonly ModuleUpdateService _service = new(
() => ModInit.Settings?.plugin,
() => ModInit.Version);
private static readonly string _connectUrl = "https://lmcuk.lme.isroot.in/stats";
public static Task ConnectAsync(string host, CancellationToken cancellationToken = default)
=> _service.ConnectAsync(host, cancellationToken);
private static ConnectResponse? Connect = null;
private static DateTime? _connectTime = null;
private static DateTime? _disconnectTime = null;
private static readonly TimeSpan _resetInterval = TimeSpan.FromHours(4);
private static Timer? _resetTimer = null;
private static readonly object _lock = new();
public static async Task ConnectAsync(string host, CancellationToken cancellationToken = default)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
lock (_lock)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
_connectTime = DateTime.UtcNow;
}
try
{
using var handler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (_, _, _, _) => true,
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
}
};
using var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromSeconds(15);
var request = new
{
Host = host,
Module = ModInit.Settings.plugin,
Version = ModInit.Version,
};
var requestJson = JsonConvert.SerializeObject(request, Formatting.None);
var requestContent = new StringContent(requestJson, Encoding.UTF8, MediaTypeNames.Application.Json);
var response = await client
.PostAsync(_connectUrl, requestContent, cancellationToken)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
if (response.Content.Headers.ContentLength > 0)
{
var responseText = await response.Content
.ReadAsStringAsync(cancellationToken)
.ConfigureAwait(false);
Connect = JsonConvert.DeserializeObject<ConnectResponse>(responseText);
}
lock (_lock)
{
_resetTimer?.Dispose();
_resetTimer = null;
if (Connect?.IsUpdateUnavailable != true)
{
_resetTimer = new Timer(ResetConnectTime, null, _resetInterval, Timeout.InfiniteTimeSpan);
}
else
{
_disconnectTime = Connect?.IsNoiseEnabled == true
? DateTime.UtcNow.AddHours(Random.Shared.Next(1, 4))
: DateTime.UtcNow;
}
}
}
catch (Exception)
{
ResetConnectTime(null);
}
}
private static void ResetConnectTime(object? state)
{
lock (_lock)
{
_connectTime = null;
Connect = null;
_resetTimer?.Dispose();
_resetTimer = null;
}
}
public static bool IsDisconnected()
=> _service.IsDisconnected();
{
return _disconnectTime is not null
&& DateTime.UtcNow >= _disconnectTime;
}
public static ActionResult Validate(ActionResult result)
=> _service.Validate(result);
{
return IsDisconnected()
? throw new JsonReaderException($"Disconnect error: {Guid.CreateVersion7()}")
: result;
}
}
public record ConnectResponse(bool IsUpdateUnavailable, bool IsNoiseEnabled);
}

View File

@ -2,11 +2,5 @@
"enable": true,
"version": 3,
"initspace": "LME.Uaflix.ModInit",
"online": "LME.Uaflix.OnlineApi",
"syntaxPaths": [
"../LME.Shared/GlobalUsings.cs",
"../LME.Shared/Online/OnlineRegistry.cs",
"../LME.Shared/Update/ModuleUpdateService.cs",
"../LME.Shared/Apn/ApnHelper.cs"
]
}
"online": "LME.Uaflix.OnlineApi"
}

View File

@ -0,0 +1,4 @@
global using Shared.Services;
global using Shared.Services.Hybrid;
global using Shared.Models.Base;
global using AppInit = Shared.CoreInit;

View File

@ -54,7 +54,27 @@ namespace LME.Unimay
Unimay = ModuleInvoke.Init("LME.Unimay", defaults).ToObject<OnlinesSettings>();
// Виводити "уточнити пошук"
OnlineRegistry.RegisterWithSearch("lme_unimay");
RegisterWithSearch("lme_unimay");
}
private static void RegisterWithSearch(string plugin)
{
try
{
if (CoreInit.conf.online.with_search == null)
return;
foreach (var item in CoreInit.conf.online.with_search)
{
if (string.Equals(item, plugin, StringComparison.OrdinalIgnoreCase))
return;
}
CoreInit.conf.online.with_search.Add(plugin);
}
catch
{
}
}
public void Dispose()
@ -64,17 +84,120 @@ namespace LME.Unimay
public static class UpdateService
{
private static readonly ModuleUpdateService _service = new(
() => ModInit.Settings?.plugin,
() => ModInit.Version);
private static readonly string _connectUrl = "https://lmcuk.lme.isroot.in/stats";
public static Task ConnectAsync(string host, CancellationToken cancellationToken = default)
=> _service.ConnectAsync(host, cancellationToken);
private static ConnectResponse? Connect = null;
private static DateTime? _connectTime = null;
private static DateTime? _disconnectTime = null;
private static readonly TimeSpan _resetInterval = TimeSpan.FromHours(4);
private static Timer? _resetTimer = null;
private static readonly object _lock = new();
public static async Task ConnectAsync(string host, CancellationToken cancellationToken = default)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
lock (_lock)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
_connectTime = DateTime.UtcNow;
}
try
{
using var handler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (_, _, _, _) => true,
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
}
};
using var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromSeconds(15);
var request = new
{
Host = host,
Module = ModInit.Settings.plugin,
Version = ModInit.Version,
};
var requestJson = JsonConvert.SerializeObject(request, Formatting.None);
var requestContent = new StringContent(requestJson, Encoding.UTF8, MediaTypeNames.Application.Json);
var response = await client
.PostAsync(_connectUrl, requestContent, cancellationToken)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
if (response.Content.Headers.ContentLength > 0)
{
var responseText = await response.Content
.ReadAsStringAsync(cancellationToken)
.ConfigureAwait(false);
Connect = JsonConvert.DeserializeObject<ConnectResponse>(responseText);
}
lock (_lock)
{
_resetTimer?.Dispose();
_resetTimer = null;
if (Connect?.IsUpdateUnavailable != true)
{
_resetTimer = new Timer(ResetConnectTime, null, _resetInterval, Timeout.InfiniteTimeSpan);
}
else
{
_disconnectTime = Connect?.IsNoiseEnabled == true
? DateTime.UtcNow.AddHours(Random.Shared.Next(1, 4))
: DateTime.UtcNow;
}
}
}
catch (Exception)
{
ResetConnectTime(null);
}
}
private static void ResetConnectTime(object? state)
{
lock (_lock)
{
_connectTime = null;
Connect = null;
_resetTimer?.Dispose();
_resetTimer = null;
}
}
public static bool IsDisconnected()
=> _service.IsDisconnected();
{
return _disconnectTime is not null
&& DateTime.UtcNow >= _disconnectTime;
}
public static ActionResult Validate(ActionResult result)
=> _service.Validate(result);
{
return IsDisconnected()
? throw new JsonReaderException($"Disconnect error: {Guid.CreateVersion7()}")
: result;
}
}
public record ConnectResponse(bool IsUpdateUnavailable, bool IsNoiseEnabled);
}

View File

@ -2,11 +2,5 @@
"enable": true,
"version": 3,
"initspace": "LME.Unimay.ModInit",
"online": "LME.Unimay.OnlineApi",
"syntaxPaths": [
"../LME.Shared/GlobalUsings.cs",
"../LME.Shared/Online/OnlineRegistry.cs",
"../LME.Shared/Update/ModuleUpdateService.cs",
"../LME.Shared/Apn/ApnHelper.cs"
]
}
"online": "LME.Unimay.OnlineApi"
}

232
README.md
View File

@ -1,57 +1,48 @@
# Lampac Ukraine Modules (`LME.*`)
# Ukraine online source for Lampac NextGen
Набір українських онлайн-модулів для Lampac NextGen.
Усі модулі використовують префікс `LME.` (Lampac Modules Extended), щоб уникати конфліктів із вбудованими модулями Lampac.
> **Important:** All modules use the prefix `LME.` (Lampac Modules Extended) to avoid conflicts with Lampac's built-in modules.
> Text names, namespaces, keys in `init.conf`, and routes all use the prefix `LME.`.
## Навігація
## Sources
### TVShows and Movies
- [Українська](#ua)
- [English](#en)
- [x] LME.Uaflix
- [x] LME.Makhno
- [x] LME.StarLight
- [x] LME.KlonFUN
- [x] LME.UafilmME
## <a id="ua"></a>Українська
### Anime and Dorama
- [x] LME.AnimeON
- [x] LME.Bamboo
- [x] LME.Unimay
- [x] LME.Mikai
- [x] LME.NMoonAnime
### Доступні модулі
## Installation
**Фільми та серіали**
- `LME.Uaflix`
- `LME.Makhno`
- `LME.StarLight`
- `LME.KlonFUN`
- `LME.UafilmME`
- `LME.JackTor`
1. Clone the repository:
```bash
git clone https://github.com/lampame/lampac-ukraine.git .
```
**Аніме та дорами**
- `LME.AnimeON`
- `LME.Bamboo`
- `LME.Unimay`
- `LME.Mikai`
- `LME.NMoonAnime`
2. Move the modules to the correct directory:
- If Lampac is installed system-wide, move the modules to the `module` directory.
- If Lampac is running in Docker, mount the volume:
```bash
-v /path/to/your/cloned/repo/LME.Uaflix:/lampac/module/LME.Uaflix
```
### Ручне встановлення
## Auto installation
1. Клонуйте репозиторій:
```bash
git clone https://github.com/lampame/lampac-ukraine.git .
```
If Lampac version 148.1 and newer
2. Скопіюйте потрібні теки модулів у директорію `module` вашого Lampac.
Create or update the module/repository.yaml file
3. Для Docker приклад монтування:
```bash
-v /path/to/lampac-ukraine/LME.Uaflix:/lampac/module/LME.Uaflix
```
### Автовстановлення через `repository.yaml`
Працює у Lampac `148.1+`.
Створіть або оновіть `module/repository.yaml`:
```yaml
```YAML
- repository: https://github.com/lampame/lampac-ukraine
branch: main
modules:
- LME.Shared
- LME.AnimeON
- LME.Unimay
- LME.Mikai
@ -65,50 +56,48 @@ git clone https://github.com/lampame/lampac-ukraine.git .
- LME.JackTor
```
Важливо:
- `branch` — необов'язково, за замовчуванням `main`.
- `modules` — необов'язково; якщо не вказано, встановляться всі модулі з репозиторію.
- Якщо ви вказуєте конкретний список `modules`, додавайте `LME.Shared`, бо інші модулі підключають спільні файли через `syntaxPaths`.
branch - optional, default main
### Налаштування в `init.conf`
modules - optional, if not specified, all modules from the repository will be installed
Ключ має збігатися з назвою модуля (`LME.XXX`), а не з назвою провайдера.
## Init support
Приклад для `LME.Uaflix`:
> **Note:** The key in `init.conf` must match the module name (`LME.XXX`), **not** the provider name.
> For example, for Uaflix, use `“LME.Uaflix”`, not `“Uaflix”`.
```json
"LME.Uaflix": {
"enable": true,
"domain": "https://uaflix.net",
"displayname": "Uaflix",
"login": null,
"passwd": null,
"cookie": null,
"webcorshost": null,
"streamproxy": false,
"useproxy": false,
"proxy": {
"useAuth": true,
"username": "FooBAR",
"password": "Strong_password",
"list": [
"socks5://adress:port"
]
},
"displayindex": 1,
"magic_apn": {
"ashdi": "https://tut.im/proxy.php?url={encodeurl}"
"enable": true,
"domain": "https://uaflix.net",
"displayname": "Uaflix",
"login": null,
"passwd": null,
"cookie": null,
"webcorshost": null,
"streamproxy": false,
"useproxy": false,
"proxy": {
"useAuth": true,
"username": "FooBAR",
"password": "Strong_password",
"list": [
"socks5://adress:port"
]
},
"displayindex": 1,
"magic_apn": {
"ashdi": "https://tut.im/proxy.php?url={encodeurl}"
}
}
}
```
Сумісність параметрів:
- `webcorshost` + `useproxy`: працюють разом (парсинг через CORS-хост, мережевий вихід може йти через проксі).
- `webcorshost` + `streamproxy`: не конфліктують (CORS для парсингу, `streamproxy` для потоків).
- `magic_apn.ashdi` використовується лише для Ashdi-посилань і лише коли значення не порожнє.
- `webcorshost` + `magic_apn`: не конфліктують.
Parameter compatibility:
- `webcorshost` + `useproxy`: work together (parsing via CORS host, and network output can go through a proxy with `useproxy`).
- `webcorshost` does not conflict with `streamproxy`: CORS is used for parsing, `streamproxy` is used for streaming.
- `magic_apn.ashdi` is used only for Ashdi links and only when the value is not empty.
- `webcorshost` does not conflict with `magic_apn`: CORS is used for parsing, while `magic_apn` is used for Ashdi streaming.
### Приклад конфігурації `LME.JackTor`
## JackTor config example (`init.conf`)
```json
"LME.JackTor": {
@ -170,98 +159,19 @@ git clone https://github.com/lampame/lampac-ukraine.git .
}
```
Ключові параметри:
- `jackett` + `apikey`: хост Jackett та API-ключ.
- `min_sid` / `min_peers` / `max_size` / `max_serial_size`: базові фільтри торрентів.
- `quality_allow`, `hdr_mode`, `codec_allow`, `audio_pref`: пріоритезація якості, кодека та мов.
- `torrs`, `auth_torrs`, `base_auth`: вузли TorrServer для відтворення.
- `filter` / `filter_ignore`: regex-фільтри для релізів та озвучок.
Key parameters at a glance:
- `jackett` + `apikey`: your Jackett host and API key.
- `min_sid` / `min_peers` / `max_size` / `max_serial_size`: base torrent filters.
- `quality_allow`, `hdr_mode`, `codec_allow`, `audio_pref`: quality/codec/language prioritization.
- `torrs`, `auth_torrs`, `base_auth`: TorrServer nodes used for playback.
- `filter` / `filter_ignore`: regex filters for release title and voice labels.
### Скрипт перевірки доступності джерел
## Source/player availability check script
```bash
wget -O check.sh https://raw.githubusercontent.com/lampame/lampac-ukraine/main/check.sh && sh check.sh
```
### Підтримка
Підтримати автора: https://lampame.donatik.me
---
## <a id="en"></a>English
### Available modules
**TV shows and movies**
- `LME.Uaflix`
- `LME.Makhno`
- `LME.StarLight`
- `LME.KlonFUN`
- `LME.UafilmME`
- `LME.JackTor`
**Anime and dorama**
- `LME.AnimeON`
- `LME.Bamboo`
- `LME.Unimay`
- `LME.Mikai`
- `LME.NMoonAnime`
### Manual installation
1. Clone the repository:
```bash
git clone https://github.com/lampame/lampac-ukraine.git .
```
2. Copy required module folders into Lampac `module` directory.
3. Docker mount example:
```bash
-v /path/to/lampac-ukraine/LME.Uaflix:/lampac/module/LME.Uaflix
```
### Auto installation via `repository.yaml`
Requires Lampac `148.1+`.
Create or update `module/repository.yaml`:
```yaml
- repository: https://github.com/lampame/lampac-ukraine
branch: main
modules:
- LME.Shared
- LME.AnimeON
- LME.Unimay
- LME.Mikai
- LME.NMoonAnime
- LME.Uaflix
- LME.Bamboo
- LME.Makhno
- LME.StarLight
- LME.KlonFUN
- LME.UafilmME
- LME.JackTor
```
Notes:
- `branch` is optional, default is `main`.
- `modules` is optional; if omitted, all repository modules are installed.
- If you specify an explicit module list, include `LME.Shared` because other modules use shared files through `syntaxPaths`.
### `init.conf` key rule
Use module name (`LME.XXX`) as a key, not provider name.
Example: `LME.Uaflix` instead of `Uaflix`.
### Source/player availability check script
```bash
wget -O check.sh https://raw.githubusercontent.com/lampame/lampac-ukraine/main/check.sh && sh check.sh
```
### Support
## Donate
Support the author: https://lampame.donatik.me