Browse Source

fix: always randomize start points for all collections the first time they are used

pull/2949/head
Jason Dove 2 weeks ago
parent
commit
93a0a3f032
No known key found for this signature in database
  1. 2
      CHANGELOG.md
  2. 3
      ErsatzTV.Core/Domain/CollectionEnumeratorState.cs
  3. 3
      ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutShuffledMediaCollectionEnumerator.cs
  4. 7
      ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs
  5. 10
      ErsatzTV.Core/Scheduling/CustomOrderCollectionEnumerator.cs
  6. 6
      ErsatzTV.Core/Scheduling/OrderedScheduleItemsEnumerator.cs
  7. 9
      ErsatzTV.Core/Scheduling/PlaylistEnumerator.cs
  8. 22
      ErsatzTV.Core/Scheduling/PlayoutBuilder.cs
  9. 6
      ErsatzTV.Core/Scheduling/RandomizedMediaCollectionEnumerator.cs
  10. 6
      ErsatzTV.Core/Scheduling/RandomizedRotatingMediaCollectionEnumerator.cs
  11. 6
      ErsatzTV.Core/Scheduling/SeasonEpisodeMediaCollectionEnumerator.cs
  12. 3
      ErsatzTV.Core/Scheduling/ShuffleInOrderCollectionEnumerator.cs
  13. 2
      ErsatzTV.Core/Scheduling/ShuffledMediaCollectionEnumerator.cs
  14. 1
      ErsatzTV.Core/Scheduling/ShuffledScheduleItemsEnumerator.cs
  15. 7060
      ErsatzTV.Infrastructure.MySql/Migrations/20260722002414_Add_CollectionEnumeratorState_Started.Designer.cs
  16. 57
      ErsatzTV.Infrastructure.MySql/Migrations/20260722002414_Add_CollectionEnumeratorState_Started.cs
  17. 9
      ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs
  18. 6887
      ErsatzTV.Infrastructure.Sqlite/Migrations/20260722002327_Add_CollectionEnumeratorState_Started.Designer.cs
  19. 57
      ErsatzTV.Infrastructure.Sqlite/Migrations/20260722002327_Add_CollectionEnumeratorState_Started.cs
  20. 9
      ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs
  21. 1
      ErsatzTV.Infrastructure/Scheduling/MultiEpisodeShuffleCollectionEnumerator.cs

2
CHANGELOG.md

@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Future periodic scans will update titles in ETV automatically (deep scans will not be required to update titles)
- Randomize start points on playlist items in classic schedules when setting is enabled
- Playlists ignored this setting in earlier builds
- Always randomize start points for all collections the first time they are used
- Previously, only collections scheduled during the first day of a playout build had their start points randomized
## [26.6.0] - 2026-07-09
### Added

3
ErsatzTV.Core/Domain/CollectionEnumeratorState.cs

@ -4,5 +4,6 @@ public class CollectionEnumeratorState @@ -4,5 +4,6 @@ public class CollectionEnumeratorState
{
public int Seed { get; set; }
public int Index { get; set; }
public CollectionEnumeratorState Clone() => new() { Seed = Seed, Index = Index };
public bool Started { get; set; }
public CollectionEnumeratorState Clone() => new() { Seed = Seed, Index = Index, Started = Started };
}

3
ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutShuffledMediaCollectionEnumerator.cs

@ -35,6 +35,7 @@ public class BlockPlayoutShuffledMediaCollectionEnumerator : IMediaCollectionEnu @@ -35,6 +35,7 @@ public class BlockPlayoutShuffledMediaCollectionEnumerator : IMediaCollectionEnu
{
State.Seed = state.Seed;
State.Index = state.Index;
State.Started = state.Started;
_shuffled = Shuffle(_mediaItems);
}
@ -54,6 +55,8 @@ public class BlockPlayoutShuffledMediaCollectionEnumerator : IMediaCollectionEnu @@ -54,6 +55,8 @@ public class BlockPlayoutShuffledMediaCollectionEnumerator : IMediaCollectionEnu
{
_shuffled = Shuffle(_mediaItems);
}
State.Started = true;
}
public Option<TimeSpan> MinimumDuration => _lazyMinimumDuration.Value;

