Browse Source

more scheduling fixes (#732)

* fix skipping days with fixed start times

* fix playouts getting "stuck" on the same items

* rebuild all playouts

* update dependencies
pull/733/head
Jason Dove 4 years ago committed by GitHub
parent
commit
af39d93442
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      CHANGELOG.md
  2. 30
      ErsatzTV.Core.Tests/Scheduling/PlayoutBuilderTests.cs
  3. 30
      ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerBaseTests.cs
  4. 29
      ErsatzTV.Core/Scheduling/PlayoutBuilder.cs
  5. 2
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs
  6. 6
      ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj
  7. 3891
      ErsatzTV.Infrastructure/Migrations/20220412140722_Rebuild_AllPlayouts20220412.Designer.cs
  8. 20
      ErsatzTV.Infrastructure/Migrations/20220412140722_Rebuild_AllPlayouts20220412.cs
  9. 6
      ErsatzTV/ErsatzTV.csproj

2
CHANGELOG.md

@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Fixed
- Fix `HLS Segmenter` bug where it would drift off of the schedule if a playout was changed while the segmenter was running
- Ensure clients that use HDHomeRun emulation (like Plex) always get an `MPEG-TS` stream, regardless of the configured streaming mode
- Fix scheduling bug that caused some days to be skipped when fixed start times were used
### Added
- Add `Preferred Subtitle Language` and `Subtitle Mode` to channel settings
@ -25,6 +26,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -25,6 +26,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- This mode is used when a schedule is updated, or when collection modifications trigger a playout rebuild
- `Reset` - this mode will rebuild the entire playout and will NOT maintain progress
- This mode is only used when the `Reset Playout` button is clicked on the Playouts page
- **This requires rebuilding all playouts, which will happen on startup after upgrading**
- Use ffmpeg to resize images; this should help reduce ErsatzTV's memory use
- Use ffprobe to check for animated logos and watermarks; this should help reduce ErsatzTV's memory use
- Allow two decimals in channel numbers (e.g. `5.73`)

30
ErsatzTV.Core.Tests/Scheduling/PlayoutBuilderTests.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using ErsatzTV.Core.Domain;
using Destructurama;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Scheduling;
@ -21,6 +22,7 @@ namespace ErsatzTV.Core.Tests.Scheduling @@ -21,6 +22,7 @@ namespace ErsatzTV.Core.Tests.Scheduling
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.Destructure.UsingAttributes()
.CreateLogger();
ILoggerFactory loggerFactory = new LoggerFactory().AddSerilog(Log.Logger);
@ -1694,6 +1696,32 @@ namespace ErsatzTV.Core.Tests.Scheduling @@ -1694,6 +1696,32 @@ namespace ErsatzTV.Core.Tests.Scheduling
playout.Anchor.NextStartOffset.Should().Be(DateTime.Today.AddDays(1));
playout.ProgramScheduleAnchors.Count.Should().Be(1);
playout.ProgramScheduleAnchors.Head().EnumeratorState.Index.Should().Be(0);
PlayoutProgramScheduleAnchor headAnchor = playout.ProgramScheduleAnchors.Head();
// throw in a detractor anchor - playout builder should prioritize the "continue" anchor
playout.ProgramScheduleAnchors.Insert(
0,
new PlayoutProgramScheduleAnchor
{
Id = headAnchor.Id + 1,
Collection = headAnchor.Collection,
CollectionId = headAnchor.CollectionId,
Playout = playout,
PlayoutId = playout.Id,
AnchorDate = DateTime.Today.ToUniversalTime(),
CollectionType = headAnchor.CollectionType,
EnumeratorState = new CollectionEnumeratorState
{ Index = headAnchor.EnumeratorState.Index + 1, Seed = headAnchor.EnumeratorState.Seed },
MediaItem = headAnchor.MediaItem,
MediaItemId = headAnchor.MediaItemId,
MultiCollection = headAnchor.MultiCollection,
MultiCollectionId = headAnchor.MultiCollectionId,
ProgramSchedule = headAnchor.ProgramSchedule,
ProgramScheduleId = headAnchor.ProgramScheduleId,
SmartCollection = headAnchor.SmartCollection,
SmartCollectionId = headAnchor.SmartCollectionId
});
// continue 1h later
DateTimeOffset start2 = HoursAfterMidnight(1);

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

@ -3,7 +3,6 @@ using ErsatzTV.Core.Domain.Filler; @@ -3,7 +3,6 @@ using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Interfaces.Scheduling;
using ErsatzTV.Core.Scheduling;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
using NUnit.Framework;
@ -286,6 +285,35 @@ public class PlayoutModeSchedulerBaseTests : SchedulerTestBase @@ -286,6 +285,35 @@ public class PlayoutModeSchedulerBaseTests : SchedulerTestBase
}
}
[TestFixture]
public class GetStartTimeAfter
{
[Test]
public void Should_Compare_Time_As_Local_Time()
{
var enumerator = new Mock<IScheduleItemsEnumerator>();
var state = new PlayoutBuilderState(
enumerator.Object,
None,
None,
false,
false,
0,
DateTime.Today.AddHours(6).ToUniversalTime());
var scheduleItem = new ProgramScheduleItemOne
{
StartTime = TimeSpan.FromHours(6)
};
DateTimeOffset result =
PlayoutModeSchedulerBase<ProgramScheduleItem>.GetStartTimeAfter(state, scheduleItem);
result.Should().Be(DateTime.Today.AddHours(6));
}
}
private static Movie TestMovie(int id, TimeSpan duration, DateTime aired) =>
new()
{

29
ErsatzTV.Core/Scheduling/PlayoutBuilder.cs

@ -205,6 +205,12 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -205,6 +205,12 @@ public class PlayoutBuilder : IPlayoutBuilder
playout.Id,
playout.Channel.Number,
playout.Channel.Name);
// remove old checkpoints
playout.ProgramScheduleAnchors.RemoveAll(
a => a.AnchorDateOffset.IfNone(SystemTime.MaxValueUtc) < parameters.Start.Date);
// _logger.LogDebug("Remaining anchors: {@Anchors}", playout.ProgramScheduleAnchors);
await BuildPlayoutItems(
playout,
@ -285,7 +291,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -285,7 +291,7 @@ public class PlayoutBuilder : IPlayoutBuilder
// playoutFinish);
// build each day with "continue" anchors
while (finish <= playoutFinish)
while (finish < playoutFinish)
{
_logger.LogDebug("Building playout from {Start} to {Finish}", start, finish);
playout = await BuildPlayoutItems(playout, start, finish, collectionMediaItems, true);
@ -632,13 +638,20 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -632,13 +638,20 @@ public class PlayoutBuilder : IPlayoutBuilder
List<MediaItem> mediaItems,
PlaybackOrder playbackOrder)
{
Option<PlayoutProgramScheduleAnchor> maybeAnchor = playout.ProgramScheduleAnchors.FirstOrDefault(
a => a.ProgramScheduleId == playout.ProgramScheduleId
&& a.CollectionType == collectionKey.CollectionType
&& a.CollectionId == collectionKey.CollectionId
&& a.MultiCollectionId == collectionKey.MultiCollectionId
&& a.SmartCollectionId == collectionKey.SmartCollectionId
&& a.MediaItemId == collectionKey.MediaItemId);
Option<PlayoutProgramScheduleAnchor> maybeAnchor = playout.ProgramScheduleAnchors
.OrderByDescending(a => a.AnchorDate is null)
.FirstOrDefault(
a => a.ProgramScheduleId == playout.ProgramScheduleId
&& a.CollectionType == collectionKey.CollectionType
&& a.CollectionId == collectionKey.CollectionId
&& a.MultiCollectionId == collectionKey.MultiCollectionId
&& a.SmartCollectionId == collectionKey.SmartCollectionId
&& a.MediaItemId == collectionKey.MediaItemId);
foreach (PlayoutProgramScheduleAnchor anchor in maybeAnchor)
{
_logger.LogDebug("Selecting anchor {@Anchor}", anchor);
}
CollectionEnumeratorState state = maybeAnchor.Match(
anchor => anchor.EnumeratorState ??

2
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs

@ -20,7 +20,7 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe @@ -20,7 +20,7 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe
PlayoutBuilderState state,
ProgramScheduleItem scheduleItem)
{
DateTimeOffset startTime = state.CurrentTime;
DateTimeOffset startTime = state.CurrentTime.ToLocalTime();
bool isIncomplete = scheduleItem is ProgramScheduleItemMultiple && state.MultipleRemaining.IsSome ||
scheduleItem is ProgramScheduleItemDuration && state.DurationFinish.IsSome ||

6
ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj

@ -14,12 +14,12 @@ @@ -14,12 +14,12 @@
<PackageReference Include="Lucene.Net" Version="4.8.0-beta00016" />
<PackageReference Include="Lucene.Net.Analysis.Common" Version="4.8.0-beta00016" />
<PackageReference Include="Lucene.Net.QueryParser" Version="4.8.0-beta00016" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.3">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.4" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.1.46">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

3891
ErsatzTV.Infrastructure/Migrations/20220412140722_Rebuild_AllPlayouts20220412.Designer.cs generated

File diff suppressed because it is too large Load Diff

20
ErsatzTV.Infrastructure/Migrations/20220412140722_Rebuild_AllPlayouts20220412.cs

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Migrations
{
public partial class Rebuild_AllPlayouts20220412 : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(@"DELETE FROM PlayoutItem");
migrationBuilder.Sql(@"DELETE FROM PlayoutProgramScheduleAnchor");
migrationBuilder.Sql(@"DELETE FROM PlayoutAnchor");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

6
ErsatzTV/ErsatzTV.csproj

@ -62,9 +62,9 @@ @@ -62,9 +62,9 @@
<PackageReference Include="Markdig" Version="0.28.1" />
<PackageReference Include="MediatR.Courier.DependencyInjection" Version="5.0.0" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="10.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.3" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.3">
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.4" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

Loading…
Cancel
Save