Browse Source

make count an expression in classic schedules

pull/2794/head
Jason Dove 6 months ago
parent
commit
6fb38f71ef
No known key found for this signature in database
  1. 22
      CHANGELOG.md
  2. 2
      ErsatzTV.Application/ProgramSchedules/Commands/AddProgramScheduleItem.cs
  3. 2
      ErsatzTV.Application/ProgramSchedules/Commands/IProgramScheduleItemRequest.cs
  4. 6
      ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs
  5. 2
      ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItems.cs
  6. 4
      ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemMultipleViewModel.cs
  7. 4
      ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ContinuePlayoutTests.cs
  8. 12
      ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/NewPlayoutTests.cs
  9. 1
      ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerBaseTests.cs
  10. 17
      ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerDurationTests.cs
  11. 20
      ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerFloodTests.cs
  12. 37
      ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerMultipleTests.cs
  13. 17
      ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerOneTests.cs
  14. 2
      ErsatzTV.Core/Domain/ProgramScheduleItemMultiple.cs
  15. 1
      ErsatzTV.Core/Interfaces/Scheduling/IPlayoutModeScheduler.cs
  16. 36
      ErsatzTV.Core/Scheduling/CountExpression.cs
  17. 4
      ErsatzTV.Core/Scheduling/PlayoutBuilder.cs
  18. 1
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs
  19. 1
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerDuration.cs
  20. 1
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerFlood.cs
  21. 10
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerMultiple.cs
  22. 1
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerOne.cs
  23. 23
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutCountHandler.cs
  24. 7024
      ErsatzTV.Infrastructure.MySql/Migrations/20260120145601_Change_ProgramScheduleItemMultipleCountString.Designer.cs
  25. 38
      ErsatzTV.Infrastructure.MySql/Migrations/20260120145601_Change_ProgramScheduleItemMultipleCountString.cs
  26. 6
      ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs
  27. 6851
      ErsatzTV.Infrastructure.Sqlite/Migrations/20260120145518_Change_ProgramScheduleItemMultipleCountString.Designer.cs
  28. 36
      ErsatzTV.Infrastructure.Sqlite/Migrations/20260120145518_Change_ProgramScheduleItemMultipleCountString.cs
  29. 6
      ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs
  30. 2
      ErsatzTV/Validators/ProgramScheduleItemEditViewModelValidator.cs
  31. 4
      ErsatzTV/ViewModels/ProgramScheduleItemEditViewModel.cs

22
CHANGELOG.md

@ -22,6 +22,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -22,6 +22,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Disable automatic artwork database cleanup
- This will be re-enabled at some point in the future (after more testing)
- For now, the API should be used to clean as needed
- Classic Schedules: make multiple `count` an expression
- The following parameters can be used:
- `count`: the total number of items in the collection
- `random`: a random number between zero and (count - 1)
- For example:
- `count / 2` will play half of the items in the collection
- `random % 4 + 1` will play between 1 and 4 items
- `2` (similar to before this change) will play exactly two items
### Fixed
- Use code signing on all Windows executables (`ErsatzTV-Windows.exe`, `ErsatzTV.exe`, `ErsatzTV.Scanner.exe`)
@ -760,13 +768,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -760,13 +768,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- `random` will start at a random point in the content
- `2` (similar to before this change) will skip the first two items in the content
- YAML playout: make `count` an expression
- The following parameters can be used:
- `count`: the total number of items in the content
- `random`: a random number between zero and (count - 1)
- For example:
- `count / 2` will play half of the items in the content
- `random % 4 + 1` will play between 1 and 4 items
- `2` (similar to before this change) will play exactly two items
- The following parameters can be used:
- `count`: the total number of items in the content
- `random`: a random number between zero and (count - 1)
- For example:
- `count / 2` will play half of the items in the content
- `random % 4 + 1` will play between 1 and 4 items
- `2` (similar to before this change) will play exactly two items
- YAML playout: add `disable_watermarks` property to all content instructions
- This property defaults to `false` (meaning watermarks are allowed by default)
- Setting to `true` will prevent watermarks from ever appearing over the content