7
ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs

@ -36,9 +36,12 @@ public sealed class ChronologicalMediaCollectionEnumerator : IMediaCollectionEnu @@ -36,9 +36,12 @@ public sealed class ChronologicalMediaCollectionEnumerator : IMediaCollectionEnu
}
}
public void ResetState(CollectionEnumeratorState state) =>
public void ResetState(CollectionEnumeratorState state)
{
// seed doesn't matter in chronological
State.Index = state.Index;
State.Started = state.Started;
}
public string SchedulingContextName => "Chronological";
@ -53,6 +56,8 @@ public sealed class ChronologicalMediaCollectionEnumerator : IMediaCollectionEnu @@ -53,6 +56,8 @@ public sealed class ChronologicalMediaCollectionEnumerator : IMediaCollectionEnu
{
State.Index = (State.Index + 1) % _sortedMediaItems.Count;
}
State.Started = true;
}
public Option<TimeSpan> MinimumDuration => _lazyMinimumDuration.Value;

10
ErsatzTV.Core/Scheduling/CustomOrderCollectionEnumerator.cs

@ -32,9 +32,12 @@ public class CustomOrderCollectionEnumerator : IMediaCollectionEnumerator @@ -32,9 +32,12 @@ public class CustomOrderCollectionEnumerator : IMediaCollectionEnumerator
}
}
public void ResetState(CollectionEnumeratorState state) =>
public void ResetState(CollectionEnumeratorState state)
{
// seed doesn't matter here
State.Index = state.Index;
State.Started = state.Started;
}
public string SchedulingContextName => "Custom Order";
@ -43,8 +46,11 @@ public class CustomOrderCollectionEnumerator : IMediaCollectionEnumerator @@ -43,8 +46,11 @@ public class CustomOrderCollectionEnumerator : IMediaCollectionEnumerator
public Option<MediaItem> Current => _sortedMediaItems.Count != 0 ? _sortedMediaItems[State.Index] : None;
public Option<bool> CurrentIncludeInProgramGuide { get; }
public void MoveNext(Option<DateTimeOffset> scheduledAt) =>
public void MoveNext(Option<DateTimeOffset> scheduledAt)
{
State.Index = (State.Index + 1) % _sortedMediaItems.Count;
State.Started = true;
}
public Option<TimeSpan> MinimumDuration => _lazyMinimumDuration.Value;

6
ErsatzTV.Core/Scheduling/OrderedScheduleItemsEnumerator.cs

@ -31,7 +31,11 @@ public class OrderedScheduleItemsEnumerator : IScheduleItemsEnumerator @@ -31,7 +31,11 @@ public class OrderedScheduleItemsEnumerator : IScheduleItemsEnumerator
public ProgramScheduleItem Current => _sortedScheduleItems[State.Index];
public void MoveNext() => State.Index = (State.Index + 1) % _sortedScheduleItems.Count;
public void MoveNext()
{
State.Index = (State.Index + 1) % _sortedScheduleItems.Count;
State.Started = true;
}
public ProgramScheduleItem Peek(int offset) =>
_sortedScheduleItems[(State.Index + offset) % _sortedScheduleItems.Count];

9
ErsatzTV.Core/Scheduling/PlaylistEnumerator.cs

