diff --git a/Anihub/AnihubInvoke.cs b/Anihub/AnihubInvoke.cs index 91548ec..4072a38 100644 --- a/Anihub/AnihubInvoke.cs +++ b/Anihub/AnihubInvoke.cs @@ -19,6 +19,16 @@ namespace Anihub { public class AnihubInvoke { + private static readonly HashSet NotAllowedHosts = + new HashSet( + new[] + { + "c3ZpdGFubW92aWU=", + "cG9ydGFsLXR2", + } + .Select(base64 => Encoding.UTF8.GetString(Convert.FromBase64String(base64))), + StringComparer.OrdinalIgnoreCase + ); private OnlinesSettings _init; private HybridCache _hybridCache; private Action _onLog; @@ -41,6 +51,9 @@ namespace Anihub string searchQuery = string.IsNullOrEmpty(title) ? original_title : title; string searchUrl = $"{_init.apihost}/anime/?search={HttpUtility.UrlEncode(searchQuery)}"; + if (IsNotAllowedHost(searchUrl)) + return null; + string response = await Http.Get(searchUrl, headers: headers, proxy: _proxyManager.Get()); if (string.IsNullOrEmpty(response)) @@ -75,6 +88,9 @@ namespace Anihub ); string sourcesUrl = $"{_init.apihost}/episode-sources/{parsedAnimeId}"; + if (IsNotAllowedHost(sourcesUrl)) + return null; + string response = await Http.Get(sourcesUrl, headers: headers, proxy: _proxyManager.Get()); if (string.IsNullOrEmpty(response)) @@ -156,6 +172,17 @@ namespace Anihub return voices; } + private static bool IsNotAllowedHost(string url) + { + if (string.IsNullOrEmpty(url)) + return false; + + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) + return false; + + return NotAllowedHosts.Contains(uri.Host); + } + public async Task> GetSeasons(AnihubEpisodeSourcesResponse sources) { var seasons = new List(); diff --git a/Anihub/Controller.cs b/Anihub/Controller.cs index bb3eef2..1024de7 100644 --- a/Anihub/Controller.cs +++ b/Anihub/Controller.cs @@ -13,12 +13,23 @@ using Shared.Models.Online.Settings; using Shared.Models; using System.Net; using System.Net.Http; +using System.Text; namespace Anihub { [Route("anihub")] public class AnihubController : BaseOnlineController { + private static readonly HashSet NotAllowedHosts = + new HashSet( + new[] + { + "c3ZpdGFubW92aWU=", + "cG9ydGFsLXR2", + } + .Select(base64 => Encoding.UTF8.GetString(Convert.FromBase64String(base64))), + StringComparer.OrdinalIgnoreCase + ); ProxyManager proxyManager; public AnihubController() @@ -299,6 +310,9 @@ namespace Anihub requestUrl = iframeUrl + (iframeUrl.Contains("?") ? "&" : "?") + $"player={host}"; } + if (IsNotAllowedHost(requestUrl)) + return null; + // Створюємо HTTP клієнт з правильними заголовками using var httpClient = new HttpClient(); @@ -349,5 +363,16 @@ namespace Anihub return null; } } + + private static bool IsNotAllowedHost(string url) + { + if (string.IsNullOrEmpty(url)) + return false; + + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) + return false; + + return NotAllowedHosts.Contains(uri.Host); + } } } diff --git a/AnimeON/AnimeONInvoke.cs b/AnimeON/AnimeONInvoke.cs index eb53a6a..42c0789 100644 --- a/AnimeON/AnimeONInvoke.cs +++ b/AnimeON/AnimeONInvoke.cs @@ -6,6 +6,7 @@ using Shared.Models.Online.Settings; using Shared.Models; using System.Text.Json; using System.Linq; +using System.Text; using AnimeON.Models; using Shared.Engine; @@ -13,6 +14,16 @@ namespace AnimeON { public class AnimeONInvoke { + private static readonly HashSet NotAllowedHosts = + new HashSet( + new[] + { + "c3ZpdGFubW92aWU=", + "cG9ydGFsLXR2", + } + .Select(base64 => Encoding.UTF8.GetString(Convert.FromBase64String(base64))), + StringComparer.OrdinalIgnoreCase + ); private OnlinesSettings _init; private HybridCache _hybridCache; private Action _onLog; @@ -42,6 +53,9 @@ namespace AnimeON return null; string searchUrl = $"{_init.host}/api/anime/search?text={System.Web.HttpUtility.UrlEncode(query)}"; + if (IsNotAllowedHost(searchUrl)) + return null; + _onLog($"AnimeON: using proxy {_proxyManager.CurrentProxyIp} for {searchUrl}"); string searchJson = await Http.Get(searchUrl, headers: headers, proxy: _proxyManager.Get()); if (string.IsNullOrEmpty(searchJson)) @@ -87,6 +101,9 @@ namespace AnimeON public async Task> GetFundubs(int animeId) { string fundubsUrl = $"{_init.host}/api/player/{animeId}/translations"; + if (IsNotAllowedHost(fundubsUrl)) + return null; + _onLog($"AnimeON: using proxy {_proxyManager.CurrentProxyIp} for {fundubsUrl}"); string fundubsJson = await Http.Get(fundubsUrl, headers: new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) }, proxy: _proxyManager.Get()); if (string.IsNullOrEmpty(fundubsJson)) @@ -112,6 +129,9 @@ namespace AnimeON public async Task GetEpisodes(int animeId, int playerId, int fundubId) { string episodesUrl = $"{_init.host}/api/player/{animeId}/episodes?take=100&skip=-1&playerId={playerId}&translationId={fundubId}"; + if (IsNotAllowedHost(episodesUrl)) + return null; + _onLog($"AnimeON: using proxy {_proxyManager.CurrentProxyIp} for {episodesUrl}"); string episodesJson = await Http.Get(episodesUrl, headers: new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) }, proxy: _proxyManager.Get()); if (string.IsNullOrEmpty(episodesJson)) @@ -131,6 +151,9 @@ namespace AnimeON new HeadersModel("Referer", "https://animeon.club/") }; + if (IsNotAllowedHost(requestUrl)) + return null; + _onLog($"AnimeON: using proxy {_proxyManager.CurrentProxyIp} for {requestUrl}"); string html = await Http.Get(requestUrl, headers: headers, proxy: _proxyManager.Get()); if (string.IsNullOrEmpty(html)) @@ -160,6 +183,9 @@ namespace AnimeON new HeadersModel("Referer", "https://ashdi.vip/") }; + if (IsNotAllowedHost(url)) + return null; + _onLog($"AnimeON: using proxy {_proxyManager.CurrentProxyIp} for {url}"); string html = await Http.Get(url, headers: headers, proxy: _proxyManager.Get()); if (string.IsNullOrEmpty(html)) @@ -184,6 +210,9 @@ namespace AnimeON try { string url = $"{_init.host}/api/player/{episodeId}/episode"; + if (IsNotAllowedHost(url)) + return null; + _onLog($"AnimeON: using proxy {_proxyManager.CurrentProxyIp} for {url}"); string json = await Http.Get(url, headers: new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) }, proxy: _proxyManager.Get()); if (string.IsNullOrEmpty(json)) @@ -217,6 +246,9 @@ namespace AnimeON if (string.IsNullOrEmpty(url)) return null; + if (IsNotAllowedHost(url)) + return null; + if (url.Contains("moonanime.art")) return await ParseMoonAnimePage(url); @@ -226,6 +258,17 @@ namespace AnimeON return url; } + private static bool IsNotAllowedHost(string url) + { + if (string.IsNullOrEmpty(url)) + return false; + + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) + return false; + + return NotAllowedHosts.Contains(uri.Host); + } + public static TimeSpan cacheTime(int multiaccess, int home = 5, int mikrotik = 2, OnlinesSettings init = null, int rhub = -1) { if (init != null && init.rhub && rhub != -1) diff --git a/AnimeON/Controller.cs b/AnimeON/Controller.cs index 6edd8d1..dc0e1c7 100644 --- a/AnimeON/Controller.cs +++ b/AnimeON/Controller.cs @@ -10,6 +10,7 @@ using Shared; using Shared.Models.Templates; using AnimeON.Models; using System.Text.RegularExpressions; +using System.Text; using Shared.Models.Online.Settings; using Shared.Models; using HtmlAgilityPack; @@ -18,6 +19,16 @@ namespace AnimeON.Controllers { public class Controller : BaseOnlineController { + private static readonly HashSet NotAllowedHosts = + new HashSet( + new[] + { + "c3ZpdGFubW92aWU=", + "cG9ydGFsLXR2", + } + .Select(base64 => Encoding.UTF8.GetString(Convert.FromBase64String(base64))), + StringComparer.OrdinalIgnoreCase + ); ProxyManager proxyManager; public Controller() @@ -190,6 +201,9 @@ namespace AnimeON.Controllers async Task> GetFundubs(OnlinesSettings init, int animeId) { string fundubsUrl = $"{init.host}/api/player/{animeId}/translations"; + if (IsNotAllowedHost(fundubsUrl)) + return null; + string fundubsJson = await Http.Get(fundubsUrl, headers: new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", init.host) }); if (string.IsNullOrEmpty(fundubsJson)) return null; @@ -214,6 +228,9 @@ namespace AnimeON.Controllers async Task GetEpisodes(OnlinesSettings init, int animeId, int playerId, int fundubId) { string episodesUrl = $"{init.host}/api/player/{animeId}/episodes?take=100&skip=-1&playerId={playerId}&translationId={fundubId}"; + if (IsNotAllowedHost(episodesUrl)) + return null; + string episodesJson = await Http.Get(episodesUrl, headers: new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", init.host) }); if (string.IsNullOrEmpty(episodesJson)) return null; @@ -237,6 +254,9 @@ namespace AnimeON.Controllers return null; string searchUrl = $"{init.host}/api/anime/search?text={HttpUtility.UrlEncode(query)}"; + if (IsNotAllowedHost(searchUrl)) + return null; + string searchJson = await Http.Get(searchUrl, headers: headers); if (string.IsNullOrEmpty(searchJson)) return null; @@ -326,5 +346,16 @@ namespace AnimeON.Controllers OnLog("AnimeON Play: return call JSON"); return Content(jsonResult, "application/json; charset=utf-8"); } + + private static bool IsNotAllowedHost(string url) + { + if (string.IsNullOrEmpty(url)) + return false; + + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) + return false; + + return NotAllowedHosts.Contains(uri.Host); + } } } diff --git a/Bamboo/BambooInvoke.cs b/Bamboo/BambooInvoke.cs index b8ada27..8818135 100644 --- a/Bamboo/BambooInvoke.cs +++ b/Bamboo/BambooInvoke.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; @@ -15,6 +16,16 @@ namespace Bamboo { public class BambooInvoke { + private static readonly HashSet NotAllowedHosts = + new HashSet( + new[] + { + "c3ZpdGFubW92aWU=", + "cG9ydGFsLXR2", + } + .Select(base64 => Encoding.UTF8.GetString(Convert.FromBase64String(base64))), + StringComparer.OrdinalIgnoreCase + ); private readonly OnlinesSettings _init; private readonly HybridCache _hybridCache; private readonly Action _onLog; @@ -41,6 +52,9 @@ namespace Bamboo try { string searchUrl = $"{_init.host}/index.php?do=search&subaction=search&story={HttpUtility.UrlEncode(query)}"; + if (IsNotAllowedHost(searchUrl)) + return null; + var headers = new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), @@ -106,6 +120,9 @@ namespace Bamboo new HeadersModel("Referer", _init.host) }; + if (IsNotAllowedHost(href)) + return null; + _onLog?.Invoke($"Bamboo series page: {href}"); string html = await Http.Get(href, headers: headers, proxy: _proxyManager.Get()); if (string.IsNullOrEmpty(html)) @@ -180,6 +197,9 @@ namespace Bamboo new HeadersModel("Referer", _init.host) }; + if (IsNotAllowedHost(href)) + return null; + _onLog?.Invoke($"Bamboo movie page: {href}"); string html = await Http.Get(href, headers: headers, proxy: _proxyManager.Get()); if (string.IsNullOrEmpty(html)) @@ -281,12 +301,23 @@ namespace Bamboo return string.Empty; if (url.StartsWith("//")) - return $"https:{url}"; + return IsNotAllowedHost($"https:{url}") ? string.Empty : $"https:{url}"; if (url.StartsWith("/")) - return $"{_init.host}{url}"; + return IsNotAllowedHost(_init.host) ? string.Empty : $"{_init.host}{url}"; - return url; + return IsNotAllowedHost(url) ? string.Empty : url; + } + + private static bool IsNotAllowedHost(string url) + { + if (string.IsNullOrEmpty(url)) + return false; + + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) + return false; + + return NotAllowedHosts.Contains(uri.Host); } private static int? ExtractEpisodeNumber(string title) diff --git a/CikavaIdeya/CikavaIdeyaInvoke.cs b/CikavaIdeya/CikavaIdeyaInvoke.cs index 611a0bc..1fcb9c6 100644 --- a/CikavaIdeya/CikavaIdeyaInvoke.cs +++ b/CikavaIdeya/CikavaIdeyaInvoke.cs @@ -5,6 +5,7 @@ using Shared; using Shared.Models.Online.Settings; using Shared.Models; using System.Text.RegularExpressions; +using System.Text; using HtmlAgilityPack; using CikavaIdeya.Models; using Shared.Engine; @@ -14,6 +15,16 @@ namespace CikavaIdeya { public class CikavaIdeyaInvoke { + private static readonly HashSet NotAllowedHosts = + new HashSet( + new[] + { + "c3ZpdGFubW92aWU=", + "cG9ydGFsLXR2", + } + .Select(base64 => Encoding.UTF8.GetString(Convert.FromBase64String(base64))), + StringComparer.OrdinalIgnoreCase + ); private OnlinesSettings _init; private HybridCache _hybridCache; private Action _onLog; @@ -71,6 +82,9 @@ namespace CikavaIdeya string searchUrl = $"{_init.host}/index.php?do=search&subaction=search&story={System.Web.HttpUtility.UrlEncode(searchTitle)}"; var headers = new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) }; + if (IsNotAllowedHost(searchUrl)) + return null; + var searchHtml = await Http.Get(searchUrl, headers: headers, proxy: _proxyManager.Get()); // Перевіряємо, чи є результати пошуку if (searchHtml.Contains("На жаль, пошук на сайті не дав жодних результатів")) @@ -124,6 +138,9 @@ namespace CikavaIdeya filmUrl = _init.host + filmUrl; // Отримуємо список епізодів (для фільмів - один епізод, для серіалів - всі епізоди) + if (IsNotAllowedHost(filmUrl)) + return null; + var filmHtml = await Http.Get(filmUrl, headers: headers, proxy: _proxyManager.Get()); // Перевіряємо, чи не видалено контент if (filmHtml.Contains("Видалено на прохання правовласника")) @@ -286,6 +303,9 @@ namespace CikavaIdeya } // Інакше парсимо сторінку + if (IsNotAllowedHost(url)) + return result; + string html = await Http.Get(url, headers: new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) }, proxy: _proxyManager.Get()); var doc = new HtmlDocument(); doc.LoadHtml(html); @@ -313,6 +333,9 @@ namespace CikavaIdeya { _onLog($"GetStreamUrlFromAshdi: trying to get stream URL from {url}"); var headers = new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", "https://ashdi.vip/") }; + if (IsNotAllowedHost(url)) + return null; + string html = await Http.Get(url, headers: headers, proxy: _proxyManager.Get()); _onLog($"GetStreamUrlFromAshdi: received HTML, length={html.Length}"); @@ -347,6 +370,17 @@ namespace CikavaIdeya return null; } + private static bool IsNotAllowedHost(string url) + { + if (string.IsNullOrEmpty(url)) + return false; + + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) + return false; + + return NotAllowedHosts.Contains(uri.Host); + } + public static TimeSpan cacheTime(int multiaccess, int home = 5, int mikrotik = 2, OnlinesSettings init = null, int rhub = -1) { if (init != null && init.rhub && rhub != -1) diff --git a/CikavaIdeya/Controller.cs b/CikavaIdeya/Controller.cs index ae6615f..cd1ca0c 100644 --- a/CikavaIdeya/Controller.cs +++ b/CikavaIdeya/Controller.cs @@ -9,6 +9,7 @@ using HtmlAgilityPack; using Shared; using Shared.Models.Templates; using System.Text.RegularExpressions; +using System.Text; using Shared.Models.Online.Settings; using Shared.Models; using CikavaIdeya.Models; @@ -17,6 +18,16 @@ namespace CikavaIdeya.Controllers { public class Controller : BaseOnlineController { + private static readonly HashSet NotAllowedHosts = + new HashSet( + new[] + { + "c3ZpdGFubW92aWU=", + "cG9ydGFsLXR2", + } + .Select(base64 => Encoding.UTF8.GetString(Convert.FromBase64String(base64))), + StringComparer.OrdinalIgnoreCase + ); ProxyManager proxyManager; public Controller() @@ -157,6 +168,9 @@ namespace CikavaIdeya.Controllers string searchUrl = $"{init.host}/index.php?do=search&subaction=search&story={HttpUtility.UrlEncode(searchTitle)}"; var headers = new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", init.host) }; + if (IsNotAllowedHost(searchUrl)) + return null; + var searchHtml = await Http.Get(searchUrl, headers: headers); // Перевіряємо, чи є результати пошуку if (searchHtml.Contains("На жаль, пошук на сайті не дав жодних результатів")) @@ -210,6 +224,9 @@ namespace CikavaIdeya.Controllers filmUrl = init.host + filmUrl; // Отримуємо список епізодів (для фільмів - один епізод, для серіалів - всі епізоди) + if (IsNotAllowedHost(filmUrl)) + return null; + var filmHtml = await Http.Get(filmUrl, headers: headers); // Перевіряємо, чи не видалено контент if (filmHtml.Contains("Видалено на прохання правовласника")) @@ -361,6 +378,9 @@ namespace CikavaIdeya.Controllers } // Інакше парсимо сторінку + if (IsNotAllowedHost(url)) + return result; + string html = await Http.Get(url, headers: new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", init.host) }); var doc = new HtmlDocument(); doc.LoadHtml(html); @@ -382,5 +402,16 @@ namespace CikavaIdeya.Controllers } return result; } + + private static bool IsNotAllowedHost(string url) + { + if (string.IsNullOrEmpty(url)) + return false; + + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) + return false; + + return NotAllowedHosts.Contains(uri.Host); + } } } diff --git a/README.md b/README.md index c2db5f0..3df6fdf 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,14 @@ # Ukraine online source for Lampac -## Table of contents - - - [Installation](#installation) - - [Auto installation](#auto-installation) - - [Init support](#init-support) - - [Donate](#donate) +- [x] AnimeON +- [x] [RIP] AniHUB +- [x] BambooUA +- [x] CikavaIdeya +- [x] StarLight +- [x] UAKino +- [x] UAFlix +- [x] UATuTFun +- [x] Unimay ## Installation @@ -67,6 +70,7 @@ modules - optional, if not specified, all modules from the repository will be in "displayindex": 1 } ``` + ## Donate Support the author: https://lampame.donatik.me diff --git a/StarLight/Controller.cs b/StarLight/Controller.cs index 0d793a3..63dc423 100644 --- a/StarLight/Controller.cs +++ b/StarLight/Controller.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Web; diff --git a/StarLight/OnlineApi.cs b/StarLight/OnlineApi.cs index ce3b4c3..20bb35b 100644 --- a/StarLight/OnlineApi.cs +++ b/StarLight/OnlineApi.cs @@ -1,4 +1,5 @@ using Shared.Models.Base; +using System; using System.Collections.Generic; namespace StarLight @@ -9,6 +10,9 @@ namespace StarLight { var online = new List<(string name, string url, string plugin, int index)>(); + if (!string.Equals(original_language, "uk", StringComparison.OrdinalIgnoreCase)) + return online; + var init = ModInit.StarLight; if (init.enable && !init.rip) { diff --git a/StarLight/StarLightInvoke.cs b/StarLight/StarLightInvoke.cs index 5d0e7d4..76c78ea 100644 --- a/StarLight/StarLightInvoke.cs +++ b/StarLight/StarLightInvoke.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using System.Text.Json; using System.Threading.Tasks; using System.Web; @@ -17,6 +18,16 @@ namespace StarLight private const string PlayerApi = "https://vcms-api2.starlight.digital/player-api"; private const string PlayerReferer = "https://teleportal.ua/"; private const string Language = "ua"; + private static readonly HashSet NotAllowedHosts = + new HashSet( + new[] + { + "c3ZpdGFubW92aWU=", + "cG9ydGFsLXR2", + } + .Select(base64 => Encoding.UTF8.GetString(Convert.FromBase64String(base64))), + StringComparer.OrdinalIgnoreCase + ); private readonly OnlinesSettings _init; private readonly HybridCache _hybridCache; private readonly Action _onLog; @@ -41,6 +52,9 @@ namespace StarLight return cached; string url = $"{_init.host}/{Language}/live-search?q={HttpUtility.UrlEncode(query)}"; + if (IsNotAllowedHost(url)) + return null; + var headers = new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), @@ -107,6 +121,9 @@ namespace StarLight try { + if (IsNotAllowedHost(href)) + return null; + _onLog?.Invoke($"StarLight project: {href}"); string payload = await Http.Get(href, headers: headers, proxy: _proxyManager.Get()); if (string.IsNullOrEmpty(payload)) @@ -252,6 +269,9 @@ namespace StarLight return null; string url = $"{PlayerApi}/{hash}?referer={HttpUtility.UrlEncode(PlayerReferer)}&lang={Language}"; + if (IsNotAllowedHost(url)) + return null; + var headers = new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), @@ -310,9 +330,20 @@ namespace StarLight return string.Empty; if (path.StartsWith("http", StringComparison.OrdinalIgnoreCase)) - return path; + return IsNotAllowedHost(path) ? string.Empty : path; - return $"{_init.host}{path}"; + return IsNotAllowedHost(_init.host) ? string.Empty : $"{_init.host}{path}"; + } + + private static bool IsNotAllowedHost(string url) + { + if (string.IsNullOrEmpty(url)) + return false; + + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) + return false; + + return NotAllowedHosts.Contains(uri.Host); } public static TimeSpan cacheTime(int multiaccess, int home = 5, int mikrotik = 2, OnlinesSettings init = null, int rhub = -1) diff --git a/UAKino/UAKinoInvoke.cs b/UAKino/UAKinoInvoke.cs index 03bd315..2725d5e 100644 --- a/UAKino/UAKinoInvoke.cs +++ b/UAKino/UAKinoInvoke.cs @@ -1,8 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Security; +using System.Security.Authentication; +using System.Text; using System.Text.Json; using System.Text.RegularExpressions; +using System.Threading; using System.Threading.Tasks; using System.Web; using HtmlAgilityPack; @@ -19,6 +25,16 @@ namespace UAKino private const string PlaylistPath = "/engine/ajax/playlists.php"; private const string PlaylistField = "playlist"; private const string BlacklistRegex = "(/news/)|(/franchise/)"; + private static readonly HashSet NotAllowedHosts = + new HashSet( + new[] + { + "c3ZpdGFubW92aWU=", + "cG9ydGFsLXR2", + } + .Select(base64 => Encoding.UTF8.GetString(Convert.FromBase64String(base64))), + StringComparer.OrdinalIgnoreCase + ); private readonly OnlinesSettings _init; private readonly HybridCache _hybridCache; private readonly Action _onLog; @@ -62,7 +78,7 @@ namespace UAKino }; _onLog?.Invoke($"UAKino search: {searchUrl}"); - string html = await Http.Get(searchUrl, headers: headers, proxy: _proxyManager.Get()); + string html = await GetString(searchUrl, headers); if (string.IsNullOrEmpty(html)) continue; @@ -147,7 +163,7 @@ namespace UAKino try { _onLog?.Invoke($"UAKino playlist: {url}"); - string payload = await Http.Get(url, headers: headers, proxy: _proxyManager.Get()); + string payload = await GetString(url, headers); if (string.IsNullOrEmpty(payload)) return null; @@ -189,7 +205,7 @@ namespace UAKino try { _onLog?.Invoke($"UAKino movie page: {href}"); - string html = await Http.Get(href, headers: headers, proxy: _proxyManager.Get()); + string html = await GetString(href, headers); if (string.IsNullOrEmpty(html)) return null; @@ -232,7 +248,7 @@ namespace UAKino try { _onLog?.Invoke($"UAKino parse player: {url}"); - string html = await Http.Get(url, headers: headers, proxy: _proxyManager.Get()); + string html = await GetString(url, headers); if (string.IsNullOrEmpty(html)) return null; @@ -253,6 +269,50 @@ namespace UAKino } } + private async Task GetString(string url, List headers, int timeoutSeconds = 15) + { + if (IsNotAllowedHost(url)) + return null; + + var handler = new SocketsHttpHandler + { + AllowAutoRedirect = true, + AutomaticDecompression = DecompressionMethods.Brotli | DecompressionMethods.GZip | DecompressionMethods.Deflate, + SslOptions = new SslClientAuthenticationOptions + { + RemoteCertificateValidationCallback = (_, _, _, _) => true, + EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13 + } + }; + + var proxy = _proxyManager.Get(); + if (proxy != null) + { + handler.UseProxy = true; + handler.Proxy = proxy; + } + else + { + handler.UseProxy = false; + } + + using var client = new HttpClient(handler); + using var req = new HttpRequestMessage(HttpMethod.Get, url); + + if (headers != null) + { + foreach (var h in headers) + req.Headers.TryAddWithoutValidation(h.name, h.val); + } + + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(Math.Max(5, timeoutSeconds))); + using var response = await client.SendAsync(req, cts.Token).ConfigureAwait(false); + if (!response.IsSuccessStatusCode) + return null; + + return await response.Content.ReadAsStringAsync(cts.Token).ConfigureAwait(false); + } + private List ParsePlaylistHtml(string html) { var items = new List(); @@ -357,12 +417,12 @@ namespace UAKino return string.Empty; if (url.StartsWith("//")) - return $"https:{url}"; + return IsNotAllowedHost($"https:{url}") ? string.Empty : $"https:{url}"; if (url.StartsWith("/")) - return $"{_init.host}{url}"; + return IsNotAllowedHost(_init.host) ? string.Empty : $"{_init.host}{url}"; - return url; + return IsNotAllowedHost(url) ? string.Empty : url; } private static bool LooksLikeDirectStream(string url) @@ -375,6 +435,17 @@ namespace UAKino return Regex.IsMatch(url ?? string.Empty, BlacklistRegex, RegexOptions.IgnoreCase); } + private static bool IsNotAllowedHost(string url) + { + if (string.IsNullOrEmpty(url)) + return false; + + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) + return false; + + return NotAllowedHosts.Contains(uri.Host); + } + private static bool IsSeriesUrl(string url) { return url.Contains("/seriesss/") || url.Contains("/anime-series/") || url.Contains("/cartoonseries/"); diff --git a/UaTUT/UaTUTInvoke.cs b/UaTUT/UaTUTInvoke.cs index 8a80983..aa2613c 100644 --- a/UaTUT/UaTUTInvoke.cs +++ b/UaTUT/UaTUTInvoke.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; +using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; @@ -15,6 +16,16 @@ namespace UaTUT { public class UaTUTInvoke { + private static readonly HashSet NotAllowedHosts = + new HashSet( + new[] + { + "c3ZpdGFubW92aWU=", + "cG9ydGFsLXR2", + } + .Select(base64 => Encoding.UTF8.GetString(Convert.FromBase64String(base64))), + StringComparer.OrdinalIgnoreCase + ); private OnlinesSettings _init; private HybridCache _hybridCache; private Action _onLog; @@ -63,6 +74,9 @@ namespace UaTUT string url = $"{searchUrl}?q={HttpUtility.UrlEncode(query)}"; _onLog($"UaTUT searching: {url}"); + if (IsNotAllowedHost(url)) + return null; + var headers = new List() { new HeadersModel("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") }; var response = await Http.Get(url, headers: headers, proxy: _proxyManager.Get()); @@ -89,6 +103,9 @@ namespace UaTUT string url = $"{_init.apihost}/{movieId}"; _onLog($"UaTUT getting movie page: {url}"); + if (IsNotAllowedHost(url)) + return null; + var headers = new List() { new HeadersModel("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") }; var response = await Http.Get(url, headers: headers, proxy: _proxyManager.Get()); @@ -130,6 +147,9 @@ namespace UaTUT { _onLog($"UaTUT getting player data from: {playerUrl}"); + if (IsNotAllowedHost(playerUrl)) + return null; + var headers = new List() { new HeadersModel("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") }; var response = await Http.Get(playerUrl, headers: headers, proxy: _proxyManager.Get()); @@ -145,6 +165,17 @@ namespace UaTUT } } + private static bool IsNotAllowedHost(string url) + { + if (string.IsNullOrEmpty(url)) + return false; + + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) + return false; + + return NotAllowedHosts.Contains(uri.Host); + } + private PlayerData ParsePlayerData(string playerHtml) { try diff --git a/Uaflix/Controller.cs b/Uaflix/Controller.cs index fb73002..5f9d952 100644 --- a/Uaflix/Controller.cs +++ b/Uaflix/Controller.cs @@ -10,6 +10,7 @@ using HtmlAgilityPack; using Shared; using Shared.Models.Templates; using System.Text.RegularExpressions; +using System.Text; using Shared.Models.Online.Settings; using Shared.Models; using Uaflix.Models; @@ -19,6 +20,16 @@ namespace Uaflix.Controllers public class Controller : BaseOnlineController { + private static readonly HashSet NotAllowedHosts = + new HashSet( + new[] + { + "c3ZpdGFubW92aWU=", + "cG9ydGFsLXR2", + } + .Select(base64 => Encoding.UTF8.GetString(Convert.FromBase64String(base64))), + StringComparer.OrdinalIgnoreCase + ); ProxyManager proxyManager; public Controller() @@ -50,6 +61,9 @@ namespace Uaflix.Controllers string searchUrl = $"{init.host}/index.php?do=search&subaction=search&story={System.Web.HttpUtility.UrlEncode(filmTitle)}"; var headers = new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", init.host) }; + if (IsNotAllowedHost(searchUrl)) + return OnError("uaflix", proxyManager); + var searchHtml = await Http.Get(searchUrl, headers: headers, proxy: proxyManager.Get(), timeoutSeconds: 10); // Швидка перевірка наявності результатів без повного парсингу @@ -282,7 +296,8 @@ namespace Uaflix.Controllers s: s.ToString(), e: ep.Number.ToString(), link: accsArgs(callUrl), - method: "call" + method: "call", + streamlink: accsArgs($"{callUrl}&play=true") ); } else @@ -328,7 +343,16 @@ namespace Uaflix.Controllers return rjson ? Content(tpl.ToJson(), "application/json; charset=utf-8") : Content(tpl.ToHtml(), "text/html; charset=utf-8"); } } - + + private static bool IsNotAllowedHost(string url) + { + if (string.IsNullOrEmpty(url)) + return false; + + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) + return false; + + return NotAllowedHosts.Contains(uri.Host); + } } } - diff --git a/Uaflix/UaflixInvoke.cs b/Uaflix/UaflixInvoke.cs index cd65b65..224f245 100644 --- a/Uaflix/UaflixInvoke.cs +++ b/Uaflix/UaflixInvoke.cs @@ -14,11 +14,22 @@ using Shared.Models.Templates; using System.Net; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using System.Text; namespace Uaflix { public class UaflixInvoke { + private static readonly HashSet NotAllowedHosts = + new HashSet( + new[] + { + "c3ZpdGFubW92aWU=", + "cG9ydGFsLXR2", + } + .Select(base64 => Encoding.UTF8.GetString(Convert.FromBase64String(base64))), + StringComparer.OrdinalIgnoreCase + ); private OnlinesSettings _init; private HybridCache _hybridCache; private Action _onLog; @@ -90,6 +101,9 @@ namespace Uaflix } } + if (IsNotAllowedHost(requestUrl)) + return null; + string html = await Http.Get(requestUrl, headers: headers, proxy: _proxyManager.Get()); // Знайти JSON у new Playerjs({file:'...'}) @@ -201,6 +215,9 @@ namespace Uaflix try { + if (IsNotAllowedHost(iframeUrl)) + return (null, null); + string html = await Http.Get(iframeUrl, headers: headers, proxy: _proxyManager.Get()); // Знайти file:"url" @@ -235,6 +252,9 @@ namespace Uaflix try { + if (IsNotAllowedHost(iframeUrl)) + return (null, null); + string html = await Http.Get(iframeUrl, headers: headers, proxy: _proxyManager.Get()); // Шукаємо Playerjs конфігурацію з file параметром @@ -341,6 +361,9 @@ namespace Uaflix new HeadersModel("Referer", _init.host) }; + if (IsNotAllowedHost(firstEpisode.url)) + continue; + string html = await Http.Get(firstEpisode.url, headers: headers, proxy: _proxyManager.Get()); var doc = new HtmlDocument(); @@ -500,6 +523,8 @@ namespace Uaflix return null; } + NormalizeUaflixVoiceNames(structure); + // Edge Case 9: Перевірка наявності епізодів у озвучках bool hasEpisodes = structure.Voices.Values.Any(v => v.Seasons.Values.Any(s => s.Any())); if (!hasEpisodes) @@ -555,6 +580,9 @@ namespace Uaflix string searchUrl = $"{_init.host}/index.php?do=search&subaction=search&story={System.Web.HttpUtility.UrlEncode(filmTitle)}"; var headers = new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) }; + if (IsNotAllowedHost(searchUrl)) + return null; + var searchHtml = await Http.Get(searchUrl, headers: headers, proxy: _proxyManager.Get()); var doc = new HtmlDocument(); doc.LoadHtml(searchHtml); @@ -647,6 +675,9 @@ namespace Uaflix try { var headers = new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) }; + if (IsNotAllowedHost(filmUrl)) + return null; + var filmHtml = await Http.Get(filmUrl, headers: headers, proxy: _proxyManager.Get()); var doc = new HtmlDocument(); doc.LoadHtml(filmHtml); @@ -722,6 +753,9 @@ namespace Uaflix try { var headers = new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) }; + if (IsNotAllowedHost(filmUrl)) + return null; + var filmHtml = await Http.Get(filmUrl, headers: headers, proxy: _proxyManager.Get()); var filmDoc = new HtmlDocument(); filmDoc.LoadHtml(filmHtml); @@ -756,7 +790,11 @@ namespace Uaflix seasonUrls.Add(filmUrl); } - var seasonTasks = seasonUrls.Select(url => Http.Get(url, headers: headers, proxy: _proxyManager.Get()).AsTask()); + var safeSeasonUrls = seasonUrls.Where(url => !IsNotAllowedHost(url)).ToList(); + if (safeSeasonUrls.Count == 0) + return null; + + var seasonTasks = safeSeasonUrls.Select(url => Http.Get(url, headers: headers, proxy: _proxyManager.Get()).AsTask()); var seasonPagesHtml = await Task.WhenAll(seasonTasks); foreach (var html in seasonPagesHtml) @@ -817,6 +855,9 @@ namespace Uaflix var result = new Uaflix.Models.PlayResult() { streams = new List<(string, string)>() }; try { + if (IsNotAllowedHost(url)) + return result; + string html = await Http.Get(url, headers: new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) }, proxy: _proxyManager.Get()); var doc = new HtmlDocument(); doc.LoadHtml(html); @@ -888,9 +929,44 @@ namespace Uaflix return result; } + private void NormalizeUaflixVoiceNames(SerialAggregatedStructure structure) + { + const string baseName = "Uaflix"; + const string zetName = "Uaflix #2"; + const string ashdiName = "Uaflix #3"; + + if (structure == null || structure.Voices == null || structure.Voices.Count == 0) + return; + + bool hasBase = structure.Voices.ContainsKey(baseName); + bool hasZet = structure.Voices.ContainsKey(zetName); + bool hasAshdi = structure.Voices.ContainsKey(ashdiName); + + if (hasBase) + return; + + if (hasZet && !hasAshdi) + { + var voice = structure.Voices[zetName]; + voice.DisplayName = baseName; + structure.Voices.Remove(zetName); + structure.Voices[baseName] = voice; + } + else if (hasAshdi && !hasZet) + { + var voice = structure.Voices[ashdiName]; + voice.DisplayName = baseName; + structure.Voices.Remove(ashdiName); + structure.Voices[baseName] = voice; + } + } + async Task> ParseAllZetvideoSources(string iframeUrl) { var result = new List<(string link, string quality)>(); + if (IsNotAllowedHost(iframeUrl)) + return result; + var html = await Http.Get(iframeUrl, headers: new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", "https://zetvideo.net/") }, proxy: _proxyManager.Get()); if (string.IsNullOrEmpty(html)) return result; @@ -922,6 +998,9 @@ namespace Uaflix async Task> ParseAllAshdiSources(string iframeUrl) { var result = new List<(string link, string quality)>(); + if (IsNotAllowedHost(iframeUrl)) + return result; + var html = await Http.Get(iframeUrl, headers: new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", "https://ashdi.vip/") }, proxy: _proxyManager.Get()); if (string.IsNullOrEmpty(html)) return result; @@ -941,7 +1020,11 @@ namespace Uaflix async Task GetAshdiSubtitles(string id) { - var html = await Http.Get($"https://ashdi.vip/vod/{id}", headers: new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", "https://ashdi.vip/") }, proxy: _proxyManager.Get()); + string url = $"https://ashdi.vip/vod/{id}"; + if (IsNotAllowedHost(url)) + return null; + + var html = await Http.Get(url, headers: new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", "https://ashdi.vip/") }, proxy: _proxyManager.Get()); string subtitle = new Regex("subtitle(\")?:\"([^\"]+)\"").Match(html).Groups[2].Value; if (!string.IsNullOrEmpty(subtitle)) { @@ -969,6 +1052,17 @@ namespace Uaflix return TimeSpan.FromMinutes(ctime); } + + private static bool IsNotAllowedHost(string url) + { + if (string.IsNullOrEmpty(url)) + return false; + + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) + return false; + + return NotAllowedHosts.Contains(uri.Host); + } /// /// Оновлений метод кешування згідно стандарту Lampac @@ -985,4 +1079,4 @@ namespace Uaflix return TimeSpan.FromMinutes(ctime); } } -} \ No newline at end of file +} diff --git a/Unimay/UnimayInvoke.cs b/Unimay/UnimayInvoke.cs index 2d9ad82..e8a1277 100644 --- a/Unimay/UnimayInvoke.cs +++ b/Unimay/UnimayInvoke.cs @@ -8,11 +8,22 @@ using System.Linq; using Unimay.Models; using Shared.Engine; using System.Net; +using System.Text; namespace Unimay { public class UnimayInvoke { + private static readonly HashSet NotAllowedHosts = + new HashSet( + new[] + { + "c3ZpdGFubW92aWU=", + "cG9ydGFsLXR2", + } + .Select(base64 => Encoding.UTF8.GetString(Convert.FromBase64String(base64))), + StringComparer.OrdinalIgnoreCase + ); private OnlinesSettings _init; private ProxyManager _proxyManager; private HybridCache _hybridCache; @@ -37,6 +48,9 @@ namespace Unimay string searchQuery = System.Web.HttpUtility.UrlEncode(title ?? original_title ?? ""); string searchUrl = $"{_init.host}/release/search?page=0&page_size=10&title={searchQuery}"; + if (IsNotAllowedHost(searchUrl)) + return null; + var headers = httpHeaders(_init); SearchResponse root = await Http.Get(searchUrl, timeoutSeconds: 8, proxy: _proxyManager.Get(), headers: headers); @@ -67,6 +81,9 @@ namespace Unimay { string releaseUrl = $"{_init.host}/release?code={code}"; + if (IsNotAllowedHost(releaseUrl)) + return null; + var headers = httpHeaders(_init); ReleaseResponse root = await Http.Get(releaseUrl, timeoutSeconds: 8, proxy: _proxyManager.Get(), headers: headers); @@ -158,6 +175,18 @@ namespace Unimay new HeadersModel("Accept", "application/json") }; } + + private static bool IsNotAllowedHost(string url) + { + if (string.IsNullOrEmpty(url)) + return false; + + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) + return false; + + return NotAllowedHosts.Contains(uri.Host); + } + public static TimeSpan cacheTime(int multiaccess, int home = 5, int mikrotik = 2, OnlinesSettings init = null, int rhub = -1) { if (init != null && init.rhub && rhub != -1) @@ -170,4 +199,4 @@ namespace Unimay return TimeSpan.FromMinutes(ctime); } } -} \ No newline at end of file +}