From b83fe53ef175e69d13e2635e7d1bb0ef7108e2ac Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Fri, 26 Jul 2024 19:52:43 -0500 Subject: [PATCH] add wait until instruction (#1815) --- .../YamlScheduling/YamlPlayoutBuilder.cs | 88 +++++++++++++++---- .../YamlScheduling/YamlPlayoutDefinition.cs | 3 + .../YamlPlayoutWaitUntilInstruction.cs | 10 +++ 3 files changed, 85 insertions(+), 16 deletions(-) create mode 100644 ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutWaitUntilInstruction.cs diff --git a/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutBuilder.cs b/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutBuilder.cs index 884e2734..6e3f0d76 100644 --- a/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutBuilder.cs +++ b/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutBuilder.cs @@ -2,7 +2,6 @@ using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Scheduling; -using LanguageExt.UnsafeValueAccess; using Microsoft.Extensions.Logging; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; @@ -36,12 +35,10 @@ public class YamlPlayoutBuilder( return playout; } - // these are only for reset - playout.Seed = new Random().Next(); - playout.Items.Clear(); - DateTimeOffset currentTime = start; + currentTime = HandleResetActions(playout, playoutDefinition, currentTime); + // load content and content enumerators on demand Dictionary enumerators = new(); System.Collections.Generic.HashSet missingContentKeys = []; @@ -58,18 +55,23 @@ public class YamlPlayoutBuilder( YamlPlayoutInstruction playoutItem = playoutDefinition.Playout[index]; - // repeat resets index into YAML playout - if (playoutItem is YamlPlayoutRepeatInstruction) + // handle instructions that don't reference content + switch (playoutItem) { - index = 0; - if (playout.Items.Count == itemsAfterRepeat) - { - logger.LogWarning("Repeat encountered without adding any playout items; aborting"); + case YamlPlayoutWaitUntilInstruction waitUntil: + currentTime = HandleWaitUntil(currentTime, waitUntil); break; - } + case YamlPlayoutRepeatInstruction: + // repeat resets index into YAML playout + index = 0; + if (playout.Items.Count == itemsAfterRepeat) + { + logger.LogWarning("Repeat encountered without adding any playout items; aborting"); + break; + } - itemsAfterRepeat = playout.Items.Count; - continue; + itemsAfterRepeat = playout.Items.Count; + continue; } Option maybeEnumerator = await GetCachedEnumeratorForContent( @@ -132,6 +134,59 @@ public class YamlPlayoutBuilder( return playout; } + private DateTimeOffset HandleResetActions( + Playout playout, + YamlPlayoutDefinition playoutDefinition, + DateTimeOffset currentTime) + { + // these are only for reset + playout.Seed = new Random().Next(); + playout.Items.Clear(); + + foreach (YamlPlayoutInstruction instruction in playoutDefinition.Reset) + { + switch (instruction) + { + case YamlPlayoutWaitUntilInstruction waitUntil: + currentTime = HandleWaitUntil(currentTime, waitUntil); + break; + default: + logger.LogInformation( + "Skipping unsupported reset instruction {Instruction}", + instruction.GetType().Name); + break; + } + } + + return currentTime; + } + + private static DateTimeOffset HandleWaitUntil(DateTimeOffset currentTime, YamlPlayoutWaitUntilInstruction waitUntil) + { + if (TimeOnly.TryParse(waitUntil.WaitUntil, out TimeOnly result)) + { + var dayOnly = DateOnly.FromDateTime(currentTime.LocalDateTime); + var timeOnly = TimeOnly.FromDateTime(currentTime.LocalDateTime); + + if (timeOnly > result) + { + if (waitUntil.Tomorrow) + { + // this is wrong when offset changes + dayOnly = dayOnly.AddDays(1); + currentTime = new DateTimeOffset(dayOnly, result, currentTime.Offset); + } + } + else + { + // this is wrong when offset changes + currentTime = new DateTimeOffset(dayOnly, result, currentTime.Offset); + } + } + + return currentTime; + } + private async Task GetDaysToBuild() => await configElementRepository .GetValue(ConfigElementKey.PlayoutDaysToBuild) @@ -161,7 +216,7 @@ public class YamlPlayoutBuilder( foreach (IMediaCollectionEnumerator e in maybeEnumerator) { - enumerator = maybeEnumerator.ValueUnsafe(); + enumerator = e; enumerators.Add(contentKey, enumerator); } } @@ -231,7 +286,8 @@ public class YamlPlayoutBuilder( { "count", typeof(YamlPlayoutCountInstruction) }, { "duration", typeof(YamlPlayoutDurationInstruction) }, { "pad_to_next", typeof(YamlPlayoutPadToNextInstruction) }, - { "repeat", typeof(YamlPlayoutRepeatInstruction) } + { "repeat", typeof(YamlPlayoutRepeatInstruction) }, + { "wait_until", typeof(YamlPlayoutWaitUntilInstruction) } }; o.AddUniqueKeyTypeDiscriminator(instructionKeyMappings); diff --git a/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutDefinition.cs b/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutDefinition.cs index 29f8fff0..b2544e41 100644 --- a/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutDefinition.cs +++ b/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutDefinition.cs @@ -3,5 +3,8 @@ namespace ErsatzTV.Core.Scheduling.YamlScheduling; public class YamlPlayoutDefinition { public List Content { get; set; } = []; + + public List Reset { get; set; } = []; + public List Playout { get; set; } = []; } diff --git a/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutWaitUntilInstruction.cs b/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutWaitUntilInstruction.cs new file mode 100644 index 00000000..f93a971e --- /dev/null +++ b/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutWaitUntilInstruction.cs @@ -0,0 +1,10 @@ +using YamlDotNet.Serialization; + +namespace ErsatzTV.Core.Scheduling.YamlScheduling; + +public class YamlPlayoutWaitUntilInstruction : YamlPlayoutInstruction +{ + [YamlMember(Alias = "wait_until", ApplyNamingConventions = false)] + public string WaitUntil { get; set; } + public bool Tomorrow { get; set; } +}