diff --git a/Mikai/Controller.cs b/Mikai/Controller.cs index c8cb05a..5ec5ddf 100644 --- a/Mikai/Controller.cs +++ b/Mikai/Controller.cs @@ -57,11 +57,14 @@ namespace Mikai.Controllers if (isSerial) { - var seasonNumbers = voices.Values - .SelectMany(v => v.Seasons.Keys) - .Distinct() - .OrderBy(n => n) - .ToList(); + bool restrictByVoice = !string.IsNullOrEmpty(t) && voices.TryGetValue(t, out var voiceForSeasons); + var seasonNumbers = restrictByVoice + ? GetSeasonSet(voiceForSeasons).OrderBy(n => n).ToList() + : voices.Values + .SelectMany(v => GetSeasonSet(v)) + .Distinct() + .OrderBy(n => n) + .ToList(); if (seasonNumbers.Count == 0) return OnError("mikai", _proxyManager); @@ -72,6 +75,8 @@ namespace Mikai.Controllers foreach (var seasonNumber in seasonNumbers) { string link = $"{host}/mikai?imdb_id={imdb_id}&kinopoisk_id={kinopoisk_id}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&year={year}&serial=1&s={seasonNumber}"; + if (restrictByVoice) + link += $"&t={HttpUtility.UrlEncode(t)}"; seasonTpl.Append($"{seasonNumber}", link, seasonNumber.ToString()); } @@ -89,16 +94,29 @@ namespace Mikai.Controllers if (string.IsNullOrEmpty(t)) t = voicesForSeason[0].Key; + else if (!voices.ContainsKey(t)) + t = voicesForSeason[0].Key; var voiceTpl = new VoiceTpl(); + var selectedVoiceInfo = voices[t]; + var selectedSeasonSet = GetSeasonSet(selectedVoiceInfo); foreach (var voice in voicesForSeason) { - string voiceLink = $"{host}/mikai?imdb_id={imdb_id}&kinopoisk_id={kinopoisk_id}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&year={year}&serial=1&s={s}&t={HttpUtility.UrlEncode(voice.Key)}"; + var targetSeasonSet = GetSeasonSet(voice.Value); + bool sameSeasonSet = targetSeasonSet.SetEquals(selectedSeasonSet); + string voiceLink = $"{host}/mikai?imdb_id={imdb_id}&kinopoisk_id={kinopoisk_id}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&year={year}&serial=1"; + if (sameSeasonSet) + voiceLink += $"&s={s}&t={HttpUtility.UrlEncode(voice.Key)}"; + else + voiceLink += $"&s=-1&t={HttpUtility.UrlEncode(voice.Key)}"; voiceTpl.Append(voice.Key, voice.Key == t, voiceLink); } if (!voices.ContainsKey(t) || !voices[t].Seasons.ContainsKey(s)) - return OnError("mikai", _proxyManager); + { + string redirectUrl = $"{host}/mikai?imdb_id={imdb_id}&kinopoisk_id={kinopoisk_id}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&year={year}&serial=1&s=-1&t={HttpUtility.UrlEncode(t)}"; + return Redirect(redirectUrl); + } var episodeTpl = new EpisodeTpl(); foreach (var ep in voices[t].Seasons[s].OrderBy(e => e.Number)) @@ -367,6 +385,17 @@ namespace Mikai.Controllers streamLink.Contains("moonanime.art", StringComparison.OrdinalIgnoreCase); } + private static HashSet GetSeasonSet(MikaiVoiceInfo voice) + { + if (voice?.Seasons == null || voice.Seasons.Count == 0) + return new HashSet(); + + return voice.Seasons + .Where(kv => kv.Value != null && kv.Value.Any(ep => !string.IsNullOrEmpty(ep.Url))) + .Select(kv => kv.Key) + .ToHashSet(); + } + private static string StripLampacArgs(string url) { if (string.IsNullOrEmpty(url)) diff --git a/Mikai/ModInit.cs b/Mikai/ModInit.cs index eb2a898..c9ef993 100644 --- a/Mikai/ModInit.cs +++ b/Mikai/ModInit.cs @@ -24,7 +24,7 @@ namespace Mikai { public class ModInit { - public static double Version => 3.3; + public static double Version => 3.4; public static OnlinesSettings Mikai; public static bool ApnHostProvided; diff --git a/Uaflix/Controller.cs b/Uaflix/Controller.cs index be1ed92..bf70816 100644 --- a/Uaflix/Controller.cs +++ b/Uaflix/Controller.cs @@ -167,11 +167,21 @@ namespace Uaflix.Controllers // s == -1: Вибір сезону if (s == -1) { - var allSeasons = structure.Voices - .SelectMany(v => v.Value.Seasons.Keys) - .Distinct() - .OrderBy(sn => sn) - .ToList(); + List allSeasons; + bool restrictByVoice = !string.IsNullOrEmpty(t) && structure.Voices.TryGetValue(t, out var tVoice) && IsAshdiVoice(tVoice); + if (restrictByVoice) + { + allSeasons = GetSeasonSet(tVoice).OrderBy(sn => sn).ToList(); + OnLog($"Ashdi voice selected (t='{t}'), seasons count={allSeasons.Count}"); + } + else + { + allSeasons = structure.Voices + .SelectMany(v => GetSeasonSet(v.Value)) + .Distinct() + .OrderBy(sn => sn) + .ToList(); + } OnLog($"Found {allSeasons.Count} seasons in structure: {string.Join(", ", allSeasons)}"); @@ -205,6 +215,8 @@ namespace Uaflix.Controllers foreach (var season in seasonsWithValidEpisodes) { string link = $"{host}/uaflix?imdb_id={imdb_id}&kinopoisk_id={kinopoisk_id}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&year={year}&serial=1&s={season}&href={HttpUtility.UrlEncode(filmUrl)}"; + if (restrictByVoice) + link += $"&t={HttpUtility.UrlEncode(t)}"; season_tpl.Append($"{season}", link, season.ToString()); OnLog($"Added season {season} to template"); } @@ -239,12 +251,31 @@ namespace Uaflix.Controllers t = voicesForSeason[0].DisplayName; OnLog($"Auto-selected first voice: {t}"); } - + else if (!structure.Voices.ContainsKey(t)) + { + t = voicesForSeason[0].DisplayName; + OnLog($"Voice '{t}' not found, fallback to first voice: {t}"); + } + // Створюємо VoiceTpl з усіма озвучками var voice_tpl = new VoiceTpl(); + var selectedVoiceInfo = structure.Voices[t]; + var selectedSeasonSet = GetSeasonSet(selectedVoiceInfo); + bool selectedIsAshdi = IsAshdiVoice(selectedVoiceInfo); + foreach (var voice in voicesForSeason) { - string voiceLink = $"{host}/uaflix?imdb_id={imdb_id}&kinopoisk_id={kinopoisk_id}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&year={year}&serial=1&s={s}&t={HttpUtility.UrlEncode(voice.DisplayName)}&href={HttpUtility.UrlEncode(filmUrl)}"; + bool targetIsAshdi = IsAshdiVoice(voice.Info); + var targetSeasonSet = GetSeasonSet(voice.Info); + bool sameSeasonSet = targetSeasonSet.SetEquals(selectedSeasonSet); + bool needSeasonReset = (selectedIsAshdi || targetIsAshdi) && !sameSeasonSet; + + string voiceLink = $"{host}/uaflix?imdb_id={imdb_id}&kinopoisk_id={kinopoisk_id}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&year={year}&serial=1&href={HttpUtility.UrlEncode(filmUrl)}"; + if (needSeasonReset) + voiceLink += $"&s=-1&t={HttpUtility.UrlEncode(voice.DisplayName)}"; + else + voiceLink += $"&s={s}&t={HttpUtility.UrlEncode(voice.DisplayName)}"; + bool isActive = voice.DisplayName == t; voice_tpl.Append(voice.DisplayName, isActive, voiceLink); } @@ -261,6 +292,13 @@ namespace Uaflix.Controllers if (!structure.Voices[t].Seasons.ContainsKey(s)) { OnLog($"Season {s} not found for voice '{t}'"); + if (IsAshdiVoice(structure.Voices[t])) + { + string redirectUrl = $"{host}/uaflix?imdb_id={imdb_id}&kinopoisk_id={kinopoisk_id}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&year={year}&serial=1&s=-1&t={HttpUtility.UrlEncode(t)}&href={HttpUtility.UrlEncode(filmUrl)}"; + OnLog($"Ashdi voice missing season, redirect to season selector: {redirectUrl}"); + return Redirect(redirectUrl); + } + OnLog("=== RETURN: season not found for voice OnError ==="); return OnError("uaflix", proxyManager); } @@ -369,5 +407,24 @@ namespace Uaflix.Controllers cleaned = cleaned.Replace("?&", "?").Replace("&&", "&").TrimEnd('?', '&'); return cleaned; } + + private static bool IsAshdiVoice(VoiceInfo voice) + { + if (voice == null || string.IsNullOrEmpty(voice.PlayerType)) + return false; + + return voice.PlayerType == "ashdi-serial" || voice.PlayerType == "ashdi-vod"; + } + + private static HashSet GetSeasonSet(VoiceInfo voice) + { + if (voice?.Seasons == null || voice.Seasons.Count == 0) + return new HashSet(); + + return voice.Seasons + .Where(kv => kv.Value != null && kv.Value.Any(ep => !string.IsNullOrEmpty(ep.File))) + .Select(kv => kv.Key) + .ToHashSet(); + } } } diff --git a/Uaflix/ModInit.cs b/Uaflix/ModInit.cs index 50dbf56..370547b 100644 --- a/Uaflix/ModInit.cs +++ b/Uaflix/ModInit.cs @@ -25,7 +25,7 @@ namespace Uaflix { public class ModInit { - public static double Version => 3.3; + public static double Version => 3.4; public static OnlinesSettings UaFlix; public static bool ApnHostProvided;