63 lines
2.6 KiB
C#
63 lines
2.6 KiB
C#
using System.Globalization;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace TeleWave.Application.Library;
|
|
|
|
/// <summary>Разбор сезона/серии из имени файла (SxxEyy либо NxNN). Номера в модели не хранятся —
|
|
/// вытаскиваются из имён по мере надобности (сводка в списке шоу, метка в расписании админки).</summary>
|
|
public static class EpisodeName
|
|
{
|
|
/// <summary>Потолок на один Match. Шаблоны ниже линейные и в катастрофический бэктрекинг не уходят,
|
|
/// но имя файла приходит извне (загрузка, inbox), а разбор идёт в цикле по всей библиотеке —
|
|
/// страховка стоит ноль, а её отсутствие превращает любую будущую правку шаблона в риск.</summary>
|
|
private static readonly TimeSpan MatchTimeout = TimeSpan.FromSeconds(1);
|
|
|
|
private static readonly Regex SxxEyy = new(
|
|
@"[Ss](\d{1,2})[ ._-]*[Ee](\d{1,3})",
|
|
RegexOptions.Compiled,
|
|
MatchTimeout
|
|
);
|
|
|
|
private static readonly Regex NxNN = new(
|
|
@"(?:^|[^\d])(\d{1,2})x(\d{1,3})",
|
|
RegexOptions.Compiled | RegexOptions.IgnoreCase,
|
|
MatchTimeout
|
|
);
|
|
|
|
// Ведущий номер серии: «01. Название», «02 - Название», «03_Название» — сезон считаем первым.
|
|
private static readonly Regex LeadingNumber = new(
|
|
@"^\s*(\d{1,3})[\s._)\]-]",
|
|
RegexOptions.Compiled,
|
|
MatchTimeout
|
|
);
|
|
|
|
public static (int Season, int Episode)? Parse(string? name)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
return null;
|
|
|
|
var match = SxxEyy.Match(name);
|
|
if (match.Success)
|
|
return (Int(match, 1), Int(match, 2));
|
|
|
|
match = NxNN.Match(name);
|
|
if (match.Success)
|
|
return (Int(match, 1), Int(match, 2));
|
|
|
|
match = LeadingNumber.Match(name);
|
|
if (match.Success)
|
|
return (1, Int(match, 1));
|
|
|
|
return null;
|
|
}
|
|
|
|
private static int Int(Match match, int group) =>
|
|
int.Parse(match.Groups[group].Value, CultureInfo.InvariantCulture);
|
|
|
|
public static int? ParseSeason(string? name) => Parse(name)?.Season;
|
|
|
|
/// <summary>Метка вида «S16E03», либо null если распознать не удалось.</summary>
|
|
public static string? ParseLabel(string? name) =>
|
|
Parse(name) is { } p ? $"S{p.Season:D2}E{p.Episode:D2}" : null;
|
|
}
|