2
ErsatzTV.Application/ProgramSchedules/Commands/AddProgramScheduleItem.cs

@ -26,7 +26,7 @@ public record AddProgramScheduleItem( @@ -26,7 +26,7 @@ public record AddProgramScheduleItem(
int? MarathonBatchSize,
FillWithGroupMode FillWithGroupMode,
MultipleMode MultipleMode,
int? MultipleCount,
string MultipleCount,
TimeSpan? PlayoutDuration,
TailMode TailMode,
int? DiscardToFillAttempts,

2
ErsatzTV.Application/ProgramSchedules/Commands/IProgramScheduleItemRequest.cs

@ -24,7 +24,7 @@ public interface IProgramScheduleItemRequest @@ -24,7 +24,7 @@ public interface IProgramScheduleItemRequest
int? MarathonBatchSize { get; }
FillWithGroupMode FillWithGroupMode { get; }
MultipleMode MultipleMode { get; }
int? MultipleCount { get; }
string MultipleCount { get; }
TimeSpan? PlayoutDuration { get; }
TailMode TailMode { get; }
int? DiscardToFillAttempts { get; }

6
ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs

@ -79,10 +79,10 @@ public abstract class ProgramScheduleItemCommandBase @@ -79,10 +79,10 @@ public abstract class ProgramScheduleItemCommandBase
"[MultipleMode] cannot be [PlaylistItemSize] when collection is not a playlist");
}
if (item.MultipleMode is MultipleMode.Count && item.MultipleCount.GetValueOrDefault() < 1)
if (item.MultipleMode is MultipleMode.Count && string.IsNullOrWhiteSpace(item.MultipleCount))
{
return BaseError.New(
"[MultipleCount] must be greater than 0 for playout mode 'multiple / count'");
"[MultipleCount] must be valid for playout mode 'multiple / count'");
}
break;
@ -298,7 +298,7 @@ public abstract class ProgramScheduleItemCommandBase @@ -298,7 +298,7 @@ public abstract class ProgramScheduleItemCommandBase
MarathonBatchSize = item.MarathonBatchSize,
FillWithGroupMode = item.FillWithGroupMode,
MultipleMode = item.MultipleMode,
Count = item.MultipleMode is MultipleMode.Count ? item.MultipleCount.GetValueOrDefault() : 0,
Count = item.MultipleMode is MultipleMode.Count ? item.MultipleCount ?? "0" : "0",
CustomTitle = item.CustomTitle,
GuideMode = item.GuideMode,
PreRollFillerId = item.PreRollFillerId,

2
ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItems.cs

@ -26,7 +26,7 @@ public record ReplaceProgramScheduleItem( @@ -26,7 +26,7 @@ public record ReplaceProgramScheduleItem(
int? MarathonBatchSize,
FillWithGroupMode FillWithGroupMode,
MultipleMode MultipleMode,
int? MultipleCount,
string MultipleCount,
TimeSpan? PlayoutDuration,
TailMode TailMode,
int? DiscardToFillAttempts,

4
ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemMultipleViewModel.cs

@ -32,7 +32,7 @@ public record ProgramScheduleItemMultipleViewModel : ProgramScheduleItemViewMode @@ -32,7 +32,7 @@ public record ProgramScheduleItemMultipleViewModel : ProgramScheduleItemViewMode
int? marathonBatchSize,
FillWithGroupMode fillWithGroupMode,
MultipleMode multipleMode,
int count,
string count,
string customTitle,
GuideMode guideMode,
FillerPresetViewModel preRollFiller,
@ -87,5 +87,5 @@ public record ProgramScheduleItemMultipleViewModel : ProgramScheduleItemViewMode @@ -87,5 +87,5 @@ public record ProgramScheduleItemMultipleViewModel : ProgramScheduleItemViewMode
public MultipleMode MultipleMode { get; set; }
public int Count { get; }
public string Count { get; }
}

4
ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ContinuePlayoutTests.cs

@ -666,7 +666,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -666,7 +666,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
Collection = collectionOne,
CollectionId = collectionOne.Id,
StartTime = null,
Count = 3,
Count = "3",
PlaybackOrder = PlaybackOrder.Chronological
},
new ProgramScheduleItemMultiple
@ -676,7 +676,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase @@ -676,7 +676,7 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase
Collection = collectionTwo,
CollectionId = collectionTwo.Id,
StartTime = null,
Count = 3,
Count = "3",
PlaybackOrder = PlaybackOrder.Chronological
}
};

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

