From 3e1cab4531ac0a37278a50287d058568711d97aa Mon Sep 17 00:00:00 2001 From: Felix Date: Sun, 14 Sep 2025 19:04:12 +0300 Subject: [PATCH] Add CikavaIdeya Add Unimay --- Unimay/Controllers/Controller.cs | 198 +++++++++++++++++++++++++++++++ Unimay/ModInit.cs | 24 ++++ Unimay/OnlineApi.cs | 25 ++++ Unimay/Unimay.csproj | 15 +++ Unimay/manifest.json | 6 + 5 files changed, 268 insertions(+) create mode 100644 Unimay/Controllers/Controller.cs create mode 100644 Unimay/ModInit.cs create mode 100644 Unimay/OnlineApi.cs create mode 100644 Unimay/Unimay.csproj create mode 100644 Unimay/manifest.json diff --git a/Unimay/Controllers/Controller.cs b/Unimay/Controllers/Controller.cs new file mode 100644 index 0000000..2d2698e --- /dev/null +++ b/Unimay/Controllers/Controller.cs @@ -0,0 +1,198 @@ +using Shared.Engine; +using System; +using System.Threading.Tasks; +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.Web; +using Newtonsoft.Json.Linq; +using Shared.Models.Templates; +using Shared.Models.Online.Settings; +using Shared; + +namespace Unimay.Controllers +{ + public class Controller : BaseOnlineController + { + ProxyManager proxyManager; + + public Controller() + { + proxyManager = new ProxyManager(ModInit.Unimay); + } + + [HttpGet] + [Route("unimay")] + async public ValueTask Index(string title, string original_title, string code, int serial = -1, int s = -1, int e = -1, bool play = false, bool rjson = false) + { + var init = await loadKit(ModInit.Unimay); + if (await IsBadInitialization(init, rch: false)) + return badInitMsg; + + var proxy = proxyManager.Get(); + + if (!string.IsNullOrEmpty(code)) + { + // Fetch release details + return await Release(init, proxy, code, title, original_title, serial, s, e, play, rjson); + } + else + { + // Search + return await Search(init, proxy, title, original_title, serial, rjson); + } + } + + async ValueTask Search(OnlinesSettings init, System.Net.WebProxy proxy, string title, string original_title, int serial, bool rjson) + { + string memKey = $"unimay:search:{title}:{original_title}:{serial}"; + + return await InvkSemaphore(init, memKey, async () => + { + if (!hybridCache.TryGetValue(memKey, out JArray searchResults)) + { + string searchQuery = HttpUtility.UrlEncode(title ?? original_title ?? ""); + string searchUrl = $"{init.host}/release/search?page=0&page_size=10&title={searchQuery}"; + + var headers = httpHeaders(init); + JObject root = await Http.Get(searchUrl, timeoutSeconds: 8, proxy: proxy, headers: headers); + + if (root == null || !root.ContainsKey("content") || ((JArray)root["content"]).Count == 0) + { + proxyManager.Refresh(); + return OnError("search failed"); + } + + searchResults = (JArray)root["content"]; + hybridCache.Set(memKey, searchResults, cacheTime(30, init: init)); + } + + if (searchResults == null || searchResults.Count == 0) + return OnError("no results"); + + var stpl = new SimilarTpl(searchResults.Count); + + foreach (JObject item in searchResults) + { + string itemCode = item.Value("code"); + string itemTitle = item["names"]?["ukr"]?.Value() ?? item.Value("title"); + string itemYear = item.Value("year"); + string itemType = item.Value("type"); // "Телесеріал" or "Фільм" + + // Filter by serial if specified (0: movie "Фільм", 1: serial "Телесеріал") + if (serial != -1) + { + bool isMovie = itemType == "Фільм"; + if ((serial == 0 && !isMovie) || (serial == 1 && isMovie)) + continue; + } + + string releaseUrl = $"{host}/unimay?code={itemCode}&title={HttpUtility.UrlEncode(itemTitle)}&original_title={HttpUtility.UrlEncode(original_title ?? "")}&serial={serial}"; + stpl.Append(itemTitle, itemYear, itemType, releaseUrl); + } + + return ContentTo(rjson ? stpl.ToJson() : stpl.ToHtml()); + }); + } + + async ValueTask Release(OnlinesSettings init, System.Net.WebProxy proxy, string code, string title, string original_title, int serial, int s, int e, bool play, bool rjson) + { + string memKey = $"unimay:release:{code}"; + + return await InvkSemaphore(init, memKey, async () => + { + if (!hybridCache.TryGetValue(memKey, out JObject releaseDetail)) + { + string releaseUrl = $"{init.host}/release?code={code}"; + + var headers = httpHeaders(init); + JObject root = await Http.Get(releaseUrl, timeoutSeconds: 8, proxy: proxy, headers: headers); + + if (root == null) + { + proxyManager.Refresh(); + return OnError("release failed"); + } + + releaseDetail = root; + hybridCache.Set(memKey, releaseDetail, cacheTime(60, init: init)); + } + + if (releaseDetail == null) + return OnError("no release detail"); + + string itemType = releaseDetail.Value("type"); + JArray playlist = (JArray)releaseDetail["playlist"]; + + if (playlist == null || playlist.Count == 0) + return OnError("no playlist"); + + if (play) + { + // Get specific episode + JObject episode = null; + if (itemType == "Телесеріал") + { + if (s <= 0 || e <= 0) return OnError("invalid episode"); + episode = playlist.FirstOrDefault(ep => (int?)ep["number"] == e) as JObject; + } + else // Movie + { + episode = playlist[0] as JObject; + } + + if (episode == null) + return OnError("episode not found"); + + string masterUrl = episode["hls"]?["master"]?.Value(); + if (string.IsNullOrEmpty(masterUrl)) + return OnError("no stream"); + + return Redirect(HostStreamProxy(init, masterUrl, proxy: proxy)); + } + + if (itemType == "Фільм") + { + JObject movieEpisode = playlist[0] as JObject; + string movieLink = $"{host}/unimay?code={code}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&serial=0&play=true"; + var mtpl = new MovieTpl(title, original_title, 1); + mtpl.Append(movieEpisode["title"]?.Value() ?? title, movieLink); + return ContentTo(rjson ? mtpl.ToJson() : mtpl.ToHtml()); + } + else if (itemType == "Телесеріал") + { + if (s == -1) + { + // Assume single season + var stpl = new SeasonTpl(); + stpl.Append("Сезон 1", $"{host}/unimay?code={code}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&serial=1&s=1", "1"); + return ContentTo(rjson ? stpl.ToJson() : stpl.ToHtml()); + } + else + { + // Episodes for season 1 + var episodes = new List(); + foreach (JObject ep in playlist) + { + int epNum = (int)ep["number"]; + if (epNum >= 1 && epNum <= 24) // Assume season 1 + episodes.Add(ep); + } + + var mtpl = new MovieTpl(title, original_title, episodes.Count); + foreach (JObject ep in episodes.OrderBy(ep => (int)ep["number"])) + { + int epNum = (int)ep["number"]; + string epTitle = ep["title"]?.Value() ?? $"Епізод {epNum}"; + string epLink = $"{host}/unimay?code={code}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&serial=1&s=1&e={epNum}&play=true"; + mtpl.Append(epTitle, epLink); + } + return ContentTo(rjson ? mtpl.ToJson() : mtpl.ToHtml()); + } + } + + return OnError("unsupported type"); + }); + } + } +} \ No newline at end of file diff --git a/Unimay/ModInit.cs b/Unimay/ModInit.cs new file mode 100644 index 0000000..872538f --- /dev/null +++ b/Unimay/ModInit.cs @@ -0,0 +1,24 @@ +using Shared; +using Shared.Models.Online.Settings; + +namespace Unimay +{ + public class ModInit + { + public static OnlinesSettings Unimay; + + /// + /// модуль загружен + /// + public static void loaded() + { + Unimay = new OnlinesSettings("Unimay", "https://api.unimay.media/v1", streamproxy: true) + { + displayname = "Unimay" + }; + + // Виводити "уточнити пошук" + AppInit.conf.online.with_search.Add("unimay"); + } + } +} \ No newline at end of file diff --git a/Unimay/OnlineApi.cs b/Unimay/OnlineApi.cs new file mode 100644 index 0000000..34ec986 --- /dev/null +++ b/Unimay/OnlineApi.cs @@ -0,0 +1,25 @@ +using Shared.Models.Base; +using System.Collections.Generic; + +namespace Unimay +{ + public class OnlineApi + { + public static List<(string name, string url, string plugin, int index)> Events(string host, 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) + { + var online = new List<(string name, string url, string plugin, int index)>(); + + var init = ModInit.Unimay; + if (init.enable && !init.rip) + { + string url = init.overridehost; + if (string.IsNullOrEmpty(url)) + url = $"{host}/unimay"; + + online.Add((init.displayname, url, "unimay", init.displayindex > 0 ? init.displayindex : online.Count)); + } + + return online; + } + } +} \ No newline at end of file diff --git a/Unimay/Unimay.csproj b/Unimay/Unimay.csproj new file mode 100644 index 0000000..6c529d1 --- /dev/null +++ b/Unimay/Unimay.csproj @@ -0,0 +1,15 @@ + + + + net8.0 + enable + enable + false + latest + + + + + + + \ No newline at end of file diff --git a/Unimay/manifest.json b/Unimay/manifest.json new file mode 100644 index 0000000..f9646f2 --- /dev/null +++ b/Unimay/manifest.json @@ -0,0 +1,6 @@ +{ + "enable": true, + "version": 1, + "initspace": "Unimay.ModInit", + "online": "Unimay.OnlineApi" +} \ No newline at end of file