@ -31,9 +31,12 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator @@ -31,9 +31,12 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator
public int EnumeratorIndex { get; private set; }
public void ResetState(CollectionEnumeratorState state) =>
public void ResetState(CollectionEnumeratorState state)
{
// seed doesn't matter here
State.Index = state.Index;
State.Started = state.Started;
}
public string SchedulingContextName => "Playlist";
@ -111,6 +114,8 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator @@ -111,6 +114,8 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator
_sortedEnumerators = ShufflePlaylistItems();
}
}
State.Started = true;
}
public void SetEnumeratorIndex(int enumeratorIndex) => EnumeratorIndex = enumeratorIndex % _sortedEnumerators.Count;
@ -134,7 +139,7 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator @@ -134,7 +139,7 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator
// random start points only apply to a fresh build with no saved state
var random = new Random(state.Seed);
randomStartPoint = randomStartPoint && state.Index == 0;
randomStartPoint = randomStartPoint && !state.Started;
// collections should share enumerators
var enumeratorMap = new Dictionary<CollectionKey, IMediaCollectionEnumerator>();

22
ErsatzTV.Core/Scheduling/PlayoutBuilder.cs

@ -276,7 +276,6 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -276,7 +276,6 @@ public class PlayoutBuilder : IPlayoutBuilder
parameters.Start,
parameters.Finish,
parameters.CollectionMediaItems,
false,
cancellationToken);
}
@ -314,7 +313,6 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -314,7 +313,6 @@ public class PlayoutBuilder : IPlayoutBuilder
parameters.Start,
parameters.Finish,
parameters.CollectionMediaItems,
true,
cancellationToken);
foreach (BaseError error in maybeResult.LeftToSeq())
@ -362,7 +360,6 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -362,7 +360,6 @@ public class PlayoutBuilder : IPlayoutBuilder
parameters.Start,
parameters.Finish,
parameters.CollectionMediaItems,
false,
cancellationToken);
}
@ -437,7 +434,6 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -437,7 +434,6 @@ public class PlayoutBuilder : IPlayoutBuilder
DateTimeOffset playoutStart,
DateTimeOffset playoutFinish,
Map<CollectionKey, List<MediaItem>> collectionMediaItems,
bool randomStartPoint,
CancellationToken cancellationToken)
{
DateTimeOffset trimBefore = playoutStart.AddHours(-4);
@ -476,7 +472,6 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -476,7 +472,6 @@ public class PlayoutBuilder : IPlayoutBuilder
finish,
collectionMediaItems,
true,
randomStartPoint,
cancellationToken);
foreach (BaseError error in buildResult.LeftToSeq())
@ -489,9 +484,6 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -489,9 +484,6 @@ public class PlayoutBuilder : IPlayoutBuilder
result = r;
}
// only randomize once (at the start of the playout)
randomStartPoint = false;
start = playout.Anchor?.NextStartOffset ?? start;
finish = finish.AddDays(1);
}
@ -513,7 +505,6 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -513,7 +505,6 @@ public class PlayoutBuilder : IPlayoutBuilder
playoutFinish,
collectionMediaItems,
false,
randomStartPoint,
cancellationToken);
foreach (BaseError error in buildResult.LeftToSeq())
@ -565,7 +556,6 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -565,7 +556,6 @@ public class PlayoutBuilder : IPlayoutBuilder
DateTimeOffset playoutFinish,
Map<CollectionKey, List<MediaItem>> collectionMediaItems,
bool saveAnchorDate,
bool randomStartPoint,
CancellationToken cancellationToken)
{
var random = new Random(playout.Seed);
@ -589,9 +579,6 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -589,9 +579,6 @@ public class PlayoutBuilder : IPlayoutBuilder
// _logger.LogDebug("Active schedule is: {Schedule}", activeSchedule.Name);
// random start points are disabled in some scenarios, so ensure it's enabled and active
randomStartPoint = randomStartPoint && activeSchedule.RandomStartPoint;
var sortedScheduleItems = activeSchedule.Items.OrderBy(i => i.Index).ToList();
CollectionEnumeratorState scheduleItemsEnumeratorState =
playout.Anchor?.ScheduleItemsEnumeratorState ?? new CollectionEnumeratorState
@ -619,7 +606,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -619,7 +606,7 @@ public class PlayoutBuilder : IPlayoutBuilder
scheduleItem.MarathonShuffleGroups,
scheduleItem.MarathonShuffleItems,
scheduleItem.MarathonBatchSize,
randomStartPoint,
activeSchedule.RandomStartPoint,
random,
cancellationToken);
@ -640,7 +627,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -640,7 +627,7 @@ public class PlayoutBuilder : IPlayoutBuilder
marathonShuffleGroups: false,
marathonShuffleItems: false,
marathonBatchSize: null,
randomStartPoint,
activeSchedule.RandomStartPoint,
random,
cancellationToken);
@ -718,7 +705,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -718,7 +705,7 @@ public class PlayoutBuilder : IPlayoutBuilder
scheduleItem.MarathonShuffleGroups,
scheduleItem.MarathonShuffleItems,
scheduleItem.MarathonBatchSize,
randomStartPoint,
activeSchedule.RandomStartPoint,
random,
cancellationToken);
@ -1347,8 +1334,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -1347,8 +1334,7 @@ public class PlayoutBuilder : IPlayoutBuilder
}
}
// index shouldn't ever be greater than zero with randomStartPoint since anchors shouldn't exist, but
randomStartPoint = randomStartPoint && state.Index == 0;
randomStartPoint = randomStartPoint && !state.Started;
switch (playbackOrder)
{

6
ErsatzTV.Core/Scheduling/RandomizedMediaCollectionEnumerator.cs

@ -37,9 +37,12 @@ public class RandomizedMediaCollectionEnumerator : IMediaCollectionEnumerator @@ -37,9 +37,12 @@ public class RandomizedMediaCollectionEnumerator : IMediaCollectionEnumerator
}
}
public void ResetState(CollectionEnumeratorState state) =>
public void ResetState(CollectionEnumeratorState state)
{
// seed never changes here, no need to reset
State.Index = state.Index;
State.Started = state.Started;
}
public string SchedulingContextName => "Random";
@ -57,6 +60,7 @@ public class RandomizedMediaCollectionEnumerator : IMediaCollectionEnumerator @@ -57,6 +60,7 @@ public class RandomizedMediaCollectionEnumerator : IMediaCollectionEnumerator
_index = _random.Next() % _mediaItems.Count;
State.Index++;
State.Started = true;
}
public Option<TimeSpan> MinimumDuration => _lazyMinimumDuration.Value;

