mirror of
https://github.com/lampame/lampac-ukraine.git
synced 2026-06-17 12:08:54 +00:00
feat(uaflix): detect missing player and retry movie streams
Add page-status detection for episode parsing so the controller can distinguish between a missing page, an existing page without a player, and a page with available streams. When a movie page exists but no player is present, retry once after a short delay before failing. If the player still is unavailable, return a non-cached error so temporary site state does not get persisted.
This commit is contained in:
parent
444e4c876e
commit
6a398317a4
@ -14,6 +14,7 @@ using System.Text;
|
|||||||
using Shared.Models.Online.Settings;
|
using Shared.Models.Online.Settings;
|
||||||
using Shared.Models;
|
using Shared.Models;
|
||||||
using LME.Uaflix.Models;
|
using LME.Uaflix.Models;
|
||||||
|
using LME.Uaflix;
|
||||||
|
|
||||||
namespace LME.Uaflix.Controllers
|
namespace LME.Uaflix.Controllers
|
||||||
{
|
{
|
||||||
@ -107,7 +108,7 @@ namespace LME.Uaflix.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
OnLog("=== RETURN: play no streams ===");
|
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')
|
// Якщо є episode_url але немає play=true, це виклик для отримання інформації про стрім (для method: 'call')
|
||||||
@ -145,7 +146,7 @@ namespace LME.Uaflix.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
OnLog("=== RETURN: call method no streams ===");
|
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;
|
string filmUrl = href;
|
||||||
@ -370,9 +371,37 @@ namespace LME.Uaflix.Controllers
|
|||||||
}
|
}
|
||||||
else // Фільм
|
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 (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 ===");
|
OnLog("=== RETURN: movie no streams ===");
|
||||||
return OnError("lme_uaflix", refresh_proxy: true);
|
return OnError("lme_uaflix", refresh_proxy: true);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,13 @@ using System.Text;
|
|||||||
|
|
||||||
namespace LME.Uaflix
|
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
|
public class UaflixInvoke
|
||||||
{
|
{
|
||||||
private static readonly Regex Quality4kRegex = new Regex(@"(^|[^0-9])(2160p?)([^0-9]|$)|\b4k\b|\buhd\b", RegexOptions.IgnoreCase);
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse episode with page status detection — distinguishes between
|
||||||
|
/// "page not found", "page exists but no player", and "has streams"
|
||||||
|
/// </summary>
|
||||||
|
public async Task<(PlayResult result, PageStatus status)> ParseEpisodeWithStatus(string url)
|
||||||
|
{
|
||||||
|
var result = new PlayResult() { streams = new List<PlayStream>() };
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var headers = new List<HeadersModel>()
|
||||||
|
{
|
||||||
|
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)
|
private void NormalizeUaflixVoiceNames(SerialAggregatedStructure structure)
|
||||||
{
|
{
|
||||||
const string baseName = "Uaflix";
|
const string baseName = "Uaflix";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user