diff --git a/LME.Uaflix/Controller.cs b/LME.Uaflix/Controller.cs
index 2d6cddc..1a7ddf7 100644
--- a/LME.Uaflix/Controller.cs
+++ b/LME.Uaflix/Controller.cs
@@ -14,6 +14,7 @@ using System.Text;
using Shared.Models.Online.Settings;
using Shared.Models;
using LME.Uaflix.Models;
+using LME.Uaflix;
namespace LME.Uaflix.Controllers
{
@@ -107,7 +108,7 @@ namespace LME.Uaflix.Controllers
}
OnLog("=== RETURN: play no streams ===");
- return OnError("lme_uaflix", refresh_proxy: true);
+ return OnError("lme_uaflix", gbcache: false, refresh_proxy: true);
}
// Якщо є episode_url але немає play=true, це виклик для отримання інформації про стрім (для method: 'call')
@@ -145,7 +146,7 @@ namespace LME.Uaflix.Controllers
}
OnLog("=== RETURN: call method no streams ===");
- return OnError("lme_uaflix", refresh_proxy: true);
+ return OnError("lme_uaflix", gbcache: false, refresh_proxy: true);
}
string filmUrl = href;
@@ -370,9 +371,37 @@ namespace LME.Uaflix.Controllers
}
else // Фільм
{
- var playResult = await invoke.ParseEpisode(filmUrl);
+ var (playResult, pageStatus) = await invoke.ParseEpisodeWithStatus(filmUrl);
+
+ // Retry: якщо сторінка існує, але плеєр ще не додано — чекаємо 3 сек і пробуємо знову
+ if (pageStatus == PageStatus.PageExistsNoPlayer
+ && (playResult?.streams == null || playResult.streams.Count == 0))
+ {
+ OnLog("Movie page exists but no player found, retrying in 3 seconds...");
+ await Task.Delay(3000);
+
+ var retryResult = await invoke.ParseEpisode(filmUrl);
+ if (retryResult?.streams != null && retryResult.streams.Count > 0)
+ {
+ playResult = retryResult;
+ pageStatus = PageStatus.HasStreams;
+ OnLog("Retry successful: streams found after delay");
+ }
+ else
+ {
+ OnLog("Retry: still no streams after delay");
+ }
+ }
+
if (playResult?.streams == null || playResult.streams.Count == 0)
{
+ if (pageStatus == PageStatus.PageExistsNoPlayer)
+ {
+ OnLog("=== RETURN: movie page exists but player temporarily unavailable ===");
+ // gbcache: false — не кешувати помилку, щоб наступні запити не блокувалися
+ return OnError("Плеєр тимчасово недоступний, спробуйте пізніше", gbcache: false, refresh_proxy: false);
+ }
+
OnLog("=== RETURN: movie no streams ===");
return OnError("lme_uaflix", refresh_proxy: true);
}
diff --git a/LME.Uaflix/UaflixInvoke.cs b/LME.Uaflix/UaflixInvoke.cs
index 4a1dd5b..f49d05f 100644
--- a/LME.Uaflix/UaflixInvoke.cs
+++ b/LME.Uaflix/UaflixInvoke.cs
@@ -18,6 +18,13 @@ using System.Text;
namespace LME.Uaflix
{
+ public enum PageStatus
+ {
+ PageNotFound,
+ PageExistsNoPlayer, // Page loaded (HTTP 200) but no playable streams found
+ HasStreams // At least one stream found
+ }
+
public class UaflixInvoke
{
private static readonly Regex Quality4kRegex = new Regex(@"(^|[^0-9])(2160p?)([^0-9]|$)|\b4k\b|\buhd\b", RegexOptions.IgnoreCase);
@@ -2092,6 +2099,133 @@ namespace LME.Uaflix
return result;
}
+ ///
+ /// Parse episode with page status detection — distinguishes between
+ /// "page not found", "page exists but no player", and "has streams"
+ ///
+ public async Task<(PlayResult result, PageStatus status)> ParseEpisodeWithStatus(string url)
+ {
+ var result = new PlayResult() { streams = new List() };
+ try
+ {
+ var headers = new List()
+ {
+ new HeadersModel("User-Agent", "Mozilla/5.0"),
+ new HeadersModel("Referer", _init.host)
+ };
+
+ string html = await GetHtml(url, headers);
+
+ if (string.IsNullOrWhiteSpace(html))
+ {
+ _onLog($"ParseEpisodeWithStatus: Page not found or empty for {url}");
+ return (result, PageStatus.PageNotFound);
+ }
+
+ var doc = new HtmlDocument();
+ doc.LoadHtml(html);
+
+ var videoNode = doc.DocumentNode.SelectSingleNode("//video");
+ if (videoNode != null)
+ {
+ string videoUrl = videoNode.GetAttributeValue("src", "");
+ if (!string.IsNullOrEmpty(videoUrl))
+ {
+ result.streams.Add(new PlayStream
+ {
+ link = videoUrl,
+ quality = "1080p",
+ title = BuildDisplayTitle("Основне джерело", videoUrl, 1)
+ });
+ return (result, PageStatus.HasStreams);
+ }
+ }
+
+ var allIframes = ExtractAllIframeUrls(doc);
+
+ var zetvideoIframes = allIframes
+ .Where(u => u != null && u.Contains("zetvideo.net"))
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToList();
+
+ if (zetvideoIframes.Count > 0)
+ {
+ int streamIndex = 0;
+ foreach (var zetIframe in zetvideoIframes)
+ {
+ var streams = await ParseAllZetvideoSources(zetIframe);
+ if (streams == null || streams.Count == 0)
+ continue;
+
+ foreach (var stream in streams)
+ {
+ string label = streamIndex == 0 ? "Uaflix" : "Оригінал";
+ stream.title = label;
+
+ _onLog($"ParseEpisodeWithStatus: zetvideo stream #{streamIndex + 1}: {label} -> {zetIframe}" +
+ (stream.subtitles?.data?.Count > 0
+ ? " (has subtitles)" : ""));
+ }
+
+ result.streams.AddRange(streams);
+ streamIndex++;
+ }
+
+ if (result.streams.Count > 0)
+ return (result, PageStatus.HasStreams);
+ }
+ else
+ {
+ string iframeUrl = ExtractIframeUrl(doc);
+ if (!string.IsNullOrEmpty(iframeUrl))
+ {
+ if (iframeUrl.Contains("ashdi.vip/serial/"))
+ {
+ result.ashdi_url = iframeUrl;
+ return (result, PageStatus.HasStreams);
+ }
+
+ if (iframeUrl.Contains("youtube.com/embed/"))
+ {
+ _onLog($"ParseEpisodeWithStatus: Only YouTube trailer found on page: {iframeUrl}");
+ return (result, PageStatus.PageExistsNoPlayer);
+ }
+
+ if (iframeUrl.Contains("ashdi.vip"))
+ {
+ if (iframeUrl.Contains("/vod/"))
+ {
+ result.streams = await ParseAshdiVodEpisode(iframeUrl);
+ }
+ else
+ {
+ result.streams = await ParseAllAshdiSources(iframeUrl);
+ var idMatch = Regex.Match(iframeUrl, @"_(\d+)|vod/(\d+)");
+ if (idMatch.Success)
+ {
+ string ashdiId = idMatch.Groups[1].Success ? idMatch.Groups[1].Value : idMatch.Groups[2].Value;
+ result.subtitles = await GetAshdiSubtitles(ashdiId);
+ }
+ }
+
+ if (result.streams.Count > 0)
+ return (result, PageStatus.HasStreams);
+ }
+ }
+ }
+
+ _onLog($"ParseEpisodeWithStatus: Page exists but no playable streams for {url}");
+ return (result, PageStatus.PageExistsNoPlayer);
+ }
+ catch (Exception ex)
+ {
+ _onLog($"ParseEpisodeWithStatus error: {ex.Message}");
+ }
+
+ _onLog($"ParseEpisodeWithStatus result: streams.count={result.streams.Count}, ashdi_url={result.ashdi_url}");
+ return (result, result.streams.Count > 0 ? PageStatus.HasStreams : PageStatus.PageNotFound);
+ }
+
private void NormalizeUaflixVoiceNames(SerialAggregatedStructure structure)
{
const string baseName = "Uaflix";