Browse Source

add graphics_on and graphics_off yaml playout instructions (#2283)

pull/2284/head
Jason Dove 12 months ago committed by GitHub
parent
commit
2dc5bf58a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      CHANGELOG.md
  2. 20
      ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs
  3. 8
      ErsatzTV.Core/Interfaces/Repositories/IGraphicsElementRepository.cs
  4. 13
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutAllHandler.cs
  5. 13
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutCountHandler.cs
  6. 13
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutDurationHandler.cs
  7. 64
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutGraphicsOffHandler.cs
  8. 62
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutGraphicsOnHandler.cs
  9. 2
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutMidRollHandler.cs
  10. 2
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutPostRollHandler.cs
  11. 2
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutPreRollHandler.cs
  12. 9
      ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutGraphicsOffInstruction.cs
  13. 9
      ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutGraphicsOnInstruction.cs
  14. 2
      ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutMidRollInstruction.cs
  15. 2
      ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutPostRollInstruction.cs
  16. 2
      ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutPreRollInstruction.cs
  17. 17
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutBuilder.cs
  18. 65
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContext.cs
  19. 24
      ErsatzTV.Infrastructure/Data/Repositories/GraphicsElementRepository.cs
  20. 18
      ErsatzTV/Resources/yaml-playout-import.schema.json
  21. 20
      ErsatzTV/Resources/yaml-playout.schema.json
  22. 1
      ErsatzTV/Startup.cs

3
CHANGELOG.md

@ -25,6 +25,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Supported in playback troubleshooting - Supported in playback troubleshooting
- Displays multi-line text in a specified font, color, location, z-index - Displays multi-line text in a specified font, color, location, z-index
- Supports constant opacity and opacity expression - Supports constant opacity and opacity expression
- YAML playout: add `graphics_on` and `graphics_off` instructions to control graphics elements
- `graphics_on` requires the name of a graphics element template, e.g. `text/cool_element.yml`
- `graphics_off` will turn off a specific element, or all elements if none are specified
### Fix ### Fix
- Fix database operations that were slowing down playout builds - Fix database operations that were slowing down playout builds

20
ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs

@ -136,7 +136,8 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
changeCount += 1; changeCount += 1;
var bulkConfig = new BulkConfig(); var bulkConfig = new BulkConfig();
bool anyWatermarks = result.AddedItems.Any(i => i.PlayoutItemWatermarks is not null && i.PlayoutItemWatermarks.Count > 0); bool anyWatermarks = result.AddedItems.Any(i => i.PlayoutItemWatermarks is not null && i.PlayoutItemWatermarks.Count > 0);
if (anyWatermarks) bool anyGraphicsElements = result.AddedItems.Any(i => i.PlayoutItemGraphicsElements is not null && i.PlayoutItemGraphicsElements.Count > 0);
if (anyWatermarks || anyGraphicsElements)
{ {
bulkConfig.SetOutputIdentity = true; bulkConfig.SetOutputIdentity = true;
} }
@ -157,6 +158,23 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
await dbContext.BulkInsertAsync(allWatermarks, cancellationToken: cancellationToken); await dbContext.BulkInsertAsync(allWatermarks, cancellationToken: cancellationToken);
} }
if (anyGraphicsElements)
{
Console.WriteLine("has graphics elements!");
// copy playout item ids back to graphics elements
var allGraphicsElements = result.AddedItems.SelectMany(item =>
item.PlayoutItemGraphicsElements.Select(graphicsElement =>
{
graphicsElement.PlayoutItemId = item.Id;
graphicsElement.PlayoutItem = null;
return graphicsElement;
})
).ToList();
await dbContext.BulkInsertAsync(allGraphicsElements, cancellationToken: cancellationToken);
}
} }
if (result.HistoryToRemove.Count > 0) if (result.HistoryToRemove.Count > 0)

8
ErsatzTV.Core/Interfaces/Repositories/IGraphicsElementRepository.cs

