Implement asynchronous bumper rendering and add integration tests: refactor BumperAsset to support async rendering, introduce BumperRenderBackgroundService for processing, and create TeleWave.Integration.Tests with Testcontainers-Postgres for comprehensive testing of scheduling and bumper generation scenarios. Update documentation and ensure tests skip if Docker is not available.

This commit is contained in:
Leonid Pershin
2026-07-25 23:42:40 +03:00
parent 6c18a9da79
commit d6ad8e11a4
22 changed files with 1967 additions and 197 deletions
@@ -1,10 +1,12 @@
namespace TeleWave.Domain.Broadcast;
/// <summary>
/// Кэш отрендеренной ТВ-заставки перехода. Один сгенерированный <see cref="MediaAssetId"/> на
/// уникальную комбинацию (<see cref="FromShowId"/> → <see cref="ToShowId"/>) при данной
/// <see cref="Signature"/> (хэш названий шоу и версии шаблона). Переиспользуется между днями и
/// каналами; при смене названий/шаблона <see cref="Signature"/> меняется и рендерится новый ассет.
/// Кэш ТВ-заставки перехода. Один сгенерированный <see cref="MediaAssetId"/> на уникальную комбинацию
/// (<see cref="FromShowId"/> → <see cref="ToShowId"/>) при данной <see cref="Signature"/> (хэш названий
/// шоу и версии шаблона). Ассет создаётся в статусе Pending и рендерится ffmpeg'ом асинхронно фоновым
/// сервисом — поэтому здесь же храним, ЧЕМ его рендерить (<see cref="ChannelId"/>/<see cref="TemplateId"/>/
/// <see cref="VariantId"/>), чтобы фоновый рендерер восстановил спецификацию без участия планировщика.
/// Переиспользуется между днями и каналами; при смене названий/шаблона сигнатура меняется — новый ассет.
/// </summary>
public class BumperAsset
{
@@ -12,10 +14,15 @@ public class BumperAsset
public Guid FromShowId { get; private set; }
public Guid ToShowId { get; private set; }
/// <summary>Канал/блок/подблок, по которым фоновый рендерер восстановит спецификацию заставки.</summary>
public Guid ChannelId { get; private set; }
public Guid TemplateId { get; private set; }
public Guid VariantId { get; private set; }
/// <summary>Хэш входных данных рендера (названия «из/в» + версия шаблона).</summary>
public string Signature { get; private set; } = string.Empty;
/// <summary>Сгенерированный ассет-заставка (нарезан в assets/{id}, статус Ready).</summary>
/// <summary>Сгенерированный ассет-заставка (нарезается в assets/{id} фоновым рендерером).</summary>
public Guid MediaAssetId { get; private set; }
public DateTimeOffset CreatedAt { get; private set; }
@@ -23,6 +30,9 @@ public class BumperAsset
private BumperAsset() { }
public static BumperAsset Create(
Guid channelId,
Guid templateId,
Guid variantId,
Guid fromShowId,
Guid toShowId,
string signature,
@@ -31,6 +41,9 @@ public class BumperAsset
new()
{
Id = Guid.NewGuid(),
ChannelId = channelId,
TemplateId = templateId,
VariantId = variantId,
FromShowId = fromShowId,
ToShowId = toShowId,
Signature = signature,
@@ -56,8 +56,9 @@ public class MediaAsset
}
/// <summary>
/// Ассет, сгенерированный системой (ТВ-заставка). Нарезку делает не общий пайплайн обработки, а
/// специализированный рендерер, поэтому сразу помечаем <see cref="MarkReady"/> после создания.
/// Ассет, сгенерированный системой (ТВ-заставка). Создаётся в <see cref="MediaAssetStatus.Pending"/>
/// и нарезается не общим пайплайном обработки, а специализированным фоновым рендерером
/// (BumperRenderBackgroundService), который по готовности вызывает <see cref="MarkReady"/>.
/// </summary>
public static MediaAsset RegisterGenerated(string displayName)
{