Implement background clip functionality for bumpers
Enhanced the bumper system to support a new background type, 'Clip', allowing text to be overlaid on existing video clips. This update includes the addition of a BackgroundClipShowId property in various records and classes, ensuring that the clip's duration dictates the bumper length. Updated validation rules, mapping, and rendering logic to accommodate this new feature. Localization strings were also updated to reflect the new background option and its implications for audio handling.
This commit is contained in:
@@ -57,6 +57,7 @@ public sealed class BumperResolver(
|
||||
tokens,
|
||||
cancellationToken
|
||||
);
|
||||
var clipAssets = await LoadClipAssetsAsync(templates.Values, cancellationToken);
|
||||
var requests = new List<(int Index, BumperRequest Request)>();
|
||||
|
||||
foreach (var (item, index) in reserved)
|
||||
@@ -91,6 +92,15 @@ public sealed class BumperResolver(
|
||||
_ => (Guid?)null,
|
||||
};
|
||||
|
||||
// Ролик-подложка: нет готового ассета — подблок работает как обычно, по фону блока.
|
||||
// Ронять заставку из-за необработанного ролика нельзя, она уже стоит в ленте.
|
||||
var clipAssetId =
|
||||
variant.Background == BumperBackground.Clip
|
||||
&& variant.BackgroundClipShowId is { } clipShowId
|
||||
&& clipAssets.TryGetValue(clipShowId, out var found)
|
||||
? found
|
||||
: (Guid?)null;
|
||||
|
||||
var linesJson = BumperRenderedText.ToJson(lines);
|
||||
requests.Add(
|
||||
(
|
||||
@@ -100,7 +110,8 @@ public sealed class BumperResolver(
|
||||
variant.Id,
|
||||
linesJson,
|
||||
posterShowId,
|
||||
ComputeSignature(template, variant.Id, linesJson, posterShowId)
|
||||
clipAssetId,
|
||||
ComputeSignature(template, variant.Id, linesJson, posterShowId, clipAssetId)
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -138,7 +149,8 @@ public sealed class BumperResolver(
|
||||
request.Signature,
|
||||
request.Lines,
|
||||
request.PosterShowId,
|
||||
asset.Id
|
||||
asset.Id,
|
||||
request.BackgroundAssetId
|
||||
)
|
||||
);
|
||||
|
||||
@@ -214,22 +226,69 @@ public sealed class BumperResolver(
|
||||
BumperTemplate template,
|
||||
Guid variantId,
|
||||
string linesJson,
|
||||
Guid? posterShowId
|
||||
Guid? posterShowId,
|
||||
Guid? backgroundAssetId
|
||||
)
|
||||
{
|
||||
var raw = string.Create(
|
||||
CultureInfo.InvariantCulture,
|
||||
$"{template.Id}|{template.Revision}|{variantId}|{posterShowId}|{linesJson}"
|
||||
$"{template.Id}|{template.Revision}|{variantId}|{posterShowId}|{backgroundAssetId}|{linesJson}"
|
||||
);
|
||||
return Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(raw)))[..32];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Готовые ассеты роликов-подложек по шоу. Берётся первая готовая серия: у ролика она всегда
|
||||
/// одна, а необработанному в эфире взяться неоткуда.
|
||||
/// </summary>
|
||||
private async Task<Dictionary<Guid, Guid>> LoadClipAssetsAsync(
|
||||
IEnumerable<BumperTemplate> templates,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var showIds = templates
|
||||
.SelectMany(t => t.Variants)
|
||||
.Where(v => v.Background == BumperBackground.Clip)
|
||||
.Select(v => v.BackgroundClipShowId)
|
||||
.Where(id => id is not null)
|
||||
.Select(id => id!.Value)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
if (showIds.Count == 0)
|
||||
return [];
|
||||
|
||||
var episodes = await dbContext
|
||||
.Shows.AsNoTracking()
|
||||
.SelectMany(s => s.Episodes)
|
||||
.Where(e => showIds.Contains(e.ShowId))
|
||||
.OrderBy(e => e.Position)
|
||||
.Select(e => new { e.ShowId, e.MediaAssetId })
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var ready = await dbContext
|
||||
.MediaAssets.AsNoTracking()
|
||||
.Where(a =>
|
||||
a.Status == MediaAssetStatus.Ready
|
||||
&& a.Duration != null
|
||||
&& episodes.Select(e => e.MediaAssetId).Contains(a.Id)
|
||||
)
|
||||
.Select(a => a.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var readySet = ready.ToHashSet();
|
||||
return episodes
|
||||
.Where(e => readySet.Contains(e.MediaAssetId))
|
||||
.GroupBy(e => e.ShowId)
|
||||
.ToDictionary(g => g.Key, g => g.First().MediaAssetId);
|
||||
}
|
||||
|
||||
/// <summary>Что нужно отрендерить для одной записи ленты.</summary>
|
||||
private sealed record BumperRequest(
|
||||
BumperTemplate Template,
|
||||
Guid VariantId,
|
||||
string Lines,
|
||||
Guid? PosterShowId,
|
||||
Guid? BackgroundAssetId,
|
||||
string Signature
|
||||
)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user