Implement episode metadata management: add functionality to refresh episode metadata from external sources, including new API endpoints and UI integration. Enhance Show and Episode models to support additional metadata fields, and update database schema accordingly. Update ShowDetail and ShowMetadataCard components to display refreshed episode information and provide user feedback on metadata updates.
This commit is contained in:
@@ -24,6 +24,7 @@ public sealed class ScheduleGenerator(
|
||||
IRandomSource random,
|
||||
IBumperRenderer bumperRenderer,
|
||||
IBumperTemplateStorage bumperStorage,
|
||||
IMetadataImageStore metadataImages,
|
||||
IOptions<SchedulerOptions> options,
|
||||
IOptions<BumperOptions> bumperOptions,
|
||||
IOptions<StreamingOptions> streamingOptions,
|
||||
@@ -192,6 +193,13 @@ public sealed class ScheduleGenerator(
|
||||
var fromIds = pairs.Select(p => p.From).Distinct().ToList();
|
||||
var toIds = pairs.Select(p => p.To).Distinct().ToList();
|
||||
|
||||
// Постеры шоу-получателей — как фон заставки (если у канала нет своего фона).
|
||||
var showIds = fromIds.Concat(toIds).Distinct().ToList();
|
||||
var posterByShow = await dbContext.Shows.AsNoTracking()
|
||||
.Where(s => showIds.Contains(s.Id) && s.PosterPath != null)
|
||||
.Select(s => new { s.Id, s.PosterPath })
|
||||
.ToDictionaryAsync(s => s.Id, s => s.PosterPath!, cancellationToken);
|
||||
|
||||
// Кандидаты из кэша + статусы их ассетов (годятся только Ready — файлы могли удалить).
|
||||
var cached = await dbContext.BumperAssets.AsNoTracking()
|
||||
.Where(b => fromIds.Contains(b.FromShowId) && toIds.Contains(b.ToShowId))
|
||||
@@ -215,7 +223,8 @@ public sealed class ScheduleGenerator(
|
||||
{
|
||||
var fromName = showNames.GetValueOrDefault(pair.From, "…");
|
||||
var toName = showNames.GetValueOrDefault(pair.To, "…");
|
||||
var signature = ComputeSignature(fromName, toName, styleSignature);
|
||||
var toPosterRel = posterByShow.GetValueOrDefault(pair.To);
|
||||
var signature = ComputeSignature(fromName, toName, styleSignature, toPosterRel ?? "-");
|
||||
|
||||
var hit = cached.FirstOrDefault(c =>
|
||||
c.FromShowId == pair.From
|
||||
@@ -238,6 +247,7 @@ public sealed class ScheduleGenerator(
|
||||
fromName,
|
||||
toName,
|
||||
signature,
|
||||
toPosterRel,
|
||||
cancellationToken
|
||||
);
|
||||
result[pair] = assetId;
|
||||
@@ -263,13 +273,17 @@ public sealed class ScheduleGenerator(
|
||||
string fromName,
|
||||
string toName,
|
||||
string signature,
|
||||
string? toPosterRelative,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var asset = MediaAsset.RegisterGenerated($"Заставка: {fromName} → {toName}");
|
||||
var posterAbs = toPosterRelative is null
|
||||
? null
|
||||
: metadataImages.ResolveAbsolutePath(toPosterRelative);
|
||||
var render = await bumperRenderer.RenderAsync(
|
||||
asset.Id,
|
||||
BuildSpec(channel, fromName, toName),
|
||||
BuildSpec(channel, fromName, toName, posterAbs),
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
@@ -291,7 +305,12 @@ public sealed class ScheduleGenerator(
|
||||
return asset.Id;
|
||||
}
|
||||
|
||||
private BumperRenderSpec BuildSpec(Channel channel, string fromName, string toName) =>
|
||||
private BumperRenderSpec BuildSpec(
|
||||
Channel channel,
|
||||
string fromName,
|
||||
string toName,
|
||||
string? posterAbsolutePath
|
||||
) =>
|
||||
new(
|
||||
AlignedBumperDuration(channel),
|
||||
_bumper.Width,
|
||||
@@ -306,7 +325,8 @@ public sealed class ScheduleGenerator(
|
||||
channel.BumperNextLabel,
|
||||
toName,
|
||||
bumperStorage.BackgroundPath(channel.Id, channel.BumperBackgroundExtension),
|
||||
bumperStorage.MusicPath(channel.Id, channel.BumperMusicExtension)
|
||||
bumperStorage.MusicPath(channel.Id, channel.BumperMusicExtension),
|
||||
posterAbsolutePath
|
||||
);
|
||||
|
||||
private string FontPath(BumperFont font) =>
|
||||
@@ -340,9 +360,14 @@ public sealed class ScheduleGenerator(
|
||||
channel.BumperMusicExtension ?? "-"
|
||||
);
|
||||
|
||||
private static string ComputeSignature(string fromName, string toName, string styleSignature)
|
||||
private static string ComputeSignature(
|
||||
string fromName,
|
||||
string toName,
|
||||
string styleSignature,
|
||||
string poster
|
||||
)
|
||||
{
|
||||
var raw = string.Join('', fromName, toName, styleSignature);
|
||||
var raw = string.Join('', fromName, toName, styleSignature, poster);
|
||||
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(raw));
|
||||
return Convert.ToHexString(hash);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user