diff --git a/LME.StreamData/Controller.cs b/LME.StreamData/Controller.cs deleted file mode 100644 index 27970d7..0000000 --- a/LME.StreamData/Controller.cs +++ /dev/null @@ -1,306 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using System.Web; -using LME.StreamData.Models; -using Microsoft.AspNetCore.Mvc; -using Shared; -using Shared.Engine; -using Shared.Models; -using Shared.Models.Online.Settings; -using Shared.Models.Templates; - -namespace LME.StreamData.Controllers -{ - public class Controller : BaseOnlineController - { - ProxyManager proxyManager; - - public Controller() : base(ModInit.Settings) - { - proxyManager = new ProxyManager(ModInit.StreamDataSettings); - } - - /// - /// Головний ендпоінт модуля StreamData - /// Працює виключно через TMDB ID (параметр id) - /// - [HttpGet] - [Route("lite/lme_streamdata")] - 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, string t, int s = -1, int e = -1, bool play = false, bool rjson = false, string href = null, bool checksearch = false) - { - await UpdateService.ConnectAsync(host); - - var init = loadKit(ModInit.StreamDataSettings); - if (!init.enable) - return Forbid(); - - var invoke = new StreamDataInvoke(init, hybridCache, OnLog, proxyManager, httpHydra); - - // checksearch — перевірка доступності - if (checksearch) - { - if (!IsCheckOnlineSearchEnabled()) - return OnError("lme_streamdata", refresh_proxy: true); - - if (id > 0) - return Content("data-json=", "text/plain; charset=utf-8"); - - return OnError("lme_streamdata", refresh_proxy: true); - } - - // play — повернути стрім для конкретного епізоду (call метод) - if (play) - { - return await HandlePlay(invoke, init, id, s, e, title, original_title, t); - } - - // Фільм - if (serial != 1) - { - return await HandleMovie(invoke, init, id, title, original_title, rjson); - } - - // Серіал - return await HandleSerial(invoke, init, id, title, original_title, s, e, t, rjson); - } - - /// - /// Обробка фільму: отримуємо всі stream_urls та показуємо їх - /// - private async Task HandleMovie(StreamDataInvoke invoke, OnlinesSettings init, long tmdbId, string title, string originalTitle, bool rjson) - { - var response = await invoke.GetMovie(tmdbId); - if (response?.data?.stream_urls == null || response.data.stream_urls.Count == 0) - return OnError("lme_streamdata", refresh_proxy: true); - - var streamUrls = response.data.stream_urls; - var subs = CollectSubtitles(response); - - var displayTitle = !string.IsNullOrEmpty(title) ? title : (!string.IsNullOrEmpty(originalTitle) ? originalTitle : response.data.title); - var tpl = new MovieTpl(displayTitle, originalTitle); - - int index = 1; - foreach (var streamUrl in streamUrls) - { - if (string.IsNullOrWhiteSpace(streamUrl)) - continue; - - string label = $"Джерело #{index}"; - string processedUrl = BuildStreamUrl(init, streamUrl); - tpl.Append(label, processedUrl, subtitles: subs); - index++; - } - - if (tpl.data == null || tpl.data.Count == 0) - return OnError("lme_streamdata", refresh_proxy: true); - - return Content( - rjson ? tpl.ToJson() : tpl.ToHtml(), - rjson ? "application/json; charset=utf-8" : "text/html; charset=utf-8" - ); - } - - /// - /// Обробка серіалу: eps → сезони → епізоди з voice-вкладками (джерела) - /// - private async Task HandleSerial(StreamDataInvoke invoke, OnlinesSettings init, long tmdbId, string title, string originalTitle, int s, int e, string t, bool rjson) - { - var response = await invoke.GetTvSeries(tmdbId); - if (response?.data?.eps == null || response.data.eps.Count == 0) - return OnError("lme_streamdata", refresh_proxy: true); - - var eps = response.data.eps; - var seasons = eps.Keys - .Select(k => int.TryParse(k, out int sn) ? sn : 0) - .Where(sn => sn > 0) - .OrderBy(sn => sn) - .ToList(); - - if (seasons.Count == 0) - return OnError("lme_streamdata", refresh_proxy: true); - - // Кількість джерел (CDN) з першого запиту - var sources = response.data?.stream_urls?.Where(u => !string.IsNullOrWhiteSpace(u)).ToList() ?? new List(); - int sourceCount = Math.Max(1, sources.Count); - - var displayTitle = !string.IsNullOrEmpty(title) ? title : (!string.IsNullOrEmpty(originalTitle) ? originalTitle : response.data.title); - - // Список сезонів - if (s <= 0) - { - var seasonTpl = new SeasonTpl(seasons.Count); - foreach (var season in seasons) - { - string seasonLink = $"{host}/lite/lme_streamdata?id={tmdbId}&serial=1&s={season}&title={HttpUtility.UrlEncode(displayTitle)}&original_title={HttpUtility.UrlEncode(originalTitle)}"; - seasonTpl.Append($"Сезон {season}", seasonLink, season.ToString()); - } - - return Content( - rjson ? seasonTpl.ToJson() : seasonTpl.ToHtml(), - rjson ? "application/json; charset=utf-8" : "text/html; charset=utf-8" - ); - } - - // Список епізодів з voice-вкладками для вибору джерела - string seasonKey = s.ToString(); - if (!eps.ContainsKey(seasonKey) || eps[seasonKey] == null || eps[seasonKey].Count == 0) - return OnError("lme_streamdata", refresh_proxy: true); - - var episodeNumbers = eps[seasonKey] - .Select(ep => int.TryParse(ep, out int en) ? en : 0) - .Where(en => en > 0) - .OrderBy(en => en) - .ToList(); - - if (episodeNumbers.Count == 0) - return OnError("lme_streamdata", refresh_proxy: true); - - // Voice-вкладки: кожне джерело як окрема "озвучка" - string selectedSource = string.IsNullOrEmpty(t) ? "1" : t; - int selectedIndex = int.TryParse(selectedSource, out int si) && si >= 1 && si <= sourceCount ? si : 1; - - var voiceTpl = new VoiceTpl(); - for (int i = 1; i <= sourceCount; i++) - { - string voiceLink = $"{host}/lite/lme_streamdata?id={tmdbId}&serial=1&s={s}&t={i}&title={HttpUtility.UrlEncode(displayTitle)}&original_title={HttpUtility.UrlEncode(originalTitle)}"; - voiceTpl.Append($"Джерело #{i}", i == selectedIndex, voiceLink); - } - - // Епізоди з посиланнями на вибране джерело - var episodeTpl = new EpisodeTpl(episodeNumbers.Count); - foreach (var epNum in episodeNumbers) - { - string episodeName = $"Епізод {epNum}"; - string callUrl = $"{host}/lite/lme_streamdata?id={tmdbId}&serial=1&s={s}&e={epNum}&play=true&t={selectedSource}&title={HttpUtility.UrlEncode(displayTitle)}&original_title={HttpUtility.UrlEncode(originalTitle)}"; - episodeTpl.Append(episodeName, displayTitle, s.ToString(), epNum.ToString("D2"), accsArgs(callUrl), "call"); - } - - episodeTpl.Append(voiceTpl); - - return Content( - rjson ? episodeTpl.ToJson() : episodeTpl.ToHtml(), - rjson ? "application/json; charset=utf-8" : "text/html; charset=utf-8" - ); - } - - /// - /// Обробка play-запиту: API з season/episode → JSON з вибраним джерелом - /// - private async Task HandlePlay(StreamDataInvoke invoke, OnlinesSettings init, long tmdbId, int season, int episode, string title, string originalTitle, string t) - { - if (tmdbId <= 0 || season <= 0 || episode <= 0) - return OnError("lme_streamdata", refresh_proxy: true); - - var response = await invoke.GetEpisode(tmdbId, season, episode); - if (response?.data?.stream_urls == null || response.data.stream_urls.Count == 0) - return OnError("lme_streamdata", refresh_proxy: true); - - var streamUrls = response.data.stream_urls.Where(u => !string.IsNullOrWhiteSpace(u)).ToList(); - if (streamUrls.Count == 0) - return OnError("lme_streamdata", refresh_proxy: true); - - // Вибираємо джерело за індексом з voice-вкладки t (1-based) - int sourceIndex = int.TryParse(t, out int si) && si >= 1 && si <= streamUrls.Count ? si - 1 : 0; - string streamUrl = BuildStreamUrl(init, streamUrls[sourceIndex]); - - var subs = CollectSubtitles(response); - string displayTitle = !string.IsNullOrEmpty(title) ? title : (!string.IsNullOrEmpty(originalTitle) ? originalTitle : response.data.title); - - return UpdateService.Validate(Content( - VideoTpl.ToJson("play", streamUrl, displayTitle, subtitles: subs), - "application/json; charset=utf-8" - )); - } - - /// - /// Зібрати субтитри з відповіді API - /// - private SubtitleTpl CollectSubtitles(StreamDataResponse response) - { - if (response?.default_subs == null || response.default_subs.Count == 0) - return null; - - var tpl = new SubtitleTpl(response.default_subs.Count); - foreach (var sub in response.default_subs) - { - if (!string.IsNullOrWhiteSpace(sub?.url) && !string.IsNullOrWhiteSpace(sub?.lang)) - { - tpl.Append(sub.lang, sub.url); - } - } - - return tpl; - } - - string BuildStreamUrl(OnlinesSettings init, string streamLink) - { - string link = StripLampacArgs(streamLink?.Trim()); - if (string.IsNullOrEmpty(link)) - return link; - - 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); - } - - return HostStreamProxy(init, link); - } - - 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 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); - } - } -} diff --git a/LME.StreamData/ModInit.cs b/LME.StreamData/ModInit.cs deleted file mode 100644 index 8d731b5..0000000 --- a/LME.StreamData/ModInit.cs +++ /dev/null @@ -1,91 +0,0 @@ -using Newtonsoft.Json.Linq; -using Shared; -using Shared.Engine; -using Shared.Models.Module; -using Shared.Models.Module.Interfaces; -using Microsoft.AspNetCore.Mvc; -using Shared.Models; -using Shared.Models.Events; -using Shared.Models.Online.Settings; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace LME.StreamData -{ - public class ModInit : IModuleLoaded - { - public static double Version => 1.5; - - public static OnlinesSettings StreamDataSettings; - public static bool ApnHostProvided; - - public static OnlinesSettings Settings - { - get => StreamDataSettings; - set => StreamDataSettings = value; - } - - /// - /// Модуль завантажено - /// - public void Loaded(InitspaceModel initspace) - { - StreamDataSettings = new OnlinesSettings("LME.StreamData", "https://streamdata.vaplayer.ru", streamproxy: false, useproxy: false) - { - displayname = "StreamData", - displayindex = 0, - proxy = new Shared.Models.Base.ProxySettings() - { - useAuth = true, - username = "", - password = "", - list = new string[] { "socks5://ip:port" } - } - }; - - var defaults = JObject.FromObject(StreamDataSettings); - defaults["enabled"] = true; - var conf = ModuleInvoke.Init("LME.StreamData", defaults); - bool hasApn = ApnHelper.TryGetInitConf(conf, out bool apnEnabled, out string apnHost); - conf.Remove("apn"); - conf.Remove("apn_host"); - StreamDataSettings = conf.ToObject(); - if (hasApn) - ApnHelper.ApplyInitConf(apnEnabled, apnHost, StreamDataSettings, useDefaultHostWhenEmpty: true); - ApnHostProvided = hasApn && apnEnabled && !string.IsNullOrWhiteSpace(apnHost); - if (hasApn && apnEnabled) - { - StreamDataSettings.streamproxy = false; - } - else if (StreamDataSettings.streamproxy) - { - StreamDataSettings.apnstream = false; - StreamDataSettings.apn = null; - } - - // Реєструємо плагін без пошуку — працюємо тільки через TMDB ID - OnlineRegistry.RegisterWithSearch("lme_streamdata"); - } - - public void Dispose() - { - } - } - - public static class UpdateService - { - private static readonly ModuleUpdateService _service = new( - () => ModInit.Settings?.plugin, - () => ModInit.Version); - - public static Task ConnectAsync(string host, CancellationToken cancellationToken = default) - => _service.ConnectAsync(host, cancellationToken); - - public static bool IsDisconnected() - => _service.IsDisconnected(); - - public static ActionResult Validate(ActionResult result) - => _service.Validate(result); - } -} diff --git a/LME.StreamData/Models/StreamDataModels.cs b/LME.StreamData/Models/StreamDataModels.cs deleted file mode 100644 index 8dcbd05..0000000 --- a/LME.StreamData/Models/StreamDataModels.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Generic; - -namespace LME.StreamData.Models -{ - public class SubtitleItem - { - public string lang { get; set; } - public string code { get; set; } - public string url { get; set; } - } - - public class StreamDataResponse - { - public string status_code { get; set; } - public StreamDataInfo data { get; set; } - public List default_subs { get; set; } - } - - public class StreamDataInfo - { - public string title { get; set; } - public string imdb_id { get; set; } - public int season { get; set; } - public string episode { get; set; } - public Dictionary> eps { get; set; } - public string file_name { get; set; } - public string backdrop { get; set; } - public List stream_urls { get; set; } - public string thumbnails_url { get; set; } - } -} diff --git a/LME.StreamData/OnlineApi.cs b/LME.StreamData/OnlineApi.cs deleted file mode 100644 index eb4c2d8..0000000 --- a/LME.StreamData/OnlineApi.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Shared.Models; -using Shared.Models.Module; -using Shared.Models.Module.Interfaces; -using System.Collections.Generic; - -namespace LME.StreamData -{ - public class OnlineApi : IModuleOnline - { - public List Invoke(HttpContext httpContext, RequestModel requestInfo, string host, OnlineEventsModel args) - { - var online = new List(); - - var init = ModInit.StreamDataSettings; - if (init.enable && !init.rip) - { - if (UpdateService.IsDisconnected()) - init.overridehost = null; - - // StreamData працює з TMDB ID — показуємо для всього контенту - online.Add(new ModuleOnlineItem(init, "lme_streamdata")); - } - - return online; - } - } -} diff --git a/LME.StreamData/StreamData.csproj b/LME.StreamData/StreamData.csproj deleted file mode 100644 index c280999..0000000 --- a/LME.StreamData/StreamData.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - net10.0 - library - true - - - - - ..\..\Shared.dll - - - - diff --git a/LME.StreamData/StreamDataInvoke.cs b/LME.StreamData/StreamDataInvoke.cs deleted file mode 100644 index 432b96e..0000000 --- a/LME.StreamData/StreamDataInvoke.cs +++ /dev/null @@ -1,155 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using LME.StreamData.Models; -using Newtonsoft.Json; -using Shared; -using Shared.Engine; -using Shared.Models; -using Shared.Models.Online.Settings; - -namespace LME.StreamData -{ - public class StreamDataInvoke - { - private readonly OnlinesSettings _init; - private readonly IHybridCache _hybridCache; - private readonly Action _onLog; - private readonly ProxyManager _proxyManager; - private readonly HttpHydra _httpHydra; - - private const string API_BASE = "https://streamdata.vaplayer.ru/api.php"; - - public StreamDataInvoke(OnlinesSettings init, IHybridCache hybridCache, Action onLog, ProxyManager proxyManager, HttpHydra httpHydra = null) - { - _init = init; - _hybridCache = hybridCache; - _onLog = onLog; - _proxyManager = proxyManager; - _httpHydra = httpHydra; - } - - /// - /// Отримати дані для фільму за TMDB ID - /// - public async Task GetMovie(long tmdbId) - { - string memKey = $"StreamData:movie:{tmdbId}"; - if (_hybridCache.TryGetValue(memKey, out StreamDataResponse cached)) - return cached; - - try - { - string url = $"{API_BASE}?tmdb={tmdbId}&type=movie"; - _onLog?.Invoke($"StreamData movie: {url}"); - - string json = await ApiGet(url); - if (string.IsNullOrEmpty(json)) - return null; - - var response = JsonConvert.DeserializeObject(json); - if (response?.status_code != "200" || response?.data?.stream_urls == null || response.data.stream_urls.Count == 0) - return null; - - _hybridCache.Set(memKey, response, cacheTime(30, init: _init)); - return response; - } - catch (Exception ex) - { - _onLog?.Invoke($"StreamData movie error: {ex.Message}"); - return null; - } - } - - /// - /// Отримати дані для серіалу (без season/episode - отримуємо eps структуру + S01E01) - /// - public async Task GetTvSeries(long tmdbId) - { - string memKey = $"StreamData:tv:{tmdbId}"; - if (_hybridCache.TryGetValue(memKey, out StreamDataResponse cached)) - return cached; - - try - { - string url = $"{API_BASE}?tmdb={tmdbId}&type=tv"; - _onLog?.Invoke($"StreamData tv: {url}"); - - string json = await ApiGet(url); - if (string.IsNullOrEmpty(json)) - return null; - - var response = JsonConvert.DeserializeObject(json); - if (response?.status_code != "200" || response?.data?.eps == null || response.data.eps.Count == 0) - return null; - - _hybridCache.Set(memKey, response, cacheTime(30, init: _init)); - return response; - } - catch (Exception ex) - { - _onLog?.Invoke($"StreamData tv error: {ex.Message}"); - return null; - } - } - - /// - /// Отримати стріми для конкретного епізоду - /// - public async Task GetEpisode(long tmdbId, int season, int episode) - { - string memKey = $"StreamData:ep:{tmdbId}:s{season}e{episode}"; - if (_hybridCache.TryGetValue(memKey, out StreamDataResponse cached)) - return cached; - - try - { - string url = $"{API_BASE}?tmdb={tmdbId}&type=tv&season={season}&episode={episode}"; - _onLog?.Invoke($"StreamData episode: {url}"); - - string json = await ApiGet(url); - if (string.IsNullOrEmpty(json)) - return null; - - var response = JsonConvert.DeserializeObject(json); - if (response?.status_code != "200" || response?.data?.stream_urls == null || response.data.stream_urls.Count == 0) - return null; - - _hybridCache.Set(memKey, response, cacheTime(30, init: _init)); - return response; - } - catch (Exception ex) - { - _onLog?.Invoke($"StreamData episode error: {ex.Message}"); - return null; - } - } - - private Task ApiGet(string url) - { - var headers = new List() - { - new HeadersModel("User-Agent", "Mozilla/5.0"), - new HeadersModel("Referer", "https://brightpathsignals.com/"), - new HeadersModel("X-Requested-With", "XMLHttpRequest") - }; - - if (_httpHydra != null) - return _httpHydra.Get(url, newheaders: headers); - - return Http.Get(_init.cors(url), headers: headers, proxy: _proxyManager.Get()); - } - - public static TimeSpan cacheTime(int multiaccess, int home = 5, int mikrotik = 2, OnlinesSettings init = null, int rhub = -1) - { - if (init != null && init.rhub && rhub != -1) - return TimeSpan.FromMinutes(rhub); - - int ctime = init != null && init.cache_time > 0 ? init.cache_time : multiaccess; - if (ctime > multiaccess) - ctime = multiaccess; - - return TimeSpan.FromMinutes(ctime); - } - } -} diff --git a/LME.StreamData/manifest.json b/LME.StreamData/manifest.json deleted file mode 100644 index 7b2fd84..0000000 --- a/LME.StreamData/manifest.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "enable": true, - "version": 3, - "initspace": "LME.StreamData.ModInit", - "online": "LME.StreamData.OnlineApi", - "syntaxPaths": [ - "../LME.Shared/GlobalUsings.cs", - "../LME.Shared/Online/OnlineRegistry.cs", - "../LME.Shared/Update/ModuleUpdateService.cs", - "../LME.Shared/Apn/ApnHelper.cs" - ] -}