Усі модулі тепер коректно передають об'єкт SubtitleTpl у шаблони Lampac, що дозволяє відображати субтитри в інтерфейсі плеєра.

This commit is contained in:
Felix 2026-05-02 15:54:56 +03:00
parent ce58434c31
commit 04bb7d48b5
15 changed files with 191 additions and 65 deletions

1
.gitignore vendored
View File

@ -17,3 +17,4 @@ obj
.vscode/settings.json .vscode/settings.json
.qwen .qwen
log log
.kilo

View File

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Shared; using Shared;
using Shared.Models.Online.Settings; using Shared.Models.Online.Settings;
using Shared.Models; using Shared.Models;
using Shared.Models.Templates;
using System.Text.Json; using System.Text.Json;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -14,6 +15,13 @@ using Shared.Engine;
namespace LME.AnimeON namespace LME.AnimeON
{ {
public class AshdiStream
{
public string Title { get; set; }
public string Link { get; set; }
public SubtitleTpl Subtitles { get; set; }
}
public class AnimeONInvoke public class AnimeONInvoke
{ {
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);
@ -192,12 +200,12 @@ namespace LME.AnimeON
public async Task<string> ParseAshdiPage(string url, bool disableAshdiMultivoiceForVod = false) public async Task<string> ParseAshdiPage(string url, bool disableAshdiMultivoiceForVod = false)
{ {
var streams = await ParseAshdiPageStreams(url, disableAshdiMultivoiceForVod); var streams = await ParseAshdiPageStreams(url, disableAshdiMultivoiceForVod);
return streams?.FirstOrDefault().link; return streams?.FirstOrDefault()?.Link;
} }
public async Task<List<(string title, string link)>> ParseAshdiPageStreams(string url, bool disableAshdiMultivoiceForVod = false) public async Task<List<AshdiStream>> ParseAshdiPageStreams(string url, bool disableAshdiMultivoiceForVod = false)
{ {
var streams = new List<(string title, string link)>(); var streams = new List<AshdiStream>();
try try
{ {
var headers = new List<HeadersModel>() var headers = new List<HeadersModel>()
@ -234,7 +242,12 @@ namespace LME.AnimeON
continue; continue;
string rawTitle = item.TryGetProperty("title", out var titleProp) ? titleProp.GetString() : null; string rawTitle = item.TryGetProperty("title", out var titleProp) ? titleProp.GetString() : null;
streams.Add((BuildDisplayTitle(rawTitle, file, index), file)); streams.Add(new AshdiStream
{
Title = BuildDisplayTitle(rawTitle, file, index),
Link = file,
Subtitles = ApnHelper.ParseSubtitles(item.TryGetProperty("subtitle", out var subtitleProp) ? subtitleProp.GetString() : null)
});
index++; index++;
} }
@ -247,7 +260,12 @@ namespace LME.AnimeON
if (match.Success) if (match.Success)
{ {
string file = match.Groups[1].Value; string file = match.Groups[1].Value;
streams.Add((BuildDisplayTitle("Основне джерело", file, 1), file)); streams.Add(new AshdiStream
{
Title = BuildDisplayTitle("Основне джерело", file, 1),
Link = file,
Subtitles = ApnHelper.ParseSubtitles(ApnHelper.ExtractPlayerSubtitle(html))
});
} }
} }
catch (Exception ex) catch (Exception ex)

View File

@ -251,10 +251,13 @@ namespace LME.AnimeON.Controllers
{ {
foreach (var ashdiStream in ashdiStreams) foreach (var ashdiStream in ashdiStreams)
{ {
string optionName = $"{translationName} {ashdiStream.title}"; string optionName = $"{translationName} {ashdiStream.Title}";
string callUrl = $"{host}/lite/lme_animeon/play?url={HttpUtility.UrlEncode(ashdiStream.link)}"; string subtitlesParam = ashdiStream.Subtitles != null ? $"&subtitles={HttpUtility.UrlEncode(JsonSerializer.Serialize(ashdiStream.Subtitles.ToObject()))}" : string.Empty;
tpl.Append(optionName, accsArgs(callUrl), "call"); string callUrl = $"{host}/lite/lme_animeon/play?url={HttpUtility.UrlEncode(ashdiStream.Link)}{subtitlesParam}";
movieTpl.Append(optionName, accsArgs(callUrl), "call");
} }
}
continue; continue;
} }
} }
@ -375,7 +378,7 @@ namespace LME.AnimeON.Controllers
} }
[HttpGet("lite/lme_animeon/play")] [HttpGet("lite/lme_animeon/play")]
public async Task<ActionResult> Play(string url, int episode_id = 0, string title = null, int serial = 0) public async Task<ActionResult> Play(string url, int episode_id = 0, string title = null, int serial = 0, string subtitles = null)
{ {
await UpdateService.ConnectAsync(host); await UpdateService.ConnectAsync(host);
@ -421,10 +424,25 @@ namespace LME.AnimeON.Controllers
forceProxy = true; forceProxy = true;
} }
SubtitleTpl subtitleTpl = null;
if (!string.IsNullOrEmpty(subtitles))
{
try
{
var subtitleDtos = JsonSerializer.Deserialize<List<SubtitleDto>>(subtitles);
if (subtitleDtos != null && subtitleDtos.Count > 0)
{
subtitleTpl = new SubtitleTpl(subtitleDtos.Count);
foreach (var sub in subtitleDtos)
subtitleTpl.Append(sub.label, sub.url);
}
}
catch { }
}
string streamUrl = BuildStreamUrl(init, streamLink, streamHeaders, forceProxy); string streamUrl = BuildStreamUrl(init, streamLink, streamHeaders, forceProxy);
string jsonResult = $"{{\"method\":\"play\",\"url\":\"{streamUrl}\",\"title\":\"{title ?? string.Empty}\"}}";
OnLog("AnimeON Play: return call JSON"); OnLog("AnimeON Play: return call JSON");
return UpdateService.Validate(Content(jsonResult, "application/json; charset=utf-8")); return UpdateService.Validate(Content(VideoTpl.ToJson("play", streamUrl, title ?? string.Empty, subtitles: subtitleTpl), "application/json; charset=utf-8"));
} }
private static string StripLampacArgs(string url) private static string StripLampacArgs(string url)