@ -0,0 +1,8 @@
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Core.Interfaces.Repositories;
public interface IGraphicsElementRepository
{
Task<Option<GraphicsElement>> GetGraphicsElementByPath(string path);
}

13
ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutAllHandler.cs

@ -65,7 +65,8 @@ public class YamlPlayoutAllHandler(EnumeratorCache enumeratorCache) : YamlPlayou
//BlockKey = JsonConvert.SerializeObject(effectiveBlock.BlockKey), //BlockKey = JsonConvert.SerializeObject(effectiveBlock.BlockKey),
//CollectionKey = JsonConvert.SerializeObject(collectionKey, JsonSettings), //CollectionKey = JsonConvert.SerializeObject(collectionKey, JsonSettings),
//CollectionEtag = collectionEtags[collectionKey] //CollectionEtag = collectionEtags[collectionKey]
PlayoutItemWatermarks = [] PlayoutItemWatermarks = [],
PlayoutItemGraphicsElements = []
}; };
foreach (int watermarkId in context.GetChannelWatermarkIds()) foreach (int watermarkId in context.GetChannelWatermarkIds())
@ -78,6 +79,16 @@ public class YamlPlayoutAllHandler(EnumeratorCache enumeratorCache) : YamlPlayou
}); });
} }
foreach (int graphicsElementId in context.GetGraphicsElementIds())
{
playoutItem.PlayoutItemGraphicsElements.Add(
new PlayoutItemGraphicsElement
{
PlayoutItem = playoutItem,
GraphicsElementId = graphicsElementId
});
}
await AddItemAndMidRoll(context, playoutItem, mediaItem, executeSequence); await AddItemAndMidRoll(context, playoutItem, mediaItem, executeSequence);
context.AdvanceGuideGroup(); context.AdvanceGuideGroup();

13
ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutCountHandler.cs

@ -90,7 +90,8 @@ public class YamlPlayoutCountHandler(EnumeratorCache enumeratorCache) : YamlPlay
//BlockKey = JsonConvert.SerializeObject(effectiveBlock.BlockKey), //BlockKey = JsonConvert.SerializeObject(effectiveBlock.BlockKey),
//CollectionKey = JsonConvert.SerializeObject(collectionKey, JsonSettings), //CollectionKey = JsonConvert.SerializeObject(collectionKey, JsonSettings),
//CollectionEtag = collectionEtags[collectionKey], //CollectionEtag = collectionEtags[collectionKey],
PlayoutItemWatermarks = [] PlayoutItemWatermarks = [],
PlayoutItemGraphicsElements = []
}; };
foreach (int watermarkId in context.GetChannelWatermarkIds()) foreach (int watermarkId in context.GetChannelWatermarkIds())
@ -103,6 +104,16 @@ public class YamlPlayoutCountHandler(EnumeratorCache enumeratorCache) : YamlPlay
}); });
} }
foreach (int graphicsElementId in context.GetGraphicsElementIds())
{
playoutItem.PlayoutItemGraphicsElements.Add(
new PlayoutItemGraphicsElement
{
PlayoutItem = playoutItem,
GraphicsElementId = graphicsElementId
});
}
await AddItemAndMidRoll(context, playoutItem, mediaItem, executeSequence); await AddItemAndMidRoll(context, playoutItem, mediaItem, executeSequence);
context.AdvanceGuideGroup(); context.AdvanceGuideGroup();

13
ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutDurationHandler.cs

