using Microsoft.AspNetCore.Mvc; using Shared.Engine.RxEnumerate; namespace Online.Controllers { public class Animebesst : BaseOnlineController { public Animebesst() : base(AppInit.conf.Animebesst) { } [HttpGet] [Route("lite/animebesst")] async public Task Index(string title, string uri, int s, bool rjson = false, bool similar = false) { if (await IsRequestBlocked(rch: true)) return badInitMsg; rhubFallback: if (string.IsNullOrEmpty(uri)) { if (string.IsNullOrWhiteSpace(title)) return OnError(); #region Поиск var cache = await InvokeCacheResult>($"animebesst:search:{title}", 40, async e => { bool reqOk = false; List<(string title, string year, string uri, string s, string img)> catalog = null; string data = $"do=search&subaction=search&search_start=0&full_search=0&result_from=1&story={HttpUtility.UrlEncode(title)}"; await httpHydra.PostSpan($"{init.corsHost()}/index.php?do=search", data, search => { reqOk = search.Contains(">Поиск по сайту<", StringComparison.OrdinalIgnoreCase); var sidebar = Rx.Split("id=\"sidebar\"", search); if (sidebar.Count == 0) return; var rx = Rx.Split("class=\"shortstory-listab\"", sidebar[0].Span, 1); if (rx.Count == 0) return; catalog = new List<(string title, string year, string uri, string s, string img)>(rx.Count); foreach (var row in rx.Rows()) { if (row.Contains("Новости")) continue; var g = row.Groups("class=\"shortstory-listab-title\">([^<]+)"); if (!string.IsNullOrWhiteSpace(g[1].Value) && !string.IsNullOrWhiteSpace(g[2].Value)) { string season = "0"; if (g[2].Value.Contains("сезон")) { season = Regex.Match(g[2].Value, "([0-9]+) сезон").Groups[1].Value; if (string.IsNullOrEmpty(season)) season = "1"; } string img = row.Match("([0-9]{4})"), g[1].Value, season, img)); } } }); if ((catalog == null || catalog.Count == 0) && !reqOk) return e.Fail("catalog", refresh_proxy: true); return e.Success(catalog); }); if (IsRhubFallback(cache)) goto rhubFallback; if (cache.Value != null && cache.Value.Count == 0) return OnError(); if (!similar && cache.Value != null && cache.Value.Count == 1) return LocalRedirect(accsArgs($"/lite/animebesst?rjson={rjson}&title={HttpUtility.UrlEncode(title)}&uri={HttpUtility.UrlEncode(cache.Value[0].uri)}&s={cache.Value[0].s}")); return await ContentTpl(cache, () => { var stpl = new SimilarTpl(cache.Value.Count); foreach (var res in cache.Value) { string _u = $"{host}/lite/animebesst?title={HttpUtility.UrlEncode(title)}&uri={HttpUtility.UrlEncode(res.uri)}&s={res.s}"; stpl.Append(res.title, res.year, string.Empty, _u, PosterApi.Size(res.img)); } return stpl; }); #endregion } else { #region Серии var cache = await InvokeCacheResult>($"animebesst:playlist:{uri}", 30, async e => { var links = new List<(string episode, string name, string uri)>(5); await httpHydra.GetSpan(uri, news => { string videoList = Rx.Match(news, "var videoList ?=([^\n\r]+)"); if (videoList == null) return; var match = Regex.Match(videoList, "\"id\":\"([0-9]+)( [^\"]+)?\",\"link\":\"(https?:)?\\\\/\\\\/([^\"]+)\""); while (match.Success) { if (!string.IsNullOrWhiteSpace(match.Groups[1].Value) && !string.IsNullOrWhiteSpace(match.Groups[4].Value)) links.Add((match.Groups[1].Value, match.Groups[2].Value.Trim(), match.Groups[4].Value.Replace("\\", ""))); match = match.NextMatch(); } }); if (links.Count == 0) return e.Fail("links", refresh_proxy: true); return e.Success(links); }); if (IsRhubFallback(cache)) goto rhubFallback; return await ContentTpl(cache, () => { var etpl = new EpisodeTpl(cache.Value.Count); foreach (var l in cache.Value) { string name = string.IsNullOrEmpty(l.name) ? $"{l.episode} серия" : $"{l.episode} {l.name}"; string voice_name = !string.IsNullOrEmpty(l.name) ? Regex.Replace(l.name, "(^\\(|\\)$)", "") : ""; string link = accsArgs($"{host}/lite/animebesst/video.m3u8?uri={HttpUtility.UrlEncode(l.uri)}&title={HttpUtility.UrlEncode(title)}"); etpl.Append(name, $"{title} / {name}", s.ToString(), l.episode, link, "call", streamlink: $"{link}&play=true", voice_name: Regex.Unescape(voice_name)); } return etpl; }); #endregion } } #region Video [HttpGet] [Route("lite/animebesst/video.m3u8")] async public ValueTask Video(string uri, string title, bool play) { if (await IsRequestBlocked(rch: true, rch_check: false)) return badInitMsg; if (rch != null) { if (rch.IsNotConnected()) { if (init.rhub_fallback && play) rch.Disabled(); else return ContentTo(rch.connectionMsg); } if (!play && rch.IsRequiredConnected()) return ContentTo(rch.connectionMsg); if (rch.IsNotSupport(out string rch_error)) return ShowError(rch_error); } rhubFallback: var cache = await InvokeCacheResult($"animebesst:video:{uri}", 30, async e => { string hls = null; await httpHydra.GetSpan($"https://{uri}", addheaders: HeadersModel.Init("referer", init.host), spanAction: iframe => { hls = Rx.Match(iframe, "file:\"(https?://[^\"]+\\.m3u8)\""); }); if (string.IsNullOrEmpty(hls)) return e.Fail("hls", refresh_proxy: true); return e.Success(hls); }); if (IsRhubFallback(cache)) goto rhubFallback; if (!cache.IsSuccess) return OnError(cache.ErrorMsg); string link = HostStreamProxy(cache.Value); if (play) return RedirectToPlay(link); return ContentTo(VideoTpl.ToJson("play", link, title, vast: init.vast)); } #endregion } }