@ -852,7 +852,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase @@ -852,7 +852,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
Collection = fixedCollection,
CollectionId = fixedCollection.Id,
StartTime = TimeSpan.FromHours(3),
Count = 2,
Count = "2",
PlaybackOrder = PlaybackOrder.Chronological
}
};
@ -981,7 +981,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase @@ -981,7 +981,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
Collection = fixedCollection,
CollectionId = fixedCollection.Id,
StartTime = TimeSpan.FromHours(3),
Count = 2,
Count = "2",
PlaybackOrder = PlaybackOrder.Chronological
}
};
@ -1361,7 +1361,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase @@ -1361,7 +1361,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
Collection = multipleCollection,
CollectionId = multipleCollection.Id,
StartTime = null,
Count = 2,
Count = "2",
PlaybackOrder = PlaybackOrder.Chronological
},
new ProgramScheduleItemDuration
@ -1494,7 +1494,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase @@ -1494,7 +1494,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
Collection = collectionOne,
CollectionId = collectionOne.Id,
StartTime = null,
Count = 0,
Count = "0",
MultipleMode = MultipleMode.CollectionSize,
PlaybackOrder = PlaybackOrder.Chronological
},
@ -1505,7 +1505,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase @@ -1505,7 +1505,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
Collection = collectionTwo,
CollectionId = collectionTwo.Id,
StartTime = null,
Count = 0,
Count = "0",
MultipleMode = MultipleMode.CollectionSize,
PlaybackOrder = PlaybackOrder.Chronological
}
@ -1777,7 +1777,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase @@ -1777,7 +1777,7 @@ public class NewPlayoutTests : PlayoutBuilderTestBase
Collection = collectionOne,
CollectionId = collectionOne.Id,
StartTime = null,
Count = 1,
Count = "1",
PlaybackOrder = PlaybackOrder.Chronological,
PostRollFiller = new FillerPreset
{

1
ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerBaseTests.cs

@ -796,6 +796,7 @@ public class PlayoutModeSchedulerBaseTests : SchedulerTestBase @@ -796,6 +796,7 @@ public class PlayoutModeSchedulerBaseTests : SchedulerTestBase
ProgramScheduleItem scheduleItem,
ProgramScheduleItem nextScheduleItem,
DateTimeOffset hardStop,
Random random,
CancellationToken cancellationToken) =>
throw new NotSupportedException();
}

17
ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerDurationTests.cs

@ -13,9 +13,14 @@ namespace ErsatzTV.Core.Tests.Scheduling; @@ -13,9 +13,14 @@ namespace ErsatzTV.Core.Tests.Scheduling;
public class PlayoutModeSchedulerDurationTests : SchedulerTestBase
{
[SetUp]
public void SetUp() => _cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
public void SetUp()
{
_cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
_random = new Random();
}
private CancellationToken _cancellationToken;
private Random _random;
private readonly ILogger<PlayoutModeSchedulerDuration> _logger;
public PlayoutModeSchedulerDurationTests()
@ -66,6 +71,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase @@ -66,6 +71,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -139,6 +145,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase @@ -139,6 +145,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -211,6 +218,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase @@ -211,6 +218,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 45, 0)));
@ -280,6 +288,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase @@ -280,6 +288,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
// duration block should end after exact duration, with gap
@ -363,6 +372,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase @@ -363,6 +372,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -450,6 +460,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase @@ -450,6 +460,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -549,6 +560,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase @@ -549,6 +560,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -665,6 +677,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase @@ -665,6 +677,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -816,6 +829,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase @@ -816,6 +829,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddMinutes(30));
@ -880,6 +894,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase @@ -880,6 +894,7 @@ public class PlayoutModeSchedulerDurationTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutItems.ShouldBeEmpty();