@ -124,7 +124,8 @@ public class YamlPlayoutDurationHandler(EnumeratorCache enumeratorCache) : YamlP
FillerKind = fillerKind, FillerKind = fillerKind,
CustomTitle = string.IsNullOrWhiteSpace(customTitle) ? null : customTitle, CustomTitle = string.IsNullOrWhiteSpace(customTitle) ? null : customTitle,
DisableWatermarks = disableWatermarks, DisableWatermarks = disableWatermarks,
PlayoutItemWatermarks = [] PlayoutItemWatermarks = [],
PlayoutItemGraphicsElements = []
}; };
foreach (int watermarkId in context.GetChannelWatermarkIds()) foreach (int watermarkId in context.GetChannelWatermarkIds())
@ -137,6 +138,16 @@ public class YamlPlayoutDurationHandler(EnumeratorCache enumeratorCache) : YamlP
}); });
} }
foreach (int graphicsElementId in context.GetGraphicsElementIds())
{
playoutItem.PlayoutItemGraphicsElements.Add(
new PlayoutItemGraphicsElement
{
PlayoutItem = playoutItem,
GraphicsElementId = graphicsElementId
});
}
if (remainingToFill - itemDuration >= TimeSpan.Zero || !stopBeforeEnd) if (remainingToFill - itemDuration >= TimeSpan.Zero || !stopBeforeEnd)
{ {
context.AddedItems.Add(playoutItem); context.AddedItems.Add(playoutItem);

64
ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutGraphicsOffHandler.cs

@ -0,0 +1,64 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Scheduling.YamlScheduling.Models;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.Core.Scheduling.YamlScheduling.Handlers;
public class YamlPlayoutGraphicsOffHandler(IGraphicsElementRepository graphicsElementRepository) : IYamlPlayoutHandler
{
private readonly Dictionary<string, Option<GraphicsElement>> _graphicsElementCache = new();
public bool Reset => false;
public async Task<bool> Handle(
YamlPlayoutContext context,
YamlPlayoutInstruction instruction,
PlayoutBuildMode mode,
Func<string, Task> executeSequence,
ILogger<YamlPlayoutBuilder> logger,
CancellationToken cancellationToken)
{
if (instruction is not YamlPlayoutGraphicsOffInstruction graphicsOff)
{
return false;
}
if (string.IsNullOrWhiteSpace(graphicsOff.GraphicsOff))
{
context.ClearGraphicsElements();
}
else
{
foreach (var ge in await GetGraphicsElementByPath(graphicsOff.GraphicsOff))
{
context.RemoveGraphicsElementId(ge.Id);
}
}
return true;
}
private async Task<Option<GraphicsElement>> GetGraphicsElementByPath(string path)
{
if (_graphicsElementCache.TryGetValue(path, out var cachedGraphicsElement))
{
foreach (GraphicsElement graphicsElement in cachedGraphicsElement)
{
return graphicsElement;
}
}
else
{
Option<GraphicsElement> maybeGraphicsElement =
await graphicsElementRepository.GetGraphicsElementByPath(path);
_graphicsElementCache.Add(path, maybeGraphicsElement);
foreach (GraphicsElement graphicsElement in maybeGraphicsElement)
{
return graphicsElement;
}
}
return Option<GraphicsElement>.None;
}
}

62
ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutGraphicsOnHandler.cs

@ -0,0 +1,62 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Scheduling.YamlScheduling.Models;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.Core.Scheduling.YamlScheduling.Handlers;
public class YamlPlayoutGraphicsOnHandler(IGraphicsElementRepository graphicsElementRepository) : IYamlPlayoutHandler
{
private readonly Dictionary<string, Option<GraphicsElement>> _graphicsElementCache = new();
public bool Reset => false;
public async Task<bool> Handle(
YamlPlayoutContext context,
YamlPlayoutInstruction instruction,
PlayoutBuildMode mode,
Func<string, Task> executeSequence,
ILogger<YamlPlayoutBuilder> logger,
CancellationToken cancellationToken)
{
if (instruction is not YamlPlayoutGraphicsOnInstruction graphicsOn)
{
return false;
}
if (string.IsNullOrWhiteSpace(graphicsOn.GraphicsOn))
{
return false;
}
foreach (var ge in await GetGraphicsElementByPath(graphicsOn.GraphicsOn))
{
context.SetGraphicsElementId(ge.Id);
}
return true;
}
private async Task<Option<GraphicsElement>> GetGraphicsElementByPath(string path)
{
if (_graphicsElementCache.TryGetValue(path, out var cachedGraphicsElement))
{
foreach (GraphicsElement graphicsElement in cachedGraphicsElement)
{
return graphicsElement;
}
}
else
{
Option<GraphicsElement> maybeGraphicsElement =
await graphicsElementRepository.GetGraphicsElementByPath(path);
_graphicsElementCache.Add(path, maybeGraphicsElement);
foreach (GraphicsElement graphicsElement in maybeGraphicsElement)
{
return graphicsElement;
}
}
return Option<GraphicsElement>.None;
}
}

2
ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutMidRollHandler.cs

@ -15,7 +15,7 @@ public class YamlPlayoutMidRollHandler : IYamlPlayoutHandler
ILogger<YamlPlayoutBuilder> logger, ILogger<YamlPlayoutBuilder> logger,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (instruction is not YamlMidRollInstruction midRoll) if (instruction is not YamlPlayoutMidRollInstruction midRoll)
{ {
return Task.FromResult(false); return Task.FromResult(false);
} }

2
ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutPostRollHandler.cs

@ -15,7 +15,7 @@ public class YamlPlayoutPostRollHandler : IYamlPlayoutHandler
ILogger<YamlPlayoutBuilder> logger, ILogger<YamlPlayoutBuilder> logger,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (instruction is not YamlPostRollInstruction postRoll) if (instruction is not YamlPlayoutPostRollInstruction postRoll)
{ {
return Task.FromResult(false); return Task.FromResult(false);
} }

2
ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutPreRollHandler.cs

@ -15,7 +15,7 @@ public class YamlPlayoutPreRollHandler : IYamlPlayoutHandler
ILogger<YamlPlayoutBuilder> logger, ILogger<YamlPlayoutBuilder> logger,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (instruction is not YamlPreRollInstruction preRoll) if (instruction is not YamlPlayoutPreRollInstruction preRoll)
{ {
return Task.FromResult(false); return Task.FromResult(false);
} }

9
ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutGraphicsOffInstruction.cs

@ -0,0 +1,9 @@
using YamlDotNet.Serialization;
namespace ErsatzTV.Core.Scheduling.YamlScheduling.Models;
public class YamlPlayoutGraphicsOffInstruction : YamlPlayoutInstruction
{
[YamlMember(Alias = "graphics_off", ApplyNamingConventions = false)]
public string GraphicsOff { get; set; }
}

9
ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutGraphicsOnInstruction.cs

@ -0,0 +1,9 @@
using YamlDotNet.Serialization;
namespace ErsatzTV.Core.Scheduling.YamlScheduling.Models;
public class YamlPlayoutGraphicsOnInstruction : YamlPlayoutInstruction
{
[YamlMember(Alias = "graphics_on", ApplyNamingConventions = false)]
public string GraphicsOn { get; set; }
}

2
ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlMidRollInstruction.cs → ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutMidRollInstruction.cs

@ -2,7 +2,7 @@ using YamlDotNet.Serialization;
namespace ErsatzTV.Core.Scheduling.YamlScheduling.Models; namespace ErsatzTV.Core.Scheduling.YamlScheduling.Models;
public class YamlMidRollInstruction : YamlPlayoutInstruction public class YamlPlayoutMidRollInstruction : YamlPlayoutInstruction
{ {
[YamlMember(Alias = "mid_roll", ApplyNamingConventions = false)] [YamlMember(Alias = "mid_roll", ApplyNamingConventions = false)]
public bool MidRoll { get; set; } public bool MidRoll { get; set; }

2
ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPostRollInstruction.cs → ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutPostRollInstruction.cs

@ -2,7 +2,7 @@ using YamlDotNet.Serialization;
namespace ErsatzTV.Core.Scheduling.YamlScheduling.Models; namespace ErsatzTV.Core.Scheduling.YamlScheduling.Models;
public class YamlPostRollInstruction : YamlPlayoutInstruction public class YamlPlayoutPostRollInstruction : YamlPlayoutInstruction
{ {
[YamlMember(Alias = "post_roll", ApplyNamingConventions = false)] [YamlMember(Alias = "post_roll", ApplyNamingConventions = false)]
public bool PostRoll { get; set; } public bool PostRoll { get; set; }

2
ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPreRollInstruction.cs → ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutPreRollInstruction.cs

@ -2,7 +2,7 @@ using YamlDotNet.Serialization;
namespace ErsatzTV.Core.Scheduling.YamlScheduling.Models; namespace ErsatzTV.Core.Scheduling.YamlScheduling.Models;
public class YamlPreRollInstruction : YamlPlayoutInstruction public class YamlPlayoutPreRollInstruction : YamlPlayoutInstruction
{ {
[YamlMember(Alias = "pre_roll", ApplyNamingConventions = false)] [YamlMember(Alias = "pre_roll", ApplyNamingConventions = false)]
public bool PreRoll { get; set; } public bool PreRoll { get; set; }

17
ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutBuilder.cs

@ -19,6 +19,7 @@ public class YamlPlayoutBuilder(
IConfigElementRepository configElementRepository, IConfigElementRepository configElementRepository,
IMediaCollectionRepository mediaCollectionRepository, IMediaCollectionRepository mediaCollectionRepository,
IChannelRepository channelRepository, IChannelRepository channelRepository,
IGraphicsElementRepository graphicsElementRepository,
IYamlScheduleValidator yamlScheduleValidator, IYamlScheduleValidator yamlScheduleValidator,
ILogger<YamlPlayoutBuilder> logger) ILogger<YamlPlayoutBuilder> logger)
: IYamlPlayoutBuilder : IYamlPlayoutBuilder
@ -395,9 +396,11 @@ public class YamlPlayoutBuilder(
YamlPlayoutEpgGroupInstruction => new YamlPlayoutEpgGroupHandler(), YamlPlayoutEpgGroupInstruction => new YamlPlayoutEpgGroupHandler(),
YamlPlayoutWatermarkInstruction => new YamlPlayoutWatermarkHandler(channelRepository), YamlPlayoutWatermarkInstruction => new YamlPlayoutWatermarkHandler(channelRepository),
YamlPlayoutShuffleSequenceInstruction => new YamlPlayoutShuffleSequenceHandler(), YamlPlayoutShuffleSequenceInstruction => new YamlPlayoutShuffleSequenceHandler(),
YamlPreRollInstruction => new YamlPlayoutPreRollHandler(), YamlPlayoutPreRollInstruction => new YamlPlayoutPreRollHandler(),
YamlPostRollInstruction => new YamlPlayoutPostRollHandler(), YamlPlayoutPostRollInstruction => new YamlPlayoutPostRollHandler(),
YamlMidRollInstruction => new YamlPlayoutMidRollHandler(), YamlPlayoutMidRollInstruction => new YamlPlayoutMidRollHandler(),
YamlPlayoutGraphicsOnInstruction => new YamlPlayoutGraphicsOnHandler(graphicsElementRepository),
YamlPlayoutGraphicsOffInstruction => new YamlPlayoutGraphicsOffHandler(graphicsElementRepository),
YamlPlayoutSkipItemsInstruction => new YamlPlayoutSkipItemsHandler(enumeratorCache), YamlPlayoutSkipItemsInstruction => new YamlPlayoutSkipItemsHandler(enumeratorCache),
YamlPlayoutSkipToItemInstruction => new YamlPlayoutSkipToItemHandler(enumeratorCache), YamlPlayoutSkipToItemInstruction => new YamlPlayoutSkipToItemHandler(enumeratorCache),
@ -457,12 +460,14 @@ public class YamlPlayoutBuilder(
{ "count", typeof(YamlPlayoutCountInstruction) }, { "count", typeof(YamlPlayoutCountInstruction) },
{ "duration", typeof(YamlPlayoutDurationInstruction) }, { "duration", typeof(YamlPlayoutDurationInstruction) },
{ "epg_group", typeof(YamlPlayoutEpgGroupInstruction) }, { "epg_group", typeof(YamlPlayoutEpgGroupInstruction) },
{ "graphics_on", typeof(YamlPlayoutGraphicsOnInstruction) },
{ "graphics_off", typeof(YamlPlayoutGraphicsOffInstruction) },
{ "watermark", typeof(YamlPlayoutWatermarkInstruction) }, { "watermark", typeof(YamlPlayoutWatermarkInstruction) },
{ "pad_to_next", typeof(YamlPlayoutPadToNextInstruction) }, { "pad_to_next", typeof(YamlPlayoutPadToNextInstruction) },
{ "pad_until", typeof(YamlPlayoutPadUntilInstruction) }, { "pad_until", typeof(YamlPlayoutPadUntilInstruction) },
{ "pre_roll", typeof(YamlPreRollInstruction) }, { "pre_roll", typeof(YamlPlayoutPreRollInstruction) },
{ "post_roll", typeof(YamlPostRollInstruction) }, { "post_roll", typeof(YamlPlayoutPostRollInstruction) },
{ "mid_roll", typeof(YamlMidRollInstruction) }, { "mid_roll", typeof(YamlPlayoutMidRollInstruction) },
{ "repeat", typeof(YamlPlayoutRepeatInstruction) }, { "repeat", typeof(YamlPlayoutRepeatInstruction) },
{ "rewind", typeof(YamlPlayoutRewindInstruction) }, { "rewind", typeof(YamlPlayoutRewindInstruction) },
{ "sequence", typeof(YamlPlayoutSequenceInstruction) }, { "sequence", typeof(YamlPlayoutSequenceInstruction) },

65
ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContext.cs

@ -16,6 +16,7 @@ public class YamlPlayoutContext(Playout playout, YamlPlayoutDefinition definitio
private readonly System.Collections.Generic.HashSet<int> _visitedInstructions = []; private readonly System.Collections.Generic.HashSet<int> _visitedInstructions = [];
private readonly Stack<FillerKind> _fillerKind = new(); private readonly Stack<FillerKind> _fillerKind = new();
private readonly System.Collections.Generic.HashSet<int> _channelWatermarkIds = []; private readonly System.Collections.Generic.HashSet<int> _channelWatermarkIds = [];
private readonly System.Collections.Generic.HashSet<int> _graphicsElementIds = [];
private int _guideGroup = guideGroup; private int _guideGroup = guideGroup;
private bool _guideGroupLocked; private bool _guideGroupLocked;
private int _instructionIndex; private int _instructionIndex;
@ -87,66 +88,30 @@ public class YamlPlayoutContext(Playout playout, YamlPlayoutDefinition definitio
public void UnlockGuideGroup() => _guideGroupLocked = false; public void UnlockGuideGroup() => _guideGroupLocked = false;
public void SetChannelWatermarkId(int id) public void SetChannelWatermarkId(int id) => _channelWatermarkIds.Add(id);
{ public void RemoveChannelWatermarkId(int id) => _channelWatermarkIds.Remove(id);
_channelWatermarkIds.Add(id); public void ClearChannelWatermarkIds() => _channelWatermarkIds.Clear();
}
public void RemoveChannelWatermarkId(int id)
{
_channelWatermarkIds.Remove(id);
}
public void ClearChannelWatermarkIds()
{
_channelWatermarkIds.Clear();
}
public List<int> GetChannelWatermarkIds() => _channelWatermarkIds.ToList(); public List<int> GetChannelWatermarkIds() => _channelWatermarkIds.ToList();
public void SetPreRollSequence(string sequence) public void SetGraphicsElementId(int id) => _graphicsElementIds.Add(id);
{ public void RemoveGraphicsElementId(int id) => _graphicsElementIds.Remove(id);
_preRollSequence = sequence; public void ClearGraphicsElements() => _graphicsElementIds.Clear();
} public List<int> GetGraphicsElementIds() => _graphicsElementIds.ToList();
public void ClearPreRollSequence()
{
_preRollSequence = Option<string>.None;
}
public void SetPreRollSequence(string sequence) => _preRollSequence = sequence;
public void ClearPreRollSequence() => _preRollSequence = Option<string>.None;
public Option<string> GetPreRollSequence() => _preRollSequence; public Option<string> GetPreRollSequence() => _preRollSequence;
public void SetPostRollSequence(string sequence) public void SetPostRollSequence(string sequence) => _postRollSequence = sequence;
{ public void ClearPostRollSequence() => _postRollSequence = Option<string>.None;
_postRollSequence = sequence;
}
public void ClearPostRollSequence()
{
_postRollSequence = Option<string>.None;
}
public Option<string> GetPostRollSequence() => _postRollSequence; public Option<string> GetPostRollSequence() => _postRollSequence;
public void SetMidRollSequence(MidRollSequence sequence) public void SetMidRollSequence(MidRollSequence sequence) => _midRollSequence = sequence;
{ public void ClearMidRollSequence() => _midRollSequence = Option<MidRollSequence>.None;
_midRollSequence = sequence;
}
public void ClearMidRollSequence()
{
_midRollSequence = Option<MidRollSequence>.None;
}
public Option<MidRollSequence> GetMidRollSequence() => _midRollSequence; public Option<MidRollSequence> GetMidRollSequence() => _midRollSequence;
public void PushFillerKind(FillerKind fillerKind) public void PushFillerKind(FillerKind fillerKind) => _fillerKind.Push(fillerKind);
{
_fillerKind.Push(fillerKind);
}
public void PopFillerKind() => _fillerKind.Pop(); public void PopFillerKind() => _fillerKind.Pop();
public Option<FillerKind> GetFillerKind() => public Option<FillerKind> GetFillerKind() =>
_fillerKind.TryPeek(out FillerKind fillerKind) ? fillerKind : Option<FillerKind>.None; _fillerKind.TryPeek(out FillerKind fillerKind) ? fillerKind : Option<FillerKind>.None;

24
ErsatzTV.Infrastructure/Data/Repositories/GraphicsElementRepository.cs

@ -0,0 +1,24 @@
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Infrastructure.Extensions;
using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Infrastructure.Data.Repositories;
public class GraphicsElementRepository(IDbContextFactory<TvContext> dbContextFactory) : IGraphicsElementRepository
{
public async Task<Option<GraphicsElement>> GetGraphicsElementByPath(string path)
{
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync();
if (!path.StartsWith(FileSystemLayout.GraphicsElementsTemplatesFolder, StringComparison.Ordinal))
{
path = Path.Combine(FileSystemLayout.GraphicsElementsTemplatesFolder, path);
}
return await dbContext.GraphicsElements
.AsNoTracking()
.SelectOneAsync(ge => ge.Path, ge => ge.Path == path);
}
}

18
ErsatzTV/Resources/yaml-playout-import.schema.json

@ -39,6 +39,8 @@
{ "$ref": "#/$defs/scheduling/padUntilInstruction" }, { "$ref": "#/$defs/scheduling/padUntilInstruction" },
{ "$ref": "#/$defs/scheduling/sequenceInstruction" }, { "$ref": "#/$defs/scheduling/sequenceInstruction" },
{ "$ref": "#/$defs/control/epgGroupInstruction" }, { "$ref": "#/$defs/control/epgGroupInstruction" },
{ "$ref": "#/$defs/control/graphicsOnInstruction" },
{ "$ref": "#/$defs/control/graphicsOffInstruction" },
{ "$ref": "#/$defs/control/preRollInstruction"}, { "$ref": "#/$defs/control/preRollInstruction"},
{ "$ref": "#/$defs/control/postRollInstruction"}, { "$ref": "#/$defs/control/postRollInstruction"},
{ "$ref": "#/$defs/control/midRollInstruction"}, { "$ref": "#/$defs/control/midRollInstruction"},
@ -276,6 +278,22 @@
"required": [ "epg_group" ], "required": [ "epg_group" ],
"additionalProperties": false "additionalProperties": false
}, },
"graphicsOnInstruction": {
"type": "object",
"properties": {
"graphics_on": { "type": "string" }
},
"required": [ "graphics_on" ],
"additionalProperties": false
},
"graphicsOffInstruction": {
"type": "object",
"properties": {
"graphics_off": { "type": ["null", "string"] }
},
"required": [ "graphics_off" ],
"additionalProperties": false
},
"preRollInstruction": { "preRollInstruction": {
"type": "object", "type": "object",
"properties": { "properties": {

20
ErsatzTV/Resources/yaml-playout.schema.json

@ -46,6 +46,8 @@
{ "$ref": "#/$defs/scheduling/padUntilInstruction" }, { "$ref": "#/$defs/scheduling/padUntilInstruction" },
{ "$ref": "#/$defs/scheduling/sequenceInstruction" }, { "$ref": "#/$defs/scheduling/sequenceInstruction" },
{ "$ref": "#/$defs/control/epgGroupInstruction" }, { "$ref": "#/$defs/control/epgGroupInstruction" },
{ "$ref": "#/$defs/control/graphicsOnInstruction" },
{ "$ref": "#/$defs/control/graphicsOffInstruction" },
{ "$ref": "#/$defs/control/preRollInstruction"}, { "$ref": "#/$defs/control/preRollInstruction"},
{ "$ref": "#/$defs/control/postRollInstruction"}, { "$ref": "#/$defs/control/postRollInstruction"},
{ "$ref": "#/$defs/control/midRollInstruction"}, { "$ref": "#/$defs/control/midRollInstruction"},
@ -88,6 +90,8 @@
{ "$ref": "#/$defs/scheduling/padUntilInstruction" }, { "$ref": "#/$defs/scheduling/padUntilInstruction" },
{ "$ref": "#/$defs/scheduling/sequenceInstruction" }, { "$ref": "#/$defs/scheduling/sequenceInstruction" },
{ "$ref": "#/$defs/control/epgGroupInstruction" }, { "$ref": "#/$defs/control/epgGroupInstruction" },
{ "$ref": "#/$defs/control/graphicsOnInstruction" },
{ "$ref": "#/$defs/control/graphicsOffInstruction" },
{ "$ref": "#/$defs/control/preRollInstruction"}, { "$ref": "#/$defs/control/preRollInstruction"},
{ "$ref": "#/$defs/control/postRollInstruction"}, { "$ref": "#/$defs/control/postRollInstruction"},
{ "$ref": "#/$defs/control/midRollInstruction"}, { "$ref": "#/$defs/control/midRollInstruction"},
@ -325,6 +329,22 @@
"required": [ "epg_group" ], "required": [ "epg_group" ],
"additionalProperties": false "additionalProperties": false
}, },
"graphicsOnInstruction": {
"type": "object",
"properties": {
"graphics_on": { "type": "string" }
},
"required": [ "graphics_on" ],
"additionalProperties": false
},
"graphicsOffInstruction": {
"type": "object",
"properties": {
"graphics_off": { "type": ["null", "string"] }
},
"required": [ "graphics_off" ],
"additionalProperties": false
},
"preRollInstruction": { "preRollInstruction": {
"type": "object", "type": "object",
"properties": { "properties": {

1
ErsatzTV/Startup.cs

@ -719,6 +719,7 @@ public class Startup
MultiEpisodeShuffleCollectionEnumeratorFactory>(); MultiEpisodeShuffleCollectionEnumeratorFactory>();
services.AddScoped<IChannelLogoGenerator, ChannelLogoGenerator>(); services.AddScoped<IChannelLogoGenerator, ChannelLogoGenerator>();
services.AddScoped<IGraphicsEngine, GraphicsEngine>(); services.AddScoped<IGraphicsEngine, GraphicsEngine>();
services.AddScoped<IGraphicsElementRepository, GraphicsElementRepository>();
services.AddScoped<IFFmpegProcessService, FFmpegLibraryProcessService>(); services.AddScoped<IFFmpegProcessService, FFmpegLibraryProcessService>();
services.AddScoped<IPipelineBuilderFactory, PipelineBuilderFactory>(); services.AddScoped<IPipelineBuilderFactory, PipelineBuilderFactory>();

Loading…
Cancel
Save