6
ErsatzTV.Core/Scheduling/RandomizedRotatingMediaCollectionEnumerator.cs

@ -61,9 +61,12 @@ public class RandomizedRotatingMediaCollectionEnumerator : IMediaCollectionEnume @@ -61,9 +61,12 @@ public class RandomizedRotatingMediaCollectionEnumerator : IMediaCollectionEnume
}
}
public void ResetState(CollectionEnumeratorState state) =>
public void ResetState(CollectionEnumeratorState state)
{
// seed never changes here, no need to reset
State.Index = state.Index;
State.Started = state.Started;
}
public string SchedulingContextName => "Random Rotating";
@ -102,6 +105,7 @@ public class RandomizedRotatingMediaCollectionEnumerator : IMediaCollectionEnume @@ -102,6 +105,7 @@ public class RandomizedRotatingMediaCollectionEnumerator : IMediaCollectionEnume
int itemNumber = nextRandom % _groupMedia[groups[_groupNumber]].Count;
_index = _groupMedia[groups[_groupNumber]][itemNumber];
State.Index++;
State.Started = true;
}
public Option<TimeSpan> MinimumDuration => _lazyMinimumDuration.Value;

6
ErsatzTV.Core/Scheduling/SeasonEpisodeMediaCollectionEnumerator.cs

