From f699576582f700660beac734b1fe25859d6a47a7 Mon Sep 17 00:00:00 2001 From: Leonid Pershin Date: Sun, 26 Jul 2026 14:50:32 +0300 Subject: [PATCH] Enhance channel and settings functionalities: introduce viewer settings in ChannelEndpoints, update SiteSettings to include channel number toggling, and refactor related data structures. Implement new endpoints for validating templates and diffing scheduling changes, improving overall user experience and configuration management. --- .../Endpoints/ChannelEndpoints.cs | 48 + .../Endpoints/SettingsEndpoints.cs | 108 +- .../Endpoints/StreamingEndpoints.cs | 381 ++--- .../Endpoints/TemplateEndpoints.cs | 48 + .../Broadcast/ChannelDtos.cs | 13 +- .../GetChannel/GetChannelQueryHandler.cs | 9 +- .../UpdateViewerSettingsCommand.cs | 29 + .../UpdateViewerSettingsCommandHandler.cs | 38 + .../Common/Interfaces/ISiteSettings.cs | 37 +- .../Planning/Diff/PreviewApplyDiffQuery.cs | 28 + .../Diff/PreviewApplyDiffQueryHandler.cs | 143 ++ .../Planning/GridScheduleGenerator.cs | 25 +- .../Programming/Planning/PostCheckRunner.cs | 177 +++ .../Planning/Trace/GetEntryTraceQuery.cs | 37 + .../Trace/GetEntryTraceQueryHandler.cs | 141 ++ .../CopyTemplate/CopyTemplateCommand.cs | 31 + .../CopyTemplateCommandHandler.cs | 165 ++ .../Programming/Templates/PlanningRules.cs | 151 +- .../Validate/ValidateTemplateQuery.cs | 50 + .../Validate/ValidateTemplateQueryHandler.cs | 306 ++++ .../GetSiteSettingsQueryHandler.cs | 37 +- .../Settings/SettingKeys.cs | 28 +- .../Settings/SiteSettingsDto.cs | 13 +- .../UpdateSiteSettingsCommand.cs | 19 +- .../UpdateSiteSettingsCommandHandler.cs | 64 +- .../ListPublicChannelsQueryHandler.cs | 170 +- .../Streaming/StreamingDtos.cs | 76 +- .../src/TeleWave.Domain/Broadcast/Channel.cs | 34 + .../Broadcast/ChannelOverlay.cs | 14 + .../Programming/Planning/PlanningModels.cs | 11 + .../DependencyInjection.cs | 1 + ...26114417_ChannelViewerSettings.Designer.cs | 1386 +++++++++++++++++ .../20260726114417_ChannelViewerSettings.cs | 73 + .../Migrations/AppDbContextModelSnapshot.cs | 15 + .../Settings/SiteSettings.cs | 92 +- .../Programming/PlanningRulesTests.cs | 94 ++ .../Broadcast/ChannelTests.cs | 33 + docs/tv-scheduler-tasks.md | 32 +- .../features/admin/channels/ChannelDetail.tsx | 90 +- frontend/src/features/admin/channels/api.ts | 33 + .../admin/channels/components/ApplyDialog.tsx | 102 ++ .../channels/components/EntryTraceDialog.tsx | 104 ++ .../admin/channels/components/RulesCard.tsx | 429 ++--- .../channels/components/SchedulePreview.tsx | 119 +- .../channels/components/TemplateIssues.tsx | 88 ++ .../channels/components/TemplatePreview.tsx | 532 ++++--- .../admin/channels/components/ViewerCard.tsx | 146 ++ .../features/admin/settings/SettingsPanel.tsx | 193 ++- frontend/src/features/streaming/AirPage.tsx | 586 +++---- .../src/features/streaming/ChannelPlayer.tsx | 520 ++++--- .../src/features/streaming/PlayerOverlays.tsx | 103 ++ frontend/src/features/streaming/api.ts | 53 +- frontend/src/shared/api/types.ts | 100 +- frontend/src/shared/lib/i18n.ts | 133 +- 54 files changed, 5857 insertions(+), 1631 deletions(-) create mode 100644 backend/src/TeleWave.Application/Broadcast/UpdateViewerSettings/UpdateViewerSettingsCommand.cs create mode 100644 backend/src/TeleWave.Application/Broadcast/UpdateViewerSettings/UpdateViewerSettingsCommandHandler.cs create mode 100644 backend/src/TeleWave.Application/Programming/Planning/Diff/PreviewApplyDiffQuery.cs create mode 100644 backend/src/TeleWave.Application/Programming/Planning/Diff/PreviewApplyDiffQueryHandler.cs create mode 100644 backend/src/TeleWave.Application/Programming/Planning/PostCheckRunner.cs create mode 100644 backend/src/TeleWave.Application/Programming/Planning/Trace/GetEntryTraceQuery.cs create mode 100644 backend/src/TeleWave.Application/Programming/Planning/Trace/GetEntryTraceQueryHandler.cs create mode 100644 backend/src/TeleWave.Application/Programming/Templates/CopyTemplate/CopyTemplateCommand.cs create mode 100644 backend/src/TeleWave.Application/Programming/Templates/CopyTemplate/CopyTemplateCommandHandler.cs create mode 100644 backend/src/TeleWave.Application/Programming/Templates/Validate/ValidateTemplateQuery.cs create mode 100644 backend/src/TeleWave.Application/Programming/Templates/Validate/ValidateTemplateQueryHandler.cs create mode 100644 backend/src/TeleWave.Domain/Broadcast/ChannelOverlay.cs create mode 100644 backend/src/TeleWave.Infrastructure/Migrations/20260726114417_ChannelViewerSettings.Designer.cs create mode 100644 backend/src/TeleWave.Infrastructure/Migrations/20260726114417_ChannelViewerSettings.cs create mode 100644 backend/tests/TeleWave.Application.Tests/Programming/PlanningRulesTests.cs create mode 100644 frontend/src/features/admin/channels/components/ApplyDialog.tsx create mode 100644 frontend/src/features/admin/channels/components/EntryTraceDialog.tsx create mode 100644 frontend/src/features/admin/channels/components/TemplateIssues.tsx create mode 100644 frontend/src/features/admin/channels/components/ViewerCard.tsx create mode 100644 frontend/src/features/streaming/PlayerOverlays.tsx diff --git a/backend/src/TeleWave.Api/Endpoints/ChannelEndpoints.cs b/backend/src/TeleWave.Api/Endpoints/ChannelEndpoints.cs index a38d96a..6a3376c 100644 --- a/backend/src/TeleWave.Api/Endpoints/ChannelEndpoints.cs +++ b/backend/src/TeleWave.Api/Endpoints/ChannelEndpoints.cs @@ -9,6 +9,8 @@ using TeleWave.Application.Broadcast.UpdateChannelSettings; using TeleWave.Application.Broadcast.UpdateChannelTime; using TeleWave.Domain.Broadcast; using TeleWave.Infrastructure.Identity; +using TeleWave.Application.Broadcast.UpdateViewerSettings; +using TeleWave.Application.Programming.Planning.Trace; namespace TeleWave.Api.Endpoints; @@ -92,10 +94,47 @@ public static partial class ChannelEndpoints admin .MapGet("/{id:guid}/schedule", GetSchedule) .Produces>(); + admin + .MapPut("/{id:guid}/viewer", UpdateViewerSettings) + .Produces(StatusCodes.Status204NoContent); + + // «Почему это здесь»: цепочка происхождения записи, записанная в момент генерации. + admin.MapGet("/entries/{entryId:guid}/trace", GetEntryTrace).Produces(); return app; } + private static async Task UpdateViewerSettings( + Guid id, + UpdateViewerSettingsBody body, + ISender sender, + CancellationToken cancellationToken + ) + { + var result = await sender.Send( + new UpdateViewerSettingsCommand( + id, + body.LogoImageId, + body.LogoCorner, + body.LogoOpacity, + body.ShowClock, + body.AnalogFilterStrength + ), + cancellationToken + ); + return result.ToHttpResult(); + } + + private static async Task GetEntryTrace( + Guid entryId, + ISender sender, + CancellationToken cancellationToken + ) + { + var result = await sender.Send(new GetEntryTraceQuery(entryId), cancellationToken); + return result.ToHttpResult(); + } + private static async Task CreateChannel( CreateChannelCommand command, ISender sender, @@ -203,3 +242,12 @@ public sealed record UpdateChannelSettingsBody( BumperSettingsInput Bumper, Guid? FillerAssetId ); + +/// Оверлеи и аналоговый фильтр — как канал выглядит у зрителя (см. 6.8). +public sealed record UpdateViewerSettingsBody( + Guid? LogoImageId, + LogoCorner LogoCorner, + double LogoOpacity, + bool ShowClock, + double AnalogFilterStrength +); diff --git a/backend/src/TeleWave.Api/Endpoints/SettingsEndpoints.cs b/backend/src/TeleWave.Api/Endpoints/SettingsEndpoints.cs index d52763f..d26bb62 100644 --- a/backend/src/TeleWave.Api/Endpoints/SettingsEndpoints.cs +++ b/backend/src/TeleWave.Api/Endpoints/SettingsEndpoints.cs @@ -1,53 +1,55 @@ -using LiteCqrs; -using TeleWave.Api.Common; -using TeleWave.Application.Settings; -using TeleWave.Application.Settings.GetSiteSettings; -using TeleWave.Application.Settings.UpdateSiteSettings; -using TeleWave.Infrastructure.Identity; - -namespace TeleWave.Api.Endpoints; - -public static class SettingsEndpoints -{ - public static IEndpointRouteBuilder MapSettingsEndpoints(this IEndpointRouteBuilder app) - { - var admin = app.MapGroup("/api/admin/settings") - .WithTags("Admin.Settings") - .RequireAuthorization(policy => policy.RequireRole(RoleNames.Admin)); - - admin.MapGet("", GetSettings).Produces(); - admin.MapPut("", UpdateSettings).Produces(StatusCodes.Status204NoContent); - - return app; - } - - private static async Task GetSettings( - ISender sender, - CancellationToken cancellationToken - ) - { - var settings = await sender.Send(new GetSiteSettingsQuery(), cancellationToken); - return Results.Ok(settings); - } - - private static async Task UpdateSettings( - UpdateSiteSettingsBody body, - ISender sender, - CancellationToken cancellationToken - ) - { - var result = await sender.Send( - new UpdateSiteSettingsCommand( - body.RegistrationEnabled, - body.PreferredAudioLanguages ?? "" - ), - cancellationToken - ); - return result.ToHttpResult(); - } -} - -public sealed record UpdateSiteSettingsBody( - bool RegistrationEnabled, - string? PreferredAudioLanguages -); +using LiteCqrs; +using TeleWave.Api.Common; +using TeleWave.Application.Settings; +using TeleWave.Application.Settings.GetSiteSettings; +using TeleWave.Application.Settings.UpdateSiteSettings; +using TeleWave.Infrastructure.Identity; + +namespace TeleWave.Api.Endpoints; + +public static class SettingsEndpoints +{ + public static IEndpointRouteBuilder MapSettingsEndpoints(this IEndpointRouteBuilder app) + { + var admin = app.MapGroup("/api/admin/settings") + .WithTags("Admin.Settings") + .RequireAuthorization(policy => policy.RequireRole(RoleNames.Admin)); + + admin.MapGet("", GetSettings).Produces(); + admin.MapPut("", UpdateSettings).Produces(StatusCodes.Status204NoContent); + + return app; + } + + private static async Task GetSettings( + ISender sender, + CancellationToken cancellationToken + ) + { + var settings = await sender.Send(new GetSiteSettingsQuery(), cancellationToken); + return Results.Ok(settings); + } + + private static async Task UpdateSettings( + UpdateSiteSettingsBody body, + ISender sender, + CancellationToken cancellationToken + ) + { + var result = await sender.Send( + new UpdateSiteSettingsCommand( + body.RegistrationEnabled, + body.PreferredAudioLanguages ?? "", + body.ChannelNumbersEnabled + ), + cancellationToken + ); + return result.ToHttpResult(); + } +} + +public sealed record UpdateSiteSettingsBody( + bool RegistrationEnabled, + string? PreferredAudioLanguages, + bool ChannelNumbersEnabled +); diff --git a/backend/src/TeleWave.Api/Endpoints/StreamingEndpoints.cs b/backend/src/TeleWave.Api/Endpoints/StreamingEndpoints.cs index aff5998..64759c3 100644 --- a/backend/src/TeleWave.Api/Endpoints/StreamingEndpoints.cs +++ b/backend/src/TeleWave.Api/Endpoints/StreamingEndpoints.cs @@ -1,184 +1,197 @@ -using System.Globalization; -using System.Text; -using System.Text.RegularExpressions; -using LiteCqrs; -using Microsoft.Extensions.Hosting; -using TeleWave.Api.Common; -using TeleWave.Application.Common.Interfaces; -using TeleWave.Application.Streaming; -using TeleWave.Application.Streaming.GetLivePlaylist; -using TeleWave.Application.Streaming.GetPublicEpg; -using TeleWave.Application.Streaming.ListPublicChannels; -using TeleWave.Infrastructure.Media; -using TeleWave.Infrastructure.Streaming; - -namespace TeleWave.Api.Endpoints; - -public static class StreamingEndpoints -{ - private const string StreamCookieName = "tw_stream"; - private static readonly Regex SegmentFileName = new(@"^seg\d{1,6}\.ts$", RegexOptions.Compiled); - - public static IEndpointRouteBuilder MapStreamingEndpoints(this IEndpointRouteBuilder app) - { - // Публичный API канала (Bearer): список, EPG, выдача stream-cookie. - var channels = app.MapGroup("/api/channels").WithTags("Channels").RequireAuthorization(); - channels.MapGet("", ListChannels).Produces>(); - channels.MapPost("/{slug}/watch", Watch).Produces(StatusCodes.Status204NoContent); - channels.MapGet("/{slug}/epg", Epg); - - // Раздача эфира (cookie tw_stream): плейлист и сегменты — их грузит