View File

@ -166,7 +166,7 @@ namespace LME.KlonFUN.Controllers
: $"Серія {episode.Number}"; : $"Серія {episode.Number}";
string streamUrl = BuildStreamUrl(init, episode.Link); string streamUrl = BuildStreamUrl(init, episode.Link);
episodeTpl.Append(episodeTitle, contentTitle, s.ToString(), episode.Number.ToString("D2"), streamUrl); episodeTpl.Append(episodeTitle, contentTitle, s.ToString(), episode.Number.ToString("D2"), streamUrl, subtitles: episode.Subtitles);
} }
episodeTpl.Append(voiceTpl); episodeTpl.Append(voiceTpl);
@ -190,7 +190,7 @@ namespace LME.KlonFUN.Controllers
: $"Варіант {i + 1}"; : $"Варіант {i + 1}";
string streamUrl = BuildStreamUrl(init, stream.Link); string streamUrl = BuildStreamUrl(init, stream.Link);
movieTpl.Append(label, streamUrl); movieTpl.Append(label, streamUrl, subtitles: stream.Subtitles);
} }
return rjson return rjson

View File

@ -206,7 +206,8 @@ namespace LME.KlonFUN
streams.Add(new MovieStream streams.Add(new MovieStream
{ {
Title = voiceTitle, Title = voiceTitle,
Link = link Link = link,
Subtitles = ApnHelper.ParseSubtitles(item.Value<string>("subtitle"))
}); });
index++; index++;
@ -221,7 +222,8 @@ namespace LME.KlonFUN
streams.Add(new MovieStream streams.Add(new MovieStream
{ {
Title = FormatMovieTitle("Основне джерело", directMatch.Groups["url"].Value, 1), Title = FormatMovieTitle("Основне джерело", directMatch.Groups["url"].Value, 1),
Link = directMatch.Groups["url"].Value Link = directMatch.Groups["url"].Value,
Subtitles = ApnHelper.ParseSubtitles(ApnHelper.ExtractPlayerSubtitle(playerHtml))
}); });
} }
} }
@ -310,7 +312,8 @@ namespace LME.KlonFUN
{ {
Number = episodeNumber, Number = episodeNumber,
Title = string.IsNullOrWhiteSpace(episodeTitle) ? $"Серія {episodeNumber}" : episodeTitle, Title = string.IsNullOrWhiteSpace(episodeTitle) ? $"Серія {episodeNumber}" : episodeTitle,
Link = link Link = link,
Subtitles = ApnHelper.ParseSubtitles(episodeObj.Value<string>("subtitle"))
}); });
episodeFallback++; episodeFallback++;

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using Shared.Models.Templates;
namespace LME.KlonFUN.Models namespace LME.KlonFUN.Models
{ {
@ -47,6 +48,7 @@ namespace LME.KlonFUN.Models
{ {
public string Title { get; set; } public string Title { get; set; }
public string Link { get; set; } public string Link { get; set; }
public SubtitleTpl Subtitles { get; set; }
} }
public class SerialEpisode public class SerialEpisode
@ -54,6 +56,7 @@ namespace LME.KlonFUN.Models
public int Number { get; set; } public int Number { get; set; }
public string Title { get; set; } public string Title { get; set; }
public string Link { get; set; } public string Link { get; set; }
public SubtitleTpl Subtitles { get; set; }
} }
public class SerialVoice public class SerialVoice

