using HtmlAgilityPack; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; using Microsoft.CodeAnalysis.Scripting; using Microsoft.Playwright; using Newtonsoft.Json; using Shared.Models.CSharpGlobals; using Shared.Models.SISI.NextHUB; using Shared.PlaywrightCore; namespace SISI.Controllers.NextHUB { public class ViewController : BaseSisiController { public ViewController() : base(default) { } [HttpGet] [Route("nexthub/vidosik")] async public Task Index(string uri, bool related) { if (!AppInit.conf.sisi.NextHUB) return OnError("disabled", rcache: false); string plugin = uri.Split("_-:-_")[0]; string url = uri.Split("_-:-_")[1]; var _nxtInit = Root.goInit(plugin); if (_nxtInit == null) return OnError("init not found", rcache: false); if (await IsRequestBlocked(_nxtInit, rch: _nxtInit.rch_access != null)) return badInitMsg; if (init.view.initUrlEval != null) url = CSharpEval.Execute(init.view.initUrlEval, new NxtUrlRequest(init.corsHost(), plugin, url, HttpContext.Request.Query, related)); return await SemaphoreResult($"nexthub:InvkSemaphore:{url}", async e => { (string file, List headers, List recomends) video = default; if ((init.view.priorityBrowser ?? init.priorityBrowser) == "http" && init.view.viewsource && (init.view.nodeFile != null || init.view.eval != null || init.view.regexMatch != null) && init.view.routeEval == null && init.cookies == null && init.view.evalJS == null) { reset: if (rch == null || rch.enable == false) await e.semaphore.WaitAsync(); video = await goVideoToHttp(plugin, init.cors(url), init); if (string.IsNullOrEmpty(video.file)) { if (IsRhubFallback()) goto reset; return OnError("file", rcache: !init.debug); } } else { if (rch?.enable == true) return OnError("rch not supported", rcache: false); await e.semaphore.WaitAsync(); video = await goVideoToBrowser(plugin, init.cors(url), init); if (string.IsNullOrEmpty(video.file)) return OnError("file", rcache: !init.debug); } var stream_links = new StreamItem() { qualitys = new Dictionary() { ["auto"] = video.file }, recomends = video.recomends }; if (related) return await PlaylistResult(stream_links?.recomends, false, null, total_pages: 1); return OnResult(stream_links); }); } #region goVideoToBrowser async ValueTask<(string file, List headers, List recomends)> goVideoToBrowser(string plugin, string url, NxtSettings init) { if (string.IsNullOrEmpty(url)) return default; try { string memKey = $"nexthub:view18:goVideo:{url}"; if (init.view.bindingToIP && proxyManager != null) memKey += $":{proxyManager.CurrentProxyIp}"; if (!hybridCache.TryGetValue(memKey, out (string file, List headers, List recomends) cache)) { using (var browser = new PlaywrightBrowser(init.view.priorityBrowser ?? init.priorityBrowser)) { var page = await browser.NewPageAsync(init.plugin, httpHeaders(init).ToDictionary(), proxy_data, keepopen: init.view.keepopen, deferredDispose: init.view.playbtn != null).ConfigureAwait(false); if (page == default) return default; if (init.cookies != null) await page.Context.AddCookiesAsync(init.cookies).ConfigureAwait(false); if (!string.IsNullOrEmpty(init.view.addInitScript)) await page.AddInitScriptAsync(init.view.addInitScript).ConfigureAwait(false); string routeEval = init.view.routeEval; if (!string.IsNullOrEmpty(routeEval) && routeEval.EndsWith(".cs")) routeEval = FileCache.ReadAllText($"NextHUB/sites/{routeEval}"); #region RouteAsync await page.RouteAsync("**/*", async route => { try { if (browser.IsCompleted || (init.view.patternAbort != null && Regex.IsMatch(route.Request.Url, init.view.patternAbort, RegexOptions.IgnoreCase))) { PlaywrightBase.ConsoleLog(() => $"Playwright: Abort {route.Request.Url}"); await route.AbortAsync(); return; } #region routeEval if (routeEval != null) { var options = ScriptOptions.Default .AddReferences(CSharpEval.ReferenceFromFile("Microsoft.Playwright.dll")) .AddImports("Microsoft.Playwright"); bool _next = await CSharpEval.ExecuteAsync(routeEval, new NxtRoute(route, HttpContext.Request.Query, url, null, null, null, null, 0), options); if (!_next) return; } #endregion #region patternFile if (init.view.patternFile != null && Regex.IsMatch(route.Request.Url, init.view.patternFile, RegexOptions.IgnoreCase)) { if (init.view.waitForResponse) { string result = null; await browser.ClearContinueAsync(route, page); var response = await page.WaitForResponseAsync(route.Request.Url); if (response != null) result = await response.TextAsync().ConfigureAwait(false); PlaywrightBase.ConsoleLog(() => $"\nPlaywright: {result}\n"); browser.SetPageResult(result); } else { void setHeaders(Dictionary _headers) { if (_headers != null && _headers.Count > 0) { cache.headers = new List(_headers.Count); foreach (var item in _headers) { if (item.Key.ToLower() is "host" or "accept-encoding" or "connection" or "range") continue; cache.headers.Add(new HeadersModel(item.Key, item.Value.ToString())); } } } setHeaders(route.Request.Headers); if (init.view.waitLocationFile) { await browser.ClearContinueAsync(route, page); string setUri = route.Request.Url; var response = await page.WaitForResponseAsync(route.Request.Url); if (response != null && response.Headers.ContainsKey("location")) { setHeaders(response.Request.Headers); setUri = response.Headers["location"]; } if (setUri.StartsWith("//")) setUri = $"{(init.host.StartsWith("https") ? "https" : "http")}:{setUri}"; PlaywrightBase.ConsoleLog(() => $"\nPlaywright: SET {setUri}\n{JsonConvert.SerializeObject(cache.headers.ToDictionary(), Formatting.Indented)}\n"); browser.SetPageResult(setUri); } else { PlaywrightBase.ConsoleLog(() => $"\nPlaywright: SET {route.Request.Url}\n{JsonConvert.SerializeObject(cache.headers.ToDictionary(), Formatting.Indented)}\n"); browser.SetPageResult(route.Request.Url); await route.AbortAsync(); } } return; } #endregion #region patternAbortEnd if (init.view.patternAbortEnd != null && Regex.IsMatch(route.Request.Url, init.view.patternAbortEnd, RegexOptions.IgnoreCase)) { PlaywrightBase.ConsoleLog(() => $"Playwright: Abort {route.Request.Url}"); await route.AbortAsync(); return; } #endregion #region patternWhiteRequest if (init.view.patternWhiteRequest != null && route.Request.Url != url && !Regex.IsMatch(route.Request.Url, init.view.patternWhiteRequest, RegexOptions.IgnoreCase)) { PlaywrightBase.ConsoleLog(() => $"Playwright: Abort {route.Request.Url}"); await route.AbortAsync(); return; } #endregion #region abortMedia if (init.view.abortMedia || init.view.fullCacheJS) { if (await PlaywrightBase.AbortOrCache(page, route, abortMedia: init.view.abortMedia, fullCacheJS: init.view.fullCacheJS)) return; } else { PlaywrightBase.ConsoleLog(() => $"Playwright: {route.Request.Method} {route.Request.Url}"); } #endregion await browser.ClearContinueAsync(route, page); } catch { } }); #endregion #region GotoAsync resetGotoAsync: string html = null; var responce = await page.GotoAsync(init.view.viewsource ? $"view-source:{url}" : url, new PageGotoOptions() { Timeout = 10_000, WaitUntil = WaitUntilState.DOMContentLoaded }).ConfigureAwait(false); if (responce != null) html = await responce.TextAsync().ConfigureAwait(false); #endregion if (init.view.waitForResponse) html = await browser.WaitPageResult().ConfigureAwait(false); #region WaitForSelector if (!string.IsNullOrEmpty(init.view.waitForSelector) || !string.IsNullOrEmpty(init.view.playbtn)) { try { await page.WaitForSelectorAsync(init.view.waitForSelector ?? init.view.playbtn, new PageWaitForSelectorOptions { Timeout = init.view.waitForSelector_timeout }).ConfigureAwait(false); } catch { } html = await page.ContentAsync().ConfigureAwait(false); } #endregion #region iframe if (init.view.iframe != null && url.Contains(init.host)) { string iframeUrl = CSharpEval.Execute(evalCodeToRegexMatch(init.view.iframe), new NxtRegexMatch(html, init.view.iframe)); if (!string.IsNullOrEmpty(iframeUrl) && iframeUrl != url) { url = init.view.iframe.format != null ? init.view.iframe.format.Replace("{value}", iframeUrl) : iframeUrl; goto resetGotoAsync; } } #endregion if (!string.IsNullOrEmpty(init.view.playbtn)) await page.ClickAsync(init.view.playbtn).ConfigureAwait(false); if (init.view.nodeFile != null) { #region nodeFile string goFile(string _content) { if (!string.IsNullOrEmpty(_content)) { var doc = new HtmlDocument(); doc.LoadHtml(_content); var videoNode = doc.DocumentNode.SelectSingleNode(init.view.nodeFile.node); if (videoNode != null) return (!string.IsNullOrEmpty(init.view.nodeFile.attribute) ? videoNode.GetAttributeValue(init.view.nodeFile.attribute, null) : videoNode.InnerText)?.Trim(); } return null; } if (init.view.NetworkIdle) { for (int i = 0; i < 10; i++) { cache.file = goFile(await page.ContentAsync().ConfigureAwait(false)); if (!string.IsNullOrEmpty(cache.file)) break; PlaywrightBase.ConsoleLog(() => "ContentAsync: " + (i + 1)); await Task.Delay(800).ConfigureAwait(false); } } else { cache.file = goFile(html); } PlaywrightBase.ConsoleLog(() => $"Playwright: SET {cache.file}"); #endregion } else if (init.view.regexMatch != null) { #region regexMatch if (init.view.NetworkIdle) { for (int i = 0; i < 10; i++) { cache.file = CSharpEval.Execute(evalCodeToRegexMatch(init.view.regexMatch), new NxtRegexMatch(html, init.view.regexMatch)); if (!string.IsNullOrEmpty(cache.file) && init.view.regexMatch.format != null) cache.file = init.view.regexMatch.format.Replace("{value}", cache.file).Replace("{host}", init.host); if (!string.IsNullOrEmpty(cache.file)) break; PlaywrightBase.ConsoleLog(() => "ContentAsync: " + (i + 1)); await Task.Delay(800).ConfigureAwait(false); } } else { cache.file = CSharpEval.Execute(evalCodeToRegexMatch(init.view.regexMatch), new NxtRegexMatch(html, init.view.regexMatch)); if (!string.IsNullOrEmpty(cache.file) && init.view.regexMatch.format != null) cache.file = init.view.regexMatch.format.Replace("{value}", cache.file).Replace("{host}", init.host); } PlaywrightBase.ConsoleLog(() => $"Playwright: SET {cache.file}"); #endregion } else if (string.IsNullOrEmpty(init.view.eval ?? init.view.evalJS)) { cache.file = await browser.WaitPageResult().ConfigureAwait(false); } cache.file = cache.file?.Replace("\\", "")?.Replace("&", "&"); #region eval if (!string.IsNullOrEmpty(init.view.eval ?? init.view.evalJS)) { Task goFile(string _content) { if (!string.IsNullOrEmpty(_content)) { string infile = $"NextHUB/sites/{init.view.eval ?? init.view.evalJS}"; if ((infile.EndsWith(".cs") || infile.EndsWith(".js")) && System.IO.File.Exists(infile)) { string evaluate = FileCache.ReadAllText(infile); if (infile.EndsWith(".js")) return page.EvaluateAsync($"(html, plugin, url, file) => {{ {evaluate} }}", new { _content, plugin, url, cache.file }); var nxt = new NxtEvalView(init, HttpContext.Request.Query, _content, plugin, url, cache.file, cache.headers, proxyManager); return CSharpEval.ExecuteAsync(goEval(evaluate), nxt, Root.evalOptionsFull); } else { if (init.view.evalJS != null) return page.EvaluateAsync($"(html, plugin, url, file) => {{ {init.view.evalJS} }}", new { _content, plugin, url, cache.file }); var nxt = new NxtEvalView(init, HttpContext.Request.Query, _content, plugin, url, cache.file, cache.headers, proxyManager); return CSharpEval.ExecuteAsync(goEval(init.view.eval), nxt, Root.evalOptionsFull); } } return null; } if (init.view.NetworkIdle) { for (int i = 0; i < 10; i++) { cache.file = await goFile(await page.ContentAsync().ConfigureAwait(false)).ConfigureAwait(false); if (!string.IsNullOrEmpty(cache.file)) break; PlaywrightBase.ConsoleLog(() => "ContentAsync: " + (i + 1)); await Task.Delay(800).ConfigureAwait(false); } } else { cache.file = await goFile(html).ConfigureAwait(false); } PlaywrightBase.ConsoleLog(() => $"Playwright: SET {cache.file}"); } #endregion if (string.IsNullOrEmpty(cache.file)) { proxyManager?.Refresh(); return default; } if (cache.file.StartsWith("GotoAsync:")) { url = cache.file.Replace("GotoAsync:", "").Trim(); goto resetGotoAsync; } #region related if (init.view.related && cache.recomends == null) { if (init.view.NetworkIdle) { string contetnt = await page.ContentAsync().ConfigureAwait(false); cache.recomends = ListController.goPlaylist(requestInfo, host, init.view.relatedParse ?? init.contentParse, init, contetnt, plugin); } else { cache.recomends = ListController.goPlaylist(requestInfo, host, init.view.relatedParse ?? init.contentParse, init, html, plugin); } } #endregion } proxyManager?.Success(); hybridCache.Set(memKey, cache, cacheTime(init.view.cache_time)); } return cache; } catch (Exception ex) { if (init.debug) Console.WriteLine(ex); return default; } } #endregion #region goVideoToHttp async ValueTask<(string file, List headers, List recomends)> goVideoToHttp(string plugin, string url, NxtSettings init) { if (string.IsNullOrEmpty(url)) return default; try { string memKey = $"nexthub:view18:goVideo:{url}"; if (init.view.bindingToIP) memKey = ipkey(memKey); if (!hybridCache.TryGetValue(memKey, out (string file, List headers, List recomends) cache)) { resetGotoAsync: string html = await httpHydra.Get(url); if (string.IsNullOrEmpty(html)) return default; #region iframe if (init.view.iframe != null && url.Contains(init.host)) { string iframeUrl = CSharpEval.Execute(evalCodeToRegexMatch(init.view.iframe), new NxtRegexMatch(html, init.view.iframe)); if (!string.IsNullOrEmpty(iframeUrl) && iframeUrl != url) { url = init.view.iframe.format != null ? init.view.iframe.format.Replace("{value}", iframeUrl) : iframeUrl; goto resetGotoAsync; } } #endregion if (init.view.nodeFile != null) { #region nodeFile string goFile(string _content) { var doc = new HtmlDocument(); doc.LoadHtml(_content); var videoNode = doc.DocumentNode.SelectSingleNode(init.view.nodeFile.node); if (videoNode != null) return (!string.IsNullOrEmpty(init.view.nodeFile.attribute) ? videoNode.GetAttributeValue(init.view.nodeFile.attribute, null) : videoNode.InnerText)?.Trim(); return null; } cache.file = goFile(html); PlaywrightBase.ConsoleLog(() => $"Playwright: SET {cache.file}"); #endregion } else if (init.view.regexMatch != null) { #region regexMatch cache.file = CSharpEval.Execute(evalCodeToRegexMatch(init.view.regexMatch), new NxtRegexMatch(html, init.view.regexMatch)); if (!string.IsNullOrEmpty(cache.file) && init.view.regexMatch.format != null) cache.file = init.view.regexMatch.format.Replace("{value}", cache.file).Replace("{host}", init.host); PlaywrightBase.ConsoleLog(() => $"Playwright: SET {cache.file}"); #endregion } cache.file = cache.file?.Replace("\\", "")?.Replace("&", "&"); #region eval if (!string.IsNullOrEmpty(init.view.eval)) { var nxt = new NxtEvalView(init, HttpContext.Request.Query, html, plugin, url, cache.file, cache.headers, proxyManager); cache.file = await CSharpEval.ExecuteAsync(goEval(init.view.eval), nxt, Root.evalOptionsFull).ConfigureAwait(false); PlaywrightBase.ConsoleLog(() => $"Playwright: SET {cache.file}"); } #endregion if (string.IsNullOrEmpty(cache.file)) { proxyManager?.Refresh(); return default; } if (cache.file.StartsWith("GotoAsync:")) { url = cache.file.Replace("GotoAsync:", "").Trim(); goto resetGotoAsync; } if (init.view.related && cache.recomends == null) cache.recomends = ListController.goPlaylist(requestInfo, host, init.view.relatedParse ?? init.contentParse, init, html, plugin); proxyManager?.Success(); hybridCache.Set(memKey, cache, cacheTime(init.view.cache_time)); } return cache; } catch (Exception ex) { if (init.debug) Console.WriteLine(ex); return default; } } #endregion #region evalCodeToRegexMatch static string evalCodeToRegexMatch(RegexMatchSettings rm) { return @"if (m.matches != null && m.matches.Length > 0) { foreach (string q in m.matches) { string file = Regex.Match(html, m.pattern.Replace(""{value}"", $""{q}""), RegexOptions.IgnoreCase).Groups[m.index].Value; if (!string.IsNullOrEmpty(file)) return file; } return null; } return Regex.Match(html, m.pattern, RegexOptions.IgnoreCase).Groups[m.index].Value;"; } #endregion #region goEval static string goEval(string evalcode) { string infile = $"NextHUB/sites/{evalcode}"; if (infile.EndsWith(".cs") && System.IO.File.Exists(infile)) evalcode = FileCache.ReadAllText(infile); if (evalcode.Contains("{include:")) { string includePattern = @"{include:(?[^}]+)}"; var matches = Regex.Matches(evalcode, includePattern); foreach (Match match in matches) { string file = match.Groups["file"].Value.Trim(); if (System.IO.File.Exists($"NextHUB/utils/{file}")) { string includeCode = FileCache.ReadAllText($"NextHUB/utils/{file}"); evalcode = evalcode.Replace(match.Value, includeCode); } } } return evalcode; } #endregion } }