mirror of
https://github.com/lampame/lampac-ukraine.git
synced 2026-04-16 17:32:20 +00:00
Adds a comprehensive host blocking system that prevents requests to specific disallowed hosts across all online streaming services. The implementation includes: - New `NotAllowedHosts` HashSet containing base64-encoded blocked hostnames - `IsNotAllowedHost()` method to validate URLs before making HTTP requests - Integration in all service classes (Anihub, AnimeON, Bamboo, CikavaIdeya, StarLight, UAKino, UaTUT, Uaflix, Unimay) - Checks applied to search, content fetching, and streaming URL resolution - Consistent implementation pattern across both Invoke and Controller classes This security enhancement prevents connections to potentially malicious or unauthorized streaming hosts while maintaining existing functionality for allowed sources.
354 lines
13 KiB
C#
354 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
using Shared;
|
|
using Shared.Models.Online.Settings;
|
|
using Shared.Models;
|
|
using System.Text.Json;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using AnimeON.Models;
|
|
using Shared.Engine;
|
|
|
|
namespace AnimeON
|
|
{
|
|
public class AnimeONInvoke
|
|
{
|
|
private static readonly HashSet<string> NotAllowedHosts =
|
|
new HashSet<string>(
|
|
new[]
|
|
{
|
|
"c3ZpdGFubW92aWU=",
|
|
"cG9ydGFsLXR2"
|
|
"bGFtcGEuc3RyZWFt"
|
|
}
|
|
.Select(base64 => Encoding.UTF8.GetString(Convert.FromBase64String(base64))),
|
|
StringComparer.OrdinalIgnoreCase
|
|
);
|
|
private OnlinesSettings _init;
|
|
private HybridCache _hybridCache;
|
|
private Action<string> _onLog;
|
|
private ProxyManager _proxyManager;
|
|
|
|
public AnimeONInvoke(OnlinesSettings init, HybridCache hybridCache, Action<string> onLog, ProxyManager proxyManager)
|
|
{
|
|
_init = init;
|
|
_hybridCache = hybridCache;
|
|
_onLog = onLog;
|
|
_proxyManager = proxyManager;
|
|
}
|
|
|
|
public async Task<List<SearchModel>> Search(string imdb_id, long kinopoisk_id, string title, string original_title, int year)
|
|
{
|
|
string memKey = $"AnimeON:search:{kinopoisk_id}:{imdb_id}";
|
|
if (_hybridCache.TryGetValue(memKey, out List<SearchModel> res))
|
|
return res;
|
|
|
|
try
|
|
{
|
|
var headers = new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) };
|
|
|
|
async Task<List<SearchModel>> FindAnime(string query)
|
|
{
|
|
if (string.IsNullOrEmpty(query))
|
|
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))
|
|
return null;
|
|
|
|
var searchResponse = JsonSerializer.Deserialize<SearchResponseModel>(searchJson);
|
|
return searchResponse?.Result;
|
|
}
|
|
|
|
var searchResults = await FindAnime(title) ?? await FindAnime(original_title);
|
|
if (searchResults == null)
|
|
return null;
|
|
|
|
if (!string.IsNullOrEmpty(imdb_id))
|
|
{
|
|
var seasons = searchResults.Where(a => a.ImdbId == imdb_id).ToList();
|
|
if (seasons.Count > 0)
|
|
{
|
|
_hybridCache.Set(memKey, seasons, cacheTime(5));
|
|
return seasons;
|
|
}
|
|
}
|
|
|
|
// Fallback to first result if no imdb match
|
|
var firstResult = searchResults.FirstOrDefault();
|
|
if (firstResult != null)
|
|
{
|
|
var list = new List<SearchModel> { firstResult };
|
|
_hybridCache.Set(memKey, list, cacheTime(5));
|
|
return list;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_onLog($"AnimeON error: {ex.Message}");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public async Task<List<FundubModel>> 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<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) }, proxy: _proxyManager.Get());
|
|
if (string.IsNullOrEmpty(fundubsJson))
|
|
return null;
|
|
|
|
var fundubsResponse = JsonSerializer.Deserialize<FundubsResponseModel>(fundubsJson);
|
|
if (fundubsResponse?.Translations == null || fundubsResponse.Translations.Count == 0)
|
|
return null;
|
|
|
|
var fundubs = new List<FundubModel>();
|
|
foreach (var translation in fundubsResponse.Translations)
|
|
{
|
|
var fundubModel = new FundubModel
|
|
{
|
|
Fundub = translation.Translation,
|
|
Player = translation.Player
|
|
};
|
|
fundubs.Add(fundubModel);
|
|
}
|
|
return fundubs;
|
|
}
|
|
|
|
public async Task<EpisodeModel> 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<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) }, proxy: _proxyManager.Get());
|
|
if (string.IsNullOrEmpty(episodesJson))
|
|
return null;
|
|
|
|
return JsonSerializer.Deserialize<EpisodeModel>(episodesJson);
|
|
}
|
|
|
|
public async Task<string> ParseMoonAnimePage(string url)
|
|
{
|
|
try
|
|
{
|
|
string requestUrl = $"{url}?player=animeon.club";
|
|
var headers = new List<HeadersModel>()
|
|
{
|
|
new HeadersModel("User-Agent", "Mozilla/5.0"),
|
|
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))
|
|
return null;
|
|
|
|
var match = System.Text.RegularExpressions.Regex.Match(html, @"file:\s*""([^""]+\.m3u8)""");
|
|
if (match.Success)
|
|
{
|
|
return match.Groups[1].Value;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_onLog($"AnimeON ParseMoonAnimePage error: {ex.Message}");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public async Task<string> ParseAshdiPage(string url)
|
|
{
|
|
try
|
|
{
|
|
var headers = new List<HeadersModel>()
|
|
{
|
|
new HeadersModel("User-Agent", "Mozilla/5.0"),
|
|
new HeadersModel("Referer", _init.host)
|
|
};
|
|
|
|
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))
|
|
return null;
|
|
|
|
var match = System.Text.RegularExpressions.Regex.Match(html, @"file:\s*""([^""]+)""");
|
|
if (match.Success)
|
|
{
|
|
return match.Groups[1].Value;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_onLog($"AnimeON ParseAshdiPage error: {ex.Message}");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public async Task<string> ResolveEpisodeStream(int episodeId)
|
|
{
|
|
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<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", _init.host) }, proxy: _proxyManager.Get());
|
|
if (string.IsNullOrEmpty(json))
|
|
return null;
|
|
|
|
using var doc = JsonDocument.Parse(json);
|
|
var root = doc.RootElement;
|
|
if (root.TryGetProperty("fileUrl", out var fileProp))
|
|
{
|
|
string fileUrl = fileProp.GetString();
|
|
if (!string.IsNullOrEmpty(fileUrl))
|
|
return fileUrl;
|
|
}
|
|
|
|
if (root.TryGetProperty("videoUrl", out var videoProp))
|
|
{
|
|
string videoUrl = videoProp.GetString();
|
|
return await ResolveVideoUrl(videoUrl);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_onLog($"AnimeON ResolveEpisodeStream error: {ex.Message}");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public async Task<string> ResolveVideoUrl(string url)
|
|
{
|
|
if (string.IsNullOrEmpty(url))
|
|
return null;
|
|
|
|
if (IsNotAllowedHost(url))
|
|
return null;
|
|
|
|
if (url.Contains("moonanime.art"))
|
|
return await ParseMoonAnimePage(url);
|
|
|
|
if (url.Contains("ashdi.vip/vod"))
|
|
return await ParseAshdiPage(url);
|
|
|
|
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)
|
|
return TimeSpan.FromMinutes(rhub);
|
|
|
|
int ctime = AppInit.conf.mikrotik ? mikrotik : AppInit.conf.multiaccess ? init != null && init.cache_time > 0 ? init.cache_time : multiaccess : home;
|
|
if (ctime > multiaccess)
|
|
ctime = multiaccess;
|
|
|
|
return TimeSpan.FromMinutes(ctime);
|
|
}
|
|
public async Task<AnimeON.Models.AnimeONAggregatedStructure> AggregateSerialStructure(int animeId, int season)
|
|
{
|
|
string memKey = $"AnimeON:aggregated:{animeId}:{season}";
|
|
if (_hybridCache.TryGetValue(memKey, out AnimeON.Models.AnimeONAggregatedStructure cached))
|
|
return cached;
|
|
|
|
try
|
|
{
|
|
var structure = new AnimeON.Models.AnimeONAggregatedStructure
|
|
{
|
|
AnimeId = animeId,
|
|
Season = season,
|
|
Voices = new Dictionary<string, AnimeON.Models.AnimeONVoiceInfo>()
|
|
};
|
|
|
|
var fundubs = await GetFundubs(animeId);
|
|
if (fundubs == null || fundubs.Count == 0)
|
|
return null;
|
|
|
|
foreach (var fundub in fundubs)
|
|
{
|
|
if (fundub?.Fundub == null || fundub.Player == null)
|
|
continue;
|
|
|
|
foreach (var player in fundub.Player)
|
|
{
|
|
string display = $"[{player.Name}] {fundub.Fundub.Name}";
|
|
|
|
var episodesData = await GetEpisodes(animeId, player.Id, fundub.Fundub.Id);
|
|
if (episodesData?.Episodes == null || episodesData.Episodes.Count == 0)
|
|
continue;
|
|
|
|
var voiceInfo = new AnimeON.Models.AnimeONVoiceInfo
|
|
{
|
|
Name = fundub.Fundub.Name,
|
|
PlayerType = player.Name?.ToLower(),
|
|
DisplayName = display,
|
|
PlayerId = player.Id,
|
|
FundubId = fundub.Fundub.Id,
|
|
Episodes = episodesData.Episodes
|
|
.OrderBy(ep => ep.EpisodeNum)
|
|
.Select(ep => new AnimeON.Models.AnimeONEpisodeInfo
|
|
{
|
|
Number = ep.EpisodeNum,
|
|
Title = ep.Name,
|
|
Hls = ep.Hls,
|
|
VideoUrl = ep.VideoUrl,
|
|
EpisodeId = ep.Id
|
|
})
|
|
.ToList()
|
|
};
|
|
|
|
structure.Voices[display] = voiceInfo;
|
|
}
|
|
}
|
|
|
|
if (!structure.Voices.Any())
|
|
return null;
|
|
|
|
_hybridCache.Set(memKey, structure, cacheTime(20, init: _init));
|
|
return structure;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_onLog?.Invoke($"AnimeON AggregateSerialStructure error: {ex.Message}");
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
}
|