Add bumper variant management: implement API endpoints for adding, updating, and removing bumper text variants, enhance data models to support variant details, and update scheduling logic to utilize variants. Refactor related components for improved bumper template handling and ensure proper error management for variant operations.

This commit is contained in:
Leonid Pershin
2026-07-25 13:24:11 +03:00
parent f640af1fc4
commit a65bcf4258
34 changed files with 2009 additions and 170 deletions
@@ -1,16 +1,17 @@
namespace TeleWave.Domain.Broadcast;
/// <summary>
/// Блок ТВ-заставки канала: свой звук + своё оформление (цвета, опциональная фон-картинка). На
/// переходе между шоу генератор рендерит «Сейчас/Далее» стилем блока поверх его звука; длительность
/// заставки определяется длиной звука (выравнивается на сегмент при рендере). Общие для канала шрифт,
/// подписи и правила показа живут на <see cref="Channel"/>.
/// Блок ТВ-заставки канала: свой звук + своё оформление (цвета, опциональная фон-картинка) + набор
/// подблоков (<see cref="Variants"/>) с разным текстом и правилом показа. Длительность заставки — по
/// длине звука (выравнивается на сегмент при рендере). Общий для канала — только шрифт.
///
/// Первый блок (<see cref="Position"/> == 0) — дефолтный, не удаляется; если звук в нём не загружен,
/// рендер синтезирует джингл по умолчанию.
/// </summary>
public class BumperTemplate
{
private readonly List<BumperTextVariant> _variants = new();
public Guid Id { get; private set; }
public Guid ChannelId { get; private set; }
@@ -46,10 +47,16 @@ public class BumperTemplate
public bool IsDefault => Position == 0;
/// <summary>Подблоки (текст-варианты); порядок — по <see cref="BumperTextVariant.Position"/>.</summary>
public IReadOnlyList<BumperTextVariant> Variants => _variants;
private const string DefaultVariantName = "Текст 1";
private BumperTemplate() { }
internal static BumperTemplate Create(Guid channelId, int position, string name) =>
new()
internal static BumperTemplate Create(Guid channelId, int position, string name)
{
var template = new BumperTemplate
{
Id = Guid.NewGuid(),
ChannelId = channelId,
@@ -65,6 +72,35 @@ public class BumperTemplate
Revision = 0,
CreatedAt = DateTimeOffset.UtcNow,
};
// Дефолтный подблок «Сейчас/Далее», показывается на смене шоу.
template._variants.Add(
BumperTextVariant.Create(template.Id, 0, DefaultVariantName, BumperTrigger.OnShowChange)
);
return template;
}
public BumperTextVariant AddVariant(string name)
{
var nextPosition = _variants.Count == 0 ? 0 : _variants.Max(v => v.Position) + 1;
var variant = BumperTextVariant.Create(Id, nextPosition, name, BumperTrigger.OnShowChange);
_variants.Add(variant);
return variant;
}
public BumperTextVariant? FindVariant(Guid variantId) =>
_variants.FirstOrDefault(v => v.Id == variantId);
/// <summary>Удалить подблок. Последний подблок удалить нельзя (нужен хотя бы один) — вернёт false.</summary>
public bool RemoveVariant(Guid variantId)
{
if (_variants.Count <= 1)
return false;
var variant = _variants.FirstOrDefault(v => v.Id == variantId);
if (variant is null)
return false;
_variants.Remove(variant);
return true;
}
/// <summary>Обновить имя и цвета блока. Цвета — в нотации ffmpeg (0xRRGGBB или имя).</summary>
public void UpdateStyle(