diff --git a/CHANGELOG.md b/CHANGELOG.md
index 345b8d1c..7ad987e6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,7 +25,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Supported in playback troubleshooting and YAML playouts
- Displays multi-line text in a specified font, color, location, z-index
- Supports constant opacity and opacity expression
- - Supports variable replacement for music videos
+ - Supports EPG and Media Item variable replacement
+ - EPG data is sourced from XMLTV
+ - Media Item data is sourced from the currently playing media item
- Add `image` graphics element type
- Supported in playback troubleshooting and YAML playouts
- Displays an image, similar to a watermark
diff --git a/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs b/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs
index 98fdc758..1d7df740 100644
--- a/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs
+++ b/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs
@@ -515,6 +515,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
graphicsEngineInput = new GraphicsEngineInput();
graphicsEngineContext = new GraphicsEngineContext(
+ channel.Number,
audioVersion.MediaItem,
graphicsElementContexts,
new Resolution { Width = desiredState.ScaledSize.Width, Height = desiredState.ScaledSize.Height },
diff --git a/ErsatzTV.Core/Interfaces/Repositories/ITemplateDataRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/ITemplateDataRepository.cs
index 57322df6..e1da3e38 100644
--- a/ErsatzTV.Core/Interfaces/Repositories/ITemplateDataRepository.cs
+++ b/ErsatzTV.Core/Interfaces/Repositories/ITemplateDataRepository.cs
@@ -4,8 +4,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories;
public interface ITemplateDataRepository
{
- public Task>> GetMusicVideoTemplateData(
- Resolution resolution,
- TimeSpan streamSeek,
- int musicVideoId);
+ public Task >> GetMediaItemTemplateData(MediaItem mediaItem);
+
+ public Task >> GetEpgTemplateData(string channelNumber, DateTimeOffset time);
}
\ No newline at end of file
diff --git a/ErsatzTV.Core/Interfaces/Streaming/GraphicsEngineContext.cs b/ErsatzTV.Core/Interfaces/Streaming/GraphicsEngineContext.cs
index b4aa9b1a..1b171c39 100644
--- a/ErsatzTV.Core/Interfaces/Streaming/GraphicsEngineContext.cs
+++ b/ErsatzTV.Core/Interfaces/Streaming/GraphicsEngineContext.cs
@@ -5,6 +5,7 @@ using ErsatzTV.Core.Graphics;
namespace ErsatzTV.Core.Interfaces.Streaming;
public record GraphicsEngineContext(
+ string ChannelNumber,
MediaItem MediaItem,
List Elements,
Resolution SquarePixelFrameSize,
diff --git a/ErsatzTV.Core/Metadata/EpgTemplateDataKey.cs b/ErsatzTV.Core/Metadata/EpgTemplateDataKey.cs
new file mode 100644
index 00000000..6e5240eb
--- /dev/null
+++ b/ErsatzTV.Core/Metadata/EpgTemplateDataKey.cs
@@ -0,0 +1,11 @@
+namespace ErsatzTV.Core.Metadata;
+
+public static class EpgTemplateDataKey
+{
+ public static readonly string Title = "Epg_Title";
+ public static readonly string SubTitle = "Epg_SubTitle";
+ public static readonly string Description = "Epg_Description";
+ public static readonly string Rating = "Epg_Rating";
+ public static readonly string Categories = "Epg_Categories";
+ public static readonly string Date = "Epg_Date";
+}
diff --git a/ErsatzTV.Core/Metadata/MediaItemTemplateDataKey.cs b/ErsatzTV.Core/Metadata/MediaItemTemplateDataKey.cs
new file mode 100644
index 00000000..89b37257
--- /dev/null
+++ b/ErsatzTV.Core/Metadata/MediaItemTemplateDataKey.cs
@@ -0,0 +1,29 @@
+namespace ErsatzTV.Core.Metadata;
+
+public static class MediaItemTemplateDataKey
+{
+ public static readonly string Resolution = "Resolution";
+
+ public static readonly string Duration = "MediaItem_Duration";
+ public static readonly string StreamSeek = "MediaItem_StreamSeek";
+
+ // common
+ public static readonly string Title = "MediaItem_Title";
+ public static readonly string Plot = "MediaItem_Plot";
+ public static readonly string ReleaseDate = "MediaItem_ReleaseDate";
+ public static readonly string Studios = "MediaItem_Studios";
+ public static readonly string Directors = "MediaItem_Directors";
+ public static readonly string Genres = "MediaItem_Genres";
+
+ // episode/show
+ public static readonly string ShowTitle = "MediaItem_ShowTitle";
+ public static readonly string ShowYear = "MediaItem_ShowYear";
+ public static readonly string ShowContentRating = "MediaItem_ShowContentRating";
+ public static readonly string ShowGenres = "MediaItem_ShowGenres";
+
+ // music video
+ public static readonly string Track = "MediaItem_Track";
+ public static readonly string Album = "MediaItem_Album";
+ public static readonly string Artist = "MediaItem_Artist";
+ public static readonly string Artists = "MediaItem_Artists";
+}
diff --git a/ErsatzTV.Infrastructure/Data/Repositories/TemplateDataRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/TemplateDataRepository.cs
index 126b85e4..789f5d6c 100644
--- a/ErsatzTV.Infrastructure/Data/Repositories/TemplateDataRepository.cs
+++ b/ErsatzTV.Infrastructure/Data/Repositories/TemplateDataRepository.cs
@@ -1,17 +1,146 @@
+using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Extensions;
+using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.Core.Interfaces.Repositories;
+using ErsatzTV.Core.Metadata;
+using ErsatzTV.Infrastructure.Epg;
using ErsatzTV.Infrastructure.Extensions;
using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Infrastructure.Data.Repositories;
-public class TemplateDataRepository(IDbContextFactory dbContextFactory) : ITemplateDataRepository
+public class TemplateDataRepository(ILocalFileSystem localFileSystem, IDbContextFactory dbContextFactory)
+ : ITemplateDataRepository
{
- public async Task>> GetMusicVideoTemplateData(
- Resolution resolution,
- TimeSpan streamSeek,
- int musicVideoId)
+ public async Task >> GetMediaItemTemplateData(MediaItem mediaItem) =>
+ mediaItem switch
+ {
+ Movie => await GetMovieTemplateData(mediaItem.Id),
+ Episode => await GetEpisodeTemplateData(mediaItem.Id),
+ MusicVideo => await GetMusicVideoTemplateData(mediaItem.Id),
+ _ => Option>.None
+ };
+
+ public async Task>> GetEpgTemplateData(string channelNumber, DateTimeOffset time)
+ {
+ try
+ {
+ string targetFile = Path.Combine(FileSystemLayout.ChannelGuideCacheFolder, $"{channelNumber}.xml");
+ if (localFileSystem.FileExists(targetFile))
+ {
+ await using var stream = File.OpenRead(targetFile);
+ var maybeEpgProgramme = EpgReader.FindProgrammeAt(stream, time);
+ foreach (var epgProgramme in maybeEpgProgramme)
+ {
+ return new Dictionary
+ {
+ [EpgTemplateDataKey.Title] = epgProgramme.Title?.Value,
+ [EpgTemplateDataKey.SubTitle] = epgProgramme.SubTitle?.Value,
+ [EpgTemplateDataKey.Description] = epgProgramme.Description?.Value,
+ [EpgTemplateDataKey.Rating] = epgProgramme.Rating?.Value,
+ [EpgTemplateDataKey.Categories] = (epgProgramme.Categories ?? []).Map(c => c.Value).ToArray(),
+ [EpgTemplateDataKey.Date] = epgProgramme.Date?.Value
+ };
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ throw;
+ }
+
+ return Option>.None;
+ }
+
+ private async Task>> GetMovieTemplateData(int movieId)
+ {
+ await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync();
+
+ Option maybeMovie = await dbContext.Movies
+ .AsNoTracking()
+ .Include(m => m.MediaVersions)
+ .Include(m => m.MovieMetadata)
+ .ThenInclude(mm => mm.Studios)
+ .Include(m => m.MovieMetadata)
+ .ThenInclude(mm => mm.Directors)
+ .Include(m => m.MovieMetadata)
+ .ThenInclude(mm => mm.Genres)
+ .SelectOneAsync(m => m.Id, m => m.Id == movieId);
+
+ foreach (var movie in maybeMovie)
+ {
+ foreach (var metadata in movie.MovieMetadata.HeadOrNone())
+ {
+ return new Dictionary
+ {
+ [MediaItemTemplateDataKey.Title] = metadata.Title,
+ [MediaItemTemplateDataKey.Plot] = metadata.Plot,
+ [MediaItemTemplateDataKey.ReleaseDate] = metadata.ReleaseDate,
+ [MediaItemTemplateDataKey.Studios] = (metadata.Studios ?? []).Map(s => s.Name).OrderBy(identity),
+ [MediaItemTemplateDataKey.Directors] = (metadata.Directors ?? []).Map(d => d.Name).OrderBy(identity),
+ [MediaItemTemplateDataKey.Genres] = (metadata.Genres ?? []).Map(g => g.Name).OrderBy(identity),
+ [MediaItemTemplateDataKey.Duration] = movie.GetHeadVersion().Duration
+ };
+ }
+ }
+
+ return Option>.None;
+ }
+
+ private async Task>> GetEpisodeTemplateData(int episodeId)
+ {
+ await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync();
+
+ Option maybeEpisode = await dbContext.Episodes
+ .AsNoTracking()
+ .Include(e => e.MediaVersions)
+ .Include(e => e.Season)
+ .ThenInclude(s => s.Show)
+ .ThenInclude(s => s.ShowMetadata)
+ .Include(e => e.EpisodeMetadata)
+ .ThenInclude(em => em.Studios)
+ .Include(e => e.EpisodeMetadata)
+ .ThenInclude(em => em.Directors)
+ .Include(e => e.EpisodeMetadata)
+ .ThenInclude(em => em.Genres)
+ .SelectOneAsync(e => e.Id, e => e.Id == episodeId);
+
+ var result = new Dictionary();
+
+ foreach (var episode in maybeEpisode)
+ {
+ foreach (var showMetadata in Optional(episode.Season?.Show?.ShowMetadata.HeadOrNone()).Flatten())
+ {
+ result.Add(MediaItemTemplateDataKey.ShowTitle, showMetadata.Title);
+ result.Add(MediaItemTemplateDataKey.ShowYear, showMetadata.Year);
+ result.Add(MediaItemTemplateDataKey.ShowContentRating, showMetadata.ContentRating);
+ result.Add(MediaItemTemplateDataKey.ShowGenres,
+ (showMetadata.Genres ?? []).Map(s => s.Name).OrderBy(identity));
+ }
+
+ foreach (var metadata in episode.EpisodeMetadata.HeadOrNone())
+ {
+ result.Add(MediaItemTemplateDataKey.Title, metadata.Title);
+ result.Add(MediaItemTemplateDataKey.Plot, metadata.Plot);
+ result.Add(MediaItemTemplateDataKey.ReleaseDate, metadata.ReleaseDate);
+ result.Add(MediaItemTemplateDataKey.Studios,
+ (metadata.Studios ?? []).Map(s => s.Name).OrderBy(identity));
+ result.Add(MediaItemTemplateDataKey.Directors,
+ (metadata.Directors ?? []).Map(s => s.Name).OrderBy(identity));
+ result.Add(MediaItemTemplateDataKey.Genres,
+ (metadata.Genres ?? []).Map(s => s.Name).OrderBy(identity));
+ result.Add(MediaItemTemplateDataKey.Duration, episode.GetHeadVersion().Duration);
+ }
+
+ return result;
+ }
+
+ return Option>.None;
+ }
+
+ private async Task>> GetMusicVideoTemplateData(int musicVideoId)
{
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync();
@@ -26,6 +155,8 @@ public class TemplateDataRepository(IDbContextFactory dbContextFactor
.ThenInclude(mvm => mvm.Studios)
.Include(mv => mv.MusicVideoMetadata)
.ThenInclude(mvm => mvm.Directors)
+ .Include(mv => mv.MusicVideoMetadata)
+ .ThenInclude(mvm => mvm.Genres)
.SelectOneAsync(mv => mv.Id, mv => mv.Id == musicVideoId);
foreach (var musicVideo in maybeMusicVideo)
@@ -40,18 +171,17 @@ public class TemplateDataRepository(IDbContextFactory dbContextFactor
return new Dictionary
{
- ["Resolution"] = resolution,
- ["Title"] = metadata.Title,
- ["Track"] = metadata.Track,
- ["Album"] = metadata.Album,
- ["Plot"] = metadata.Plot,
- ["ReleaseDate"] = metadata.ReleaseDate,
- ["Artists"] = (metadata.Artists ?? []).Map(a => a.Name).ToList(),
- ["Artist"] = artist,
- ["Studios"] = (metadata.Studios ?? []).Map(s => s.Name).ToList(),
- ["Directors"] = (metadata.Directors ?? []).Map(s => s.Name).ToList(),
- ["Duration"] = musicVideo.GetHeadVersion().Duration,
- ["StreamSeek"] = streamSeek
+ [MediaItemTemplateDataKey.Title] = metadata.Title,
+ [MediaItemTemplateDataKey.Track] = metadata.Track,
+ [MediaItemTemplateDataKey.Album] = metadata.Album,
+ [MediaItemTemplateDataKey.Plot] = metadata.Plot,
+ [MediaItemTemplateDataKey.ReleaseDate] = metadata.ReleaseDate,
+ [MediaItemTemplateDataKey.Artists] = (metadata.Artists ?? []).Map(a => a.Name).OrderBy(identity),
+ [MediaItemTemplateDataKey.Artist] = artist,
+ [MediaItemTemplateDataKey.Studios] = (metadata.Studios ?? []).Map(s => s.Name).OrderBy(identity),
+ [MediaItemTemplateDataKey.Directors] = (metadata.Directors ?? []).Map(d => d.Name).OrderBy(identity),
+ [MediaItemTemplateDataKey.Genres] = (metadata.Genres ?? []).Map(g => g.Name).OrderBy(identity),
+ [MediaItemTemplateDataKey.Duration] = musicVideo.GetHeadVersion().Duration
};
}
}
diff --git a/ErsatzTV.Infrastructure/Epg/EpgReader.cs b/ErsatzTV.Infrastructure/Epg/EpgReader.cs
new file mode 100644
index 00000000..4a9484a6
--- /dev/null
+++ b/ErsatzTV.Infrastructure/Epg/EpgReader.cs
@@ -0,0 +1,51 @@
+using System.Globalization;
+using System.Xml;
+using System.Xml.Serialization;
+using ErsatzTV.Infrastructure.Epg.Models;
+
+namespace ErsatzTV.Infrastructure.Epg;
+
+public static class EpgReader
+{
+ private const string XmlTvDateFormat = "yyyyMMddHHmmss zzz";
+
+ public static Option FindProgrammeAt(Stream xmlStream, DateTimeOffset targetTime)
+ {
+ var serializer = new XmlSerializer(typeof(EpgProgramme));
+
+ var settings = new XmlReaderSettings
+ {
+ ConformanceLevel = ConformanceLevel.Fragment
+ };
+
+ using var reader = XmlReader.Create(xmlStream, settings);
+
+ while (reader.Read())
+ {
+ if (reader.NodeType != XmlNodeType.Element || reader.Name != "programme")
+ {
+ continue;
+ }
+
+ string startStr = reader.GetAttribute("start");
+ string stopStr = reader.GetAttribute("stop");
+
+ if (startStr == null || stopStr == null)
+ {
+ continue;
+ }
+
+ if (DateTimeOffset.TryParseExact(startStr, XmlTvDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out var start) &&
+ DateTimeOffset.TryParseExact(stopStr, XmlTvDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out var stop))
+ {
+ if (start <= targetTime && targetTime < stop)
+ {
+ using var subtreeReader = reader.ReadSubtree();
+ return Optional(serializer.Deserialize(subtreeReader) as EpgProgramme);
+ }
+ }
+ }
+
+ return Option.None;
+ }
+}
diff --git a/ErsatzTV.Infrastructure/Epg/Models/EpgCategory.cs b/ErsatzTV.Infrastructure/Epg/Models/EpgCategory.cs
new file mode 100644
index 00000000..25ef5ab0
--- /dev/null
+++ b/ErsatzTV.Infrastructure/Epg/Models/EpgCategory.cs
@@ -0,0 +1,12 @@
+using System.Xml.Serialization;
+
+namespace ErsatzTV.Infrastructure.Epg.Models;
+
+public class EpgCategory
+{
+ [XmlAttribute("lang")]
+ public string Lang { get; set; }
+
+ [XmlText]
+ public string Value { get; set; }
+}
diff --git a/ErsatzTV.Infrastructure/Epg/Models/EpgDate.cs b/ErsatzTV.Infrastructure/Epg/Models/EpgDate.cs
new file mode 100644
index 00000000..1e2b50c2
--- /dev/null
+++ b/ErsatzTV.Infrastructure/Epg/Models/EpgDate.cs
@@ -0,0 +1,9 @@
+using System.Xml.Serialization;
+
+namespace ErsatzTV.Infrastructure.Epg.Models;
+
+public class EpgDate
+{
+ [XmlText]
+ public string Value { get; set; }
+}
diff --git a/ErsatzTV.Infrastructure/Epg/Models/EpgDescription.cs b/ErsatzTV.Infrastructure/Epg/Models/EpgDescription.cs
new file mode 100644
index 00000000..c7e20647
--- /dev/null
+++ b/ErsatzTV.Infrastructure/Epg/Models/EpgDescription.cs
@@ -0,0 +1,12 @@
+using System.Xml.Serialization;
+
+namespace ErsatzTV.Infrastructure.Epg.Models;
+
+public class EpgDescription
+{
+ [XmlAttribute("lang")]
+ public string Lang { get; set; }
+
+ [XmlText]
+ public string Value { get; set; }
+}
diff --git a/ErsatzTV.Infrastructure/Epg/Models/EpgEpisodeNum.cs b/ErsatzTV.Infrastructure/Epg/Models/EpgEpisodeNum.cs
new file mode 100644
index 00000000..f50b6d86
--- /dev/null
+++ b/ErsatzTV.Infrastructure/Epg/Models/EpgEpisodeNum.cs
@@ -0,0 +1,12 @@
+using System.Xml.Serialization;
+
+namespace ErsatzTV.Infrastructure.Epg.Models;
+
+public class EpgEpisodeNum
+{
+ [XmlAttribute("system")]
+ public string System { get; set; }
+
+ [XmlText]
+ public string Value { get; set; }
+}
diff --git a/ErsatzTV.Infrastructure/Epg/Models/EpgIcon.cs b/ErsatzTV.Infrastructure/Epg/Models/EpgIcon.cs
new file mode 100644
index 00000000..1a71f3f9
--- /dev/null
+++ b/ErsatzTV.Infrastructure/Epg/Models/EpgIcon.cs
@@ -0,0 +1,9 @@
+using System.Xml.Serialization;
+
+namespace ErsatzTV.Infrastructure.Epg.Models;
+
+public class EpgIcon
+{
+ [XmlAttribute("src")]
+ public string Src { get; set; }
+}
diff --git a/ErsatzTV.Infrastructure/Epg/Models/EpgProgramme.cs b/ErsatzTV.Infrastructure/Epg/Models/EpgProgramme.cs
new file mode 100644
index 00000000..8afd3b28
--- /dev/null
+++ b/ErsatzTV.Infrastructure/Epg/Models/EpgProgramme.cs
@@ -0,0 +1,43 @@
+using System.Xml.Serialization;
+
+namespace ErsatzTV.Infrastructure.Epg.Models;
+
+[XmlRoot("programme")]
+public class EpgProgramme
+{
+ [XmlAttribute("start")]
+ public string Start { get; set; }
+
+ [XmlAttribute("stop")]
+ public string Stop { get; set; }
+
+ [XmlAttribute("channel")]
+ public string Channel { get; set; }
+
+ [XmlElement("title")]
+ public EpgTitle Title { get; set; }
+
+ [XmlElement("sub-title")]
+ public EpgTitle SubTitle { get; set; }
+
+ [XmlElement("desc")]
+ public EpgDescription Description { get; set; }
+
+ [XmlElement("category")]
+ public List Categories { get; set; }
+
+ [XmlElement("icon")]
+ public EpgIcon Icon { get; set; }
+
+ [XmlElement("episode-num")]
+ public List EpisodeNums { get; set; }
+
+ [XmlElement("rating")]
+ public EpgRating Rating { get; set; }
+
+ [XmlElement("previously-shown")]
+ public object PreviouslyShown { get; set; } // Use object for presence check
+
+ [XmlElement("date")]
+ public EpgDate Date { get; set; }
+}
diff --git a/ErsatzTV.Infrastructure/Epg/Models/EpgRating.cs b/ErsatzTV.Infrastructure/Epg/Models/EpgRating.cs
new file mode 100644
index 00000000..d666f802
--- /dev/null
+++ b/ErsatzTV.Infrastructure/Epg/Models/EpgRating.cs
@@ -0,0 +1,9 @@
+using System.Xml.Serialization;
+
+namespace ErsatzTV.Infrastructure.Epg.Models;
+
+public class EpgRating
+{
+ [XmlElement("value")]
+ public string Value { get; set; }
+}
diff --git a/ErsatzTV.Infrastructure/Epg/Models/EpgTitle.cs b/ErsatzTV.Infrastructure/Epg/Models/EpgTitle.cs
new file mode 100644
index 00000000..1d9f90f6
--- /dev/null
+++ b/ErsatzTV.Infrastructure/Epg/Models/EpgTitle.cs
@@ -0,0 +1,12 @@
+using System.Xml.Serialization;
+
+namespace ErsatzTV.Infrastructure.Epg.Models;
+
+public class EpgTitle
+{
+ [XmlAttribute("lang")]
+ public string Lang { get; set; }
+
+ [XmlText]
+ public string Value { get; set; }
+}
diff --git a/ErsatzTV.Infrastructure/Streaming/GraphicsEngine.cs b/ErsatzTV.Infrastructure/Streaming/GraphicsEngine.cs
index ffc7b594..82e5360a 100644
--- a/ErsatzTV.Infrastructure/Streaming/GraphicsEngine.cs
+++ b/ErsatzTV.Infrastructure/Streaming/GraphicsEngine.cs
@@ -1,8 +1,8 @@
using System.IO.Pipelines;
using ErsatzTV.Core;
-using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Interfaces.Streaming;
+using ErsatzTV.Core.Metadata;
using Microsoft.Extensions.Logging;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
@@ -16,6 +16,39 @@ public class GraphicsEngine(ITemplateDataRepository templateDataRepository, ILog
{
GraphicsEngineFonts.LoadFonts(FileSystemLayout.FontsCacheFolder);
+ var templateVariables = new Dictionary();
+
+ // init text element variables once
+ if (context.Elements.OfType().Any())
+ {
+ // common variables
+ templateVariables[MediaItemTemplateDataKey.Resolution] = context.FrameSize;
+ templateVariables[MediaItemTemplateDataKey.StreamSeek] = context.Seek;
+
+ // media item variables
+ var maybeTemplateData =
+ await templateDataRepository.GetMediaItemTemplateData(context.MediaItem);
+ foreach (var templateData in maybeTemplateData)
+ {
+ foreach (var variable in templateData)
+ {
+ templateVariables.Add(variable.Key, variable.Value);
+ }
+ }
+
+ // epg variables
+ var startTime = context.ContentStartTime + context.Seek;
+ var maybeEpgData =
+ await templateDataRepository.GetEpgTemplateData(context.ChannelNumber, startTime);
+ foreach (var templateData in maybeEpgData)
+ {
+ foreach (var variable in templateData)
+ {
+ templateVariables.Add(variable.Key, variable.Value);
+ }
+ }
+ }
+
var elements = new List();
foreach (var element in context.Elements)
{
@@ -33,28 +66,12 @@ public class GraphicsEngine(ITemplateDataRepository templateDataRepository, ILog
elements.Add(new ImageElement(imageElementContext.ImageElement, logger));
break;
case TextElementContext textElementContext:
- var variables = new Dictionary();
+ var variables = templateVariables.ToDictionary();
foreach (var variable in textElementContext.Variables)
{
variables.Add(variable.Key, variable.Value);
}
- if (context.MediaItem is MusicVideo musicVideo)
- {
- var maybeTemplateData = await templateDataRepository.GetMusicVideoTemplateData(
- context.FrameSize,
- context.Seek,
- musicVideo.Id);
-
- foreach (var templateData in maybeTemplateData)
- {
- foreach (var variable in templateData)
- {
- variables.Add(variable.Key, variable.Value);
- }
- }
-
- }
elements.Add(new TextElement(textElementContext.TextElement, variables, logger));
break;
}