Enhance schedule and show management: add season and episode information to ScheduleEntryDto, update ListShowsQueryHandler to include season counts, and modify frontend components to display this new data. Improve EPG handling by consolidating program entries into blocks for better clarity in the streaming interface.
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace TeleWave.Application.Library;
|
||||
|
||||
/// <summary>Разбор сезона/серии из имени файла (SxxEyy либо NxNN). Номера в модели не хранятся —
|
||||
/// вытаскиваются из имён по мере надобности (сводка в списке шоу, метка в расписании админки).</summary>
|
||||
public static class EpisodeName
|
||||
{
|
||||
private static readonly Regex SxxEyy = new(
|
||||
@"[Ss](\d{1,2})[ ._-]*[Ee](\d{1,3})",
|
||||
RegexOptions.Compiled
|
||||
);
|
||||
|
||||
private static readonly Regex NxNN = new(
|
||||
@"(?:^|[^\d])(\d{1,2})x(\d{1,3})",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase
|
||||
);
|
||||
|
||||
public static (int Season, int Episode)? Parse(string? name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return null;
|
||||
|
||||
var match = SxxEyy.Match(name);
|
||||
if (!match.Success)
|
||||
match = NxNN.Match(name);
|
||||
|
||||
return match.Success
|
||||
? (int.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture),
|
||||
int.Parse(match.Groups[2].Value, CultureInfo.InvariantCulture))
|
||||
: null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -12,9 +12,28 @@ public sealed class ListShowsQueryHandler(IAppDbContext dbContext)
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
return await dbContext.Shows.AsNoTracking()
|
||||
var shows = await dbContext.Shows.AsNoTracking()
|
||||
.Include(s => s.Episodes)
|
||||
.OrderBy(s => s.Name)
|
||||
.Select(s => new ShowSummaryDto(s.Id, s.Name, s.Kind, s.Episodes.Count, s.CreatedAt))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// Имена ассетов нужны, чтобы распознать сезоны (номера в модели не хранятся).
|
||||
var assetIds = shows.SelectMany(s => s.Episodes.Select(e => e.MediaAssetId)).Distinct().ToList();
|
||||
var names = await dbContext.MediaAssets.AsNoTracking()
|
||||
.Where(a => assetIds.Contains(a.Id))
|
||||
.Select(a => new { a.Id, a.OriginalFileName })
|
||||
.ToDictionaryAsync(a => a.Id, a => a.OriginalFileName, cancellationToken);
|
||||
|
||||
return shows
|
||||
.Select(s =>
|
||||
{
|
||||
var seasons = s.Episodes
|
||||
.Select(e => names.TryGetValue(e.MediaAssetId, out var n) ? EpisodeName.ParseSeason(n) : null)
|
||||
.Where(season => season is not null)
|
||||
.Distinct()
|
||||
.Count();
|
||||
return new ShowSummaryDto(s.Id, s.Name, s.Kind, s.Episodes.Count, seasons, s.CreatedAt);
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ public sealed record ShowSummaryDto(
|
||||
string Name,
|
||||
ShowKind Kind,
|
||||
int EpisodeCount,
|
||||
int SeasonCount,
|
||||
DateTimeOffset CreatedAt
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user