diff --git a/LME.UAKino/Controller.cs b/LME.UAKino/Controller.cs
index 41b9ed7..ef65682 100644
--- a/LME.UAKino/Controller.cs
+++ b/LME.UAKino/Controller.cs
@@ -118,9 +118,8 @@ namespace LME.UAKino.Controllers
}
else
{
- if (ApnHelper.IsAshdiUrl(fallbackUrl) && !fallbackUrl.Contains("multivoice"))
- fallbackUrl += (fallbackUrl.Contains("?") ? "&" : "?") + "multivoice";
- string streamUrl = BuildStreamUrl(init, fallbackUrl);
+ string resolvedUrl = await invoke.ResolveAshdiVod(fallbackUrl);
+ string streamUrl = BuildStreamUrl(init, resolvedUrl);
var movie_tpl = new MovieTpl(title, original_title);
movie_tpl.Append("Фільм", streamUrl);
return rjson
@@ -137,7 +136,7 @@ namespace LME.UAKino.Controllers
}
else
{
- return HandleMovie(init, voices, title, original_title, rjson);
+ return await HandleMovie(init, voices, title, original_title, rjson, invoke);
}
}
@@ -192,12 +191,13 @@ namespace LME.UAKino.Controllers
if (selected == null || selected.Episodes.Count == 0)
return OnError("lme_uakino", refresh_proxy: true);
+ string seasonStr = s >= 0 ? s.ToString() : "1";
foreach (var ep in selected.Episodes.OrderBy(e => e.EpisodeNumber ?? int.MaxValue))
{
int epNum = ep.EpisodeNumber ?? 1;
string epName = string.IsNullOrEmpty(ep.Title) ? $"Епізод {epNum}" : ep.Title;
string streamUrl = BuildStreamUrl(init, ep.FileUrl);
- episode_tpl.Append(epName, title ?? original_title, "1", epNum.ToString("D2"), streamUrl);
+ episode_tpl.Append(epName, title ?? original_title, seasonStr, epNum.ToString("D2"), streamUrl);
}
episode_tpl.Append(voice_tpl);
@@ -208,8 +208,9 @@ namespace LME.UAKino.Controllers
}
/// Фільм: список стрімів
- private ActionResult HandleMovie(OnlinesSettings init, List voices, string title, string original_title, bool rjson)
+ private async Task HandleMovie(OnlinesSettings init, List voices, string title, string original_title, bool rjson, UAKinoInvoke invoke)
{
+ var processed = new HashSet();
var movie_tpl = new MovieTpl(title, original_title);
foreach (var voice in voices)
@@ -221,10 +222,13 @@ namespace LME.UAKino.Controllers
label = ep.Title;
string fileUrl = ep.FileUrl;
- if (ApnHelper.IsAshdiUrl(fileUrl) && !fileUrl.Contains("multivoice"))
- fileUrl += (fileUrl.Contains("?") ? "&" : "?") + "multivoice";
+ // Резолвимо Ashdi VOD — отримуємо реальний .m3u8 стрім
+ string resolvedUrl = await invoke.ResolveAshdiVod(fileUrl);
+ // Дедуплікація: якщо той самий стрім — пропускаємо
+ if (!processed.Add(resolvedUrl))
+ continue;
- string streamUrl = BuildStreamUrl(init, fileUrl);
+ string streamUrl = BuildStreamUrl(init, resolvedUrl);
movie_tpl.Append(label, streamUrl);
}
}
diff --git a/LME.UAKino/UAKinoInvoke.cs b/LME.UAKino/UAKinoInvoke.cs
index e8cb8da..22a553e 100644
--- a/LME.UAKino/UAKinoInvoke.cs
+++ b/LME.UAKino/UAKinoInvoke.cs
@@ -193,6 +193,71 @@ namespace LME.UAKino
}
}
+ ///
+ /// Резолв Ashdi VOD сторінки: отримати реальний .m3u8 стрім з Playerjs file:'...'
+ ///
+ public async Task ResolveAshdiVod(string vodUrl)
+ {
+ if (string.IsNullOrEmpty(vodUrl) || !ApnHelper.IsAshdiUrl(vodUrl))
+ return vodUrl;
+
+ try
+ {
+ string fetchUrl = vodUrl;
+ if (!fetchUrl.Contains("multivoice"))
+ fetchUrl += (fetchUrl.Contains("?") ? "&" : "?") + "multivoice";
+
+ _onLog?.Invoke($"UAKino resolve Ashdi: {fetchUrl}");
+
+ var headers = new List()
+ {
+ new HeadersModel("User-Agent", Http.UserAgent),
+ new HeadersModel("Referer", "https://ashdi.vip/")
+ };
+
+ if (ApnHelper.IsEnabled(_init) && string.IsNullOrWhiteSpace(_init.webcorshost))
+ fetchUrl = ApnHelper.WrapUrl(_init, fetchUrl);
+
+ string html = await HttpGet(fetchUrl, headers);
+ if (string.IsNullOrEmpty(html))
+ return vodUrl;
+
+ // Спершу простий pattern file:'url'
+ var fileMatch = Regex.Match(html, @"file:\s*'([^']+)'", RegexOptions.IgnoreCase);
+ if (!fileMatch.Success)
+ fileMatch = Regex.Match(html, @"file:\s*""([^""]+)""", RegexOptions.IgnoreCase);
+
+ if (fileMatch.Success)
+ {
+ string resolvedUrl = fileMatch.Groups[1].Value;
+ if (!string.IsNullOrEmpty(resolvedUrl) && !resolvedUrl.StartsWith("["))
+ {
+ _onLog?.Invoke($"UAKino resolved Ashdi: {resolvedUrl}");
+ return resolvedUrl;
+ }
+ }
+
+ // Складний плеєр — пробуємо PlayerJsDecoder
+ try
+ {
+ var playerPayload = LME.Common.Playerjs.PlayerJsDecoder.ExtractPlayerPayload(html);
+ if (playerPayload?.FilePayload is string strUrl && !string.IsNullOrEmpty(strUrl))
+ {
+ _onLog?.Invoke($"UAKino resolved Ashdi (PlayerJsDecoder): {strUrl}");
+ return strUrl;
+ }
+ }
+ catch { }
+
+ return vodUrl;
+ }
+ catch (Exception ex)
+ {
+ _onLog?.Invoke($"UAKino resolve Ashdi error: {ex.Message}");
+ return vodUrl;
+ }
+ }
+
///
/// Витягнути news_id з URL контенту
///