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/).
- Future periodic scans will update titles in ETV automatically (deep scans will not be required to update titles) - 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 - Randomize start points on playlist items in classic schedules when setting is enabled
- Playlists ignored this setting in earlier builds - 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 ## [26.6.0] - 2026-07-09
### Added ### Added

3
ErsatzTV.Core/Domain/CollectionEnumeratorState.cs

@ -4,5 +4,6 @@ public class CollectionEnumeratorState
{ {
public int Seed { get; set; } public int Seed { get; set; }
public int Index { 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
{ {
State.Seed = state.Seed; State.Seed = state.Seed;
State.Index = state.Index; State.Index = state.Index;
State.Started = state.Started;
_shuffled = Shuffle(_mediaItems); _shuffled = Shuffle(_mediaItems);
} }
@ -54,6 +55,8 @@ public class BlockPlayoutShuffledMediaCollectionEnumerator : IMediaCollectionEnu
{ {
_shuffled = Shuffle(_mediaItems); _shuffled = Shuffle(_mediaItems);
} }
State.Started = true;
} }
public Option<TimeSpan> MinimumDuration => _lazyMinimumDuration.Value; public Option<TimeSpan> MinimumDuration => _lazyMinimumDuration.Value;

7
ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs

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

10
ErsatzTV.Core/Scheduling/CustomOrderCollectionEnumerator.cs

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

6
ErsatzTV.Core/Scheduling/OrderedScheduleItemsEnumerator.cs

@ -31,7 +31,11 @@ public class OrderedScheduleItemsEnumerator : IScheduleItemsEnumerator
public ProgramScheduleItem Current => _sortedScheduleItems[State.Index]; 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) => public ProgramScheduleItem Peek(int offset) =>
_sortedScheduleItems[(State.Index + offset) % _sortedScheduleItems.Count]; _sortedScheduleItems[(State.Index + offset) % _sortedScheduleItems.Count];

9
ErsatzTV.Core/Scheduling/PlaylistEnumerator.cs

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

22
ErsatzTV.Core/Scheduling/PlayoutBuilder.cs