View File

@ -107,7 +107,7 @@ namespace LME.Makhno
if (play) if (play)
return UpdateService.Validate(Redirect(streamUrl)); return UpdateService.Validate(Redirect(streamUrl));
return UpdateService.Validate(Content(VideoTpl.ToJson("play", streamUrl, episodeTitle), "application/json; charset=utf-8")); return UpdateService.Validate(Content(VideoTpl.ToJson("play", streamUrl, episodeTitle, subtitles: episode.Subtitles), "application/json; charset=utf-8"));
} }
} }
@ -150,7 +150,7 @@ namespace LME.Makhno
if (play) if (play)
return UpdateService.Validate(Redirect(streamUrl)); return UpdateService.Validate(Redirect(streamUrl));
return UpdateService.Validate(Content(VideoTpl.ToJson("play", streamUrl, title ?? original_title), "application/json; charset=utf-8")); return UpdateService.Validate(Content(VideoTpl.ToJson("play", streamUrl, title ?? original_title, subtitles: playerData.Subtitles ?? playerData.Movies?.FirstOrDefault(m => m.File == playerData.File)?.Subtitles), "application/json; charset=utf-8"));
} }
private async Task<ActionResult> HandleMovie(string playUrl, string imdb_id, string title, string original_title, int year, bool rjson, MakhnoInvoke invoke) private async Task<ActionResult> HandleMovie(string playUrl, string imdb_id, string title, string original_title, int year, bool rjson, MakhnoInvoke invoke)
@ -171,7 +171,8 @@ namespace LME.Makhno
{ {
File = playerData.File, File = playerData.File,
Title = "Основне джерело", Title = "Основне джерело",
Quality = "auto" Quality = "auto",
Subtitles = playerData.Subtitles
}); });
} }
@ -189,7 +190,7 @@ namespace LME.Makhno
? stream.Title ? stream.Title
: $"Варіант {index}"; : $"Варіант {index}";
tpl.Append(label, BuildStreamUrl(init, stream.File)); tpl.Append(label, BuildStreamUrl(init, stream.File), subtitles: stream.Subtitles);
index++; index++;
} }
@ -395,8 +396,10 @@ namespace LME.Makhno
title ?? original_title, title ?? original_title,
requestedSeason.ToString(), requestedSeason.ToString(),
(i + 1).ToString("D2"), (i + 1).ToString("D2"),
streamUrl streamUrl,
subtitles: episode.Subtitles
); );
} }
} }
} }

View File

