using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; namespace JacRed.Controllers { [Route("animelayer/[action]")] public class AnimeLayerController : JacBaseController { #region search public static Task search(string host, ConcurrentBag torrents, string query) { if (!jackett.Animelayer.enable || string.IsNullOrEmpty(jackett.Animelayer.cookie ?? jackett.Animelayer.login.u) || jackett.Animelayer.showdown) return Task.FromResult(false); return Joinparse(torrents, () => parsePage(host, query)); } #endregion #region parseMagnet async public Task parseMagnet(string url) { if (!jackett.Animelayer.enable) return Content("disable"); string cookie = await getCookie(); if (string.IsNullOrEmpty(cookie)) return Content("cookie == null"); var proxyManager = new ProxyManager("animelayer", jackett.Animelayer); byte[] _t = await Http.Download($"{url}download/", proxy: proxyManager.Get(), cookie: cookie, referer: jackett.Animelayer.host); if (_t != null && BencodeTo.Magnet(_t) != null) return File(_t, "application/x-bittorrent"); return Content("error"); } #endregion #region parsePage async static ValueTask> parsePage(string host, string query) { #region Авторизация string cookie = await getCookie(); if (string.IsNullOrEmpty(cookie)) { consoleErrorLog("animelayer"); return null; } #endregion var torrents = new List(); var proxyManager = new ProxyManager("animelayer", jackett.Animelayer); #region html string html = await Http.Get($"{jackett.Animelayer.host}/torrents/anime/?q={HttpUtility.UrlEncode(query)}", proxy: proxyManager.Get(), cookie: cookie, timeoutSeconds: jackett.timeoutSeconds); if (html != null && html.Contains("id=\"wrapper\"")) { if (!html.Contains($">{jackett.Animelayer.login.u}<")) { consoleErrorLog("animelayer"); return null; } } else if (html == null) { consoleErrorLog("animelayer"); return null; } #endregion foreach (string row in html.Split("class=\"torrent-item torrent-item-medium panel\"").Skip(1)) { if (string.IsNullOrWhiteSpace(row)) continue; #region Локальный метод - Match string Match(string pattern, int index = 1) { string res = new Regex(pattern, RegexOptions.IgnoreCase).Match(row).Groups[index].Value.Trim(); res = Regex.Replace(res, "[\n\r\t ]+", " "); return res.Trim(); } #endregion #region Дата создания DateTime createTime = default; if (Regex.IsMatch(row, "(Добавл|Обновл)[^<]+( )?[0-9]+ [^ ]+ [0-9]{4}")) { createTime = tParse.ParseCreateTime(Match(">(Добавл|Обновл)[^<]+( )?([0-9]+ [^ ]+ [0-9]{4})", 3), "dd.MM.yyyy"); } else { string date = Match("(Добавл|Обновл)[^<]+([^\n]+) в", 2); if (!string.IsNullOrWhiteSpace(date)) createTime = tParse.ParseCreateTime($"{date} {DateTime.Today.Year}", "dd.MM.yyyy"); } #endregion #region Данные раздачи var gurl = Regex.Match(row, "([^<]+)").Groups; string url = gurl[1].Value; string title = gurl[2].Value; string _sid = Match("class=\"icon s-icons-upload\">( )?([0-9]+)", 2); string _pir = Match("class=\"icon s-icons-download\">( )?([0-9]+)", 2); string sizeName = Match("[^<]+[^<]+[\n\r\t ]+([^\n\r<]+)").Trim(); if (string.IsNullOrWhiteSpace(url) || string.IsNullOrWhiteSpace(title)) continue; if (Regex.IsMatch(row, "Разрешение: ?1920x1080")) title += " [1080p]"; else if (Regex.IsMatch(row, "Разрешение: ?1280x720")) title += " [720p]"; #endregion int.TryParse(_sid, out int sid); int.TryParse(_pir, out int pir); torrents.Add(new TorrentDetails() { types = new string[] { "anime" }, url = $"{jackett.Animelayer.host}/{url}/", title = title, sid = sid, pir = pir, sizeName = sizeName, createTime = createTime, parselink = $"{host}/animelayer/parsemagnet?url={HttpUtility.UrlEncode(url)}" }); } return torrents; } #endregion #region getCookie async static ValueTask getCookie() { if (!string.IsNullOrEmpty(jackett.Animelayer.cookie)) return jackett.Animelayer.cookie; string authKey = "Animelayer:TakeLogin()"; if (Startup.memoryCache.TryGetValue(authKey, out string _cookie)) return _cookie; if (Startup.memoryCache.TryGetValue($"{authKey}:error", out _)) return null; Startup.memoryCache.Set($"{authKey}:error", 0, TimeSpan.FromSeconds(20)); try { using (var clientHandler = new System.Net.Http.HttpClientHandler() { AllowAutoRedirect = false }) { clientHandler.ServerCertificateCustomValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; using (var client = new System.Net.Http.HttpClient(clientHandler)) { client.Timeout = TimeSpan.FromSeconds(jackett.timeoutSeconds); client.MaxResponseContentBufferSize = 2000000; // 2MB foreach (var h in Http.defaultFullHeaders) client.DefaultRequestHeaders.TryAddWithoutValidation(h.Key, h.Value); var postParams = new Dictionary { { "login", jackett.Animelayer.login.u }, { "password", jackett.Animelayer.login.p } }; using (var postContent = new System.Net.Http.FormUrlEncodedContent(postParams)) { using (var response = await client.PostAsync($"{jackett.Animelayer.host}/auth/login/", postContent)) { if (response.Headers.TryGetValues("Set-Cookie", out var cook)) { string layer_id = null, layer_hash = null, PHPSESSID = null; foreach (string line in cook) { if (string.IsNullOrWhiteSpace(line)) continue; if (line.Contains("layer_id=")) layer_id = new Regex("layer_id=([^;]+)(;|$)").Match(line).Groups[1].Value; if (line.Contains("layer_hash=")) layer_hash = new Regex("layer_hash=([^;]+)(;|$)").Match(line).Groups[1].Value; if (line.Contains("PHPSESSID=")) PHPSESSID = new Regex("PHPSESSID=([^;]+)(;|$)").Match(line).Groups[1].Value; } if (!string.IsNullOrWhiteSpace(layer_id) && !string.IsNullOrWhiteSpace(layer_hash) && !string.IsNullOrWhiteSpace(PHPSESSID)) { string cookie = $"layer_id={layer_id}; layer_hash={layer_hash}; PHPSESSID={PHPSESSID};"; Startup.memoryCache.Set(authKey, cookie, DateTime.Today.AddDays(1)); return cookie; } } } } } } } catch { } return null; } #endregion } }