20
ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerFloodTests.cs

@ -12,9 +12,14 @@ namespace ErsatzTV.Core.Tests.Scheduling; @@ -12,9 +12,14 @@ namespace ErsatzTV.Core.Tests.Scheduling;
public class PlayoutModeSchedulerFloodTests : SchedulerTestBase
{
[SetUp]
public void SetUp() => _cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
public void SetUp()
{
_cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
_random = new Random();
}
private CancellationToken _cancellationToken;
private Random _random;
[Test]
public void Should_Fill_Exactly_To_Next_Schedule_Item()
@ -57,6 +62,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase @@ -57,6 +62,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -132,6 +138,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase @@ -132,6 +138,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase
scheduleItem,
scheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(6));
@ -229,6 +236,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase @@ -229,6 +236,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -314,6 +322,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase @@ -314,6 +322,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -402,6 +411,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase @@ -402,6 +411,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 45, 0)));
@ -484,6 +494,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase @@ -484,6 +494,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -582,6 +593,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase @@ -582,6 +593,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -670,6 +682,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase @@ -670,6 +682,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 57, 0)));
@ -784,6 +797,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase @@ -784,6 +797,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -896,6 +910,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase @@ -896,6 +910,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
hardStop,
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(2));
@ -1002,6 +1017,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase @@ -1002,6 +1017,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
hardStop,
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(2));
@ -1116,6 +1132,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase @@ -1116,6 +1132,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -1190,6 +1207,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase @@ -1190,6 +1207,7 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutItems.ShouldBeEmpty();

37
ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerMultipleTests.cs

