Browse Source

fix: random start point is needed with every playlist build

pull/2950/head
Jason Dove 2 weeks ago
parent
commit
d6d7c164de
No known key found for this signature in database
  1. 95
      ErsatzTV.Core.Tests/Scheduling/PlaylistEnumeratorTests.cs
  2. 3
      ErsatzTV.Core/Scheduling/PlaylistEnumerator.cs
  3. 2
      ErsatzTV.Core/Scheduling/PlayoutBuilder.cs

95
ErsatzTV.Core.Tests/Scheduling/PlaylistEnumeratorTests.cs

@ -362,6 +362,101 @@ public class PlaylistEnumeratorTests
enumerator.CountForFiller.ShouldBe(7); enumerator.CountForFiller.ShouldBe(7);
} }
[Test]
public async Task RandomStartPoint_Should_Continue_After_Rebuild()
{
IMediaCollectionRepository repo = Substitute.For<IMediaCollectionRepository>();
Dictionary<PlaylistItem, List<MediaItem>> BuildMap() => new()
{
{
new PlaylistItem
{
Id = 1,
Index = 0,
PlaybackOrder = PlaybackOrder.Chronological,
PlayAll = false,
CollectionType = CollectionType.Collection,
CollectionId = 1
},
[FakeMovie(10), FakeMovie(11), FakeMovie(12), FakeMovie(13), FakeMovie(14)]
},
{
new PlaylistItem
{
Id = 2,
Index = 1,
PlaybackOrder = PlaybackOrder.Chronological,
PlayAll = false,
CollectionType = CollectionType.Collection,
CollectionId = 2
},
[FakeMovie(15), FakeMovie(16), FakeMovie(17), FakeMovie(18), FakeMovie(19)]
},
{
new PlaylistItem
{
Id = 3,
Index = 2,
PlaybackOrder = PlaybackOrder.Chronological,
PlayAll = false,
CollectionType = CollectionType.Collection,
CollectionId = 3
},
[FakeMovie(20), FakeMovie(21), FakeMovie(22), FakeMovie(23), FakeMovie(24)]
}
};
const int SEED = 987654321;
// day 1: fresh build with a random start point
PlaylistEnumerator day1 = await PlaylistEnumerator.Create(
repo,
BuildMap(),
new CollectionEnumeratorState { Seed = SEED, Index = 0 },
shufflePlaylistItems: false,
batchSize: Option<int>.None,
randomStartPoint: true,
CancellationToken.None);
// capture a reference sequence and the state we would persist halfway through
var reference = new List<int>();
CollectionEnumeratorState persistedState = null;
for (var i = 0; i < 12; i++)
{
reference.AddRange(day1.Current.Map(mi => mi.Id));
day1.MoveNext(Option<DateTimeOffset>.None);
if (i == 5)
{
persistedState = day1.State.Clone();
}
}
// sanity: the random start point actually moved us off the natural start
reference.First().ShouldNotBe(10);
persistedState.Started.ShouldBeTrue();
// day 2: rebuild from the persisted state; the offsets must be reproduced so we continue
PlaylistEnumerator day2 = await PlaylistEnumerator.Create(
repo,
BuildMap(),
persistedState,
shufflePlaylistItems: false,
batchSize: Option<int>.None,
randomStartPoint: true,
CancellationToken.None);
var continued = new List<int>();
for (var i = 0; i < 6; i++)
{
continued.AddRange(day2.Current.Map(mi => mi.Id));
day2.MoveNext(Option<DateTimeOffset>.None);
}
// day 2 must pick up exactly where day 1 left off, not reset toward the start
continued.ShouldBe(reference.Skip(6).Take(6));
}
private static Movie FakeMovie(int id) => new() private static Movie FakeMovie(int id) => new()
{ {
Id = id, Id = id,

3
ErsatzTV.Core/Scheduling/PlaylistEnumerator.cs

@ -137,9 +137,8 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator
_batchSize = batchSize _batchSize = batchSize
}; };
// random start points only apply to a fresh build with no saved state // random start points must be applied on every build, not just the first
var random = new Random(state.Seed); var random = new Random(state.Seed);
randomStartPoint = randomStartPoint && !state.Started;
// collections should share enumerators // collections should share enumerators
var enumeratorMap = new Dictionary<CollectionKey, IMediaCollectionEnumerator>(); var enumeratorMap = new Dictionary<CollectionKey, IMediaCollectionEnumerator>();

2
ErsatzTV.Core/Scheduling/PlayoutBuilder.cs

@ -1369,7 +1369,7 @@ public class PlayoutBuilder : IPlayoutBuilder
collectionKey, collectionKey,
cancellationToken), cancellationToken),
state, state,
randomStartPoint, activeSchedule.RandomStartPoint,
cancellationToken); cancellationToken);
case PlaybackOrder.MultiEpisodeShuffle when case PlaybackOrder.MultiEpisodeShuffle when
collectionKey.CollectionType == CollectionType.TelevisionShow && collectionKey.CollectionType == CollectionType.TelevisionShow &&

Loading…
Cancel
Save