From 80f086940172d06925cbc7a0fc39bb6541188a33 Mon Sep 17 00:00:00 2001 From: Felix Date: Fri, 15 May 2026 19:06:29 +0300 Subject: [PATCH] fix(uakino): add HTML page fallback when playlist API returns no data Handle cases where UAKino playlist requests return empty results by resolving stream URLs directly from the content page HTML. Add a fallback parser that extracts video sources from `link[itemprop=video]` or `iframe#pre`, and use it to return playable movie or single-episode responses instead of failing immediately. Ensure Ashdi links consistently include `multivoice` for movie playback, including episode file URLs, to improve stream compatibility. --- LME.UAKino/Controller.cs | 36 ++++++++++++++++++++++++- LME.UAKino/UAKinoInvoke.cs | 54 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/LME.UAKino/Controller.cs b/LME.UAKino/Controller.cs index afa10e5..41b9ed7 100644 --- a/LME.UAKino/Controller.cs +++ b/LME.UAKino/Controller.cs @@ -99,7 +99,37 @@ namespace LME.UAKino.Controllers var voices = await invoke.GetPlaylist(newsId); if (voices == null || voices.Count == 0) + { + // Fallback: playlist API повернув ERR_NOT_DATA — пробуємо зі сторінки + string fallbackUrl = await invoke.GetPageFallbackUrl(itemUrl); + if (!string.IsNullOrEmpty(fallbackUrl)) + { + if (serial == 1) + { + var voice_tpl = new VoiceTpl(); + var episode_tpl = new EpisodeTpl(); + string streamUrl = BuildStreamUrl(init, fallbackUrl); + voice_tpl.Append("Озвучення", true, null); + episode_tpl.Append("Епізод 1", title ?? original_title, s >= 0 ? s.ToString() : "1", "01", streamUrl); + episode_tpl.Append(voice_tpl); + return rjson + ? Content(episode_tpl.ToJson(), "application/json; charset=utf-8") + : Content(episode_tpl.ToHtml(), "text/html; charset=utf-8"); + } + else + { + if (ApnHelper.IsAshdiUrl(fallbackUrl) && !fallbackUrl.Contains("multivoice")) + fallbackUrl += (fallbackUrl.Contains("?") ? "&" : "?") + "multivoice"; + string streamUrl = BuildStreamUrl(init, fallbackUrl); + var movie_tpl = new MovieTpl(title, original_title); + movie_tpl.Append("Фільм", streamUrl); + return rjson + ? Content(movie_tpl.ToJson(), "application/json; charset=utf-8") + : Content(movie_tpl.ToHtml(), "text/html; charset=utf-8"); + } + } return OnError("lme_uakino", refresh_proxy: true); + } if (serial == 1) { @@ -190,7 +220,11 @@ namespace LME.UAKino.Controllers if (voices.Count == 1 && voice.Episodes.Count > 1) label = ep.Title; - string streamUrl = BuildStreamUrl(init, ep.FileUrl); + string fileUrl = ep.FileUrl; + if (ApnHelper.IsAshdiUrl(fileUrl) && !fileUrl.Contains("multivoice")) + fileUrl += (fileUrl.Contains("?") ? "&" : "?") + "multivoice"; + + string streamUrl = BuildStreamUrl(init, fileUrl); movie_tpl.Append(label, streamUrl); } } diff --git a/LME.UAKino/UAKinoInvoke.cs b/LME.UAKino/UAKinoInvoke.cs index ef4d760..e8cb8da 100644 --- a/LME.UAKino/UAKinoInvoke.cs +++ b/LME.UAKino/UAKinoInvoke.cs @@ -139,6 +139,60 @@ namespace LME.UAKino } } + /// + /// Fallback: отримати стрім з HTML сторінки фільму коли playlist API недоступний + /// Парсить <link itemprop="video" value="..."> або <iframe id="pre" src="..."> + /// + public async Task GetPageFallbackUrl(string pageUrl) + { + if (string.IsNullOrEmpty(pageUrl)) + return null; + + try + { + _onLog?.Invoke($"UAKino page fallback: {pageUrl}"); + + var headers = new List() + { + new HeadersModel("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.3 Safari/605.1.15"), + new HeadersModel("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"), + new HeadersModel("Referer", _init.host) + }; + + string html = await HttpGet(pageUrl, headers); + if (string.IsNullOrEmpty(html)) + return null; + + var doc = new HtmlDocument(); + doc.LoadHtml(html); + + // Спершу пробуємо + var linkTag = doc.DocumentNode.SelectSingleNode("//link[@itemprop='video']"); + if (linkTag != null) + { + string value = linkTag.GetAttributeValue("value", ""); + if (!string.IsNullOrEmpty(value)) + return NormalizeUrl(value); + } + + // Fallback до