@ -35,9 +35,12 @@ public sealed class SeasonEpisodeMediaCollectionEnumerator : IMediaCollectionEnu @@ -35,9 +35,12 @@ public sealed class SeasonEpisodeMediaCollectionEnumerator : IMediaCollectionEnu
}
}
public void ResetState(CollectionEnumeratorState state) =>
public void ResetState(CollectionEnumeratorState state)
{
// seed doesn't matter here
State.Index = state.Index;
State.Started = state.Started;
}
public string SchedulingContextName => "Season, Episode";
@ -54,6 +57,7 @@ public sealed class SeasonEpisodeMediaCollectionEnumerator : IMediaCollectionEnu @@ -54,6 +57,7 @@ public sealed class SeasonEpisodeMediaCollectionEnumerator : IMediaCollectionEnu
}
State.Index = (State.Index + 1) % _sortedMediaItems.Count;
State.Started = true;
}
public Option<TimeSpan> MinimumDuration => _lazyMinimumDuration.Value;

3
ErsatzTV.Core/Scheduling/ShuffleInOrderCollectionEnumerator.cs

@ -57,6 +57,7 @@ public class ShuffleInOrderCollectionEnumerator : IMediaCollectionEnumerator @@ -57,6 +57,7 @@ public class ShuffleInOrderCollectionEnumerator : IMediaCollectionEnumerator
State.Seed = state.Seed;
State.Index = state.Index;
State.Started = state.Started;
}
public string SchedulingContextName => "Shuffle in Order";
@ -95,6 +96,8 @@ public class ShuffleInOrderCollectionEnumerator : IMediaCollectionEnumerator @@ -95,6 +96,8 @@ public class ShuffleInOrderCollectionEnumerator : IMediaCollectionEnumerator
{
State.Index %= _shuffled.Length;
}
State.Started = true;
}
public Option<TimeSpan> MinimumDuration => _lazyMinimumDuration.Value;

2
ErsatzTV.Core/Scheduling/ShuffledMediaCollectionEnumerator.cs

@ -92,6 +92,8 @@ public class ShuffledMediaCollectionEnumerator : IMediaCollectionEnumerator @@ -92,6 +92,8 @@ public class ShuffledMediaCollectionEnumerator : IMediaCollectionEnumerator
{
State.Index %= _mediaItemCount;
}
State.Started = true;
}
public Option<TimeSpan> MinimumDuration => _lazyMinimumDuration.Value;

1
ErsatzTV.Core/Scheduling/ShuffledScheduleItemsEnumerator.cs

@ -57,6 +57,7 @@ public class ShuffledScheduleItemsEnumerator : IScheduleItemsEnumerator @@ -57,6 +57,7 @@ public class ShuffledScheduleItemsEnumerator : IScheduleItemsEnumerator
}
State.Index %= _scheduleItemsCount;
State.Started = true;
}
public ProgramScheduleItem Peek(int offset)

7060
ErsatzTV.Infrastructure.MySql/Migrations/20260722002414_Add_CollectionEnumeratorState_Started.Designer.cs generated

File diff suppressed because it is too large Load Diff

57
ErsatzTV.Infrastructure.MySql/Migrations/20260722002414_Add_CollectionEnumeratorState_Started.cs

@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.MySql.Migrations
{
/// <inheritdoc />
public partial class Add_CollectionEnumeratorState_Started : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "Started",
table: "ScheduleItemsEnumeratorState",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<bool>(
name: "Started",
table: "FillGroupEnumeratorState",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<bool>(
name: "Started",
table: "CollectionEnumeratorState",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
// treat all existing enumerators as started so upgrades never yank an in-progress
// enumerator to a random start point; only enumerators created after this get a random start
migrationBuilder.Sql("UPDATE `ScheduleItemsEnumeratorState` SET `Started` = 1");
migrationBuilder.Sql("UPDATE `FillGroupEnumeratorState` SET `Started` = 1");
migrationBuilder.Sql("UPDATE `CollectionEnumeratorState` SET `Started` = 1");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Started",
table: "ScheduleItemsEnumeratorState");
migrationBuilder.DropColumn(
name: "Started",
table: "FillGroupEnumeratorState");
migrationBuilder.DropColumn(
name: "Started",
table: "CollectionEnumeratorState");
}
}
}

9
ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs

@ -5207,6 +5207,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -5207,6 +5207,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
b2.Property<int>("Seed")
.HasColumnType("int");
b2.Property<bool>("Started")
.HasColumnType("tinyint(1)");
b2.HasKey("PlayoutAnchorPlayoutId");
b2.ToTable("ScheduleItemsEnumeratorState", (string)null);
@ -5355,6 +5358,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -5355,6 +5358,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
b1.Property<int>("Seed")
.HasColumnType("int");
b1.Property<bool>("Started")
.HasColumnType("tinyint(1)");
b1.HasKey("PlayoutProgramScheduleAnchorId");
b1.ToTable("CollectionEnumeratorState", (string)null);
@ -5405,6 +5411,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -5405,6 +5411,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
b1.Property<int>("Seed")
.HasColumnType("int");
b1.Property<bool>("Started")
.HasColumnType("tinyint(1)");
b1.HasKey("PlayoutScheduleItemFillGroupIndexId");
b1.ToTable("FillGroupEnumeratorState", (string)null);

6887
ErsatzTV.Infrastructure.Sqlite/Migrations/20260722002327_Add_CollectionEnumeratorState_Started.Designer.cs generated

File diff suppressed because it is too large Load Diff

57
ErsatzTV.Infrastructure.Sqlite/Migrations/20260722002327_Add_CollectionEnumeratorState_Started.cs

@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
/// <inheritdoc />
public partial class Add_CollectionEnumeratorState_Started : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "Started",
table: "ScheduleItemsEnumeratorState",
type: "INTEGER",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<bool>(
name: "Started",
table: "FillGroupEnumeratorState",
type: "INTEGER",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<bool>(
name: "Started",
table: "CollectionEnumeratorState",
type: "INTEGER",
nullable: false,
defaultValue: false);
// treat all existing enumerators as started so upgrades never yank an in-progress
// enumerator to a random start point; only enumerators created after this get a random start
migrationBuilder.Sql("""UPDATE "ScheduleItemsEnumeratorState" SET "Started" = 1""");
migrationBuilder.Sql("""UPDATE "FillGroupEnumeratorState" SET "Started" = 1""");
migrationBuilder.Sql("""UPDATE "CollectionEnumeratorState" SET "Started" = 1""");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Started",
table: "ScheduleItemsEnumeratorState");
migrationBuilder.DropColumn(
name: "Started",
table: "FillGroupEnumeratorState");
migrationBuilder.DropColumn(
name: "Started",
table: "CollectionEnumeratorState");
}
}
}

9
ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs

@ -5034,6 +5034,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -5034,6 +5034,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
b2.Property<int>("Seed")
.HasColumnType("INTEGER");
b2.Property<bool>("Started")
.HasColumnType("INTEGER");
b2.HasKey("PlayoutAnchorPlayoutId");
b2.ToTable("ScheduleItemsEnumeratorState", (string)null);
@ -5182,6 +5185,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -5182,6 +5185,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
b1.Property<int>("Seed")
.HasColumnType("INTEGER");
b1.Property<bool>("Started")
.HasColumnType("INTEGER");
b1.HasKey("PlayoutProgramScheduleAnchorId");
b1.ToTable("CollectionEnumeratorState", (string)null);
@ -5232,6 +5238,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -5232,6 +5238,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
b1.Property<int>("Seed")
.HasColumnType("INTEGER");
b1.Property<bool>("Started")
.HasColumnType("INTEGER");
b1.HasKey("PlayoutScheduleItemFillGroupIndexId");
b1.ToTable("FillGroupEnumeratorState", (string)null);

1
ErsatzTV.Infrastructure/Scheduling/MultiEpisodeShuffleCollectionEnumerator.cs

@ -100,6 +100,7 @@ public class MultiEpisodeShuffleCollectionEnumerator : IMediaCollectionEnumerato @@ -100,6 +100,7 @@ public class MultiEpisodeShuffleCollectionEnumerator : IMediaCollectionEnumerato
}
State.Index = state.Index;
State.Started = state.Started;
}
public string SchedulingContextName => "Multi-Episode Shuffle";

Loading…
Cancel
Save