Browse Source

fix building playouts when fill with group mode is used with graphics elements (#2799)

pull/2800/head
Jason Dove 6 months ago committed by GitHub
parent
commit
317ca1967c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 6
      ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs
  3. 97
      ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/NewPlayoutTests.cs
  4. 25
      ErsatzTV.Core/Extensions/ProgramScheduleItemExtensions.cs
  5. 11
      ErsatzTV.Core/Scheduling/PlayoutBuilder.cs

1
CHANGELOG.md

@ -41,6 +41,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Respect `z_index` (draw order) on all graphics element types - Respect `z_index` (draw order) on all graphics element types
- Fix bug with `z_index` sorting - Fix bug with `z_index` sorting
- Restore default UI font that was erroneously removed in v26.1.1 - Restore default UI font that was erroneously removed in v26.1.1
- Classic schedules: fix building playouts when `Fill With Group Mode` schedule items also have graphics elements
- Use configured searching log level on startup, instead of the default log level of `Information` - Use configured searching log level on startup, instead of the default log level of `Information`
- MySql: fix searching for shows and seasons in schedule items editor - MySql: fix searching for shows and seasons in schedule items editor
- Fix 500 errors when serving XMLTV due to concurrent file reads and writes - Fix 500 errors when serving XMLTV due to concurrent file reads and writes

6
ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs

@ -76,6 +76,8 @@ public class FakeMediaCollectionRepository : IMediaCollectionRepository
public Task<bool> IsCustomPlaybackOrder(int collectionId) => false.AsTask(); public Task<bool> IsCustomPlaybackOrder(int collectionId) => false.AsTask();
public Task<Option<string>> GetNameFromKey(CollectionKey emptyCollection, CancellationToken cancellationToken) => Option<string>.None.AsTask(); public Task<Option<string>> GetNameFromKey(CollectionKey emptyCollection, CancellationToken cancellationToken) => Option<string>.None.AsTask();
public List<CollectionWithItems> GroupIntoFakeCollections(List<MediaItem> items, string fakeKey = null) => public List<CollectionWithItems> GroupIntoFakeCollections(List<MediaItem> items, string fakeKey = null)
throw new NotSupportedException(); {
return [new CollectionWithItems(1, 0, fakeKey, items, true, PlaybackOrder.Shuffle, false)];
}
} }

97
ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/NewPlayoutTests.cs

