diff --git a/Uaflix/Controller.cs b/Uaflix/Controller.cs
index 7b55763..8068705 100644
--- a/Uaflix/Controller.cs
+++ b/Uaflix/Controller.cs
@@ -79,7 +79,10 @@ namespace Uaflix.Controllers
if (play)
{
- var playResult = await invoke.ParseEpisode(t);
+ // Визначаємо URL для парсингу - або з параметра t, або з episode_url
+ string urlToParse = !string.IsNullOrEmpty(t) ? t : Request.Query["episode_url"];
+
+ var playResult = await invoke.ParseEpisode(urlToParse);
if (playResult.streams != null && playResult.streams.Count > 0)
{
OnLog("=== RETURN: play redirect ===");
@@ -89,6 +92,24 @@ namespace Uaflix.Controllers
OnLog("=== RETURN: play no streams ===");
return Content("Uaflix", "text/html; charset=utf-8");
}
+
+ // Якщо є episode_url але немає play=true, це виклик для отримання інформації про стрім (для method: 'call')
+ string episodeUrl = Request.Query["episode_url"];
+ if (!string.IsNullOrEmpty(episodeUrl))
+ {
+ var playResult = await invoke.ParseEpisode(episodeUrl);
+ if (playResult.streams != null && playResult.streams.Count > 0)
+ {
+ // Повертаємо JSON з інформацією про стрім для методу 'play'
+ string streamUrl = HostStreamProxy(init, accsArgs(playResult.streams.First().link));
+ string jsonResult = $"{{\"method\":\"play\",\"url\":\"{streamUrl}\",\"title\":\"{title ?? original_title}\"}}";
+ OnLog($"=== RETURN: call method JSON for episode_url ===");
+ return Content(jsonResult, "application/json; charset=utf-8");
+ }
+
+ OnLog("=== RETURN: call method no streams ===");
+ return Content("Uaflix", "text/html; charset=utf-8");
+ }
string filmUrl = href;
@@ -251,23 +272,19 @@ namespace Uaflix.Controllers
// Для ashdi/zetvideo-serial повертаємо готове посилання з play
var voice = structure.Voices[t];
- if (voice.PlayerType == "zetvideo-vod")
+ if (voice.PlayerType == "zetvideo-vod" || voice.PlayerType == "ashdi-vod")
{
- // Знайти URL епізоду зі структури AllEpisodes
- var episodeInfo = structure.AllEpisodes
- .FirstOrDefault(e => e.season == s && e.episode == ep.Number);
-
- if (episodeInfo != null)
- {
- string callUrl = $"{host}/uaflix?t={HttpUtility.UrlEncode(episodeInfo.url)}&play=true";
- episode_tpl.Append(
- name: ep.Title,
- title: title,
- s: s.ToString(),
- e: ep.Number.ToString(),
- link: accsArgs(callUrl)
- );
- }
+ // Для zetvideo-vod та ashdi-vod використовуємо URL епізоду для виклику
+ // Потрібно передати URL епізоду в інший параметр, щоб не плутати з play=true
+ string callUrl = $"{host}/uaflix?episode_url={HttpUtility.UrlEncode(ep.File)}&imdb_id={imdb_id}&kinopoisk_id={kinopoisk_id}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&year={year}&serial={serial}&s={s}&e={ep.Number}";
+ episode_tpl.Append(
+ name: ep.Title,
+ title: title,
+ s: s.ToString(),
+ e: ep.Number.ToString(),
+ link: accsArgs(callUrl),
+ method: "call"
+ );
}
else
{
diff --git a/Uaflix/Models/EpisodeLinkInfo.cs b/Uaflix/Models/EpisodeLinkInfo.cs
index 9b4adb0..2180106 100644
--- a/Uaflix/Models/EpisodeLinkInfo.cs
+++ b/Uaflix/Models/EpisodeLinkInfo.cs
@@ -10,7 +10,7 @@ namespace Uaflix.Models
public int episode { get; set; }
// Нові поля для підтримки змішаних плеєрів
- public string playerType { get; set; } // "ashdi-serial", "zetvideo-serial", "zetvideo-vod"
+ public string playerType { get; set; } // "ashdi-serial", "zetvideo-serial", "zetvideo-vod", "ashdi-vod"
public string iframeUrl { get; set; } // URL iframe для цього епізоду
}
}
\ No newline at end of file
diff --git a/Uaflix/Models/SerialAggregatedStructure.cs b/Uaflix/Models/SerialAggregatedStructure.cs
index ef2d770..d0deede 100644
--- a/Uaflix/Models/SerialAggregatedStructure.cs
+++ b/Uaflix/Models/SerialAggregatedStructure.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Uaflix.Models
{
///
- /// Агрегована структура серіалу з озвучками з усіх джерел (ashdi, zetvideo-serial, zetvideo-vod)
+ /// Агрегована структура серіалу з озвучками з усіх джерел (ashdi, zetvideo-serial, zetvideo-vod, ashdi-vod)
///
public class SerialAggregatedStructure
{
diff --git a/Uaflix/Models/VoiceInfo.cs b/Uaflix/Models/VoiceInfo.cs
index 5a52735..2e6a474 100644
--- a/Uaflix/Models/VoiceInfo.cs
+++ b/Uaflix/Models/VoiceInfo.cs
@@ -13,7 +13,7 @@ namespace Uaflix.Models
public string Name { get; set; }
///
- /// Тип плеєра: "ashdi-serial", "zetvideo-serial", "zetvideo-vod"
+ /// Тип плеєра: "ashdi-serial", "zetvideo-serial", "zetvideo-vod", "ashdi-vod"
///
public string PlayerType { get; set; }
diff --git a/Uaflix/UaflixInvoke.cs b/Uaflix/UaflixInvoke.cs
index 9df46ad..7c25c98 100644
--- a/Uaflix/UaflixInvoke.cs
+++ b/Uaflix/UaflixInvoke.cs
@@ -45,6 +45,8 @@ namespace Uaflix
// Перевіряємо на підтримувані типи плеєрів
if (iframeUrl.Contains("ashdi.vip/serial/"))
return "ashdi-serial";
+ else if (iframeUrl.Contains("ashdi.vip/vod/"))
+ return "ashdi-vod";
else if (iframeUrl.Contains("zetvideo.net/serial/"))
return "zetvideo-serial";
else if (iframeUrl.Contains("zetvideo.net/vod/"))
@@ -204,6 +206,46 @@ namespace Uaflix
}
}
+ ///
+ /// Парсинг одного епізоду з ashdi-vod (новий метод для обробки окремих епізодів з ashdi.vip/vod/)
+ ///
+ private async Task<(string file, string voiceName)> ParseAshdiVodEpisode(string iframeUrl)
+ {
+ var headers = new List()
+ {
+ new HeadersModel("User-Agent", "Mozilla/5.0"),
+ new HeadersModel("Referer", "https://uafix.net/")
+ };
+
+ try
+ {
+ string html = await Http.Get(iframeUrl, headers: headers, proxy: _proxyManager.Get());
+
+ // Шукаємо Playerjs конфігурацію з file параметром
+ var match = Regex.Match(html, @"file:\s*'?([^'""\s,}]+\.m3u8)'?");
+ if (!match.Success)
+ {
+ // Якщо не знайдено, шукаємо в іншому форматі
+ match = Regex.Match(html, @"file['""]?\s*:\s*['""]([^'""}]+\.m3u8)['""]");
+ }
+
+ if (!match.Success)
+ return (null, null);
+
+ string fileUrl = match.Groups[1].Value;
+
+ // Визначити озвучку з URL
+ string voiceName = ExtractVoiceFromUrl(fileUrl);
+
+ return (fileUrl, voiceName);
+ }
+ catch (Exception ex)
+ {
+ _onLog($"ParseAshdiVodEpisode error: {ex.Message}");
+ return (null, null);
+ }
+ }
+
///
/// Витягнути назву озвучки з URL файлу
///
@@ -363,9 +405,8 @@ namespace Uaflix
{
_onLog($"AggregateSerialStructure: Processing zetvideo-vod for season {season} with {seasonGroup.Value.Count} episodes");
- // Для zetvideo-vod достатньо визначити тип плеєра, озвучки будуть визначатися через call
- // Створюємо умовну озвучку для цього сезону
- string displayName = "[Uaflix] Uaflix";
+ // Для zetvideo-vod створюємо озвучку з реальними епізодами
+ string displayName = "Uaflix #2";
if (!structure.Voices.ContainsKey(displayName))
{
@@ -378,10 +419,61 @@ namespace Uaflix
};
}
- // Створюємо пустий список епізодів для цього сезону - вони будуть заповнені через call
- structure.Voices[displayName].Seasons[season] = new List();
+ // Створюємо епізоди для цього сезону з посиланнями на сторінки епізодів
+ var episodes = new List();
+ foreach (var episodeInfo in seasonGroup.Value)
+ {
+ episodes.Add(new EpisodeInfo
+ {
+ Number = episodeInfo.episode,
+ Title = episodeInfo.title,
+ File = episodeInfo.url, // URL сторінки епізоду для використання в call
+ Id = episodeInfo.url,
+ Poster = null,
+ Subtitle = null
+ });
+ }
- _onLog($"AggregateSerialStructure: Created placeholder voice for season {season} in zetvideo-vod");
+ structure.Voices[displayName].Seasons[season] = episodes;
+
+ _onLog($"AggregateSerialStructure: Created voice with {episodes.Count} episodes for season {season} in zetvideo-vod");
+ }
+ else if (playerType == "ashdi-vod")
+ {
+ _onLog($"AggregateSerialStructure: Processing ashdi-vod for season {season} with {seasonGroup.Value.Count} episodes");
+
+ // Для ashdi-vod створюємо озвучку з реальними епізодами
+ string displayName = "Uaflix #3";
+
+ if (!structure.Voices.ContainsKey(displayName))
+ {
+ structure.Voices[displayName] = new VoiceInfo
+ {
+ Name = "Uaflix",
+ PlayerType = "ashdi-vod",
+ DisplayName = displayName,
+ Seasons = new Dictionary>()
+ };
+ }
+
+ // Створюємо епізоди для цього сезону з посиланнями на сторінки епізодів
+ var episodes = new List();
+ foreach (var episodeInfo in seasonGroup.Value)
+ {
+ episodes.Add(new EpisodeInfo
+ {
+ Number = episodeInfo.episode,
+ Title = episodeInfo.title,
+ File = episodeInfo.url, // URL сторінки епізоду для використання в call
+ Id = episodeInfo.url,
+ Poster = null,
+ Subtitle = null
+ });
+ }
+
+ structure.Voices[displayName].Seasons[season] = episodes;
+
+ _onLog($"AggregateSerialStructure: Created voice with {episodes.Count} episodes for season {season} in ashdi-vod");
}
}
@@ -748,12 +840,26 @@ namespace Uaflix
result.streams = await ParseAllZetvideoSources(iframeUrl);
else if (iframeUrl.Contains("ashdi.vip"))
{
- result.streams = await ParseAllAshdiSources(iframeUrl);
- var idMatch = Regex.Match(iframeUrl, @"_(\d+)|vod/(\d+)");
- if (idMatch.Success)
+ // Перевіряємо, чи це ashdi-vod (окремий епізод) або ashdi-serial (багатосерійний плеєр)
+ if (iframeUrl.Contains("/vod/"))
{
- string ashdiId = idMatch.Groups[1].Success ? idMatch.Groups[1].Value : idMatch.Groups[2].Value;
- result.subtitles = await GetAshdiSubtitles(ashdiId);
+ // Це окремий епізод на ashdi.vip/vod/, обробляємо як ashdi-vod
+ var (file, voiceName) = await ParseAshdiVodEpisode(iframeUrl);
+ if (!string.IsNullOrEmpty(file))
+ {
+ result.streams.Add((file, "1080p"));
+ }
+ }
+ 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);
+ }
}
}
}