Files
TeleWave/backend/src/TeleWave.Infrastructure/Media/BumperRenderBackgroundService.cs
T

88 lines
3.7 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.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using TeleWave.Application.Broadcast.Bumpers;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Domain.Media;
namespace TeleWave.Infrastructure.Media;
/// <summary>Захваченная на рендер заставка: спецификация собирается уже в самой работе.</summary>
internal sealed record BumperRenderJob(Guid AssetId);
/// <summary>
/// Асинхронно рендерит ТВ-заставки расписания: планировщик лишь создаёт ассет (Source=Generated) в
/// статусе Pending и кэш-строку <see cref="Domain.Broadcast.BumperAsset"/>, а сам ffmpeg крутится здесь,
/// вне тика планировщика и его транзакции. Захват работы и устойчивость к рестарту — в
/// <see cref="MediaClaimingBackgroundService{TJob}"/>. До готовности ассета плейлист отдаёт филлер.
///
/// Рендер строго последовательный: ffmpeg заставки короткий, а параллелить его смысла нет —
/// очередь разбирается быстрее, чем планировщик успевает её пополнять.
/// </summary>
internal sealed class BumperRenderBackgroundService(
IBumperRenderQueue queue,
IServiceScopeFactory scopeFactory,
IBumperRenderer renderer,
ILogger<BumperRenderBackgroundService> logger
) : MediaClaimingBackgroundService<BumperRenderJob>(scopeFactory, logger)
{
protected override bool HandlesGenerated => true;
protected override string LoopErrorMessage => "Ошибка цикла рендера заставок";
protected override ValueTask WaitForWorkAsync(CancellationToken cancellationToken) =>
queue.WaitAsync(cancellationToken);
protected override BumperRenderJob ToJob(MediaAsset asset) => new(asset.Id);
protected override async Task ProcessAsync(
BumperRenderJob job,
CancellationToken cancellationToken
)
{
try
{
var spec = await WithScopeAsync<BumperSpecLoader, BumperRenderSpec?>(loader =>
loader.LoadAsync(job.AssetId, cancellationToken)
);
if (spec is null)
{
await FailAsync(
job.AssetId,
"Не удалось восстановить спецификацию заставки",
cancellationToken
);
return;
}
var render = await renderer.RenderAsync(job.AssetId, spec, cancellationToken);
await WithAssetAsync(
job.AssetId,
asset =>
asset.MarkReady(
new MediaReadyInfo(
render.Duration,
render.SegmentSeconds,
render.SegmentCount,
render.Width,
render.Height,
"h264",
"aac",
render.RelativePath
)
),
cancellationToken
);
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
throw;
}
catch (Exception ex)
{
logger.LogError(ex, "Рендер заставки {AssetId} провалился", job.AssetId);
await FailAsync(job.AssetId, ex.Message, CancellationToken.None);
}
}
}