@ -276,7 +276,6 @@ public class PlayoutBuilder : IPlayoutBuilder
parameters.Start, parameters.Start,
parameters.Finish, parameters.Finish,
parameters.CollectionMediaItems, parameters.CollectionMediaItems,
false,
cancellationToken); cancellationToken);
} }
@ -314,7 +313,6 @@ public class PlayoutBuilder : IPlayoutBuilder
parameters.Start, parameters.Start,
parameters.Finish, parameters.Finish,
parameters.CollectionMediaItems, parameters.CollectionMediaItems,
true,
cancellationToken); cancellationToken);
foreach (BaseError error in maybeResult.LeftToSeq()) foreach (BaseError error in maybeResult.LeftToSeq())
@ -362,7 +360,6 @@ public class PlayoutBuilder : IPlayoutBuilder
parameters.Start, parameters.Start,
parameters.Finish, parameters.Finish,
parameters.CollectionMediaItems, parameters.CollectionMediaItems,
false,
cancellationToken); cancellationToken);
} }
@ -437,7 +434,6 @@ public class PlayoutBuilder : IPlayoutBuilder
DateTimeOffset playoutStart, DateTimeOffset playoutStart,
DateTimeOffset playoutFinish, DateTimeOffset playoutFinish,
Map<CollectionKey, List<MediaItem>> collectionMediaItems, Map<CollectionKey, List<MediaItem>> collectionMediaItems,
bool randomStartPoint,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
DateTimeOffset trimBefore = playoutStart.AddHours(-4); DateTimeOffset trimBefore = playoutStart.AddHours(-4);
@ -476,7 +472,6 @@ public class PlayoutBuilder : IPlayoutBuilder
finish, finish,
collectionMediaItems, collectionMediaItems,
true, true,
randomStartPoint,
cancellationToken); cancellationToken);
foreach (BaseError error in buildResult.LeftToSeq()) foreach (BaseError error in buildResult.LeftToSeq())
@ -489,9 +484,6 @@ public class PlayoutBuilder : IPlayoutBuilder
result = r; result = r;
} }
// only randomize once (at the start of the playout)
randomStartPoint = false;
start = playout.Anchor?.NextStartOffset ?? start; start = playout.Anchor?.NextStartOffset ?? start;
finish = finish.AddDays(1); finish = finish.AddDays(1);
} }
@ -513,7 +505,6 @@ public class PlayoutBuilder : IPlayoutBuilder
playoutFinish, playoutFinish,
collectionMediaItems, collectionMediaItems,
false, false,
randomStartPoint,
cancellationToken); cancellationToken);
foreach (BaseError error in buildResult.LeftToSeq()) foreach (BaseError error in buildResult.LeftToSeq())
@ -565,7 +556,6 @@ public class PlayoutBuilder : IPlayoutBuilder
DateTimeOffset playoutFinish, DateTimeOffset playoutFinish,
Map<CollectionKey, List<MediaItem>> collectionMediaItems, Map<CollectionKey, List<MediaItem>> collectionMediaItems,
bool saveAnchorDate, bool saveAnchorDate,
bool randomStartPoint,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var random = new Random(playout.Seed); var random = new Random(playout.Seed);
@ -589,9 +579,6 @@ public class PlayoutBuilder : IPlayoutBuilder
// _logger.LogDebug("Active schedule is: {Schedule}", activeSchedule.Name); // _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(); var sortedScheduleItems = activeSchedule.Items.OrderBy(i => i.Index).ToList();
CollectionEnumeratorState scheduleItemsEnumeratorState = CollectionEnumeratorState scheduleItemsEnumeratorState =
playout.Anchor?.ScheduleItemsEnumeratorState ?? new CollectionEnumeratorState playout.Anchor?.ScheduleItemsEnumeratorState ?? new CollectionEnumeratorState
@ -619,7 +606,7 @@ public class PlayoutBuilder : IPlayoutBuilder
scheduleItem.MarathonShuffleGroups, scheduleItem.MarathonShuffleGroups,
scheduleItem.MarathonShuffleItems, scheduleItem.MarathonShuffleItems,
scheduleItem.MarathonBatchSize, scheduleItem.MarathonBatchSize,
randomStartPoint, activeSchedule.RandomStartPoint,
random, random,
cancellationToken); cancellationToken);
@ -640,7 +627,7 @@ public class PlayoutBuilder : IPlayoutBuilder
marathonShuffleGroups: false, marathonShuffleGroups: false,
marathonShuffleItems: false, marathonShuffleItems: false,
marathonBatchSize: null, marathonBatchSize: null,
randomStartPoint, activeSchedule.RandomStartPoint,
random, random,
cancellationToken); cancellationToken);
@ -718,7 +705,7 @@ public class PlayoutBuilder : IPlayoutBuilder
scheduleItem.MarathonShuffleGroups, scheduleItem.MarathonShuffleGroups,
scheduleItem.MarathonShuffleItems, scheduleItem.MarathonShuffleItems,
scheduleItem.MarathonBatchSize, scheduleItem.MarathonBatchSize,
randomStartPoint, activeSchedule.RandomStartPoint,
random, random,
cancellationToken); cancellationToken);
@ -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.Started;
randomStartPoint = randomStartPoint && state.Index == 0;
switch (playbackOrder) switch (playbackOrder)
{ {

6
ErsatzTV.Core/Scheduling/RandomizedMediaCollectionEnumerator.cs

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

6
ErsatzTV.Core/Scheduling/RandomizedRotatingMediaCollectionEnumerator.cs

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

6
ErsatzTV.Core/Scheduling/SeasonEpisodeMediaCollectionEnumerator.cs

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

3
ErsatzTV.Core/Scheduling/ShuffleInOrderCollectionEnumerator.cs

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

2
ErsatzTV.Core/Scheduling/ShuffledMediaCollectionEnumerator.cs

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

1
ErsatzTV.Core/Scheduling/ShuffledScheduleItemsEnumerator.cs

@ -57,6 +57,7 @@ public class ShuffledScheduleItemsEnumerator : IScheduleItemsEnumerator
} }
State.Index %= _scheduleItemsCount; State.Index %= _scheduleItemsCount;
State.Started = true;
} }
public ProgramScheduleItem Peek(int offset) 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 @@
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
b2.Property<int>("Seed") b2.Property<int>("Seed")
.HasColumnType("int"); .HasColumnType("int");
b2.Property<bool>("Started")
.HasColumnType("tinyint(1)");
b2.HasKey("PlayoutAnchorPlayoutId"); b2.HasKey("PlayoutAnchorPlayoutId");
b2.ToTable("ScheduleItemsEnumeratorState", (string)null); b2.ToTable("ScheduleItemsEnumeratorState", (string)null);
@ -5355,6 +5358,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
b1.Property<int>("Seed") b1.Property<int>("Seed")
.HasColumnType("int"); .HasColumnType("int");
b1.Property<bool>("Started")
.HasColumnType("tinyint(1)");
b1.HasKey("PlayoutProgramScheduleAnchorId"); b1.HasKey("PlayoutProgramScheduleAnchorId");
b1.ToTable("CollectionEnumeratorState", (string)null); b1.ToTable("CollectionEnumeratorState", (string)null);
@ -5405,6 +5411,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
b1.Property<int>("Seed") b1.Property<int>("Seed")
.HasColumnType("int"); .HasColumnType("int");
b1.Property<bool>("Started")
.HasColumnType("tinyint(1)");
b1.HasKey("PlayoutScheduleItemFillGroupIndexId"); b1.HasKey("PlayoutScheduleItemFillGroupIndexId");
b1.ToTable("FillGroupEnumeratorState", (string)null); 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 @@
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
b2.Property<int>("Seed") b2.Property<int>("Seed")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b2.Property<bool>("Started")
.HasColumnType("INTEGER");
b2.HasKey("PlayoutAnchorPlayoutId"); b2.HasKey("PlayoutAnchorPlayoutId");
b2.ToTable("ScheduleItemsEnumeratorState", (string)null); b2.ToTable("ScheduleItemsEnumeratorState", (string)null);
@ -5182,6 +5185,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
b1.Property<int>("Seed") b1.Property<int>("Seed")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b1.Property<bool>("Started")
.HasColumnType("INTEGER");
b1.HasKey("PlayoutProgramScheduleAnchorId"); b1.HasKey("PlayoutProgramScheduleAnchorId");
b1.ToTable("CollectionEnumeratorState", (string)null); b1.ToTable("CollectionEnumeratorState", (string)null);
@ -5232,6 +5238,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
b1.Property<int>("Seed") b1.Property<int>("Seed")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b1.Property<bool>("Started")
.HasColumnType("INTEGER");
b1.HasKey("PlayoutScheduleItemFillGroupIndexId"); b1.HasKey("PlayoutScheduleItemFillGroupIndexId");
b1.ToTable("FillGroupEnumeratorState", (string)null); b1.ToTable("FillGroupEnumeratorState", (string)null);

1
ErsatzTV.Infrastructure/Scheduling/MultiEpisodeShuffleCollectionEnumerator.cs

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

Loading…
Cancel
Save