Files
TeleWave/backend/src/TeleWave.Domain/Broadcast/BumperTemplate.cs
T

153 lines
6.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace TeleWave.Domain.Broadcast;
/// <summary>
/// Блок ТВ-заставки канала: свой звук + своё оформление (цвета, опциональная фон-картинка) + набор
/// подблоков (<see cref="Variants"/>) с разным текстом и правилом показа. Длительность заставки — по
/// длине звука (выравнивается на сегмент при рендере). Общий для канала — только шрифт.
///
/// Первый блок (<see cref="Position"/> == 0) — дефолтный, не удаляется; если звук в нём не загружен,
/// рендер синтезирует джингл по умолчанию.
/// </summary>
public class BumperTemplate
{
private readonly List<BumperTextVariant> _variants = [];
public Guid Id { get; private set; }
public Guid ChannelId { get; private set; }
/// <summary>Порядковый номер (0 — дефолтный блок). Используется ротацией и как признак дефолта.</summary>
public int Position { get; private set; }
public string Name { get; private set; } = string.Empty;
// ── Оформление блока (цвета — в нотации ffmpeg: 0xRRGGBB или имя) ──
public string BackgroundColor { get; private set; } = DefaultBackgroundColor;
public string BackgroundColor2 { get; private set; } = DefaultBackgroundColor2;
public string AccentColor { get; private set; } = DefaultAccentColor;
public string TextColor { get; private set; } = DefaultTextColor;
/// <summary>Фон-картинка блока — ссылка на запись реестра изображений или null (тогда фон градиент/постер).</summary>
public Guid? BackgroundImageId { get; private set; }
/// <summary>Расширение загруженного звука (с точкой) или null — тогда синтезируется джингл.</summary>
public string? AudioExtension { get; private set; }
/// <summary>Длина загруженного звука в секундах (замер ffprobe) или null, если звука нет.</summary>
public double? AudioDurationSeconds { get; private set; }
/// <summary>Версия файлов блока (звук/фон). Входит в кэш-ключ рендера — замена файла пересобирает заставки.</summary>
public int Revision { get; private set; }
public DateTimeOffset CreatedAt { get; private set; }
public const string DefaultBackgroundColor = "0x0b1020";
public const string DefaultBackgroundColor2 = "0x1e293b";
public const string DefaultAccentColor = "0x38bdf8";
public const string DefaultTextColor = "white";
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)
{
var template = new BumperTemplate
{
Id = Guid.NewGuid(),
ChannelId = channelId,
Position = position,
Name = name,
BackgroundColor = DefaultBackgroundColor,
BackgroundColor2 = DefaultBackgroundColor2,
AccentColor = DefaultAccentColor,
TextColor = DefaultTextColor,
BackgroundImageId = null,
AudioExtension = null,
AudioDurationSeconds = null,
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(
string name,
string backgroundColor,
string backgroundColor2,
string accentColor,
string textColor
)
{
Name = name;
BackgroundColor = backgroundColor;
BackgroundColor2 = backgroundColor2;
AccentColor = accentColor;
TextColor = textColor;
}
/// <summary>Отметить загруженный звук (extension — с точкой, нижний регистр) и его длину. Меняет ревизию.</summary>
public void SetAudio(string extension, double durationSeconds)
{
AudioExtension = extension;
AudioDurationSeconds = durationSeconds > 0 ? durationSeconds : null;
Revision++;
}
public void ClearAudio()
{
if (AudioExtension is null && AudioDurationSeconds is null)
return;
AudioExtension = null;
AudioDurationSeconds = null;
Revision++;
}
/// <summary>Привязать фон-картинку блока (ссылка на реестр изображений). Меняет ревизию.</summary>
public void SetBackgroundImage(Guid imageId)
{
BackgroundImageId = imageId;
Revision++;
}
public void ClearBackgroundImage()
{
if (BackgroundImageId is null)
return;
BackgroundImageId = null;
Revision++;
}
}