@ -12,9 +12,14 @@ namespace ErsatzTV.Core.Tests.Scheduling; @@ -12,9 +12,14 @@ namespace ErsatzTV.Core.Tests.Scheduling;
public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
{
[SetUp]
public void SetUp() => _cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
public void SetUp()
{
_cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
_random = new Random();
}
private CancellationToken _cancellationToken;
private Random _random;
[Test]
public void Should_Respect_Fixed_Start_Time()
@ -32,7 +37,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -32,7 +37,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
PlaybackOrder = PlaybackOrder.Chronological,
TailFiller = null,
FallbackFiller = null,
Count = 0,
Count = "0",
MultipleMode = MultipleMode.CollectionSize,
CustomTitle = "CustomTitle"
};
@ -59,6 +64,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -59,6 +64,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -134,7 +140,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -134,7 +140,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
PlaybackOrder = PlaybackOrder.Chronological,
TailFiller = null,
FallbackFiller = null,
Count = 0,
Count = "0",
MultipleMode = MultipleMode.MultiEpisodeGroupSize,
CustomTitle = "CustomTitle"
};
@ -161,6 +167,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -161,6 +167,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -206,7 +213,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -206,7 +213,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
PlaybackOrder = PlaybackOrder.Chronological,
TailFiller = null,
FallbackFiller = null,
Count = 3,
Count = "3",
CustomTitle = "CustomTitle"
};
@ -232,6 +239,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -232,6 +239,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -282,7 +290,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -282,7 +290,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
PlaybackOrder = PlaybackOrder.Chronological,
TailFiller = null,
FallbackFiller = null,
Count = 3
Count = "3"
};
var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
@ -307,6 +315,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -307,6 +315,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 45, 0)));
@ -360,7 +369,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -360,7 +369,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
CollectionId = collectionTwo.Id
},
FallbackFiller = null,
Count = 3
Count = "3"
};
var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
@ -390,6 +399,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -390,6 +399,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -459,7 +469,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -459,7 +469,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
Collection = collectionTwo,
CollectionId = collectionTwo.Id
},
Count = 3
Count = "3"
};
var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
@ -489,6 +499,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -489,6 +499,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -548,7 +559,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -548,7 +559,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
CollectionId = collectionTwo.Id
},
FallbackFiller = null,
Count = 3
Count = "3"
};
var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
@ -578,6 +589,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -578,6 +589,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 57, 0)));
@ -653,7 +665,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -653,7 +665,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
Collection = collectionThree,
CollectionId = collectionThree.Id
},
Count = 3
Count = "3"
};
var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
@ -694,6 +706,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -694,6 +706,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -775,7 +788,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -775,7 +788,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
Collection = collectionThree,
CollectionId = collectionThree.Id
},
Count = 3
Count = "3"
};
var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
@ -816,6 +829,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -816,6 +829,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -865,7 +879,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -865,7 +879,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
PlaybackOrder = PlaybackOrder.Chronological,
TailFiller = null,
FallbackFiller = null,
Count = 2
Count = "2"
};
var enumerator = new ChronologicalMediaCollectionEnumerator(
@ -896,6 +910,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase @@ -896,6 +910,7 @@ public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutItems.ShouldBeEmpty();

17
ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerOneTests.cs

@ -12,9 +12,14 @@ namespace ErsatzTV.Core.Tests.Scheduling; @@ -12,9 +12,14 @@ namespace ErsatzTV.Core.Tests.Scheduling;
public class PlayoutModeSchedulerOneTests : SchedulerTestBase
{
[SetUp]
public void SetUp() => _cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
public void SetUp()
{
_cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
_random = new Random();
}
private CancellationToken _cancellationToken;
private Random _random;
[Test]
public void Should_Have_Gap_With_No_Tail_No_Fallback()
@ -51,6 +56,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase @@ -51,6 +56,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(1));
@ -134,6 +140,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase @@ -134,6 +140,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(1));
@ -202,6 +209,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase @@ -202,6 +209,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -284,6 +292,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase @@ -284,6 +292,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -356,6 +365,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase @@ -356,6 +365,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.Add(new TimeSpan(2, 57, 0)));
@ -454,6 +464,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase @@ -454,6 +464,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -558,6 +569,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase @@ -558,6 +569,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -644,6 +656,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase @@ -644,6 +656,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -744,6 +757,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase @@ -744,6 +757,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutBuilderState.CurrentTime.ShouldBe(startState.CurrentTime.AddHours(3));
@ -823,6 +837,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase @@ -823,6 +837,7 @@ public class PlayoutModeSchedulerOneTests : SchedulerTestBase
scheduleItem,
NextScheduleItem,
HardStop(scheduleItemsEnumerator),
_random,
_cancellationToken);
playoutItems.ShouldBeEmpty();

2
ErsatzTV.Core/Domain/ProgramScheduleItemMultiple.cs

@ -4,5 +4,5 @@ public class ProgramScheduleItemMultiple : ProgramScheduleItem @@ -4,5 +4,5 @@ public class ProgramScheduleItemMultiple : ProgramScheduleItem
{
public MultipleMode MultipleMode { get; set; }
public int Count { get; set; }
public string Count { get; set; }
}

1
ErsatzTV.Core/Interfaces/Scheduling/IPlayoutModeScheduler.cs

@ -11,5 +11,6 @@ public interface IPlayoutModeScheduler<in T> where T : ProgramScheduleItem @@ -11,5 +11,6 @@ public interface IPlayoutModeScheduler<in T> where T : ProgramScheduleItem
T scheduleItem,
ProgramScheduleItem nextScheduleItem,
DateTimeOffset hardStop,
Random random,
CancellationToken cancellationToken);
}