@ -114,18 +114,22 @@ namespace LME.Makhno
{ {
string file = fileMatch.Groups[1].Value; string file = fileMatch.Groups[1].Value;
var posterMatch = Regex.Match(html, @"poster:[""']([^""']+)[""']", RegexOptions.IgnoreCase); var posterMatch = Regex.Match(html, @"poster:[""']([^""']+)[""']", RegexOptions.IgnoreCase);
var subtitles = ApnHelper.ParseSubtitles(ApnHelper.ExtractPlayerSubtitle(html));
return new PlayerData return new PlayerData
{ {
File = file, File = file,
Poster = posterMatch.Success ? posterMatch.Groups[1].Value : null, Poster = posterMatch.Success ? posterMatch.Groups[1].Value : null,
Voices = new List<Voice>(), Voices = new List<Voice>(),
Subtitles = subtitles,
Movies = new List<MovieVariant>() Movies = new List<MovieVariant>()
{ {
new MovieVariant new MovieVariant
{ {
File = file, File = file,
Quality = DetectQualityTag(file) ?? "auto", Quality = DetectQualityTag(file) ?? "auto",
Title = BuildMovieTitle("Основне джерело", file, 1) Title = BuildMovieTitle("Основне джерело", file, 1),
Subtitles = subtitles
} }
} }
}; };
@ -238,7 +242,8 @@ namespace LME.Makhno
Title = episode["title"]?.ToString(), Title = episode["title"]?.ToString(),
File = episode["file"]?.ToString(), File = episode["file"]?.ToString(),
Poster = episode["poster"]?.ToString(), Poster = episode["poster"]?.ToString(),
Subtitle = episode["subtitle"]?.ToString() Subtitle = episode["subtitle"]?.ToString(),
Subtitles = ApnHelper.ParseSubtitles(episode["subtitle"]?.ToString())
}); });
} }
} }
@ -289,7 +294,8 @@ namespace LME.Makhno
{ {
File = file, File = file,
Quality = DetectQualityTag($"{rawTitle} {file}") ?? "auto", Quality = DetectQualityTag($"{rawTitle} {file}") ?? "auto",
Title = BuildMovieTitle(rawTitle, file, index) Title = BuildMovieTitle(rawTitle, file, index),
Subtitles = ApnHelper.ParseSubtitles(item["subtitle"]?.ToString())
}); });
index++; index++;
} }

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using Shared.Models.Templates;
namespace LME.Makhno.Models namespace LME.Makhno.Models
{ {
@ -9,6 +10,7 @@ namespace LME.Makhno.Models
public List<Voice> Voices { get; set; } public List<Voice> Voices { get; set; }
public List<Season> Seasons { get; set; } public List<Season> Seasons { get; set; }
public List<MovieVariant> Movies { get; set; } public List<MovieVariant> Movies { get; set; }
public SubtitleTpl Subtitles { get; set; }
} }
public class Voice public class Voice
@ -30,6 +32,7 @@ namespace LME.Makhno.Models
public string Id { get; set; } public string Id { get; set; }
public string Poster { get; set; } public string Poster { get; set; }
public string Subtitle { get; set; } public string Subtitle { get; set; }
public SubtitleTpl Subtitles { get; set; }
} }
public class MovieVariant public class MovieVariant
@ -37,5 +40,6 @@ namespace LME.Makhno.Models
public string Title { get; set; } public string Title { get; set; }
public string File { get; set; } public string File { get; set; }
public string Quality { get; set; } public string Quality { get; set; }
public SubtitleTpl Subtitles { get; set; }
} }
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web; using System.Web;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -177,10 +178,13 @@ namespace LME.Mikai.Controllers
{ {
foreach (var ashdiStream in ashdiStreams) foreach (var ashdiStream in ashdiStreams)
{ {
string optionName = $"{voice.DisplayName} {ashdiStream.title}"; string optionName = $"{voice.DisplayName} {ashdiStream.Title}";
string ashdiCallUrl = $"{host}/lite/lme_mikai/play?url={HttpUtility.UrlEncode(ashdiStream.link)}&title={HttpUtility.UrlEncode(displayTitle)}"; string subtitlesParam = ashdiStream.Subtitles != null ? $"&subtitles={HttpUtility.UrlEncode(JsonSerializer.Serialize(ashdiStream.Subtitles.ToObject()))}" : string.Empty;
string ashdiCallUrl = $"{host}/lite/lme_mikai/play?url={HttpUtility.UrlEncode(ashdiStream.Link)}&title={HttpUtility.UrlEncode(displayTitle)}{subtitlesParam}";
movieTpl.Append(optionName, accsArgs(ashdiCallUrl), "call"); movieTpl.Append(optionName, accsArgs(ashdiCallUrl), "call");
} }
}
continue; continue;
} }
} }
@ -204,7 +208,7 @@ namespace LME.Mikai.Controllers
} }
[HttpGet("lite/lme_mikai/play")] [HttpGet("lite/lme_mikai/play")]
public async Task<ActionResult> Play(string url, string title = null, int serial = 0) public async Task<ActionResult> Play(string url, string title = null, int serial = 0, string subtitles = null)
{ {
await UpdateService.ConnectAsync(host); await UpdateService.ConnectAsync(host);
@ -235,9 +239,24 @@ namespace LME.Mikai.Controllers
forceProxy = true; forceProxy = true;
} }
SubtitleTpl subtitleTpl = null;
if (!string.IsNullOrEmpty(subtitles))
{
try
{
var subtitleDtos = JsonSerializer.Deserialize<List<SubtitleDto>>(subtitles);
if (subtitleDtos != null && subtitleDtos.Count > 0)
{
subtitleTpl = new SubtitleTpl(subtitleDtos.Count);
foreach (var sub in subtitleDtos)
subtitleTpl.Append(sub.label, sub.url);
}
}
catch { }
}
string streamUrl = BuildStreamUrl(init, streamLink, streamHeaders, forceProxy); string streamUrl = BuildStreamUrl(init, streamLink, streamHeaders, forceProxy);
string jsonResult = $"{{\"method\":\"play\",\"url\":\"{streamUrl}\",\"title\":\"{title ?? string.Empty}\"}}"; return UpdateService.Validate(Content(VideoTpl.ToJson("play", streamUrl, title ?? string.Empty, subtitles: subtitleTpl), "application/json; charset=utf-8"));
return UpdateService.Validate(Content(jsonResult, "application/json; charset=utf-8"));
} }
private async Task<List<MikaiAnime>> CollectSeasonDetails(MikaiAnime details, MikaiInvoke invoke) private async Task<List<MikaiAnime>> CollectSeasonDetails(MikaiAnime details, MikaiInvoke invoke)

