153 lines
6.6 KiB
C#
153 lines
6.6 KiB
C#
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++;
|
||
}
|
||
}
|