36
ErsatzTV.Core/Scheduling/CountExpression.cs

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
using ErsatzTV.Core.Interfaces.Scheduling;
using NCalc;
namespace ErsatzTV.Core.Scheduling;
public static class CountExpression
{
public static int Evaluate(
string countExpression,
IMediaCollectionEnumerator enumerator,
Random random,
CancellationToken cancellationToken)
{
int enumeratorCount = enumerator is PlaylistEnumerator playlistEnumerator
? playlistEnumerator.CountForRandom
: enumerator.Count;
var expression = new Expression(countExpression);
expression.EvaluateParameter += (name, e) =>
{
e.Result = name switch
{
"count" => enumeratorCount,
"random" => enumeratorCount > 0 ? random.Next() % enumeratorCount : 0,
_ => e.Result
};
};
object expressionResult = expression.Evaluate(cancellationToken);
return expressionResult switch
{
double doubleResult => (int)Math.Floor(doubleResult),
int intResult => intResult,
_ => 0
};
}
}

4
ErsatzTV.Core/Scheduling/PlayoutBuilder.cs

@ -857,6 +857,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -857,6 +857,7 @@ public class PlayoutBuilder : IPlayoutBuilder
multiple,
nextScheduleItem,
playoutFinish,
random,
cancellationToken),
ProgramScheduleItemDuration duration => schedulerDuration.Schedule(
playoutBuilderState,
@ -864,6 +865,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -864,6 +865,7 @@ public class PlayoutBuilder : IPlayoutBuilder
duration,
nextScheduleItem,
playoutFinish,
random,
cancellationToken),
ProgramScheduleItemFlood flood => schedulerFlood.Schedule(
playoutBuilderState,
@ -871,6 +873,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -871,6 +873,7 @@ public class PlayoutBuilder : IPlayoutBuilder
flood,
nextScheduleItem,
playoutFinish,
random,
cancellationToken),
ProgramScheduleItemOne one => schedulerOne.Schedule(
playoutBuilderState,
@ -878,6 +881,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -878,6 +881,7 @@ public class PlayoutBuilder : IPlayoutBuilder
one,
nextScheduleItem,
playoutFinish,
random,
cancellationToken),
_ => throw new NotSupportedException(nameof(scheduleItem))
};

1
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs

@ -23,6 +23,7 @@ public abstract class PlayoutModeSchedulerBase<T>(ILogger logger) : IPlayoutMode @@ -23,6 +23,7 @@ public abstract class PlayoutModeSchedulerBase<T>(ILogger logger) : IPlayoutMode
T scheduleItem,
ProgramScheduleItem nextScheduleItem,
DateTimeOffset hardStop,
Random random,
CancellationToken cancellationToken);
public static DateTimeOffset GetFillerStartTimeAfter(

1
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerDuration.cs

@ -16,6 +16,7 @@ public class PlayoutModeSchedulerDuration(ILogger logger) @@ -16,6 +16,7 @@ public class PlayoutModeSchedulerDuration(ILogger logger)
ProgramScheduleItemDuration scheduleItem,
ProgramScheduleItem nextScheduleItem,
DateTimeOffset hardStop,
Random random,
CancellationToken cancellationToken)
{
var warnings = new PlayoutBuildWarnings();

1
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerFlood.cs

@ -15,6 +15,7 @@ public class PlayoutModeSchedulerFlood(ILogger logger) : PlayoutModeSchedulerBas @@ -15,6 +15,7 @@ public class PlayoutModeSchedulerFlood(ILogger logger) : PlayoutModeSchedulerBas
ProgramScheduleItemFlood scheduleItem,
ProgramScheduleItem nextScheduleItem,
DateTimeOffset hardStop,
Random random,
CancellationToken cancellationToken)
{
var warnings = new PlayoutBuildWarnings();

10
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerMultiple.cs

@ -16,6 +16,7 @@ public class PlayoutModeSchedulerMultiple(Map<CollectionKey, int> collectionItem @@ -16,6 +16,7 @@ public class PlayoutModeSchedulerMultiple(Map<CollectionKey, int> collectionItem
ProgramScheduleItemMultiple scheduleItem,
ProgramScheduleItem nextScheduleItem,
DateTimeOffset hardStop,
Random random,
CancellationToken cancellationToken)
{
var warnings = new PlayoutBuildWarnings();
@ -28,15 +29,16 @@ public class PlayoutModeSchedulerMultiple(Map<CollectionKey, int> collectionItem @@ -28,15 +29,16 @@ public class PlayoutModeSchedulerMultiple(Map<CollectionKey, int> collectionItem
return new PlayoutSchedulerResult(playoutBuilderState, playoutItems, warnings);
}
IMediaCollectionEnumerator contentEnumerator =
collectionEnumerators[CollectionKey.ForScheduleItem(scheduleItem)];
PlayoutBuilderState nextState = playoutBuilderState with
{
CurrentTime = firstStart,
MultipleRemaining = playoutBuilderState.MultipleRemaining.IfNone(scheduleItem.Count)
MultipleRemaining = playoutBuilderState.MultipleRemaining.IfNone(
CountExpression.Evaluate(scheduleItem.Count, contentEnumerator, random, cancellationToken))
};
IMediaCollectionEnumerator contentEnumerator =
collectionEnumerators[CollectionKey.ForScheduleItem(scheduleItem)];
if (nextState.MultipleRemaining == 0)
{
switch (scheduleItem.MultipleMode)

1
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerOne.cs

@ -14,6 +14,7 @@ public class PlayoutModeSchedulerOne(ILogger logger) : PlayoutModeSchedulerBase< @@ -14,6 +14,7 @@ public class PlayoutModeSchedulerOne(ILogger logger) : PlayoutModeSchedulerBase<
ProgramScheduleItemOne scheduleItem,
ProgramScheduleItem nextScheduleItem,
DateTimeOffset hardStop,
Random random,
CancellationToken cancellationToken)
{
var warnings = new PlayoutBuildWarnings();

23
ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutCountHandler.cs

@ -5,7 +5,6 @@ using ErsatzTV.Core.Extensions; @@ -5,7 +5,6 @@ using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.Interfaces.Scheduling;
using ErsatzTV.Core.Scheduling.YamlScheduling.Models;
using Microsoft.Extensions.Logging;
using NCalc;
namespace ErsatzTV.Core.Scheduling.YamlScheduling.Handlers;
@ -34,27 +33,7 @@ public class YamlPlayoutCountHandler(EnumeratorCache enumeratorCache) : YamlPlay @@ -34,27 +33,7 @@ public class YamlPlayoutCountHandler(EnumeratorCache enumeratorCache) : YamlPlay
{
int seed = context.Playout.Seed + context.InstructionIndex + context.CurrentTime.DayOfYear;
var random = new Random(seed);
int enumeratorCount = enumerator is PlaylistEnumerator playlistEnumerator
? playlistEnumerator.CountForRandom
: enumerator.Count;
var expression = new Expression(count.Count);
expression.EvaluateParameter += (name, e) =>
{
e.Result = name switch
{
"count" => enumeratorCount,
"random" => enumeratorCount > 0 ? random.Next() % enumeratorCount : 0,
_ => e.Result
};
};
object expressionResult = expression.Evaluate(cancellationToken);
int countValue = expressionResult switch
{
double doubleResult => (int)Math.Floor(doubleResult),
int intResult => intResult,
_ => 0
};
int countValue = CountExpression.Evaluate(count.Count, enumerator, random, cancellationToken);
for (var i = 0; i < countValue; i++)
{

7024
ErsatzTV.Infrastructure.MySql/Migrations/20260120145601_Change_ProgramScheduleItemMultipleCountString.Designer.cs generated

File diff suppressed because it is too large Load Diff

38
ErsatzTV.Infrastructure.MySql/Migrations/20260120145601_Change_ProgramScheduleItemMultipleCountString.cs

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.MySql.Migrations
{
/// <inheritdoc />
public partial class Change_ProgramScheduleItemMultipleCountString : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "Count",
table: "ProgramScheduleMultipleItem",
type: "longtext",
nullable: true,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<int>(
name: "Count",
table: "ProgramScheduleMultipleItem",
type: "int",
nullable: false,
defaultValue: 0,
oldClrType: typeof(string),
oldType: "longtext",
oldNullable: true)
.OldAnnotation("MySql:CharSet", "utf8mb4");
}
}
}

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

@ -17,7 +17,7 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -17,7 +17,7 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.11")
.HasAnnotation("ProductVersion", "9.0.12")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
@ -4130,8 +4130,8 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -4130,8 +4130,8 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
{
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
b.Property<int>("Count")
.HasColumnType("int");
b.Property<string>("Count")
.HasColumnType("longtext");
b.Property<int>("MultipleMode")
.HasColumnType("int");

6851
ErsatzTV.Infrastructure.Sqlite/Migrations/20260120145518_Change_ProgramScheduleItemMultipleCountString.Designer.cs generated

File diff suppressed because it is too large Load Diff

36
ErsatzTV.Infrastructure.Sqlite/Migrations/20260120145518_Change_ProgramScheduleItemMultipleCountString.cs

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
/// <inheritdoc />
public partial class Change_ProgramScheduleItemMultipleCountString : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "Count",
table: "ProgramScheduleMultipleItem",
type: "TEXT",
nullable: true,
oldClrType: typeof(int),
oldType: "INTEGER");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<int>(
name: "Count",
table: "ProgramScheduleMultipleItem",
type: "INTEGER",
nullable: false,
defaultValue: 0,
oldClrType: typeof(string),
oldType: "TEXT",
oldNullable: true);
}
}
}

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

@ -15,7 +15,7 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -15,7 +15,7 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.11");
modelBuilder.HasAnnotation("ProductVersion", "9.0.12");
modelBuilder.Entity("ErsatzTV.Core.Domain.Actor", b =>
{
@ -3957,8 +3957,8 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -3957,8 +3957,8 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
b.Property<int>("Count")
.HasColumnType("INTEGER");
b.Property<string>("Count")
.HasColumnType("TEXT");
b.Property<int>("MultipleMode")
.HasColumnType("INTEGER");

2
ErsatzTV/Validators/ProgramScheduleItemEditViewModelValidator.cs

@ -17,7 +17,7 @@ public class ProgramScheduleItemEditViewModelValidator : AbstractValidator<Progr @@ -17,7 +17,7 @@ public class ProgramScheduleItemEditViewModelValidator : AbstractValidator<Progr
{
When(
i => i.MultipleMode is MultipleMode.Count,
() => RuleFor(i => i.MultipleCount).NotNull().GreaterThan(0));
() => RuleFor(i => i.MultipleCount).NotEmpty());
});
When(
i => i.PlayoutMode == PlayoutMode.Duration,

4
ErsatzTV/ViewModels/ProgramScheduleItemEditViewModel.cs

@ -16,7 +16,7 @@ public class ProgramScheduleItemEditViewModel : INotifyPropertyChanged @@ -16,7 +16,7 @@ public class ProgramScheduleItemEditViewModel : INotifyPropertyChanged
private CollectionType _collectionType;
private int? _discardToFillAttempts;
private FixedStartTimeBehavior? _fixedStartTimeBehavior;
private int? _multipleCount;
private string _multipleCount;
private PlaybackOrder _playbackOrder;
private TimeSpan? _playoutDuration;
private int _playoutDurationHours;
@ -171,7 +171,7 @@ public class ProgramScheduleItemEditViewModel : INotifyPropertyChanged @@ -171,7 +171,7 @@ public class ProgramScheduleItemEditViewModel : INotifyPropertyChanged
public MultipleMode MultipleMode { get; set; }
public int? MultipleCount
public string MultipleCount
{
get => PlayoutMode == PlayoutMode.Multiple ? _multipleCount : null;
set => _multipleCount = value;

Loading…
Cancel
Save