View File

@ -11,9 +11,17 @@ using Shared;
using Shared.Engine; using Shared.Engine;
using Shared.Models; using Shared.Models;
using Shared.Models.Online.Settings; using Shared.Models.Online.Settings;
using Shared.Models.Templates;
namespace LME.Mikai namespace LME.Mikai
{ {
public class AshdiStream
{
public string Title { get; set; }
public string Link { get; set; }
public SubtitleTpl Subtitles { get; set; }
}
public class MikaiInvoke public class MikaiInvoke
{ {
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);
@ -176,12 +184,12 @@ namespace LME.Mikai
public async Task<string> ParseAshdiPage(string url, bool disableAshdiMultivoiceForVod = false) public async Task<string> ParseAshdiPage(string url, bool disableAshdiMultivoiceForVod = false)
{ {
var streams = await ParseAshdiPageStreams(url, disableAshdiMultivoiceForVod); var streams = await ParseAshdiPageStreams(url, disableAshdiMultivoiceForVod);
return streams?.FirstOrDefault().link; return streams?.FirstOrDefault()?.Link;
} }
public async Task<List<(string title, string link)>> ParseAshdiPageStreams(string url, bool disableAshdiMultivoiceForVod = false) public async Task<List<AshdiStream>> ParseAshdiPageStreams(string url, bool disableAshdiMultivoiceForVod = false)
{ {
var streams = new List<(string title, string link)>(); var streams = new List<AshdiStream>();
try try
{ {
var headers = new List<HeadersModel>() var headers = new List<HeadersModel>()
@ -218,7 +226,12 @@ namespace LME.Mikai
continue; continue;
string rawTitle = item.TryGetProperty("title", out var titleProp) ? titleProp.GetString() : null; string rawTitle = item.TryGetProperty("title", out var titleProp) ? titleProp.GetString() : null;
streams.Add((BuildDisplayTitle(rawTitle, file, index), file)); streams.Add(new AshdiStream
{
Title = BuildDisplayTitle(rawTitle, file, index),
Link = file,
Subtitles = ApnHelper.ParseSubtitles(item.TryGetProperty("subtitle", out var subtitleProp) ? subtitleProp.GetString() : null)
});
index++; index++;
} }
@ -231,7 +244,12 @@ namespace LME.Mikai
if (match.Success) if (match.Success)
{ {
string file = match.Groups[1].Value; string file = match.Groups[1].Value;
streams.Add((BuildDisplayTitle("Основне джерело", file, 1), file)); streams.Add(new AshdiStream
{
Title = BuildDisplayTitle("Основне джерело", file, 1),
Link = file,
Subtitles = ApnHelper.ParseSubtitles(ApnHelper.ExtractPlayerSubtitle(html))
});
} }
} }
catch (Exception ex) catch (Exception ex)

View File

@ -1,6 +1,9 @@
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Shared.Models.Base; using Shared.Models.Base;
using Shared.Models.Templates;
using System; using System;
using System.Net;
using System.Text.RegularExpressions;
using System.Web; using System.Web;
namespace Shared.Engine namespace Shared.Engine
@ -9,6 +12,8 @@ namespace Shared.Engine
{ {
public const string DefaultHost = "https://tut.im/proxy.php?url={encodeurl}"; public const string DefaultHost = "https://tut.im/proxy.php?url={encodeurl}";
private static readonly Regex SubtitleLineRegex = new Regex(@"\[([^\]]+)\]([^,]+)", RegexOptions.Compiled);
public static bool TryGetInitConf(JObject conf, out bool enabled, out string host) public static bool TryGetInitConf(JObject conf, out bool enabled, out string host)
{ {
enabled = false; enabled = false;
@ -120,6 +125,40 @@ namespace Shared.Engine
return $"{host.TrimEnd('/')}/{url}"; return $"{host.TrimEnd('/')}/{url}";
} }
public static SubtitleTpl ParseSubtitles(string subtitleValue)
{
if (string.IsNullOrWhiteSpace(subtitleValue))
return null;
var subtitles = new SubtitleTpl();
string normalized = WebUtility.HtmlDecode(subtitleValue)
.Replace("\\/", "/")
.Replace("\\'", "'")
.Replace("\\\"", "\"");
foreach (Match match in SubtitleLineRegex.Matches(normalized))
{
string label = WebUtility.HtmlDecode(match.Groups[1].Value).Trim();
string url = WebUtility.HtmlDecode(match.Groups[2].Value).Trim();
if (!string.IsNullOrWhiteSpace(label) && !string.IsNullOrWhiteSpace(url))
subtitles.Append(label, url);
}
return subtitles.IsEmpty ? null : subtitles;
}
public static string ExtractPlayerSubtitle(string html)
{
if (string.IsNullOrWhiteSpace(html))
return null;
var match = Regex.Match(html, @"subtitle\s*:\s*['""]([^'""']+)['""]", RegexOptions.IgnoreCase);
if (!match.Success)
match = Regex.Match(html, @"subtitle['""]?\s*:\s*['""]([^'""']+)['""]", RegexOptions.IgnoreCase);
return match.Success ? match.Groups[1].Value : null;
}
private static string NormalizeHost(string host) private static string NormalizeHost(string host)
{ {
if (string.IsNullOrWhiteSpace(host)) if (string.IsNullOrWhiteSpace(host))

View File

@ -104,9 +104,10 @@ namespace LME.Uaflix.Controllers
{ {
// Повертаємо JSON з інформацією про стрім для методу 'play' // Повертаємо JSON з інформацією про стрім для методу 'play'
string streamUrl = BuildStreamUrl(init, playResult.streams.First().link); string streamUrl = BuildStreamUrl(init, playResult.streams.First().link);
string jsonResult = $"{{\"method\":\"play\",\"url\":\"{streamUrl}\",\"title\":\"{title ?? original_title}\"}}"; var subtitles = playResult.subtitles ?? playResult.streams.FirstOrDefault(s => s.subtitles != null)?.subtitles;
OnLog($"=== RETURN: call method JSON for episode_url ==="); OnLog($"=== RETURN: call method JSON for episode_url ===");
return UpdateService.Validate(Content(jsonResult, "application/json; charset=utf-8")); return UpdateService.Validate(Content(VideoTpl.ToJson("play", streamUrl, title ?? original_title, subtitles: subtitles), "application/json; charset=utf-8"));
} }
OnLog("=== RETURN: call method no streams ==="); OnLog("=== RETURN: call method no streams ===");
@ -284,7 +285,8 @@ namespace LME.Uaflix.Controllers
e: ep.Number.ToString(), e: ep.Number.ToString(),
link: accsArgs(callUrl), link: accsArgs(callUrl),
method: "call", method: "call",
streamlink: accsArgs($"{callUrl}&play=true") streamlink: accsArgs($"{callUrl}&play=true"),
subtitles: ApnHelper.ParseSubtitles(ep.Subtitle)
); );
} }
else else
@ -296,7 +298,8 @@ namespace LME.Uaflix.Controllers
title: title, title: title,
s: s.ToString(), s: s.ToString(),
e: ep.Number.ToString(), e: ep.Number.ToString(),
link: playUrl link: playUrl,
subtitles: ApnHelper.ParseSubtitles(ep.Subtitle)
); );
} }
@ -351,7 +354,7 @@ namespace LME.Uaflix.Controllers
? stream.title ? stream.title
: $"Варіант {index}"; : $"Варіант {index}";
tpl.Append(label, BuildStreamUrl(init, stream.link)); tpl.Append(label, BuildStreamUrl(init, stream.link), subtitles: stream.subtitles ?? playResult.subtitles);
index++; index++;
} }

