Add Voice logic

This commit is contained in:
Felix 2025-10-18 10:15:09 +03:00
parent 86e77561e4
commit c5d698b01a
5 changed files with 154 additions and 31 deletions

View File

@ -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 ===");
@ -90,6 +93,24 @@ namespace Uaflix.Controllers
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;
if (string.IsNullOrEmpty(filmUrl))
@ -251,24 +272,20 @@ 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";
// Для 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)
link: accsArgs(callUrl),
method: "call"
);
}
}
else
{
// Для багатосерійних плеєрів (ashdi-serial, zetvideo-serial) - пряме відтворення

View File

@ -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 для цього епізоду
}
}

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Uaflix.Models
{
/// <summary>
/// Агрегована структура серіалу з озвучками з усіх джерел (ashdi, zetvideo-serial, zetvideo-vod)
/// Агрегована структура серіалу з озвучками з усіх джерел (ashdi, zetvideo-serial, zetvideo-vod, ashdi-vod)
/// </summary>
public class SerialAggregatedStructure
{

View File

@ -13,7 +13,7 @@ namespace Uaflix.Models
public string Name { get; set; }
/// <summary>
/// Тип плеєра: "ashdi-serial", "zetvideo-serial", "zetvideo-vod"
/// Тип плеєра: "ashdi-serial", "zetvideo-serial", "zetvideo-vod", "ashdi-vod"
/// </summary>
public string PlayerType { get; set; }

View File

@ -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
}
}
/// <summary>
/// Парсинг одного епізоду з ashdi-vod (новий метод для обробки окремих епізодів з ashdi.vip/vod/)
/// </summary>
private async Task<(string file, string voiceName)> ParseAshdiVodEpisode(string iframeUrl)
{
var headers = new List<HeadersModel>()
{
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);
}
}
/// <summary>
/// Витягнути назву озвучки з URL файлу
/// </summary>
@ -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<EpisodeInfo>();
// Створюємо епізоди для цього сезону з посиланнями на сторінки епізодів
var episodes = new List<EpisodeInfo>();
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<int, List<EpisodeInfo>>()
};
}
// Створюємо епізоди для цього сезону з посиланнями на сторінки епізодів
var episodes = new List<EpisodeInfo>();
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,6 +840,19 @@ namespace Uaflix
result.streams = await ParseAllZetvideoSources(iframeUrl);
else if (iframeUrl.Contains("ashdi.vip"))
{
// Перевіряємо, чи це ashdi-vod (окремий епізод) або ashdi-serial (багатосерійний плеєр)
if (iframeUrl.Contains("/vod/"))
{
// Це окремий епізод на 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)
@ -758,6 +863,7 @@ namespace Uaflix
}
}
}
}
catch (Exception ex)
{
_onLog($"ParseEpisode error: {ex.Message}");