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

285 lines
14 KiB
C#
Raw Permalink 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 Newtonsoft.Json.Linq;
using Shared.Engine.RxEnumerate;
namespace Online.Controllers
{
public class MoonAnime : BaseOnlineController
{
static MoonAnime() {
Directory.CreateDirectory("cache/logs/MoonAnime");
}
public MoonAnime() : base(AppInit.conf.MoonAnime) { }
[HttpGet]
[Route("lite/moonanime")]
async public Task<ActionResult> Index(string imdb_id, string title, string original_title, long animeid, string t, int s = -1, bool rjson = false, bool similar = false)
{
if (await IsRequestBlocked(rch: true))
return badInitMsg;
if (string.IsNullOrEmpty(init.token))
return OnError("token", statusCode: 401, gbcache: false);
if (animeid == 0)
{
#region Поиск
return await InvkSemaphore($"moonanime:search:{imdb_id}:{title}:{original_title}", async key =>
{
if (!hybridCache.TryGetValue(key, out List<(string title, string year, long id, string poster)> catalog, inmemory: false))
{
async Task<JObject> goSearch(string arg)
{
if (string.IsNullOrEmpty(arg.Split("=")?[1]))
return null;
var search = await httpHydra.Get<JObject>($"{init.corsHost()}/api/2.0/titles?api_key={init.token}&limit=20" + arg, safety: true);
if (search == null || !search.ContainsKey("anime_list"))
return null;
if (search["anime_list"].Count() == 0)
return null;
return search;
}
JObject search = await goSearch($"&imdbid={imdb_id}")
?? await goSearch($"&japanese_title={HttpUtility.UrlEncode(original_title)}")
?? await goSearch($"&title={HttpUtility.UrlEncode(title)}");
if (search == null)
return OnError(refresh_proxy: true);
catalog = new List<(string title, string year, long id, string poster)>();
foreach (var anime in search["anime_list"])
{
string _titl = anime.Value<string>("title");
int year = anime.Value<int>("year");
if (string.IsNullOrEmpty(_titl))
continue;
catalog.Add((_titl, year.ToString(), anime.Value<long>("id"), anime.Value<string>("poster")));
}
if (catalog.Count == 0)
return OnError();
proxyManager?.Success();
hybridCache.Set(key, catalog, cacheTime(40), inmemory: false);
}
if (!similar && catalog.Count == 1)
return LocalRedirect(accsArgs($"/lite/moonanime?rjson={rjson}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&animeid={catalog[0].id}"));
var stpl = new SimilarTpl(catalog.Count);
foreach (var res in catalog)
{
string uri = $"{host}/lite/moonanime?rjson={rjson}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&animeid={res.id}";
stpl.Append(res.title, res.year, string.Empty, uri, PosterApi.Size(res.poster));
}
return await ContentTpl(stpl);
});
#endregion
}
else
{
#region Серии
return await InvkSemaphore($"moonanime:playlist:{animeid}", async key =>
{
if (!hybridCache.TryGetValue(key, out JArray root))
{
root = await httpHydra.Get<JArray>($"{init.corsHost()}/api/2.0/title/{animeid}/videos?api_key={init.token}", safety: true);
if (root == null)
return OnError(refresh_proxy: true);
proxyManager?.Success();
hybridCache.Set(key, root, cacheTime(30));
}
if (s == -1)
{
var tpl = new SeasonTpl();
var temp = new HashSet<string>();
foreach (var voices in root)
{
foreach (var voice in voices.ToObject<Dictionary<string, Dictionary<string, JArray>>>())
{
foreach (var season in voice.Value)
{
if (temp.Contains(season.Key))
continue;
temp.Add(season.Key);
tpl.Append($"{season.Key} сезон", $"{host}/lite/moonanime?rjson={rjson}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&animeid={animeid}&s={season.Key}", season.Key);
}
}
}
return await ContentTpl(tpl);
}
else
{
#region Перевод
var vtpl = new VoiceTpl();
string activTranslate = t;
foreach (var voices in root)
{
foreach (var voice in voices.ToObject<Dictionary<string, Dictionary<string, JArray>>>())
{
foreach (var season in voice.Value)
{
if (season.Key != s.ToString())
continue;
if (string.IsNullOrEmpty(activTranslate))
activTranslate = voice.Key;
vtpl.Append(voice.Key, activTranslate == voice.Key, $"{host}/lite/moonanime?rjson={rjson}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&animeid={animeid}&s={s}&t={HttpUtility.UrlEncode(voice.Key)}");
}
}
}
#endregion
var etpl = new EpisodeTpl(vtpl);
string sArhc = s.ToString();
foreach (var voices in root)
{
foreach (var voice in voices.ToObject<Dictionary<string, Dictionary<string, JArray>>>())
{
if (voice.Key != activTranslate)
continue;
foreach (var season in voice.Value)
{
if (season.Key != sArhc)
continue;
foreach (var folder in season.Value)
{
int episode = folder.Value<int>("episode");
string vod = folder.Value<string>("vod");
string link = $"{host}/lite/moonanime/video?vod={HttpUtility.UrlEncode(vod)}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}";
string streamlink = accsArgs($"{link.Replace("/video", "/video.m3u8")}&play=true");
etpl.Append($"{episode} серия", title, sArhc, episode.ToString(), link, "call", streamlink: streamlink);
}
}
}
}
return await ContentTpl(etpl);
}
});
#endregion
}
}
#region Video
[HttpGet]
[Route("lite/moonanime/video")]
[Route("lite/moonanime/video.m3u8")]
async public ValueTask<ActionResult> Video(string vod, bool play, string title, string original_title)
{
if (await IsRequestBlocked(rch: true, rch_check: !play))
return badInitMsg;
if (string.IsNullOrEmpty(init.token))
return OnError("token", statusCode: 401, gbcache: false);
return await InvkSemaphore($"moonanime:vod:{vod}", async key =>
{
if (!hybridCache.TryGetValue(key, out (string file, string subtitle) cache))
{
await httpHydra.GetSpan(vod + "?partner=lampa", useDefaultHeaders: false, addheaders: HeadersModel.Init(
("cache-control", "no-cache"),
("dnt", "1"),
("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"),
("pragma", "no-cache"),
("priority", "u=0, i"),
("sec-ch-ua", "\"Chromium\";v=\"130\", \"Microsoft Edge\";v=\"130\", \"Not?A_Brand\";v=\"99\""),
("sec-ch-ua-mobile", "?0"),
("sec-ch-ua-platform", "\"Windows\""),
("sec-fetch-dest", "document"),
("sec-fetch-mode", "navigate"),
("sec-fetch-site", "none"),
("sec-fetch-user", "?1"),
("upgrade-insecure-requests", "1"),
("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0")
), spanAction: iframe =>
{
cache.file = Rx.Match(iframe, "file: ?\"([^\"]+)\"");
cache.subtitle = Rx.Match(iframe, "subtitle: ?\"([^\"]+)\"");
if (string.IsNullOrEmpty(cache.subtitle) || cache.subtitle == "null")
cache.subtitle = Rx.Match(iframe, "thumbnails: ?\"([^\"]+)\"");
});
if (cache.file == null)
return OnError(refresh_proxy: true);
#region stats
if (rch?.enable != true)
{
_ = await Http.Post("https://moonanime.art/api/stats/", $"{{\"domain\":\"{CrypTo.DecodeBase64("bGFtcGEubXg=")}\",\"player\":\"{vod}?partner=lampa\",\"play\":1}}", timeoutSeconds: 4, httpversion: 2, removeContentType: true, useDefaultHeaders: false, proxy: proxy, headers: HeadersModel.Init(
("accept", "*/*"),
("cache-control", "no-cache"),
("dnt", "1"),
("origin", CrypTo.DecodeBase64("aHR0cDovL2xhbXBhLm14")),
("pragma", "no-cache"),
("priority", "u=1, i"),
("referer", vod),
("sec-ch-ua", "\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\""),
("sec-ch-ua-mobile", "?0"),
("sec-ch-ua-platform", "\"Windows\""),
("sec-fetch-dest", "empty"),
("sec-fetch-mode", "cors"),
("sec-fetch-site", "same-origin"),
("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36")
));
}
#endregion
proxyManager?.Success();
hybridCache.Set(key, cache, cacheTime(30));
}
var subtitles = new SubtitleTpl();
if (!string.IsNullOrEmpty(cache.subtitle))
subtitles.Append("По умолчанию", cache.subtitle);
string file = HostStreamProxy(cache.file, headers: HeadersModel.Init(
("accept", "*/*"),
("accept-language", "ru,en;q=0.9,en-GB;q=0.8,en-US;q=0.7"),
("dnt", "1"),
("origin", CrypTo.DecodeBase64("aHR0cDovL2xhbXBhLm14")),
("priority", "u=1, i"),
("sec-ch-ua", "\"Chromium\";v=\"130\", \"Microsoft Edge\";v=\"130\", \"Not?A_Brand\";v=\"99\""),
("sec-ch-ua-mobile", "?0"),
("sec-ch-ua-platform", "\"Windows\""),
("sec-fetch-dest", "empty"),
("sec-fetch-mode", "cors"),
("sec-fetch-site", "cross-site"),
("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0")
));
if (play)
return RedirectToPlay(file);
return ContentTo(VideoTpl.ToJson("play", file, (title ?? original_title), subtitles: subtitles, vast: init.vast));
});
}
#endregion
}
}