View File

@ -15,5 +15,6 @@ namespace LME.Uaflix.Models
public string link { get; set; } public string link { get; set; }
public string quality { get; set; } public string quality { get; set; }
public string title { get; set; } public string title { get; set; }
public SubtitleTpl? subtitles { get; set; }
} }
} }

View File

@ -582,7 +582,8 @@ namespace LME.Uaflix
{ {
link = fileUrl, link = fileUrl,
quality = DetectQualityTag($"{rawTitle} {fileUrl}") ?? "auto", quality = DetectQualityTag($"{rawTitle} {fileUrl}") ?? "auto",
title = BuildDisplayTitle(rawTitle, fileUrl, index) title = BuildDisplayTitle(rawTitle, fileUrl, index),
subtitles = ApnHelper.ParseSubtitles(item?["subtitle"]?.ToString())
}); });
index++; index++;
} }
@ -605,7 +606,8 @@ namespace LME.Uaflix
{ {
link = fallbackFile, link = fallbackFile,
quality = DetectQualityTag(fallbackFile) ?? "auto", quality = DetectQualityTag(fallbackFile) ?? "auto",
title = BuildDisplayTitle(ExtractVoiceFromUrl(fallbackFile), fallbackFile, 1) title = BuildDisplayTitle(ExtractVoiceFromUrl(fallbackFile), fallbackFile, 1),
subtitles = ApnHelper.ParseSubtitles(ApnHelper.ExtractPlayerSubtitle(html))
}); });
return result; return result;
@ -2050,19 +2052,7 @@ namespace LME.Uaflix
string url = $"https://ashdi.vip/vod/{id}"; string url = $"https://ashdi.vip/vod/{id}";
var html = await GetHtml(AshdiRequestUrl(url), new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", "https://ashdi.vip/") }); var html = await GetHtml(AshdiRequestUrl(url), new List<HeadersModel>() { new HeadersModel("User-Agent", "Mozilla/5.0"), new HeadersModel("Referer", "https://ashdi.vip/") });
string subtitle = new Regex("subtitle(\")?:\"([^\"]+)\"").Match(html).Groups[2].Value; string subtitle = new Regex("subtitle(\")?:\"([^\"]+)\"").Match(html).Groups[2].Value;
if (!string.IsNullOrEmpty(subtitle)) return ApnHelper.ParseSubtitles(subtitle);
{
var match = new Regex("\\[([^\\]]+)\\](https?://[^\\,]+)").Match(subtitle);
var st = new Shared.Models.Templates.SubtitleTpl();
while (match.Success)
{
st.Append(match.Groups[1].Value, match.Groups[2].Value);
match = match.NextMatch();
}
if (st.data != null && st.data.Count > 0)
return st;
}
return null;
} }
private static string WithAshdiMultivoice(string url) private static string WithAshdiMultivoice(string url)