From 04bb7d48b5662ba9177953aba1b44ebb83aff7f2 Mon Sep 17 00:00:00 2001 From: Felix Date: Sat, 2 May 2026 15:54:56 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A3=D1=81=D1=96=20=D0=BC=D0=BE=D0=B4=D1=83?= =?UTF-8?q?=D0=BB=D1=96=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80=20=D0=BA=D0=BE?= =?UTF-8?q?=D1=80=D0=B5=D0=BA=D1=82=D0=BD=D0=BE=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B4=D0=B0=D1=8E=D1=82=D1=8C=20=D0=BE=D0=B1'=D1=94=D0=BA?= =?UTF-8?q?=D1=82=20SubtitleTpl=20=D1=83=20=D1=88=D0=B0=D0=B1=D0=BB=D0=BE?= =?UTF-8?q?=D0=BD=D0=B8=20Lampac,=20=D1=89=D0=BE=20=D0=B4=D0=BE=D0=B7?= =?UTF-8?q?=D0=B2=D0=BE=D0=BB=D1=8F=D1=94=20=D0=B2=D1=96=D0=B4=D0=BE=D0=B1?= =?UTF-8?q?=D1=80=D0=B0=D0=B6=D0=B0=D1=82=D0=B8=20=D1=81=D1=83=D0=B1=D1=82?= =?UTF-8?q?=D0=B8=D1=82=D1=80=D0=B8=20=D0=B2=20=D1=96=D0=BD=D1=82=D0=B5?= =?UTF-8?q?=D1=80=D1=84=D0=B5=D0=B9=D1=81=D1=96=20=D0=BF=D0=BB=D0=B5=D1=94?= =?UTF-8?q?=D1=80=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 ++- LME.AnimeON/AnimeONInvoke.cs | 28 +++++++++++++++++---- LME.AnimeON/Controller.cs | 38 ++++++++++++++++++++-------- LME.KlonFUN/Controller.cs | 4 +-- LME.KlonFUN/KlonFUNInvoke.cs | 9 ++++--- LME.KlonFUN/Models/KlonFUNModels.cs | 3 +++ LME.Makhno/Controller.cs | 25 ++++++++++-------- LME.Makhno/MakhnoInvoke.cs | 12 ++++++--- LME.Makhno/Models/MakhnoModels.cs | 4 +++ LME.Mikai/Controller.cs | 29 +++++++++++++++++---- LME.Mikai/MikaiInvoke.cs | 28 +++++++++++++++++---- LME.Shared/Apn/ApnHelper.cs | 39 +++++++++++++++++++++++++++++ LME.Uaflix/Controller.cs | 13 ++++++---- LME.Uaflix/Models/PlayResult.cs | 1 + LME.Uaflix/UaflixInvoke.cs | 20 ++++----------- 15 files changed, 191 insertions(+), 65 deletions(-) diff --git a/.gitignore b/.gitignore index 2fba2a2..4b2e3e6 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ bin obj .vscode/settings.json .qwen -log \ No newline at end of file +log +.kilo \ No newline at end of file diff --git a/LME.AnimeON/AnimeONInvoke.cs b/LME.AnimeON/AnimeONInvoke.cs index 586efd1..eb002a3 100644 --- a/LME.AnimeON/AnimeONInvoke.cs +++ b/LME.AnimeON/AnimeONInvoke.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Shared; using Shared.Models.Online.Settings; using Shared.Models; +using Shared.Models.Templates; using System.Text.Json; using System.Linq; using System.Text; @@ -14,6 +15,13 @@ using Shared.Engine; namespace LME.AnimeON { + public class AshdiStream + { + public string Title { get; set; } + public string Link { get; set; } + public SubtitleTpl Subtitles { get; set; } + } + public class AnimeONInvoke { private static readonly Regex Quality4kRegex = new Regex(@"(^|[^0-9])(2160p?)([^0-9]|$)|\b4k\b|\buhd\b", RegexOptions.IgnoreCase); @@ -192,12 +200,12 @@ namespace LME.AnimeON public async Task ParseAshdiPage(string url, bool disableAshdiMultivoiceForVod = false) { var streams = await ParseAshdiPageStreams(url, disableAshdiMultivoiceForVod); - return streams?.FirstOrDefault().link; + return streams?.FirstOrDefault()?.Link; } - public async Task> ParseAshdiPageStreams(string url, bool disableAshdiMultivoiceForVod = false) + public async Task> ParseAshdiPageStreams(string url, bool disableAshdiMultivoiceForVod = false) { - var streams = new List<(string title, string link)>(); + var streams = new List(); try { var headers = new List() @@ -234,7 +242,12 @@ namespace LME.AnimeON continue; string rawTitle = item.TryGetProperty("title", out var titleProp) ? titleProp.GetString() : null; - streams.Add((BuildDisplayTitle(rawTitle, file, index), file)); + streams.Add(new AshdiStream + { + Title = BuildDisplayTitle(rawTitle, file, index), + Link = file, + Subtitles = ApnHelper.ParseSubtitles(item.TryGetProperty("subtitle", out var subtitleProp) ? subtitleProp.GetString() : null) + }); index++; } @@ -247,7 +260,12 @@ namespace LME.AnimeON if (match.Success) { string file = match.Groups[1].Value; - streams.Add((BuildDisplayTitle("Основне джерело", file, 1), file)); + streams.Add(new AshdiStream + { + Title = BuildDisplayTitle("Основне джерело", file, 1), + Link = file, + Subtitles = ApnHelper.ParseSubtitles(ApnHelper.ExtractPlayerSubtitle(html)) + }); } } catch (Exception ex) diff --git a/LME.AnimeON/Controller.cs b/LME.AnimeON/Controller.cs index cc0f53f..1da69be 100644 --- a/LME.AnimeON/Controller.cs +++ b/LME.AnimeON/Controller.cs @@ -247,14 +247,17 @@ namespace LME.AnimeON.Controllers if (streamLink.Contains("ashdi.vip/vod", StringComparison.OrdinalIgnoreCase)) { var ashdiStreams = await invoke.ParseAshdiPageStreams(streamLink); - if (ashdiStreams != null && ashdiStreams.Count > 0) + if (ashdiStreams != null && ashdiStreams.Count > 0) + { + foreach (var ashdiStream in ashdiStreams) { - foreach (var ashdiStream in ashdiStreams) - { - string optionName = $"{translationName} {ashdiStream.title}"; - string callUrl = $"{host}/lite/lme_animeon/play?url={HttpUtility.UrlEncode(ashdiStream.link)}"; - tpl.Append(optionName, accsArgs(callUrl), "call"); - } + string optionName = $"{translationName} {ashdiStream.Title}"; + string subtitlesParam = ashdiStream.Subtitles != null ? $"&subtitles={HttpUtility.UrlEncode(JsonSerializer.Serialize(ashdiStream.Subtitles.ToObject()))}" : string.Empty; + string callUrl = $"{host}/lite/lme_animeon/play?url={HttpUtility.UrlEncode(ashdiStream.Link)}{subtitlesParam}"; + movieTpl.Append(optionName, accsArgs(callUrl), "call"); + } + } + continue; } } @@ -375,7 +378,7 @@ namespace LME.AnimeON.Controllers } [HttpGet("lite/lme_animeon/play")] - public async Task Play(string url, int episode_id = 0, string title = null, int serial = 0) + public async Task Play(string url, int episode_id = 0, string title = null, int serial = 0, string subtitles = null) { await UpdateService.ConnectAsync(host); @@ -421,10 +424,25 @@ namespace LME.AnimeON.Controllers forceProxy = true; } + SubtitleTpl subtitleTpl = null; + if (!string.IsNullOrEmpty(subtitles)) + { + try + { + var subtitleDtos = JsonSerializer.Deserialize>(subtitles); + if (subtitleDtos != null && subtitleDtos.Count > 0) + { + subtitleTpl = new SubtitleTpl(subtitleDtos.Count); + foreach (var sub in subtitleDtos) + subtitleTpl.Append(sub.label, sub.url); + } + } + catch { } + } + string streamUrl = BuildStreamUrl(init, streamLink, streamHeaders, forceProxy); - string jsonResult = $"{{\"method\":\"play\",\"url\":\"{streamUrl}\",\"title\":\"{title ?? string.Empty}\"}}"; OnLog("AnimeON Play: return call JSON"); - return UpdateService.Validate(Content(jsonResult, "application/json; charset=utf-8")); + return UpdateService.Validate(Content(VideoTpl.ToJson("play", streamUrl, title ?? string.Empty, subtitles: subtitleTpl), "application/json; charset=utf-8")); } private static string StripLampacArgs(string url) diff --git a/LME.KlonFUN/Controller.cs b/LME.KlonFUN/Controller.cs index 635bdc2..5783cdc 100644 --- a/LME.KlonFUN/Controller.cs +++ b/LME.KlonFUN/Controller.cs @@ -166,7 +166,7 @@ namespace LME.KlonFUN.Controllers : $"Серія {episode.Number}"; string streamUrl = BuildStreamUrl(init, episode.Link); - episodeTpl.Append(episodeTitle, contentTitle, s.ToString(), episode.Number.ToString("D2"), streamUrl); + episodeTpl.Append(episodeTitle, contentTitle, s.ToString(), episode.Number.ToString("D2"), streamUrl, subtitles: episode.Subtitles); } episodeTpl.Append(voiceTpl); @@ -190,7 +190,7 @@ namespace LME.KlonFUN.Controllers : $"Варіант {i + 1}"; string streamUrl = BuildStreamUrl(init, stream.Link); - movieTpl.Append(label, streamUrl); + movieTpl.Append(label, streamUrl, subtitles: stream.Subtitles); } return rjson diff --git a/LME.KlonFUN/KlonFUNInvoke.cs b/LME.KlonFUN/KlonFUNInvoke.cs index 58814d9..db74816 100644 --- a/LME.KlonFUN/KlonFUNInvoke.cs +++ b/LME.KlonFUN/KlonFUNInvoke.cs @@ -206,7 +206,8 @@ namespace LME.KlonFUN streams.Add(new MovieStream { Title = voiceTitle, - Link = link + Link = link, + Subtitles = ApnHelper.ParseSubtitles(item.Value("subtitle")) }); index++; @@ -221,7 +222,8 @@ namespace LME.KlonFUN streams.Add(new MovieStream { Title = FormatMovieTitle("Основне джерело", directMatch.Groups["url"].Value, 1), - Link = directMatch.Groups["url"].Value + Link = directMatch.Groups["url"].Value, + Subtitles = ApnHelper.ParseSubtitles(ApnHelper.ExtractPlayerSubtitle(playerHtml)) }); } } @@ -310,7 +312,8 @@ namespace LME.KlonFUN { Number = episodeNumber, Title = string.IsNullOrWhiteSpace(episodeTitle) ? $"Серія {episodeNumber}" : episodeTitle, - Link = link + Link = link, + Subtitles = ApnHelper.ParseSubtitles(episodeObj.Value("subtitle")) }); episodeFallback++; diff --git a/LME.KlonFUN/Models/KlonFUNModels.cs b/LME.KlonFUN/Models/KlonFUNModels.cs index 495b95a..a6b14dd 100644 --- a/LME.KlonFUN/Models/KlonFUNModels.cs +++ b/LME.KlonFUN/Models/KlonFUNModels.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Shared.Models.Templates; namespace LME.KlonFUN.Models { @@ -47,6 +48,7 @@ namespace LME.KlonFUN.Models { public string Title { get; set; } public string Link { get; set; } + public SubtitleTpl Subtitles { get; set; } } public class SerialEpisode @@ -54,6 +56,7 @@ namespace LME.KlonFUN.Models public int Number { get; set; } public string Title { get; set; } public string Link { get; set; } + public SubtitleTpl Subtitles { get; set; } } public class SerialVoice diff --git a/LME.Makhno/Controller.cs b/LME.Makhno/Controller.cs index f37a688..928ea5f 100644 --- a/LME.Makhno/Controller.cs +++ b/LME.Makhno/Controller.cs @@ -107,7 +107,7 @@ namespace LME.Makhno if (play) return UpdateService.Validate(Redirect(streamUrl)); - return UpdateService.Validate(Content(VideoTpl.ToJson("play", streamUrl, episodeTitle), "application/json; charset=utf-8")); + return UpdateService.Validate(Content(VideoTpl.ToJson("play", streamUrl, episodeTitle, subtitles: episode.Subtitles), "application/json; charset=utf-8")); } } @@ -150,7 +150,7 @@ namespace LME.Makhno if (play) return UpdateService.Validate(Redirect(streamUrl)); - return UpdateService.Validate(Content(VideoTpl.ToJson("play", streamUrl, title ?? original_title), "application/json; charset=utf-8")); + return UpdateService.Validate(Content(VideoTpl.ToJson("play", streamUrl, title ?? original_title, subtitles: playerData.Subtitles ?? playerData.Movies?.FirstOrDefault(m => m.File == playerData.File)?.Subtitles), "application/json; charset=utf-8")); } private async Task HandleMovie(string playUrl, string imdb_id, string title, string original_title, int year, bool rjson, MakhnoInvoke invoke) @@ -171,7 +171,8 @@ namespace LME.Makhno { File = playerData.File, Title = "Основне джерело", - Quality = "auto" + Quality = "auto", + Subtitles = playerData.Subtitles }); } @@ -189,7 +190,7 @@ namespace LME.Makhno ? stream.Title : $"Варіант {index}"; - tpl.Append(label, BuildStreamUrl(init, stream.File)); + tpl.Append(label, BuildStreamUrl(init, stream.File), subtitles: stream.Subtitles); index++; } @@ -390,13 +391,15 @@ namespace LME.Makhno if (!string.IsNullOrEmpty(episode.File)) { string streamUrl = BuildStreamUrl(init, episode.File); - episode_tpl.Append( - episode.Title, - title ?? original_title, - requestedSeason.ToString(), - (i + 1).ToString("D2"), - streamUrl - ); + episode_tpl.Append( + episode.Title, + title ?? original_title, + requestedSeason.ToString(), + (i + 1).ToString("D2"), + streamUrl, + subtitles: episode.Subtitles + ); + } } } diff --git a/LME.Makhno/MakhnoInvoke.cs b/LME.Makhno/MakhnoInvoke.cs index 6bd3fe0..986ddea 100644 --- a/LME.Makhno/MakhnoInvoke.cs +++ b/LME.Makhno/MakhnoInvoke.cs @@ -114,18 +114,22 @@ namespace LME.Makhno { string file = fileMatch.Groups[1].Value; var posterMatch = Regex.Match(html, @"poster:[""']([^""']+)[""']", RegexOptions.IgnoreCase); + var subtitles = ApnHelper.ParseSubtitles(ApnHelper.ExtractPlayerSubtitle(html)); + return new PlayerData { File = file, Poster = posterMatch.Success ? posterMatch.Groups[1].Value : null, Voices = new List(), + Subtitles = subtitles, Movies = new List() { new MovieVariant { File = file, Quality = DetectQualityTag(file) ?? "auto", - Title = BuildMovieTitle("Основне джерело", file, 1) + Title = BuildMovieTitle("Основне джерело", file, 1), + Subtitles = subtitles } } }; @@ -238,7 +242,8 @@ namespace LME.Makhno Title = episode["title"]?.ToString(), File = episode["file"]?.ToString(), Poster = episode["poster"]?.ToString(), - Subtitle = episode["subtitle"]?.ToString() + Subtitle = episode["subtitle"]?.ToString(), + Subtitles = ApnHelper.ParseSubtitles(episode["subtitle"]?.ToString()) }); } } @@ -289,7 +294,8 @@ namespace LME.Makhno { File = file, Quality = DetectQualityTag($"{rawTitle} {file}") ?? "auto", - Title = BuildMovieTitle(rawTitle, file, index) + Title = BuildMovieTitle(rawTitle, file, index), + Subtitles = ApnHelper.ParseSubtitles(item["subtitle"]?.ToString()) }); index++; } diff --git a/LME.Makhno/Models/MakhnoModels.cs b/LME.Makhno/Models/MakhnoModels.cs index 15c26e1..2022192 100644 --- a/LME.Makhno/Models/MakhnoModels.cs +++ b/LME.Makhno/Models/MakhnoModels.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Shared.Models.Templates; namespace LME.Makhno.Models { @@ -9,6 +10,7 @@ namespace LME.Makhno.Models public List Voices { get; set; } public List Seasons { get; set; } public List Movies { get; set; } + public SubtitleTpl Subtitles { get; set; } } public class Voice @@ -30,6 +32,7 @@ namespace LME.Makhno.Models public string Id { get; set; } public string Poster { get; set; } public string Subtitle { get; set; } + public SubtitleTpl Subtitles { get; set; } } public class MovieVariant @@ -37,5 +40,6 @@ namespace LME.Makhno.Models public string Title { get; set; } public string File { get; set; } public string Quality { get; set; } + public SubtitleTpl Subtitles { get; set; } } } diff --git a/LME.Mikai/Controller.cs b/LME.Mikai/Controller.cs index 2f09dea..1275579 100644 --- a/LME.Mikai/Controller.cs +++ b/LME.Mikai/Controller.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; using System.Threading.Tasks; using System.Web; using Microsoft.AspNetCore.Mvc; @@ -177,10 +178,13 @@ namespace LME.Mikai.Controllers { foreach (var ashdiStream in ashdiStreams) { - string optionName = $"{voice.DisplayName} {ashdiStream.title}"; - string ashdiCallUrl = $"{host}/lite/lme_mikai/play?url={HttpUtility.UrlEncode(ashdiStream.link)}&title={HttpUtility.UrlEncode(displayTitle)}"; + string optionName = $"{voice.DisplayName} {ashdiStream.Title}"; + string subtitlesParam = ashdiStream.Subtitles != null ? $"&subtitles={HttpUtility.UrlEncode(JsonSerializer.Serialize(ashdiStream.Subtitles.ToObject()))}" : string.Empty; + string ashdiCallUrl = $"{host}/lite/lme_mikai/play?url={HttpUtility.UrlEncode(ashdiStream.Link)}&title={HttpUtility.UrlEncode(displayTitle)}{subtitlesParam}"; movieTpl.Append(optionName, accsArgs(ashdiCallUrl), "call"); } + } + continue; } } @@ -204,7 +208,7 @@ namespace LME.Mikai.Controllers } [HttpGet("lite/lme_mikai/play")] - public async Task Play(string url, string title = null, int serial = 0) + public async Task Play(string url, string title = null, int serial = 0, string subtitles = null) { await UpdateService.ConnectAsync(host); @@ -235,9 +239,24 @@ namespace LME.Mikai.Controllers forceProxy = true; } + SubtitleTpl subtitleTpl = null; + if (!string.IsNullOrEmpty(subtitles)) + { + try + { + var subtitleDtos = JsonSerializer.Deserialize>(subtitles); + if (subtitleDtos != null && subtitleDtos.Count > 0) + { + subtitleTpl = new SubtitleTpl(subtitleDtos.Count); + foreach (var sub in subtitleDtos) + subtitleTpl.Append(sub.label, sub.url); + } + } + catch { } + } + string streamUrl = BuildStreamUrl(init, streamLink, streamHeaders, forceProxy); - string jsonResult = $"{{\"method\":\"play\",\"url\":\"{streamUrl}\",\"title\":\"{title ?? string.Empty}\"}}"; - return UpdateService.Validate(Content(jsonResult, "application/json; charset=utf-8")); + return UpdateService.Validate(Content(VideoTpl.ToJson("play", streamUrl, title ?? string.Empty, subtitles: subtitleTpl), "application/json; charset=utf-8")); } private async Task> CollectSeasonDetails(MikaiAnime details, MikaiInvoke invoke) diff --git a/LME.Mikai/MikaiInvoke.cs b/LME.Mikai/MikaiInvoke.cs index 17dd47b..ea44c6f 100644 --- a/LME.Mikai/MikaiInvoke.cs +++ b/LME.Mikai/MikaiInvoke.cs @@ -11,9 +11,17 @@ using Shared; using Shared.Engine; using Shared.Models; using Shared.Models.Online.Settings; +using Shared.Models.Templates; namespace LME.Mikai { + public class AshdiStream + { + public string Title { get; set; } + public string Link { get; set; } + public SubtitleTpl Subtitles { get; set; } + } + public class MikaiInvoke { private static readonly Regex Quality4kRegex = new Regex(@"(^|[^0-9])(2160p?)([^0-9]|$)|\b4k\b|\buhd\b", RegexOptions.IgnoreCase); @@ -176,12 +184,12 @@ namespace LME.Mikai public async Task ParseAshdiPage(string url, bool disableAshdiMultivoiceForVod = false) { var streams = await ParseAshdiPageStreams(url, disableAshdiMultivoiceForVod); - return streams?.FirstOrDefault().link; + return streams?.FirstOrDefault()?.Link; } - public async Task> ParseAshdiPageStreams(string url, bool disableAshdiMultivoiceForVod = false) + public async Task> ParseAshdiPageStreams(string url, bool disableAshdiMultivoiceForVod = false) { - var streams = new List<(string title, string link)>(); + var streams = new List(); try { var headers = new List() @@ -218,7 +226,12 @@ namespace LME.Mikai continue; string rawTitle = item.TryGetProperty("title", out var titleProp) ? titleProp.GetString() : null; - streams.Add((BuildDisplayTitle(rawTitle, file, index), file)); + streams.Add(new AshdiStream + { + Title = BuildDisplayTitle(rawTitle, file, index), + Link = file, + Subtitles = ApnHelper.ParseSubtitles(item.TryGetProperty("subtitle", out var subtitleProp) ? subtitleProp.GetString() : null) + }); index++; } @@ -231,7 +244,12 @@ namespace LME.Mikai if (match.Success) { string file = match.Groups[1].Value; - streams.Add((BuildDisplayTitle("Основне джерело", file, 1), file)); + streams.Add(new AshdiStream + { + Title = BuildDisplayTitle("Основне джерело", file, 1), + Link = file, + Subtitles = ApnHelper.ParseSubtitles(ApnHelper.ExtractPlayerSubtitle(html)) + }); } } catch (Exception ex) diff --git a/LME.Shared/Apn/ApnHelper.cs b/LME.Shared/Apn/ApnHelper.cs index 7b1fb96..82366d7 100644 --- a/LME.Shared/Apn/ApnHelper.cs +++ b/LME.Shared/Apn/ApnHelper.cs @@ -1,6 +1,9 @@ using Newtonsoft.Json.Linq; using Shared.Models.Base; +using Shared.Models.Templates; using System; +using System.Net; +using System.Text.RegularExpressions; using System.Web; namespace Shared.Engine @@ -9,6 +12,8 @@ namespace Shared.Engine { public const string DefaultHost = "https://tut.im/proxy.php?url={encodeurl}"; + private static readonly Regex SubtitleLineRegex = new Regex(@"\[([^\]]+)\]([^,]+)", RegexOptions.Compiled); + public static bool TryGetInitConf(JObject conf, out bool enabled, out string host) { enabled = false; @@ -120,6 +125,40 @@ namespace Shared.Engine return $"{host.TrimEnd('/')}/{url}"; } + public static SubtitleTpl ParseSubtitles(string subtitleValue) + { + if (string.IsNullOrWhiteSpace(subtitleValue)) + return null; + + var subtitles = new SubtitleTpl(); + string normalized = WebUtility.HtmlDecode(subtitleValue) + .Replace("\\/", "/") + .Replace("\\'", "'") + .Replace("\\\"", "\""); + + foreach (Match match in SubtitleLineRegex.Matches(normalized)) + { + string label = WebUtility.HtmlDecode(match.Groups[1].Value).Trim(); + string url = WebUtility.HtmlDecode(match.Groups[2].Value).Trim(); + if (!string.IsNullOrWhiteSpace(label) && !string.IsNullOrWhiteSpace(url)) + subtitles.Append(label, url); + } + + return subtitles.IsEmpty ? null : subtitles; + } + + public static string ExtractPlayerSubtitle(string html) + { + if (string.IsNullOrWhiteSpace(html)) + return null; + + var match = Regex.Match(html, @"subtitle\s*:\s*['""]([^'""']+)['""]", RegexOptions.IgnoreCase); + if (!match.Success) + match = Regex.Match(html, @"subtitle['""]?\s*:\s*['""]([^'""']+)['""]", RegexOptions.IgnoreCase); + + return match.Success ? match.Groups[1].Value : null; + } + private static string NormalizeHost(string host) { if (string.IsNullOrWhiteSpace(host)) diff --git a/LME.Uaflix/Controller.cs b/LME.Uaflix/Controller.cs index ecc9048..065bb5d 100644 --- a/LME.Uaflix/Controller.cs +++ b/LME.Uaflix/Controller.cs @@ -104,9 +104,10 @@ namespace LME.Uaflix.Controllers { // Повертаємо JSON з інформацією про стрім для методу 'play' string streamUrl = BuildStreamUrl(init, playResult.streams.First().link); - string jsonResult = $"{{\"method\":\"play\",\"url\":\"{streamUrl}\",\"title\":\"{title ?? original_title}\"}}"; + var subtitles = playResult.subtitles ?? playResult.streams.FirstOrDefault(s => s.subtitles != null)?.subtitles; + OnLog($"=== RETURN: call method JSON for episode_url ==="); - return UpdateService.Validate(Content(jsonResult, "application/json; charset=utf-8")); + return UpdateService.Validate(Content(VideoTpl.ToJson("play", streamUrl, title ?? original_title, subtitles: subtitles), "application/json; charset=utf-8")); } OnLog("=== RETURN: call method no streams ==="); @@ -284,7 +285,8 @@ namespace LME.Uaflix.Controllers e: ep.Number.ToString(), link: accsArgs(callUrl), method: "call", - streamlink: accsArgs($"{callUrl}&play=true") + streamlink: accsArgs($"{callUrl}&play=true"), + subtitles: ApnHelper.ParseSubtitles(ep.Subtitle) ); } else @@ -296,7 +298,8 @@ namespace LME.Uaflix.Controllers title: title, s: s.ToString(), e: ep.Number.ToString(), - link: playUrl + link: playUrl, + subtitles: ApnHelper.ParseSubtitles(ep.Subtitle) ); } @@ -351,7 +354,7 @@ namespace LME.Uaflix.Controllers ? stream.title : $"Варіант {index}"; - tpl.Append(label, BuildStreamUrl(init, stream.link)); + tpl.Append(label, BuildStreamUrl(init, stream.link), subtitles: stream.subtitles ?? playResult.subtitles); index++; } diff --git a/LME.Uaflix/Models/PlayResult.cs b/LME.Uaflix/Models/PlayResult.cs index 30541e8..0662266 100644 --- a/LME.Uaflix/Models/PlayResult.cs +++ b/LME.Uaflix/Models/PlayResult.cs @@ -15,5 +15,6 @@ namespace LME.Uaflix.Models public string link { get; set; } public string quality { get; set; } public string title { get; set; } + public SubtitleTpl? subtitles { get; set; } } } diff --git a/LME.Uaflix/UaflixInvoke.cs b/LME.Uaflix/UaflixInvoke.cs index 8fc0ffe..d50bf59 100644 --- a/LME.Uaflix/UaflixInvoke.cs +++ b/LME.Uaflix/UaflixInvoke.cs @@ -582,7 +582,8 @@ namespace LME.Uaflix { link = fileUrl, quality = DetectQualityTag($"{rawTitle} {fileUrl}") ?? "auto", - title = BuildDisplayTitle(rawTitle, fileUrl, index) + title = BuildDisplayTitle(rawTitle, fileUrl, index), + subtitles = ApnHelper.ParseSubtitles(item?["subtitle"]?.ToString()) }); index++; } @@ -605,7 +606,8 @@ namespace LME.Uaflix { link = fallbackFile, quality = DetectQualityTag(fallbackFile) ?? "auto", - title = BuildDisplayTitle(ExtractVoiceFromUrl(fallbackFile), fallbackFile, 1) + title = BuildDisplayTitle(ExtractVoiceFromUrl(fallbackFile), fallbackFile, 1), + subtitles = ApnHelper.ParseSubtitles(ApnHelper.ExtractPlayerSubtitle(html)) }); return result; @@ -2050,19 +2052,7 @@ namespace LME.Uaflix string url = $"https://ashdi.vip/vod/{id}"; var html = await GetHtml(AshdiRequestUrl(url), new List() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", "https://ashdi.vip/") }); string subtitle = new Regex("subtitle(\")?:\"([^\"]+)\"").Match(html).Groups[2].Value; - if (!string.IsNullOrEmpty(subtitle)) - { - var match = new Regex("\\[([^\\]]+)\\](https?://[^\\,]+)").Match(subtitle); - var st = new Shared.Models.Templates.SubtitleTpl(); - while (match.Success) - { - st.Append(match.Groups[1].Value, match.Groups[2].Value); - match = match.NextMatch(); - } - if (st.data != null && st.data.Count > 0) - return st; - } - return null; + return ApnHelper.ParseSubtitles(subtitle); } private static string WithAshdiMultivoice(string url)