lampac/Catalog/CardController.cs
lampac-talks f843f04fd4 chore: initial commit 154.3
Signed-off-by: lampac-talks <lampac-talks@users.noreply.github.com>
2026-01-30 16:23:09 +03:00

398 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.AspNetCore.Mvc;
using Shared.Engine.Utilities;
using Shared.PlaywrightCore;
using System.Net.Http;
namespace Catalog.Controllers
{
public class CardController : BaseController
{
[HttpGet]
[Route("catalog/card")]
public async Task<ActionResult> Index(string plugin, string uri, string type)
{
var init = ModInit.goInit(plugin)?.Clone();
if (init == null || !init.enable)
return BadRequest("init not found");
var rch = new RchClient(HttpContext, host, init, requestInfo);
if (rch.IsNotConnected())
rch.Disabled();
var proxyManager = new ProxyManager(init, rch);
var proxy = proxyManager.BaseGet();
string memKey = $"catalog:card:{plugin}:{uri}";
return await InvkSemaphore(memKey, rch, async () =>
{
if (!hybridCache.TryGetValue(memKey, out JObject jo, inmemory: false))
{
string url = $"{init.host}/{uri}";
var headers = httpHeaders(init);
if (init.args != null)
url = url.Contains("?") ? $"{url}&{init.args}" : $"{url}?{init.args}";
if (init.card_parse.initUrl != null)
url = CSharpEval.Execute<string>(init.card_parse.initUrl, new CatalogInitUrlCard(init.host, init.args, uri, HttpContext.Request.Query, type));
if (init.card_parse.initHeader != null)
headers = CSharpEval.Execute<List<HeadersModel>>(init.card_parse.initHeader, new CatalogInitHeader(url, headers));
reset:
string html = null;
if (!string.IsNullOrEmpty(init.card_parse.postData))
{
string mediaType = init.card_parse.postData.StartsWith("{") || init.card_parse.postData.StartsWith("[") ? "application/json" : "application/x-www-form-urlencoded";
var httpdata = new StringContent(init.card_parse.postData, Encoding.UTF8, mediaType);
html = rch.enable
? await rch.Post(url, init.card_parse.postData, headers, useDefaultHeaders: init.useDefaultHeaders)
: await Http.Post(url, httpdata, headers: headers, proxy: proxy.proxy, timeoutSeconds: init.timeout, httpversion: init.httpversion, useDefaultHeaders: init.useDefaultHeaders);
}
else
{
html = rch.enable
? await rch.Get(url, headers, useDefaultHeaders: init.useDefaultHeaders)
: init.priorityBrowser == "playwright" ? await PlaywrightBrowser.Get(init, url, headers, proxy.data, cookies: init.cookies)
: await Http.Get(url, headers: headers, proxy: proxy.proxy, timeoutSeconds: init.timeout, httpversion: init.httpversion, useDefaultHeaders: init.useDefaultHeaders);
}
if (html == null)
{
if (ModInit.IsRhubFallback(init))
goto reset;
proxyManager.Refresh();
return BadRequest("html");
}
proxyManager.Success();
var parse = init.card_parse;
bool? jsonPath = parse.jsonPath;
if (jsonPath == null)
jsonPath = init.jsonPath;
#region parse doc/json
HtmlNode node = null;
JToken json = null;
if (jsonPath == true)
{
try
{
json = JToken.Parse(html);
if (!string.IsNullOrEmpty(parse.node))
{
json = json.SelectToken(parse.node);
if (json == null)
return BadRequest("parse.node");
}
}
catch
{
json = null;
return BadRequest("json");
}
}
else
{
var doc = new HtmlDocument();
doc.LoadHtml(html);
node = doc.DocumentNode;
}
#endregion
#region name / original_name / year
string name;
string original_name;
string year;
if (jsonPath == true)
{
name = ModInit.nodeValue(json, parse.name, host)?.ToString();
original_name = ModInit.nodeValue(json, parse.original_name, host)?.ToString();
year = ModInit.nodeValue(json, parse.year, host)?.ToString();
}
else
{
name = ModInit.nodeValue(node, parse.name, host)?.ToString();
original_name = ModInit.nodeValue(node, parse.original_name, host)?.ToString();
year = ModInit.nodeValue(node, parse.year, host)?.ToString();
}
#endregion
#region img
string img = jsonPath == true
? ModInit.nodeValue(json, parse.image, host)?.ToString()
: ModInit.nodeValue(node, parse.image, host)?.ToString();
if (img != null)
{
img = img.Replace("&amp;", "&").Replace("\\", "");
if (img.StartsWith("../"))
img = $"{init.host}/{img.Replace("../", "")}";
else if (img.StartsWith("//"))
img = $"https:{img}";
else if (img.StartsWith("/"))
img = init.host + img;
else if (!img.StartsWith("http"))
img = $"{init.host}/{img}";
}
#endregion
jo = new JObject()
{
["id"] = uri.Trim(),
["img"] = PosterApi.Size(host, img),
["vote_average"] = 0,
["genres"] = new JArray(),
["production_countries"] = new JArray(),
["production_companies"] = new JArray()
};
string overview = jsonPath == true
? ModInit.nodeValue(json, parse.description, host)?.ToString()
: ModInit.nodeValue(node, parse.description, host)?.ToString();
if (!string.IsNullOrEmpty(overview))
jo["overview"] = overview;
if (type == "tv")
{
jo["first_air_date"] = year;
jo["name"] = name;
if (!string.IsNullOrEmpty(original_name))
jo["original_name"] = original_name;
}
else
{
jo["release_date"] = year;
jo["title"] = name;
if (!string.IsNullOrEmpty(original_name))
jo["original_title"] = original_name;
}
#region card_args
if (init.card_args != null)
{
foreach (var arg in init.card_args)
{
object val = jsonPath == true
? ModInit.nodeValue(json, arg, host)
: ModInit.nodeValue(node, arg, host);
ModInit.setArgsValue(arg, val, jo);
}
}
#endregion
if (init.tmdb_injects != null && init.tmdb_injects.Length > 0)
await Injects(year, jo, init.tmdb_injects);
if (!jo.ContainsKey("tagline") && !string.IsNullOrEmpty(original_name))
jo["tagline"] = original_name;
hybridCache.Set(memKey, jo, cacheTimeBase(init.cache_time, init: init), inmemory: false);
}
return ContentTo(JsonConvertPool.SerializeObject(jo));
});
}
#region TMDB Injects
static readonly string[] defaultInjectskeys =
[
"imdb_id",
"external_ids",
"backdrop_path",
"created_by",
"genres",
"production_companies",
"production_countries",
"content_ratings",
"episode_run_time",
"languages",
"number_of_episodes",
"number_of_seasons",
"last_episode_to_air",
"origin_country",
"original_language",
"status",
"networks",
"seasons",
"type",
"budget",
"spoken_languages",
"alternative_titles",
"keywords",
// &append_to_response=
"videos",
"credits",
"recommendations",
"similar",
];
static readonly string[] addEmptykeys =
[
"tagline",
"overview",
"first_air_date",
"last_air_date",
"release_date",
"runtime"
];
async Task Injects(string year, JObject jo, string[] keys)
{
if (!jo.ContainsKey("imdb_id") && !jo.ContainsKey("original_title") && !jo.ContainsKey("original_name"))
return;
if (keys.Length == 1 && keys[0] == "default")
keys = defaultInjectskeys;
#region Поиск карточки в TMDB
string imdbId = null;
if (jo.ContainsKey("imdb_id"))
imdbId = jo["imdb_id"]?.ToString();
var header = HeadersModel.Init(("localrequest", AppInit.rootPasswd));
long id = 0;
string cat = string.Empty;
if (!string.IsNullOrWhiteSpace(imdbId) && imdbId.StartsWith("tt"))
{
var find = await Http.Get<JObject>($"http://{AppInit.conf.listen.localhost}:{AppInit.conf.listen.port}/tmdb/api/3/find/{imdbId}?external_source=imdb_id&api_key={AppInit.conf.tmdb.api_key}", timeoutSeconds: 5, headers: header);
if (find != null)
{
foreach (string key in new string[] { "movie_results", "tv_results" })
{
if (find.ContainsKey(key))
{
var movies = find[key] as JArray;
if (movies != null && movies.Count > 0)
{
id = movies[0].Value<long>("id");
cat = key == "movie_results" ? "movie" : "tv";
break;
}
}
}
}
}
else if (jo.ContainsKey("original_title") || jo.ContainsKey("original_name"))
{
string type = jo.ContainsKey("original_title") ? "movie" : "tv";
string originalTitle = jo.Value<string>(type == "movie" ? "original_title" : "original_name");
if (!string.IsNullOrEmpty(originalTitle) && int.TryParse(year.Split("-")[0], out int _year) && _year > 0)
{
var searchMovie = await Http.Get<JObject>($"http://{AppInit.conf.listen.localhost}:{AppInit.conf.listen.port}/tmdb/api/3/search/{type}?query={HttpUtility.UrlEncode(originalTitle)}&api_key={AppInit.conf.tmdb.api_key}", timeoutSeconds: 5, headers: header);
if (searchMovie != null && searchMovie.ContainsKey("results"))
{
var results = searchMovie["results"] as JArray;
if (results != null && results.Count > 0)
{
long foundId = 0;
for (int i = 0; i < results.Count; i++)
{
var item = results[i] as JObject;
if (item == null)
continue;
string date = item.Value<string>("release_date") ?? item.Value<string>("first_air_date");
if (string.IsNullOrEmpty(date))
continue;
// date is usually in format YYYY-MM-DD, take first 4 chars
string yearStr = date.Length >= 4 ? date.Substring(0, 4) : date;
if (int.TryParse(yearStr, out int itemYear) && itemYear == _year)
{
string _s1 = StringConvert.SearchName(originalTitle);
string _s2 = StringConvert.SearchName(item.Value<string>(type == "movie" ? "original_title" : "original_name"));
if (!string.IsNullOrEmpty(_s1) && !string.IsNullOrEmpty(_s2) && _s1 == _s2)
{
foundId = item.Value<long>("id");
break;
}
}
}
if (foundId != 0)
{
id = foundId;
cat = type;
}
}
}
}
}
#endregion
if (id == 0)
return;
string append = "content_ratings,release_dates,external_ids,keywords,alternative_titles,videos,credits,recommendations,similar";
var result = await Http.Get<JObject>($"http://{AppInit.conf.listen.localhost}:{AppInit.conf.listen.port}/tmdb/api/3/{cat}/{id}?api_key={AppInit.conf.tmdb.api_key}&append_to_response={append}&language=ru", timeoutSeconds: 5, headers: header);
if (result == null)
return;
foreach (string key in keys)
{
if (key is "videos" or "recommendations" or "similar")
{
if (result.ContainsKey(key) && result[key] is JObject _jo && _jo.ContainsKey("results"))
jo[key] = _jo["results"];
}
else if (result.ContainsKey(key))
{
jo[key] = result[key];
}
}
if (result.ContainsKey("id"))
jo["tmdb_id"] = result["id"];
if (!jo.ContainsKey("imdb_id") && result.ContainsKey("external_ids") && result["external_ids"] is JObject extIds && extIds.ContainsKey("imdb_id"))
jo["imdb_id"] = extIds["imdb_id"];
foreach (string key in addEmptykeys)
{
if (!jo.ContainsKey(key) && result.ContainsKey(key))
{
var tok = result[key];
if (tok == null)
continue;
if (tok.Type == JTokenType.String)
{
var str = tok.Value<string>();
if (!string.IsNullOrWhiteSpace(str))
jo[key] = str;
}
else
{
jo[key] = tok;
}
}
}
}
#endregion
}
}