Browse Source

fix filler playout crash (#517)

pull/518/head
Jason Dove 4 years ago committed by GitHub
parent
commit
56f94f489a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 90
      ErsatzTV.Core.Tests/Scheduling/ShuffledMediaCollectionEnumeratorTests.cs
  3. 41
      ErsatzTV.Core/Scheduling/CloneableRandom.cs
  4. 28
      ErsatzTV.Core/Scheduling/ShuffledMediaCollectionEnumerator.cs

1
CHANGELOG.md

@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Properly split song genre tags
- Properly display all songs that have an identical album and title
- Fix channel logo and watermark uploads
- Fix regression introduced with `v0.2.4-alpha` that caused some filler edge cases to crash the playout builder
### Added
- Add song genres to search index

90
ErsatzTV.Core.Tests/Scheduling/ShuffledMediaCollectionEnumeratorTests.cs

@ -0,0 +1,90 @@ @@ -0,0 +1,90 @@
using System.Collections.Generic;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Scheduling;
using FluentAssertions;
using LanguageExt;
using LanguageExt.UnsafeValueAccess;
using NUnit.Framework;
namespace ErsatzTV.Core.Tests.Scheduling
{
[TestFixture]
public class ShuffledMediaCollectionEnumeratorTests
{
private readonly List<GroupedMediaItem> _mediaItems = new()
{
new GroupedMediaItem(new MediaItem { Id = 1 }, new List<MediaItem>()),
new GroupedMediaItem(new MediaItem { Id = 2 }, new List<MediaItem>()),
new GroupedMediaItem(new MediaItem { Id = 3 }, new List<MediaItem>())
};
[Test]
public void Peek_Zero_Should_Match_Current()
{
var state = new CollectionEnumeratorState { Index = 0, Seed = 0 };
var enumerator = new ShuffledMediaCollectionEnumerator(_mediaItems, state);
Option<MediaItem> peek = enumerator.Peek(0);
Option<MediaItem> current = enumerator.Current;
peek.IsSome.Should().BeTrue();
current.IsSome.Should().BeTrue();
peek.ValueUnsafe().Id.Should().Be(1);
current.ValueUnsafe().Id.Should().Be(1);
}
[Test]
public void Peek_One_Should_Match_Next()
{
var state = new CollectionEnumeratorState { Index = 0, Seed = 0 };
var enumerator = new ShuffledMediaCollectionEnumerator(_mediaItems, state);
Option<MediaItem> peek = enumerator.Peek(1);
enumerator.MoveNext();
Option<MediaItem> next = enumerator.Current;
peek.IsSome.Should().BeTrue();
next.IsSome.Should().BeTrue();
peek.ValueUnsafe().Id.Should().Be(2);
next.ValueUnsafe().Id.Should().Be(2);
}
[Test]
public void Peek_Two_Should_Match_NextNext()
{
var state = new CollectionEnumeratorState { Index = 0, Seed = 0 };
var enumerator = new ShuffledMediaCollectionEnumerator(_mediaItems, state);
Option<MediaItem> peek = enumerator.Peek(2);
enumerator.MoveNext();
enumerator.MoveNext();
Option<MediaItem> next = enumerator.Current;
peek.IsSome.Should().BeTrue();
next.IsSome.Should().BeTrue();
peek.ValueUnsafe().Id.Should().Be(3);
next.ValueUnsafe().Id.Should().Be(3);
}
[Test]
public void Peek_Three_Should_Match_NextNextNext()
{
var state = new CollectionEnumeratorState { Index = 0, Seed = 0 };
var enumerator = new ShuffledMediaCollectionEnumerator(_mediaItems, state);
Option<MediaItem> peek = enumerator.Peek(3);
enumerator.MoveNext();
enumerator.MoveNext();
enumerator.MoveNext();
Option<MediaItem> next = enumerator.Current;
peek.IsSome.Should().BeTrue();
next.IsSome.Should().BeTrue();
peek.ValueUnsafe().Id.Should().Be(2);
next.ValueUnsafe().Id.Should().Be(2);
}
}
}

41
ErsatzTV.Core/Scheduling/CloneableRandom.cs

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
using System;
namespace ErsatzTV.Core.Scheduling
{
public class CloneableRandom
{
private readonly int _seed;
private readonly Random _random;
private int _count;
public CloneableRandom(int seed)
{
_seed = seed;
_random = new Random(_seed);
}
public CloneableRandom Clone()
{
var clone = new CloneableRandom(_seed);
for (var i = 0; i < _count; i++)
{
clone.Next();
}
return clone;
}
public int Next()
{
_count++;
return _random.Next();
}
public int Next(int maxValue)
{
_count++;
return _random.Next(maxValue);
}
}
}

28
ErsatzTV.Core/Scheduling/ShuffledMediaCollectionEnumerator.cs

@ -1,7 +1,6 @@ @@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Scheduling;
using LanguageExt;
@ -13,7 +12,7 @@ namespace ErsatzTV.Core.Scheduling @@ -13,7 +12,7 @@ namespace ErsatzTV.Core.Scheduling
{
private readonly int _mediaItemCount;
private readonly IList<GroupedMediaItem> _mediaItems;
private Random _random;
private CloneableRandom _random;
private IList<MediaItem> _shuffled;
public ShuffledMediaCollectionEnumerator(
@ -29,7 +28,7 @@ namespace ErsatzTV.Core.Scheduling @@ -29,7 +28,7 @@ namespace ErsatzTV.Core.Scheduling
state.Seed = new Random(state.Seed).Next();
}
_random = new Random(state.Seed);
_random = new CloneableRandom(state.Seed);
_shuffled = Shuffle(_mediaItems, _random);
State = new CollectionEnumeratorState { Seed = state.Seed };
@ -45,7 +44,7 @@ namespace ErsatzTV.Core.Scheduling @@ -45,7 +44,7 @@ namespace ErsatzTV.Core.Scheduling
public void MoveNext()
{
if ((State.Index + 1) % _shuffled.Count == 0)
if ((State.Index + 1) % _mediaItemCount == 0)
{
Option<MediaItem> tail = Current;
@ -53,7 +52,7 @@ namespace ErsatzTV.Core.Scheduling @@ -53,7 +52,7 @@ namespace ErsatzTV.Core.Scheduling
do
{
State.Seed = _random.Next();
_random = new Random(State.Seed);
_random = new CloneableRandom(State.Seed);
_shuffled = Shuffle(_mediaItems, _random);
} while (_mediaItems.Count > 1 && Current == tail);
}
@ -62,29 +61,28 @@ namespace ErsatzTV.Core.Scheduling @@ -62,29 +61,28 @@ namespace ErsatzTV.Core.Scheduling
State.Index++;
}
State.Index %= _shuffled.Count;
State.Index %= _mediaItemCount;
}
public Option<MediaItem> Peek(int offset)
{
if (offset == 0)
{
return Current;
}
if ((State.Index + offset) % _mediaItemCount == 0)
{
IList<MediaItem> shuffled;
Option<MediaItem> tail = Current;
// clone the random
var randomCopy = new Random();
FieldInfo seedArrayInfo = typeof(Random).GetField(
"_seedArray",
BindingFlags.NonPublic | BindingFlags.Instance);
var seedArray = seedArrayInfo.GetValue(_random) as int[];
int[] seedArrayCopy = seedArray.ToArray();
seedArrayInfo.SetValue(randomCopy, seedArrayCopy);
CloneableRandom randomCopy = _random.Clone();
do
{
int newSeed = randomCopy.Next();
randomCopy = new Random(newSeed);
randomCopy = new CloneableRandom(newSeed);
shuffled = Shuffle(_mediaItems, randomCopy);
} while (_mediaItems.Count > 1 && shuffled[0] == tail);
@ -94,7 +92,7 @@ namespace ErsatzTV.Core.Scheduling @@ -94,7 +92,7 @@ namespace ErsatzTV.Core.Scheduling
return _shuffled.Any() ? _shuffled[(State.Index + offset) % _mediaItemCount] : None;
}
private IList<MediaItem> Shuffle(IEnumerable<GroupedMediaItem> list, Random random)
private IList<MediaItem> Shuffle(IEnumerable<GroupedMediaItem> list, CloneableRandom random)
{
GroupedMediaItem[] copy = list.ToArray();

Loading…
Cancel
Save