@ -15,6 +15,103 @@ namespace ErsatzTV.Core.Tests.Scheduling.ClassicScheduling;
[TestFixture] [TestFixture]
public class NewPlayoutTests : PlayoutBuilderTestBase public class NewPlayoutTests : PlayoutBuilderTestBase
{ {
[Test]
public async Task FillWithGroupMode_Should_Not_Fail()
{
var collection = new Collection
{
Id = 1,
Name = "Multiple Items",
MediaItems =
[
TestMovie(1, TimeSpan.FromHours(1), new DateTime(2020, 1, 1)),
TestMovie(2, TimeSpan.FromHours(1), new DateTime(2020, 2, 1))
]
};
var fakeRepository = new FakeMediaCollectionRepository(
Map((collection.Id, collection.MediaItems.ToList())));
var items = new List<ProgramScheduleItem>
{
new ProgramScheduleItemMultiple
{
Id = 1,
Index = 1,
//Collection = collection,
CollectionId = collection.Id,
StartTime = null,
PlaybackOrder = PlaybackOrder.Chronological,
MultipleMode = MultipleMode.Count,
Count = "2",
FillWithGroupMode = FillWithGroupMode.FillWithShuffledGroups,
ProgramScheduleItemGraphicsElements = []
}
};
// having a graphics element reference the schedule item triggers the bug
items[0].ProgramScheduleItemGraphicsElements.Add(new ProgramScheduleItemGraphicsElement
{
GraphicsElementId = 1,
ProgramScheduleItem = items[0],
ProgramScheduleItemId = items[0].Id
});
var playout = new Playout
{
ProgramSchedule = new ProgramSchedule
{
Items = items
},
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
ProgramScheduleAnchors = [],
Items = [],
ProgramScheduleAlternates = [],
FillGroupIndices = []
};
var referenceData =
new PlayoutReferenceData(
playout.Channel,
Option<Deco>.None,
[],
[],
playout.ProgramSchedule,
[],
[],
TimeSpan.Zero);
IConfigElementRepository configRepo = Substitute.For<IConfigElementRepository>();
var televisionRepo = new FakeTelevisionRepository();
IArtistRepository artistRepo = Substitute.For<IArtistRepository>();
IMultiEpisodeShuffleCollectionEnumeratorFactory factory =
Substitute.For<IMultiEpisodeShuffleCollectionEnumeratorFactory>();
IRerunHelper rerunHelper = Substitute.For<IRerunHelper>();
var builder = new PlayoutBuilder(
configRepo,
fakeRepository,
televisionRepo,
artistRepo,
factory,
new MockFileSystem(),
rerunHelper,
Logger);
DateTimeOffset start = HoursAfterMidnight(0);
DateTimeOffset finish = start + TimeSpan.FromHours(6);
Either<BaseError, PlayoutBuildResult> buildResult = await builder.Build(
playout,
referenceData,
PlayoutBuildResult.Empty,
PlayoutBuildMode.Reset,
start,
finish,
CancellationToken);
buildResult.IsRight.ShouldBeTrue();
}
[Test] [Test]
public async Task OnlyZeroDurationItem_Should_Abort() public async Task OnlyZeroDurationItem_Should_Abort()
{ {

25
ErsatzTV.Core/Extensions/ProgramScheduleItemExtensions.cs

@ -0,0 +1,25 @@
using ErsatzTV.Core.Domain;
using Newtonsoft.Json;
namespace ErsatzTV.Core.Extensions;
public static class ProgramScheduleItemExtensions
{
public static ProgramScheduleItem DeepCopy(this ProgramScheduleItem item)
{
if (item == null)
{
return null;
}
var settings = new JsonSerializerSettings
{
// program schedule item => graphics element => (same) program schedule item should be ignored
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore
};
string json = JsonConvert.SerializeObject(item, settings);
return (ProgramScheduleItem)JsonConvert.DeserializeObject(json, item.GetType(), settings);
}
}

11
ErsatzTV.Core/Scheduling/PlayoutBuilder.cs

@ -1,5 +1,4 @@
using System.IO.Abstractions; using System.IO.Abstractions;
using System.Reflection;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler; using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Errors; using ErsatzTV.Core.Errors;
@ -673,13 +672,6 @@ public class PlayoutBuilder : IPlayoutBuilder
.Filter(c => c.ShowId > 0 || c.ArtistId > 0 || !string.IsNullOrWhiteSpace(c.Key)) .Filter(c => c.ShowId > 0 || c.ArtistId > 0 || !string.IsNullOrWhiteSpace(c.Key))
.ToList(); .ToList();
List<ProgramScheduleItem> fakeScheduleItems = []; List<ProgramScheduleItem> fakeScheduleItems = [];
// this will be used to clone a schedule item
MethodInfo generic = typeof(JsonConvert).GetMethods()
.FirstOrDefault(x => x.Name.Equals("DeserializeObject", StringComparison.OrdinalIgnoreCase) &&
x.IsGenericMethod &&
x.GetParameters().Length == 1)?.MakeGenericMethod(scheduleItem.GetType());
foreach (CollectionWithItems fakeCollection in fakeCollections) foreach (CollectionWithItems fakeCollection in fakeCollections)
{ {
CollectionKey key = (fakeCollection.ShowId, fakeCollection.ArtistId, fakeCollection.Key) switch CollectionKey key = (fakeCollection.ShowId, fakeCollection.ArtistId, fakeCollection.Key) switch
@ -709,8 +701,7 @@ public class PlayoutBuilder : IPlayoutBuilder
continue; continue;
} }
string serialized = JsonConvert.SerializeObject(scheduleItem); var copyScheduleItem = scheduleItem.DeepCopy();
var copyScheduleItem = generic.Invoke(this, [serialized]) as ProgramScheduleItem;
copyScheduleItem.CollectionType = key.CollectionType; copyScheduleItem.CollectionType = key.CollectionType;
copyScheduleItem.MediaItemId = key.MediaItemId; copyScheduleItem.MediaItemId = key.MediaItemId;
copyScheduleItem.FakeCollectionKey = key.FakeCollectionKey; copyScheduleItem.FakeCollectionKey = key.FakeCollectionKey;

Loading…
Cancel
Save