lampac-ukraine/MoonAnime/Controller.cs

343 lines
13 KiB
C#

using Microsoft.AspNetCore.Mvc;
using MoonAnime.Models;
using Shared;
using Shared.Engine;
using Shared.Models;
using Shared.Models.Online.Settings;
using Shared.Models.Templates;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
namespace MoonAnime.Controllers
{
public class Controller : BaseOnlineController
{
private readonly ProxyManager proxyManager;
public Controller() : base(ModInit.Settings)
{
proxyManager = new ProxyManager(ModInit.MoonAnime);
}
[HttpGet]
[Route("moonanime")]
public async Task<ActionResult> Index(long id, string imdb_id, long kinopoisk_id, string title, string original_title, string original_language, int year, string source, int serial, string account_email, string mal_id, string t, int s = -1, bool rjson = false, bool checksearch = false)
{
await UpdateService.ConnectAsync(host);
var init = await loadKit(ModInit.MoonAnime);
if (!init.enable)
return Forbid();
var invoke = new MoonAnimeInvoke(init, hybridCache, OnLog, proxyManager);
if (checksearch)
{
if (AppInit.conf?.online?.checkOnlineSearch != true)
return OnError("moonanime", proxyManager);
var checkResults = await invoke.Search(imdb_id, mal_id, title, original_title, year);
if (checkResults != null && checkResults.Count > 0)
return Content("data-json=", "text/plain; charset=utf-8");
return OnError("moonanime", proxyManager);
}
OnLog($"MoonAnime: title={title}, original_title={original_title}, imdb={imdb_id}, mal_id={mal_id}, serial={serial}, s={s}, t={t}");
var seasons = await invoke.Search(imdb_id, mal_id, title, original_title, year);
if (seasons == null || seasons.Count == 0)
return OnError("moonanime", proxyManager);
bool isSeries = serial == 1;
MoonAnimeSeasonContent firstSeasonData = null;
if (serial == -1)
{
firstSeasonData = await invoke.GetSeasonContent(seasons[0]);
if (firstSeasonData == null || firstSeasonData.Voices.Count == 0)
return OnError("moonanime", proxyManager);
isSeries = firstSeasonData.IsSeries;
}
if (isSeries)
{
return await RenderSerial(invoke, seasons, imdb_id, kinopoisk_id, title, original_title, year, mal_id, s, t, rjson);
}
return await RenderMovie(invoke, seasons, title, original_title, firstSeasonData, rjson);
}
[HttpGet("moonanime/play")]
public async Task<ActionResult> Play(string file, string title = null)
{
await UpdateService.ConnectAsync(host);
var init = await loadKit(ModInit.MoonAnime);
if (!init.enable)
return Forbid();
if (string.IsNullOrWhiteSpace(file))
return OnError("moonanime", proxyManager);
var invoke = new MoonAnimeInvoke(init, hybridCache, OnLog, proxyManager);
var streams = invoke.ParseStreams(file);
if (streams == null || streams.Count == 0)
return OnError("moonanime", proxyManager);
if (streams.Count == 1)
{
string singleUrl = BuildStreamUrl(init, streams[0].Url);
string singleJson = VideoTpl.ToJson("play", singleUrl, title ?? string.Empty, quality: streams[0].Quality ?? "auto");
return UpdateService.Validate(Content(singleJson, "application/json; charset=utf-8"));
}
var streamQuality = new StreamQualityTpl();
foreach (var stream in streams)
{
string streamUrl = BuildStreamUrl(init, stream.Url);
streamQuality.Append(streamUrl, stream.Quality);
}
if (!streamQuality.Any())
return OnError("moonanime", proxyManager);
var first = streamQuality.Firts();
string json = VideoTpl.ToJson("play", first.link, title ?? string.Empty, streamquality: streamQuality);
return UpdateService.Validate(Content(json, "application/json; charset=utf-8"));
}
private async Task<ActionResult> RenderSerial(
MoonAnimeInvoke invoke,
List<MoonAnimeSeasonRef> seasons,
string imdbId,
long kinopoiskId,
string title,
string originalTitle,
int year,
string malId,
int selectedSeason,
string selectedVoice,
bool rjson)
{
var orderedSeasons = seasons
.Where(s => s != null && !string.IsNullOrWhiteSpace(s.Url))
.OrderBy(s => s.SeasonNumber)
.ToList();
if (orderedSeasons.Count == 0)
return OnError("moonanime", proxyManager);
if (selectedSeason == -1)
{
var seasonTpl = new SeasonTpl(orderedSeasons.Count);
foreach (var season in orderedSeasons)
{
int seasonNumber = season.SeasonNumber <= 0 ? 1 : season.SeasonNumber;
string seasonName = $"Сезон {seasonNumber}";
string seasonLink = BuildIndexUrl(imdbId, kinopoiskId, title, originalTitle, year, 1, malId, seasonNumber, selectedVoice);
seasonTpl.Append(seasonName, seasonLink, seasonNumber);
}
return rjson
? Content(seasonTpl.ToJson(), "application/json; charset=utf-8")
: Content(seasonTpl.ToHtml(), "text/html; charset=utf-8");
}
var currentSeason = orderedSeasons.FirstOrDefault(s => s.SeasonNumber == selectedSeason) ?? orderedSeasons[0];
var seasonData = await invoke.GetSeasonContent(currentSeason);
if (seasonData == null)
return OnError("moonanime", proxyManager);
var voices = seasonData.Voices
.Where(v => v != null && v.Episodes != null && v.Episodes.Count > 0)
.ToList();
if (voices.Count == 0)
return OnError("moonanime", proxyManager);
int activeVoiceIndex = ParseVoiceIndex(selectedVoice, voices.Count);
var voiceTpl = new VoiceTpl(voices.Count);
for (int i = 0; i < voices.Count; i++)
{
string voiceName = string.IsNullOrWhiteSpace(voices[i].Name) ? $"Озвучка {i + 1}" : voices[i].Name;
string voiceLink = BuildIndexUrl(imdbId, kinopoiskId, title, originalTitle, year, 1, malId, currentSeason.SeasonNumber, i.ToString());
voiceTpl.Append(voiceName, i == activeVoiceIndex, voiceLink);
}
var selectedVoiceData = voices[activeVoiceIndex];
var episodes = selectedVoiceData.Episodes
.Where(e => e != null && !string.IsNullOrWhiteSpace(e.File))
.OrderBy(e => e.Number <= 0 ? int.MaxValue : e.Number)
.ThenBy(e => e.Name)
.ToList();
if (episodes.Count == 0)
return OnError("moonanime", proxyManager);
string displayTitle = !string.IsNullOrWhiteSpace(title)
? title
: !string.IsNullOrWhiteSpace(originalTitle)
? originalTitle
: "MoonAnime";
var episodeTpl = new EpisodeTpl(episodes.Count);
foreach (var episode in episodes)
{
int episodeNumber = episode.Number <= 0 ? 1 : episode.Number;
string episodeName = string.IsNullOrWhiteSpace(episode.Name) ? $"Епізод {episodeNumber}" : episode.Name;
string callUrl = $"{host}/moonanime/play?file={HttpUtility.UrlEncode(episode.File)}&title={HttpUtility.UrlEncode(displayTitle)}";
episodeTpl.Append(episodeName, displayTitle, currentSeason.SeasonNumber.ToString(), episodeNumber.ToString(), accsArgs(callUrl), "call");
}
episodeTpl.Append(voiceTpl);
return rjson
? Content(episodeTpl.ToJson(), "application/json; charset=utf-8")
: Content(episodeTpl.ToHtml(), "text/html; charset=utf-8");
}
private async Task<ActionResult> RenderMovie(
MoonAnimeInvoke invoke,
List<MoonAnimeSeasonRef> seasons,
string title,
string originalTitle,
MoonAnimeSeasonContent firstSeasonData,
bool rjson)
{
var currentSeason = seasons
.Where(s => s != null && !string.IsNullOrWhiteSpace(s.Url))
.OrderBy(s => s.SeasonNumber)
.FirstOrDefault();
if (currentSeason == null)
return OnError("moonanime", proxyManager);
MoonAnimeSeasonContent seasonData = firstSeasonData;
if (seasonData == null || !string.Equals(seasonData.Url, currentSeason.Url, StringComparison.OrdinalIgnoreCase))
seasonData = await invoke.GetSeasonContent(currentSeason);
if (seasonData == null || seasonData.Voices.Count == 0)
return OnError("moonanime", proxyManager);
string displayTitle = !string.IsNullOrWhiteSpace(title)
? title
: !string.IsNullOrWhiteSpace(originalTitle)
? originalTitle
: "MoonAnime";
var movieTpl = new MovieTpl(displayTitle, originalTitle);
int fallbackIndex = 1;
foreach (var voice in seasonData.Voices)
{
if (voice == null)
continue;
string file = !string.IsNullOrWhiteSpace(voice.MovieFile)
? voice.MovieFile
: voice.Episodes?.FirstOrDefault(e => !string.IsNullOrWhiteSpace(e.File))?.File;
if (string.IsNullOrWhiteSpace(file))
continue;
string voiceName = string.IsNullOrWhiteSpace(voice.Name) ? $"Озвучка {fallbackIndex}" : voice.Name;
string callUrl = $"{host}/moonanime/play?file={HttpUtility.UrlEncode(file)}&title={HttpUtility.UrlEncode(displayTitle)}";
movieTpl.Append(voiceName, accsArgs(callUrl), "call");
fallbackIndex++;
}
if (movieTpl.IsEmpty)
return OnError("moonanime", proxyManager);
return rjson
? Content(movieTpl.ToJson(), "application/json; charset=utf-8")
: Content(movieTpl.ToHtml(), "text/html; charset=utf-8");
}
private string BuildIndexUrl(string imdbId, long kinopoiskId, string title, string originalTitle, int year, int serial, string malId, int season, string voice)
{
var url = new StringBuilder();
url.Append($"{host}/moonanime?imdb_id={HttpUtility.UrlEncode(imdbId)}");
url.Append($"&kinopoisk_id={kinopoiskId}");
url.Append($"&title={HttpUtility.UrlEncode(title)}");
url.Append($"&original_title={HttpUtility.UrlEncode(originalTitle)}");
url.Append($"&year={year}");
url.Append($"&serial={serial}");
if (!string.IsNullOrWhiteSpace(malId))
url.Append($"&mal_id={HttpUtility.UrlEncode(malId)}");
if (season > 0)
url.Append($"&s={season}");
if (!string.IsNullOrWhiteSpace(voice))
url.Append($"&t={HttpUtility.UrlEncode(voice)}");
return url.ToString();
}
private int ParseVoiceIndex(string voiceValue, int totalVoices)
{
if (totalVoices <= 0)
return 0;
if (!int.TryParse(voiceValue, out int index))
return 0;
if (index < 0 || index >= totalVoices)
return 0;
return index;
}
private string BuildStreamUrl(OnlinesSettings init, string streamLink)
{
string link = StripLampacArgs(streamLink?.Trim());
if (string.IsNullOrEmpty(link))
return link;
var headers = new List<HeadersModel>
{
new HeadersModel("User-Agent", "Mozilla/5.0"),
new HeadersModel("Referer", "https://moonanime.art/")
};
if (ApnHelper.IsEnabled(init))
{
if (ModInit.ApnHostProvided)
return ApnHelper.WrapUrl(init, link);
var noApn = (OnlinesSettings)init.Clone();
noApn.apnstream = false;
noApn.apn = null;
return HostStreamProxy(noApn, link, headers: headers, proxy: proxyManager.Get());
}
return HostStreamProxy(init, link, headers: headers, proxy: proxyManager.Get());
}
private static string StripLampacArgs(string url)
{
if (string.IsNullOrEmpty(url))
return url;
string cleaned = Regex.Replace(
url,
@"([?&])(account_email|uid|nws_id)=[^&]*",
"$1",
RegexOptions.IgnoreCase
);
cleaned = cleaned.Replace("?&", "?").Replace("&&", "&").TrimEnd('?', '&');
return cleaned;
}
}
}