From cd84460ef93d9c204c3c3815f3f24cf1099de767 Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Mon, 9 Feb 2026 20:13:52 -0600 Subject: [PATCH] add scheduling context to classic filler --- CHANGELOG.md | 2 +- .../ProcessSchedulingContextHandler.cs | 52 ++++++++++++++++++- .../Scheduling/ClassicSchedulingContext.cs | 1 + .../Scheduling/PlayoutModeSchedulerBase.cs | 35 +++++++++++-- .../PlayoutModeSchedulerDuration.cs | 2 +- .../Scheduling/PlayoutModeSchedulerFlood.cs | 2 +- .../PlayoutModeSchedulerMultiple.cs | 2 +- .../Scheduling/PlayoutModeSchedulerOne.cs | 2 +- 8 files changed, 86 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c11e16d9..2b08b53d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - A small number of translations have been added for `Português (Brasil)` and `Polski` - Translation contributions are always welcome! - Add `Troubleshoot` button to playout details table to show info that may be helpful in determining the source of a playout item - - Classic schedule info includes schedule, schedule item, scheduler, playback order, random seed, collection index + - Classic schedule info includes schedule, schedule item, scheduler, filler, playback order, random seed, collection index - Block schedule info includes block, block item, playback order, random seed, collection index - E.g. items with the same random seed are part of the same shuffle diff --git a/ErsatzTV.Application/ProgramSchedules/Queries/ProcessSchedulingContextHandler.cs b/ErsatzTV.Application/ProgramSchedules/Queries/ProcessSchedulingContextHandler.cs index 0bf18ba87..86b2694fc 100644 --- a/ErsatzTV.Application/ProgramSchedules/Queries/ProcessSchedulingContextHandler.cs +++ b/ErsatzTV.Application/ProgramSchedules/Queries/ProcessSchedulingContextHandler.cs @@ -1,6 +1,7 @@ using System.Text.Json; using System.Text.Json.Serialization; using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Domain.Filler; using ErsatzTV.Core.Scheduling; using ErsatzTV.Infrastructure.Data; using Microsoft.EntityFrameworkCore; @@ -60,7 +61,43 @@ public class ProcessSchedulingContextHandler(IDbContextFactory dbCont .Include(si => si.Playlist) .Include(si => si.RerunCollection) .Include(si => si.SmartCollection) - .SingleOrDefaultAsync(s => s.Id == classicContext.ItemId, cancellationToken); + .SingleOrDefaultAsync(si => si.Id == classicContext.ItemId, cancellationToken); + + var name = "Classic"; + ClassicContextFiller filler = null; + if (classicContext.FillerPresetId is > 0) + { + name = "Classic - Filler"; + + var fillerPreset = await dbContext.FillerPresets + .AsNoTracking() + .AsSplitQuery() + .Include(p => p.Collection) + .Include(p => p.MultiCollection) + .Include(p => p.Playlist) + .Include(p => p.SmartCollection) + .SingleOrDefaultAsync(p => p.Id == classicContext.FillerPresetId, cancellationToken); + + if (fillerPreset is not null) + { + string collectionName = fillerPreset.CollectionType switch + { + CollectionType.Collection => fillerPreset.Collection.Name, + CollectionType.MultiCollection => fillerPreset.MultiCollection.Name, + CollectionType.Playlist => fillerPreset.Playlist.Name, + CollectionType.SmartCollection => fillerPreset.SmartCollection.Name, + _ => null + }; + + filler = new ClassicContextFiller( + fillerPreset.Id, + fillerPreset.Name, + fillerPreset.FillerKind, + fillerPreset.FillerMode, + fillerPreset.CollectionType, + collectionName); + } + } ClassicContextScheduleItem item; if (scheduleItem is not null) @@ -83,9 +120,10 @@ public class ProcessSchedulingContextHandler(IDbContextFactory dbCont } var context = new ClassicContext( - new ContextScheduler("Classic", classicContext.Scheduler), + new ContextScheduler(name, classicContext.Scheduler), new ClassicContextSchedule(classicContext.ScheduleId, scheduleName ?? string.Empty), item, + filler, new ContextEnumerator(classicContext.Enumerator, classicContext.Seed, classicContext.Index)); return JsonSerializer.Serialize(context, Options); @@ -96,6 +134,7 @@ public class ProcessSchedulingContextHandler(IDbContextFactory dbCont await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); var block = await dbContext.Blocks + .AsNoTracking() .Include(b => b.BlockGroup) .SingleOrDefaultAsync(s => s.Id == blockContext.BlockId, cancellationToken); @@ -141,6 +180,7 @@ public class ProcessSchedulingContextHandler(IDbContextFactory dbCont ContextScheduler Scheduler, ClassicContextSchedule Schedule, ClassicContextScheduleItem ScheduleItem, + ClassicContextFiller Filler, ContextEnumerator Enumerator); private sealed record ContextScheduler(string Type, string Mode); @@ -149,6 +189,14 @@ public class ProcessSchedulingContextHandler(IDbContextFactory dbCont private sealed record ClassicContextScheduleItem(int Id, CollectionType? CollectionType, string CollectionName); + private sealed record ClassicContextFiller( + int Id, + string Name, + FillerKind Kind, + FillerMode Mode, + CollectionType CollectionType, + string CollectionName); + private sealed record ContextEnumerator(string Name, int Seed, int Index); private sealed record BlockContext( diff --git a/ErsatzTV.Core/Scheduling/ClassicSchedulingContext.cs b/ErsatzTV.Core/Scheduling/ClassicSchedulingContext.cs index 552ce3502..c60f57fbd 100644 --- a/ErsatzTV.Core/Scheduling/ClassicSchedulingContext.cs +++ b/ErsatzTV.Core/Scheduling/ClassicSchedulingContext.cs @@ -4,6 +4,7 @@ public record ClassicSchedulingContext( string Scheduler, int ScheduleId, int ItemId, + int? FillerPresetId, string Enumerator, int Seed, int Index); diff --git a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs index 8714d5f61..09c1d3316 100644 --- a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs +++ b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs @@ -176,7 +176,8 @@ public abstract class PlayoutModeSchedulerBase(ILogger logger) : IPlayoutMode FillerKind = FillerKind.Tail, GuideGroup = nextState.NextGuideGroup, DisableWatermarks = !scheduleItem.TailFiller.AllowWatermarks, - ChapterTitle = ChapterTitleForMediaItem(mediaItem) + ChapterTitle = ChapterTitleForMediaItem(mediaItem), + SchedulingContext = GetSchedulingContext(scheduleItem, scheduleItem.TailFillerId, enumerator) }; newItems.Add(playoutItem); @@ -221,7 +222,8 @@ public abstract class PlayoutModeSchedulerBase(ILogger logger) : IPlayoutMode OutPoint = TimeSpan.Zero, GuideGroup = nextState.NextGuideGroup, FillerKind = FillerKind.Fallback, - DisableWatermarks = !scheduleItem.FallbackFiller.AllowWatermarks + DisableWatermarks = !scheduleItem.FallbackFiller.AllowWatermarks, + SchedulingContext = GetSchedulingContext(scheduleItem, scheduleItem.FallbackFillerId, enumerator) }; newItems.Add(playoutItem); @@ -417,10 +419,12 @@ public abstract class PlayoutModeSchedulerBase(ILogger logger) : IPlayoutMode result.AddRange( AddCountFiller( playoutBuilderState, + scheduleItem, e2, filler.Count.Value, scheduleItem.GuideMode == GuideMode.Filler ? FillerKind.GuideMode : FillerKind.PreRoll, filler.AllowWatermarks, + filler.Id, cancellationToken)); break; case FillerMode.RandomCount when filler.Count.HasValue: @@ -428,10 +432,12 @@ public abstract class PlayoutModeSchedulerBase(ILogger logger) : IPlayoutMode result.AddRange( AddRandomCountFiller( playoutBuilderState, + scheduleItem, e3, filler.Count.Value, scheduleItem.GuideMode == GuideMode.Filler ? FillerKind.GuideMode : FillerKind.PreRoll, filler.AllowWatermarks, + filler.Id, cancellationToken)); break; } @@ -489,12 +495,14 @@ public abstract class PlayoutModeSchedulerBase(ILogger logger) : IPlayoutMode result.AddRange( AddCountFiller( playoutBuilderState, + scheduleItem, e2, filler.Count.Value, scheduleItem.GuideMode == GuideMode.Filler ? FillerKind.GuideMode : FillerKind.MidRoll, filler.AllowWatermarks, + filler.Id, cancellationToken)); } } @@ -510,12 +518,14 @@ public abstract class PlayoutModeSchedulerBase(ILogger logger) : IPlayoutMode result.AddRange( AddRandomCountFiller( playoutBuilderState, + scheduleItem, e3, filler.Count.Value, scheduleItem.GuideMode == GuideMode.Filler ? FillerKind.GuideMode : FillerKind.MidRoll, filler.AllowWatermarks, + filler.Id, cancellationToken)); } } @@ -546,10 +556,12 @@ public abstract class PlayoutModeSchedulerBase(ILogger logger) : IPlayoutMode result.AddRange( AddCountFiller( playoutBuilderState, + scheduleItem, e2, filler.Count.Value, scheduleItem.GuideMode == GuideMode.Filler ? FillerKind.GuideMode : FillerKind.PostRoll, filler.AllowWatermarks, + filler.Id, cancellationToken)); break; case FillerMode.RandomCount when filler.Count.HasValue: @@ -557,10 +569,12 @@ public abstract class PlayoutModeSchedulerBase(ILogger logger) : IPlayoutMode result.AddRange( AddRandomCountFiller( playoutBuilderState, + scheduleItem, e3, filler.Count.Value, scheduleItem.GuideMode == GuideMode.Filler ? FillerKind.GuideMode : FillerKind.PostRoll, filler.AllowWatermarks, + filler.Id, cancellationToken)); break; } @@ -752,12 +766,14 @@ public abstract class PlayoutModeSchedulerBase(ILogger logger) : IPlayoutMode return result; } - private static List AddCountFiller( + private List AddCountFiller( PlayoutBuilderState playoutBuilderState, + ProgramScheduleItem scheduleItem, IMediaCollectionEnumerator enumerator, int count, FillerKind fillerKind, bool allowWatermarks, + int fillerPresetId, CancellationToken cancellationToken) { var result = new List(); @@ -780,7 +796,8 @@ public abstract class PlayoutModeSchedulerBase(ILogger logger) : IPlayoutMode GuideGroup = playoutBuilderState.NextGuideGroup, FillerKind = fillerKind, DisableWatermarks = !allowWatermarks, - ChapterTitle = ChapterTitleForMediaItem(mediaItem) + ChapterTitle = ChapterTitleForMediaItem(mediaItem), + SchedulingContext = GetSchedulingContext(scheduleItem, fillerPresetId, enumerator) }; result.Add(playoutItem); @@ -886,10 +903,12 @@ public abstract class PlayoutModeSchedulerBase(ILogger logger) : IPlayoutMode private List AddRandomCountFiller( PlayoutBuilderState playoutBuilderState, + ProgramScheduleItem scheduleItem, IMediaCollectionEnumerator enumerator, int count, FillerKind fillerKind, bool allowWatermarks, + int fillerPresetId, CancellationToken cancellationToken) { var result = new List(); @@ -900,10 +919,12 @@ public abstract class PlayoutModeSchedulerBase(ILogger logger) : IPlayoutMode { result = AddCountFiller( playoutBuilderState, + scheduleItem, enumerator, randomCount, fillerKind, allowWatermarks, + fillerPresetId, cancellationToken); } @@ -912,12 +933,16 @@ public abstract class PlayoutModeSchedulerBase(ILogger logger) : IPlayoutMode protected abstract string SchedulingContextName { get; } - protected string GetSchedulingContext(ProgramScheduleItem scheduleItem, IMediaCollectionEnumerator enumerator) + protected string GetSchedulingContext( + ProgramScheduleItem scheduleItem, + int? fillerPresetId, + IMediaCollectionEnumerator enumerator) { var context = new ClassicSchedulingContext( SchedulingContextName, scheduleItem.ProgramScheduleId, scheduleItem.Id, + fillerPresetId, enumerator.SchedulingContextName, enumerator.State.Seed, enumerator.State.Index); diff --git a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerDuration.cs b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerDuration.cs index 76f5ac404..8e1e6d635 100644 --- a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerDuration.cs +++ b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerDuration.cs @@ -165,7 +165,7 @@ public class PlayoutModeSchedulerDuration(ILogger logger) SubtitleMode = scheduleItem.SubtitleMode, PlayoutItemWatermarks = [], PlayoutItemGraphicsElements = [], - SchedulingContext = GetSchedulingContext(scheduleItem, contentEnumerator) + SchedulingContext = GetSchedulingContext(scheduleItem, null, contentEnumerator) }; foreach (ProgramScheduleItemWatermark programScheduleItemWatermark in scheduleItem diff --git a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerFlood.cs b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerFlood.cs index af1ca37ac..a19a13eec 100644 --- a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerFlood.cs +++ b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerFlood.cs @@ -81,7 +81,7 @@ public class PlayoutModeSchedulerFlood(ILogger logger) : PlayoutModeSchedulerBas SubtitleMode = scheduleItem.SubtitleMode, PlayoutItemWatermarks = [], PlayoutItemGraphicsElements = [], - SchedulingContext = GetSchedulingContext(scheduleItem, contentEnumerator) + SchedulingContext = GetSchedulingContext(scheduleItem, null, contentEnumerator) }; foreach (ProgramScheduleItemWatermark programScheduleItemWatermark in scheduleItem diff --git a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerMultiple.cs b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerMultiple.cs index f1c3635a0..08d9b33d7 100644 --- a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerMultiple.cs +++ b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerMultiple.cs @@ -108,7 +108,7 @@ public class PlayoutModeSchedulerMultiple(Map collectionItem SubtitleMode = scheduleItem.SubtitleMode, PlayoutItemWatermarks = [], PlayoutItemGraphicsElements = [], - SchedulingContext = GetSchedulingContext(scheduleItem, contentEnumerator) + SchedulingContext = GetSchedulingContext(scheduleItem, null, contentEnumerator) }; foreach (ProgramScheduleItemWatermark programScheduleItemWatermark in scheduleItem diff --git a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerOne.cs b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerOne.cs index 60b42f88e..3d5c5c0ce 100644 --- a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerOne.cs +++ b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerOne.cs @@ -57,7 +57,7 @@ public class PlayoutModeSchedulerOne(ILogger logger) : PlayoutModeSchedulerBase< SubtitleMode = scheduleItem.SubtitleMode, PlayoutItemWatermarks = [], PlayoutItemGraphicsElements = [], - SchedulingContext = GetSchedulingContext(scheduleItem, contentEnumerator) + SchedulingContext = GetSchedulingContext(scheduleItem, null, contentEnumerator) }; foreach (ProgramScheduleItemWatermark programScheduleItemWatermark in scheduleItem