Browse Source

playout template pad to next fixes (#1809)

* prevent loop

* add discard attempts and fallback to pad_to_next
pull/1811/head
Jason Dove 2 years ago committed by GitHub
parent
commit
0b29bb32b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplatePadToNextItem.cs
  2. 40
      ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateSchedulerPadToNext.cs
  3. 78
      ErsatzTV.Core/Scheduling/TemplateScheduling/TemplatePlayoutBuilder.cs

5
ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplatePadToNextItem.cs

@ -8,4 +8,9 @@ public class PlayoutTemplatePadToNextItem : PlayoutTemplateItem
public int PadToNext { get; set; } public int PadToNext { get; set; }
public bool Trim { get; set; } public bool Trim { get; set; }
public string Fallback { get; set; }
[YamlMember(Alias = "discard_attempts", ApplyNamingConventions = false)]
public int DiscardAttempts { get; set; }
} }

40
ErsatzTV.Core/Scheduling/TemplateScheduling/PlayoutTemplateSchedulerPadToNext.cs

@ -1,4 +1,5 @@
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Interfaces.Scheduling; using ErsatzTV.Core.Interfaces.Scheduling;
namespace ErsatzTV.Core.Scheduling.TemplateScheduling; namespace ErsatzTV.Core.Scheduling.TemplateScheduling;
@ -9,7 +10,8 @@ public class PlayoutTemplateSchedulerPadToNext : PlayoutTemplateScheduler
Playout playout, Playout playout,
DateTimeOffset currentTime, DateTimeOffset currentTime,
PlayoutTemplatePadToNextItem padToNext, PlayoutTemplatePadToNextItem padToNext,
IMediaCollectionEnumerator enumerator) IMediaCollectionEnumerator enumerator,
Option<IMediaCollectionEnumerator> fallbackEnumerator)
{ {
int currentMinute = currentTime.Minute; int currentMinute = currentTime.Minute;
@ -31,10 +33,10 @@ public class PlayoutTemplateSchedulerPadToNext : PlayoutTemplateScheduler
if (targetTime <= currentTime) if (targetTime <= currentTime)
targetTime = targetTime.AddMinutes(padToNext.PadToNext); targetTime = targetTime.AddMinutes(padToNext.PadToNext);
int discardAttempts = padToNext.DiscardAttempts;
bool done = false; bool done = false;
TimeSpan remainingToFill = targetTime - currentTime; TimeSpan remainingToFill = targetTime - currentTime;
while (!done && enumerator.Current.IsSome && remainingToFill > TimeSpan.Zero && while (!done && enumerator.Current.IsSome && remainingToFill > TimeSpan.Zero)
remainingToFill >= enumerator.MinimumDuration)
{ {
foreach (MediaItem mediaItem in enumerator.Current) foreach (MediaItem mediaItem in enumerator.Current)
{ {
@ -55,21 +57,51 @@ public class PlayoutTemplateSchedulerPadToNext : PlayoutTemplateScheduler
if (remainingToFill - itemDuration >= TimeSpan.Zero) if (remainingToFill - itemDuration >= TimeSpan.Zero)
{ {
remainingToFill -= itemDuration; remainingToFill -= itemDuration;
currentTime += itemDuration;
playout.Items.Add(playoutItem); playout.Items.Add(playoutItem);
enumerator.MoveNext(); enumerator.MoveNext();
} }
else if (discardAttempts > 0)
{
// item won't fit; try the next one
discardAttempts--;
enumerator.MoveNext();
}
else if (padToNext.Trim) else if (padToNext.Trim)
{ {
// trim item to exactly fit // trim item to exactly fit
remainingToFill = TimeSpan.Zero; remainingToFill = TimeSpan.Zero;
currentTime = targetTime;
playoutItem.Finish = targetTime.UtcDateTime; playoutItem.Finish = targetTime.UtcDateTime;
playoutItem.OutPoint = playoutItem.Finish - playoutItem.Start; playoutItem.OutPoint = playoutItem.Finish - playoutItem.Start;
playout.Items.Add(playoutItem); playout.Items.Add(playoutItem);
enumerator.MoveNext(); enumerator.MoveNext();
} }
else if (fallbackEnumerator.IsSome)
{
foreach (IMediaCollectionEnumerator fallback in fallbackEnumerator)
{
remainingToFill = TimeSpan.Zero;
done = true;
// replace with fallback content
foreach (MediaItem fallbackItem in fallback.Current)
{
playoutItem.MediaItemId = fallbackItem.Id;
playoutItem.Finish = targetTime.UtcDateTime;
playoutItem.FillerKind = FillerKind.Fallback;
playout.Items.Add(playoutItem);
fallback.MoveNext();
}
}
}
else else
{ {
// item won't fit; we're done for now // item won't fit; we're done
done = true; done = true;
} }
} }

78
ErsatzTV.Core/Scheduling/TemplateScheduling/TemplatePlayoutBuilder.cs

@ -45,6 +45,7 @@ public class TemplatePlayoutBuilder(
// load content and content enumerators on demand // load content and content enumerators on demand
Dictionary<string, IMediaCollectionEnumerator> enumerators = new(); Dictionary<string, IMediaCollectionEnumerator> enumerators = new();
int itemsAfterRepeat = playout.Items.Count;
var index = 0; var index = 0;
while (currentTime < finish) while (currentTime < finish)
{ {
@ -60,38 +61,49 @@ public class TemplatePlayoutBuilder(
if (playoutItem is PlayoutTemplateRepeatItem) if (playoutItem is PlayoutTemplateRepeatItem)
{ {
index = 0; index = 0;
if (playout.Items.Count == itemsAfterRepeat)
{
logger.LogWarning("Repeat encountered without adding any playout items; aborting");
break;
}
itemsAfterRepeat = playout.Items.Count;
continue; continue;
} }
if (!enumerators.TryGetValue(playoutItem.Content, out IMediaCollectionEnumerator enumerator)) Option<IMediaCollectionEnumerator> maybeEnumerator = await GetCachedEnumeratorForContent(
{ playout,
Option<IMediaCollectionEnumerator> maybeEnumerator = playoutTemplate,
await GetEnumeratorForContent(playout, playoutItem.Content, playoutTemplate, cancellationToken); enumerators,
playoutItem.Content,
cancellationToken);
if (maybeEnumerator.IsNone) if (maybeEnumerator.IsNone)
{ {
logger.LogWarning("Unable to locate content with key {Key}", playoutItem.Content); logger.LogWarning("Unable to locate content with key {Key}", playoutItem.Content);
continue; continue;
}
foreach (IMediaCollectionEnumerator e in maybeEnumerator)
{
enumerator = maybeEnumerator.ValueUnsafe();
enumerators.Add(playoutItem.Content, enumerator);
}
} }
IMediaCollectionEnumerator enumerator = maybeEnumerator.ValueUnsafe();
switch (playoutItem) switch (playoutItem)
{ {
case PlayoutTemplateCountItem count: case PlayoutTemplateCountItem count:
currentTime = PlayoutTemplateSchedulerCount.Schedule(playout, currentTime, count, enumerator); currentTime = PlayoutTemplateSchedulerCount.Schedule(playout, currentTime, count, enumerator);
break; break;
case PlayoutTemplatePadToNextItem padToNext: case PlayoutTemplatePadToNextItem padToNext:
Option<IMediaCollectionEnumerator> fallbackEnumerator = await GetCachedEnumeratorForContent(
playout,
playoutTemplate,
enumerators,
padToNext.Fallback,
cancellationToken);
currentTime = PlayoutTemplateSchedulerPadToNext.Schedule( currentTime = PlayoutTemplateSchedulerPadToNext.Schedule(
playout, playout,
currentTime, currentTime,
padToNext, padToNext,
enumerator); enumerator,
fallbackEnumerator);
break; break;
} }
@ -106,6 +118,38 @@ public class TemplatePlayoutBuilder(
.GetValue<int>(ConfigElementKey.PlayoutDaysToBuild) .GetValue<int>(ConfigElementKey.PlayoutDaysToBuild)
.IfNoneAsync(2); .IfNoneAsync(2);
private async Task<Option<IMediaCollectionEnumerator>> GetCachedEnumeratorForContent(
Playout playout,
PlayoutTemplate playoutTemplate,
Dictionary<string, IMediaCollectionEnumerator> enumerators,
string contentKey,
CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(contentKey))
{
return Option<IMediaCollectionEnumerator>.None;
}
if (!enumerators.TryGetValue(contentKey, out IMediaCollectionEnumerator enumerator))
{
Option<IMediaCollectionEnumerator> maybeEnumerator =
await GetEnumeratorForContent(playout, contentKey, playoutTemplate, cancellationToken);
if (maybeEnumerator.IsNone)
{
return Option<IMediaCollectionEnumerator>.None;
}
foreach (IMediaCollectionEnumerator e in maybeEnumerator)
{
enumerator = maybeEnumerator.ValueUnsafe();
enumerators.Add(contentKey, enumerator);
}
}
return Some(enumerator);
}
private async Task<Option<IMediaCollectionEnumerator>> GetEnumeratorForContent( private async Task<Option<IMediaCollectionEnumerator>> GetEnumeratorForContent(
Playout playout, Playout playout,
string contentKey, string contentKey,
@ -156,6 +200,4 @@ public class TemplatePlayoutBuilder(
return deserializer.Deserialize<PlayoutTemplate>(yaml); return deserializer.Deserialize<PlayoutTemplate>(yaml);
} }
} }

Loading…
Cancel
Save