Browse Source

set variables from yaml playout graphics_on instruction (#2284)

pull/2285/head
Jason Dove 2 days ago committed by GitHub
parent
commit
f4eadae8ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 2
      ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs
  3. 5
      ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs
  4. 2
      ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs
  5. 1
      ErsatzTV.Core/Domain/PlayoutItemGraphicsElement.cs
  6. 24
      ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs
  7. 2
      ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs
  8. 3
      ErsatzTV.Core/Interfaces/Streaming/GraphicsEngineContext.cs
  9. 5
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutAllHandler.cs
  10. 5
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutCountHandler.cs
  11. 5
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutDurationHandler.cs
  12. 2
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutGraphicsOffHandler.cs
  13. 14
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutGraphicsOnHandler.cs
  14. 2
      ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutGraphicsOnInstruction.cs
  15. 10
      ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContext.cs
  16. 6277
      ErsatzTV.Infrastructure.MySql/Migrations/20250808215226_Add_PlayoutItemGraphicsElementVariables.Designer.cs
  17. 29
      ErsatzTV.Infrastructure.MySql/Migrations/20250808215226_Add_PlayoutItemGraphicsElementVariables.cs
  18. 3
      ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs
  19. 6112
      ErsatzTV.Infrastructure.Sqlite/Migrations/20250808213026_Add_PlayoutItemGraphicsElementVariables.Designer.cs
  20. 28
      ErsatzTV.Infrastructure.Sqlite/Migrations/20250808213026_Add_PlayoutItemGraphicsElementVariables.cs
  21. 3
      ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs
  22. 2
      ErsatzTV.Infrastructure/Streaming/GraphicsEngine.cs
  23. 13
      ErsatzTV.Infrastructure/Streaming/TextElement.cs
  24. 3
      ErsatzTV/Resources/yaml-playout-import.schema.json
  25. 3
      ErsatzTV/Resources/yaml-playout.schema.json

1
CHANGELOG.md

@ -27,6 +27,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -27,6 +27,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- 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`
- The `variables` property can be used to dynamically replace text from the template
- `graphics_off` will turn off a specific element, or all elements if none are specified
### Fix

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

@ -161,8 +161,6 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro @@ -161,8 +161,6 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
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 =>

5
ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs

@ -89,7 +89,8 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< @@ -89,7 +89,8 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler<
.ThenInclude(d => d.Watermark)
// get graphics elements
.Include(i => i.GraphicsElements)
.Include(i => i.PlayoutItemGraphicsElements)
.ThenInclude(pige => pige.GraphicsElement)
// get playout templates (and deco templates/decos)
.Include(i => i.Playout)
@ -344,7 +345,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< @@ -344,7 +345,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler<
effectiveNow,
playoutItemWatermarks,
maybeGlobalWatermark,
playoutItemWithPath.PlayoutItem.GraphicsElements,
playoutItemWithPath.PlayoutItem.PlayoutItemGraphicsElements,
channel.FFmpegProfile.VaapiDisplay,
channel.FFmpegProfile.VaapiDriver,
channel.FFmpegProfile.VaapiDevice,

2
ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs

@ -145,7 +145,7 @@ public class PrepareTroubleshootingPlaybackHandler( @@ -145,7 +145,7 @@ public class PrepareTroubleshootingPlaybackHandler(
now,
watermarks,
Option<ChannelWatermark>.None,
graphicsElements,
graphicsElements.Map(ge => new PlayoutItemGraphicsElement { GraphicsElement = ge }).ToList(),
ffmpegProfile.VaapiDisplay,
ffmpegProfile.VaapiDriver,
ffmpegProfile.VaapiDevice,

1
ErsatzTV.Core/Domain/PlayoutItemGraphicsElement.cs

@ -6,4 +6,5 @@ public class PlayoutItemGraphicsElement @@ -6,4 +6,5 @@ public class PlayoutItemGraphicsElement
public PlayoutItem PlayoutItem { get; set; }
public int? GraphicsElementId { get; set; }
public GraphicsElement GraphicsElement { get; set; }
public string Variables { get; set; }
}

24
ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs

@ -14,6 +14,7 @@ using ErsatzTV.FFmpeg.Pipeline; @@ -14,6 +14,7 @@ using ErsatzTV.FFmpeg.Pipeline;
using ErsatzTV.FFmpeg.Preset;
using ErsatzTV.FFmpeg.State;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using MediaStream = ErsatzTV.Core.Domain.MediaStream;
namespace ErsatzTV.Core.FFmpeg;
@ -65,7 +66,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -65,7 +66,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
DateTimeOffset now,
List<ChannelWatermark> playoutItemWatermarks,
Option<ChannelWatermark> globalWatermark,
List<GraphicsElement> graphicsElements,
List<PlayoutItemGraphicsElement> graphicsElements,
string vaapiDisplay,
VaapiDriver vaapiDriver,
string vaapiDevice,
@ -326,7 +327,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -326,7 +327,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
Option<WatermarkInputFile> watermarkInputFile = Option<WatermarkInputFile>.None;
Option<GraphicsEngineInput> graphicsEngineInput = Option<GraphicsEngineInput>.None;
Option<GraphicsEngineContext> graphicsEngineContext = Option<GraphicsEngineContext>.None;
List<GraphicsElementContext> graphicsElementContexts = new List<GraphicsElementContext>();
List<GraphicsElementContext> graphicsElementContexts = [];
// use graphics engine for all watermarks
if (!disableWatermarks)
@ -374,28 +375,35 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -374,28 +375,35 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
graphicsElementContexts.AddRange(watermarks.Values);
}
foreach (var graphicsElement in graphicsElements)
foreach (var playoutItemGraphicsElement in graphicsElements)
{
switch (graphicsElement.Kind)
switch (playoutItemGraphicsElement.GraphicsElement.Kind)
{
case GraphicsElementKind.Text:
var maybeElement = await TextGraphicsElement.FromFile(graphicsElement.Path);
var maybeElement = await TextGraphicsElement.FromFile(playoutItemGraphicsElement.GraphicsElement.Path);
if (maybeElement.IsNone)
{
_logger.LogWarning(
"Failed to load text graphics element from file {Path}; ignoring",
graphicsElement.Path);
playoutItemGraphicsElement.GraphicsElement.Path);
}
foreach (var element in maybeElement)
{
graphicsElementContexts.Add(new TextElementContext(element));
var variables = new Dictionary<string, string>();
if (!string.IsNullOrWhiteSpace(playoutItemGraphicsElement.Variables))
{
variables = JsonConvert.DeserializeObject<Dictionary<string, string>>(
playoutItemGraphicsElement.Variables);
}
graphicsElementContexts.Add(new TextElementContext(element, variables));
}
break;
default:
_logger.LogInformation(
"Ignoring unsupported graphics element kind {Kind}",
nameof(graphicsElement.Kind));
nameof(playoutItemGraphicsElement.GraphicsElement.Kind));
break;
}
}

2
ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs

@ -28,7 +28,7 @@ public interface IFFmpegProcessService @@ -28,7 +28,7 @@ public interface IFFmpegProcessService
DateTimeOffset now,
List<ChannelWatermark> playoutItemWatermarks,
Option<ChannelWatermark> globalWatermark,
List<GraphicsElement> graphicsElements,
List<PlayoutItemGraphicsElement> graphicsElements,
string vaapiDisplay,
VaapiDriver vaapiDriver,
string vaapiDevice,

3
ErsatzTV.Core/Interfaces/Streaming/GraphicsEngineContext.cs

@ -17,4 +17,5 @@ public abstract record GraphicsElementContext; @@ -17,4 +17,5 @@ public abstract record GraphicsElementContext;
public record WatermarkElementContext(WatermarkOptions Options) : GraphicsElementContext;
public record TextElementContext(TextGraphicsElement TextElement) : GraphicsElementContext;
public record TextElementContext(TextGraphicsElement TextElement, Dictionary<string, string> Variables)
: GraphicsElementContext;

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

@ -79,13 +79,14 @@ public class YamlPlayoutAllHandler(EnumeratorCache enumeratorCache) : YamlPlayou @@ -79,13 +79,14 @@ public class YamlPlayoutAllHandler(EnumeratorCache enumeratorCache) : YamlPlayou
});
}
foreach (int graphicsElementId in context.GetGraphicsElementIds())
foreach ((int graphicsElementId, string variablesJson) in context.GetGraphicsElements())
{
playoutItem.PlayoutItemGraphicsElements.Add(
new PlayoutItemGraphicsElement
{
PlayoutItem = playoutItem,
GraphicsElementId = graphicsElementId
GraphicsElementId = graphicsElementId,
Variables = variablesJson
});
}

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

@ -104,13 +104,14 @@ public class YamlPlayoutCountHandler(EnumeratorCache enumeratorCache) : YamlPlay @@ -104,13 +104,14 @@ public class YamlPlayoutCountHandler(EnumeratorCache enumeratorCache) : YamlPlay
});
}
foreach (int graphicsElementId in context.GetGraphicsElementIds())
foreach ((int graphicsElementId, string variablesJson) in context.GetGraphicsElements())
{
playoutItem.PlayoutItemGraphicsElements.Add(
new PlayoutItemGraphicsElement
{
PlayoutItem = playoutItem,
GraphicsElementId = graphicsElementId
GraphicsElementId = graphicsElementId,
Variables = variablesJson
});
}

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

@ -138,13 +138,14 @@ public class YamlPlayoutDurationHandler(EnumeratorCache enumeratorCache) : YamlP @@ -138,13 +138,14 @@ public class YamlPlayoutDurationHandler(EnumeratorCache enumeratorCache) : YamlP
});
}
foreach (int graphicsElementId in context.GetGraphicsElementIds())
foreach ((int graphicsElementId, string variablesJson) in context.GetGraphicsElements())
{
playoutItem.PlayoutItemGraphicsElements.Add(
new PlayoutItemGraphicsElement
{
PlayoutItem = playoutItem,
GraphicsElementId = graphicsElementId
GraphicsElementId = graphicsElementId,
Variables = variablesJson
});
}

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

@ -32,7 +32,7 @@ public class YamlPlayoutGraphicsOffHandler(IGraphicsElementRepository graphicsEl @@ -32,7 +32,7 @@ public class YamlPlayoutGraphicsOffHandler(IGraphicsElementRepository graphicsEl
{
foreach (var ge in await GetGraphicsElementByPath(graphicsOff.GraphicsOff))
{
context.RemoveGraphicsElementId(ge.Id);
context.RemoveGraphicsElement(ge.Id);
}
}

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

@ -2,11 +2,17 @@ using ErsatzTV.Core.Domain; @@ -2,11 +2,17 @@ using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Scheduling.YamlScheduling.Models;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace ErsatzTV.Core.Scheduling.YamlScheduling.Handlers;
public class YamlPlayoutGraphicsOnHandler(IGraphicsElementRepository graphicsElementRepository) : IYamlPlayoutHandler
{
private static readonly JsonSerializerSettings JsonSettings = new()
{
NullValueHandling = NullValueHandling.Ignore
};
private readonly Dictionary<string, Option<GraphicsElement>> _graphicsElementCache = new();
public bool Reset => false;
@ -31,7 +37,13 @@ public class YamlPlayoutGraphicsOnHandler(IGraphicsElementRepository graphicsEle @@ -31,7 +37,13 @@ public class YamlPlayoutGraphicsOnHandler(IGraphicsElementRepository graphicsEle
foreach (var ge in await GetGraphicsElementByPath(graphicsOn.GraphicsOn))
{
context.SetGraphicsElementId(ge.Id);
string variables = null;
if (graphicsOn.Variables.Count > 0)
{
variables = JsonConvert.SerializeObject(graphicsOn.Variables, JsonSettings);
}
context.SetGraphicsElement(ge.Id, variables);
}
return true;

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

@ -6,4 +6,6 @@ public class YamlPlayoutGraphicsOnInstruction : YamlPlayoutInstruction @@ -6,4 +6,6 @@ public class YamlPlayoutGraphicsOnInstruction : YamlPlayoutInstruction
{
[YamlMember(Alias = "graphics_on", ApplyNamingConventions = false)]
public string GraphicsOn { get; set; }
public Dictionary<string, string> Variables { get; set; } = [];
}

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

@ -16,7 +16,7 @@ public class YamlPlayoutContext(Playout playout, YamlPlayoutDefinition definitio @@ -16,7 +16,7 @@ public class YamlPlayoutContext(Playout playout, YamlPlayoutDefinition definitio
private readonly System.Collections.Generic.HashSet<int> _visitedInstructions = [];
private readonly Stack<FillerKind> _fillerKind = new();
private readonly System.Collections.Generic.HashSet<int> _channelWatermarkIds = [];
private readonly System.Collections.Generic.HashSet<int> _graphicsElementIds = [];
private readonly Dictionary<int, string> _graphicsElements = [];
private int _guideGroup = guideGroup;
private bool _guideGroupLocked;
private int _instructionIndex;
@ -93,10 +93,10 @@ public class YamlPlayoutContext(Playout playout, YamlPlayoutDefinition definitio @@ -93,10 +93,10 @@ public class YamlPlayoutContext(Playout playout, YamlPlayoutDefinition definitio
public void ClearChannelWatermarkIds() => _channelWatermarkIds.Clear();
public List<int> GetChannelWatermarkIds() => _channelWatermarkIds.ToList();
public void SetGraphicsElementId(int id) => _graphicsElementIds.Add(id);
public void RemoveGraphicsElementId(int id) => _graphicsElementIds.Remove(id);
public void ClearGraphicsElements() => _graphicsElementIds.Clear();
public List<int> GetGraphicsElementIds() => _graphicsElementIds.ToList();
public void SetGraphicsElement(int id, string variablesJson) => _graphicsElements.Add(id, variablesJson);
public void RemoveGraphicsElement(int id) => _graphicsElements.Remove(id);
public void ClearGraphicsElements() => _graphicsElements.Clear();
public IReadOnlyDictionary<int, string> GetGraphicsElements() => _graphicsElements;
public void SetPreRollSequence(string sequence) => _preRollSequence = sequence;
public void ClearPreRollSequence() => _preRollSequence = Option<string>.None;

6277
ErsatzTV.Infrastructure.MySql/Migrations/20250808215226_Add_PlayoutItemGraphicsElementVariables.Designer.cs generated

File diff suppressed because it is too large Load Diff

29
ErsatzTV.Infrastructure.MySql/Migrations/20250808215226_Add_PlayoutItemGraphicsElementVariables.cs

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.MySql.Migrations
{
/// <inheritdoc />
public partial class Add_PlayoutItemGraphicsElementVariables : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Variables",
table: "PlayoutItemGraphicsElement",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Variables",
table: "PlayoutItemGraphicsElement");
}
}
}

3
ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs

@ -1921,6 +1921,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -1921,6 +1921,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
b.Property<int>("GraphicsElementId")
.HasColumnType("int");
b.Property<string>("Variables")
.HasColumnType("longtext");
b.HasKey("PlayoutItemId", "GraphicsElementId");
b.HasIndex("GraphicsElementId");

6112
ErsatzTV.Infrastructure.Sqlite/Migrations/20250808213026_Add_PlayoutItemGraphicsElementVariables.Designer.cs generated

File diff suppressed because it is too large Load Diff

28
ErsatzTV.Infrastructure.Sqlite/Migrations/20250808213026_Add_PlayoutItemGraphicsElementVariables.cs

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
/// <inheritdoc />
public partial class Add_PlayoutItemGraphicsElementVariables : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Variables",
table: "PlayoutItemGraphicsElement",
type: "TEXT",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Variables",
table: "PlayoutItemGraphicsElement");
}
}
}

3
ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs

@ -1830,6 +1830,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -1830,6 +1830,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
b.Property<int>("GraphicsElementId")
.HasColumnType("INTEGER");
b.Property<string>("Variables")
.HasColumnType("TEXT");
b.HasKey("PlayoutItemId", "GraphicsElementId");
b.HasIndex("GraphicsElementId");

2
ErsatzTV.Infrastructure/Streaming/GraphicsEngine.cs

@ -28,7 +28,7 @@ public class GraphicsEngine(ILogger<GraphicsEngine> logger) : IGraphicsEngine @@ -28,7 +28,7 @@ public class GraphicsEngine(ILogger<GraphicsEngine> logger) : IGraphicsEngine
break;
case TextElementContext textElementContext:
elements.Add(new TextElement(textElementContext.TextElement, logger));
elements.Add(new TextElement(textElementContext.TextElement, textElementContext.Variables, logger));
break;
}
}

13
ErsatzTV.Infrastructure/Streaming/TextElement.cs

@ -2,6 +2,7 @@ using ErsatzTV.Core.Domain; @@ -2,6 +2,7 @@ using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Graphics;
using Microsoft.Extensions.Logging;
using NCalc;
using Scriban;
using SixLabors.Fonts;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing;
@ -11,7 +12,7 @@ using Image=SixLabors.ImageSharp.Image; @@ -11,7 +12,7 @@ using Image=SixLabors.ImageSharp.Image;
namespace ErsatzTV.Infrastructure.Streaming;
public class TextElement(TextGraphicsElement textElement, ILogger logger) : IGraphicsElement, IDisposable
public class TextElement(TextGraphicsElement textElement, Dictionary<string, string> variables, ILogger logger) : IGraphicsElement, IDisposable
{
private Option<Expression> _maybeOpacityExpression;
private float _opacity;
@ -22,7 +23,7 @@ public class TextElement(TextGraphicsElement textElement, ILogger logger) : IGra @@ -22,7 +23,7 @@ public class TextElement(TextGraphicsElement textElement, ILogger logger) : IGra
public bool IsFailed { get; set; }
public Task InitializeAsync(Resolution frameSize, int frameRate, CancellationToken cancellationToken)
public async Task InitializeAsync(Resolution frameSize, int frameRate, CancellationToken cancellationToken)
{
try
{
@ -39,6 +40,8 @@ public class TextElement(TextGraphicsElement textElement, ILogger logger) : IGra @@ -39,6 +40,8 @@ public class TextElement(TextGraphicsElement textElement, ILogger logger) : IGra
ZIndex = textElement.ZIndex ?? 0;
string textToRender = await Template.Parse(textElement.Text).RenderAsync(variables);
var font = GraphicsEngineFonts.GetFont(textElement.FontFamily, textElement.FontSize ?? 48, FontStyle.Regular);
var fontColor = Color.White;
if (Color.TryParse(textElement.FontColor, out Color parsedColor) ||
@ -58,11 +61,11 @@ public class TextElement(TextGraphicsElement textElement, ILogger logger) : IGra @@ -58,11 +61,11 @@ public class TextElement(TextGraphicsElement textElement, ILogger logger) : IGra
// textOptions.HorizontalAlignment = parsedAlignment;
// }
FontRectangle textBounds = TextMeasurer.MeasureBounds(textElement.Text, textOptions);
FontRectangle textBounds = TextMeasurer.MeasureBounds(textToRender, textOptions);
textOptions.Origin = new PointF(-textBounds.X, -textBounds.Y);
_image = new Image<Rgba32>((int)Math.Ceiling(textBounds.Width), (int)Math.Ceiling(textBounds.Height));
_image.Mutate(ctx => ctx.DrawText(textOptions, textElement.Text, fontColor));
_image.Mutate(ctx => ctx.DrawText(textOptions, textToRender, fontColor));
int horizontalMargin = (int)Math.Round((textElement.HorizontalMarginPercent ?? 0) / 100.0 * frameSize.Width);
int verticalMargin = (int)Math.Round((textElement.VerticalMarginPercent ?? 0) / 100.0 * frameSize.Height);
@ -81,8 +84,6 @@ public class TextElement(TextGraphicsElement textElement, ILogger logger) : IGra @@ -81,8 +84,6 @@ public class TextElement(TextGraphicsElement textElement, ILogger logger) : IGra
IsFailed = true;
logger.LogWarning(ex, "Failed to initialize text element; will disable for this content");
}
return Task.CompletedTask;
}
public ValueTask<Option<PreparedElementImage>> PrepareImage(

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

@ -281,7 +281,8 @@ @@ -281,7 +281,8 @@
"graphicsOnInstruction": {
"type": "object",
"properties": {
"graphics_on": { "type": "string" }
"graphics_on": { "type": "string" },
"variables": { "type": "object" }
},
"required": [ "graphics_on" ],
"additionalProperties": false

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

@ -332,7 +332,8 @@ @@ -332,7 +332,8 @@
"graphicsOnInstruction": {
"type": "object",
"properties": {
"graphics_on": { "type": "string" }
"graphics_on": { "type": "string" },
"variables": { "type": "object" }
},
"required": [ "graphics_on" ],
"additionalProperties": false

Loading…
Cancel
Save