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
}
}