using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; using Microsoft.AspNetCore.Mvc; using Shared; using Shared.Engine; using Shared.Models.Online.Settings; using Shared.Models.Templates; using LME.StarLight.Models; namespace LME.StarLight.Controllers { public class Controller : BaseOnlineController { ProxyManager proxyManager; public Controller() : base(ModInit.Settings) { proxyManager = new ProxyManager(ModInit.StarLight); } [HttpGet] [Route("lite/lme_starlight")] async public Task 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, int s = -1, bool rjson = false, string href = null, bool checksearch = false) { await UpdateService.ConnectAsync(host); var init = loadKit(ModInit.StarLight); if (!init.enable) return Forbid(); var invoke = new StarLightInvoke(init, hybridCache, OnLog, proxyManager, httpHydra); if (checksearch) { if (!IsCheckOnlineSearchEnabled()) return OnError("lme_starlight", refresh_proxy: true); var searchResults = await invoke.Search(title, original_title); if (searchResults != null && searchResults.Count > 0) return Content("data-json=", "text/plain; charset=utf-8"); return OnError("lme_starlight", refresh_proxy: true); } string itemUrl = href; if (string.IsNullOrEmpty(itemUrl)) { var searchResults = await invoke.Search(title, original_title); if (searchResults == null || searchResults.Count == 0) return OnError("lme_starlight", refresh_proxy: true); if (searchResults.Count > 1) { var similar_tpl = new SimilarTpl(searchResults.Count); foreach (var res in searchResults) { string link = $"{host}/lite/lme_starlight?imdb_id={imdb_id}&kinopoisk_id={kinopoisk_id}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&year={year}&serial={serial}&href={HttpUtility.UrlEncode(res.Href)}"; similar_tpl.Append(res.Title, string.Empty, string.Empty, link, string.Empty); } return rjson ? Content(similar_tpl.ToJson(), "application/json; charset=utf-8") : Content(similar_tpl.ToHtml(), "text/html; charset=utf-8"); } itemUrl = searchResults[0].Href; } var project = await invoke.GetProject(itemUrl); if (project == null) return OnError("lme_starlight", refresh_proxy: true); if (serial == 1 && project.Seasons.Count > 0) { if (s == -1) { var season_tpl = new SeasonTpl(project.Seasons.Count); for (int i = 0; i < project.Seasons.Count; i++) { var seasonInfo = project.Seasons[i]; string seasonName = string.IsNullOrEmpty(seasonInfo.Title) ? $"Сезон {i + 1}" : seasonInfo.Title; string link = $"{host}/lite/lme_starlight?imdb_id={imdb_id}&kinopoisk_id={kinopoisk_id}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&year={year}&serial=1&s={i}&href={HttpUtility.UrlEncode(itemUrl)}"; season_tpl.Append(seasonName, link, i.ToString()); } return rjson ? Content(season_tpl.ToJson(), "application/json; charset=utf-8") : Content(season_tpl.ToHtml(), "text/html; charset=utf-8"); } if (s < 0 || s >= project.Seasons.Count) return OnError("lme_starlight", refresh_proxy: true); var season = project.Seasons[s]; string seasonSlug = season.Slug; var episodes = invoke.GetEpisodes(project, seasonSlug); if (episodes == null || episodes.Count == 0) return OnError("lme_starlight", refresh_proxy: true); var episode_tpl = new EpisodeTpl(); int index = 1; string seasonNumber = GetSeasonNumber(season, s); var orderedEpisodes = episodes .Select(ep => new { Episode = ep, Number = GetEpisodeNumber(ep), Date = GetEpisodeDate(ep) }) .OrderBy(ep => ep.Number ?? int.MaxValue) .ThenBy(ep => ep.Date ?? DateTime.MaxValue) .Select(ep => ep.Episode) .ToList(); foreach (var ep in orderedEpisodes) { if (string.IsNullOrEmpty(ep.Hash)) continue; string episodeName = string.IsNullOrEmpty(ep.Title) ? $"Епізод {index}" : ep.Title; string callUrl = $"{host}/lite/lme_starlight/play?hash={HttpUtility.UrlEncode(ep.Hash)}&title={HttpUtility.UrlEncode(title ?? original_title)}"; episode_tpl.Append(episodeName, title ?? original_title, seasonNumber, index.ToString("D2"), accsArgs(callUrl), "call"); index++; } return rjson ? Content(episode_tpl.ToJson(), "application/json; charset=utf-8") : Content(episode_tpl.ToHtml(), "text/html; charset=utf-8"); } else { string hash = project.Hash; if (string.IsNullOrEmpty(hash) && project.Episodes.Count > 0) hash = project.Episodes.FirstOrDefault(e => !string.IsNullOrEmpty(e.Hash))?.Hash; if (string.IsNullOrEmpty(hash)) return OnError("lme_starlight", refresh_proxy: true); string callUrl = $"{host}/lite/lme_starlight/play?hash={HttpUtility.UrlEncode(hash)}&title={HttpUtility.UrlEncode(title ?? original_title)}"; var movie_tpl = new MovieTpl(title, original_title, 1); movie_tpl.Append(string.IsNullOrEmpty(title) ? "StarLight" : title, accsArgs(callUrl), "call"); return rjson ? Content(movie_tpl.ToJson(), "application/json; charset=utf-8") : Content(movie_tpl.ToHtml(), "text/html; charset=utf-8"); } } [HttpGet] [Route("lite/lme_starlight/play")] async public Task Play(string hash, string title) { await UpdateService.ConnectAsync(host); if (string.IsNullOrEmpty(hash)) return OnError("lme_starlight", refresh_proxy: true); var init = loadKit(ModInit.StarLight); if (!init.enable) return Forbid(); var invoke = new StarLightInvoke(init, hybridCache, OnLog, proxyManager, httpHydra); var result = await invoke.ResolveStream(hash); if (result == null || string.IsNullOrEmpty(result.Stream)) return OnError("lme_starlight", refresh_proxy: true); string videoTitle = title ?? result.Name ?? ""; if (result.Streams != null && result.Streams.Count > 0) { var streamQuality = new StreamQualityTpl(); foreach (var item in result.Streams) { string streamLink = BuildStreamUrl(init, item.link); streamQuality.Append(streamLink, item.quality); } var first = streamQuality.Firts(); string streamUrl = string.IsNullOrEmpty(first.link) ? BuildStreamUrl(init, result.Stream) : first.link; return UpdateService.Validate(Content(VideoTpl.ToJson("play", streamUrl, videoTitle, streamquality: streamQuality), "application/json; charset=utf-8")); } string defaultUrl = BuildStreamUrl(init, result.Stream); return UpdateService.Validate(Content(VideoTpl.ToJson("play", defaultUrl, videoTitle), "application/json; charset=utf-8")); } string BuildStreamUrl(OnlinesSettings init, string streamLink) { string link = StripLampacArgs(streamLink?.Trim()); if (string.IsNullOrEmpty(link)) return link; if (ApnHelper.IsEnabled(init)) { if (ModInit.ApnHostProvided || ApnHelper.IsAshdiUrl(link)) return ApnHelper.WrapUrl(init, link); var noApn = (OnlinesSettings)init.Clone(); noApn.apnstream = false; noApn.apn = null; return HostStreamProxy(noApn, link, proxy: proxyManager.Get()); } return HostStreamProxy(init, link, proxy: proxyManager.Get()); } private static string StripLampacArgs(string url) { if (string.IsNullOrEmpty(url)) return url; string cleaned = System.Text.RegularExpressions.Regex.Replace( url, @"([?&])(account_email|uid|nws_id)=[^&]*", "$1", System.Text.RegularExpressions.RegexOptions.IgnoreCase ); cleaned = cleaned.Replace("?&", "?").Replace("&&", "&").TrimEnd('?', '&'); return cleaned; } private static string GetSeasonNumber(SeasonInfo season, int fallbackIndex) { if (season?.Title == null) return (fallbackIndex + 1).ToString(); var digits = new string(season.Title.Where(char.IsDigit).ToArray()); return string.IsNullOrEmpty(digits) ? (fallbackIndex + 1).ToString() : digits; } private static int? GetEpisodeNumber(EpisodeInfo episode) { if (episode == null) return null; if (episode.Number.HasValue) return episode.Number.Value; if (string.IsNullOrEmpty(episode.Title)) return null; var title = episode.Title; var markers = new[] { "випуск", "серия", "серія" }; foreach (var marker in markers) { var markerIndex = title.IndexOf(marker, StringComparison.OrdinalIgnoreCase); if (markerIndex <= 0) continue; var prefix = title.Substring(0, markerIndex); var matches = Regex.Matches(prefix, "\\d+"); if (matches.Count == 0) continue; var last = matches[matches.Count - 1].Value; if (int.TryParse(last, NumberStyles.Integer, CultureInfo.InvariantCulture, out var number)) return number; } return null; } private static DateTime? GetEpisodeDate(EpisodeInfo episode) { if (episode == null || string.IsNullOrEmpty(episode.Date)) return null; if (DateTime.TryParseExact(episode.Date, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt)) return dt; return DateTime.TryParse(episode.Date, CultureInfo.InvariantCulture, DateTimeStyles.None, out dt) ? dt : null; } private static bool IsCheckOnlineSearchEnabled() { try { var onlineType = Type.GetType("Online.ModInit"); if (onlineType == null) { foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) { onlineType = asm.GetType("Online.ModInit"); if (onlineType != null) break; } } var confField = onlineType?.GetField("conf", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); var conf = confField?.GetValue(null); var checkProp = conf?.GetType().GetProperty("checkOnlineSearch", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); if (checkProp?.GetValue(conf) is bool enabled) return enabled; } catch { } return true; } private static void OnLog(string message) { System.Console.WriteLine(message); } } }