Browse Source

fix playout builds using duration or multiple (#115)

pull/116/head
Jason Dove 5 years ago committed by GitHub
parent
commit
a62c4ecfcf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 184
      ErsatzTV.Core.Tests/Scheduling/PlayoutBuilderTests.cs
  2. 8
      ErsatzTV.Core/Domain/PlayoutAnchor.cs
  3. 10
      ErsatzTV.Core/Metadata/LocalMetadataProvider.cs
  4. 10
      ErsatzTV.Core/Scheduling/PlayoutBuilder.cs
  5. 1820
      ErsatzTV.Infrastructure/Migrations/20210329193151_Add_PlayoutAnchor_DurationMultiple.Designer.cs
  6. 34
      ErsatzTV.Infrastructure/Migrations/20210329193151_Add_PlayoutAnchor_DurationMultiple.cs
  7. 6
      ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs

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

@ -610,6 +610,190 @@ namespace ErsatzTV.Core.Tests.Scheduling @@ -610,6 +610,190 @@ namespace ErsatzTV.Core.Tests.Scheduling
result.Items[5].MediaItemId.Should().Be(4);
}
[Test]
public async Task Alternating_MultipleContent_Should_Maintain_Counts()
{
var collectionOne = new Collection
{
Id = 1,
Name = "Multiple Items 1",
MediaItems = new List<MediaItem>
{
TestMovie(1, TimeSpan.FromHours(1), new DateTime(2020, 1, 1))
}
};
var collectionTwo = new Collection
{
Id = 2,
Name = "Multiple Items 2",
MediaItems = new List<MediaItem>
{
TestMovie(2, TimeSpan.FromHours(1), new DateTime(2020, 1, 1))
}
};
var fakeRepository = new FakeMediaCollectionRepository(
Map(
(collectionOne.Id, collectionOne.MediaItems.ToList()),
(collectionTwo.Id, collectionTwo.MediaItems.ToList())));
var items = new List<ProgramScheduleItem>
{
new ProgramScheduleItemMultiple
{
Id = 1,
Index = 1,
Collection = collectionOne,
CollectionId = collectionOne.Id,
StartTime = null,
Count = 3
},
new ProgramScheduleItemMultiple
{
Id = 2,
Index = 2,
Collection = collectionTwo,
CollectionId = collectionTwo.Id,
StartTime = null,
Count = 3
}
};
var playout = new Playout
{
ProgramSchedule = new ProgramSchedule
{
Items = items,
MediaCollectionPlaybackOrder = PlaybackOrder.Chronological
},
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
Anchor = new PlayoutAnchor
{
NextStart = HoursAfterMidnight(1).UtcDateTime,
NextScheduleItem = items[0],
NextScheduleItemId = 1,
MultipleRemaining = 2
}
};
var televisionRepo = new FakeTelevisionRepository();
var builder = new PlayoutBuilder(fakeRepository, televisionRepo, _logger);
DateTimeOffset start = HoursAfterMidnight(0);
DateTimeOffset finish = start + TimeSpan.FromHours(5);
Playout result = await builder.BuildPlayoutItems(playout, start, finish);
result.Items.Count.Should().Be(4);
result.Items[0].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(1));
result.Items[0].MediaItemId.Should().Be(1);
result.Items[1].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(2));
result.Items[1].MediaItemId.Should().Be(1);
result.Items[2].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(3));
result.Items[2].MediaItemId.Should().Be(2);
result.Items[3].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(4));
result.Items[3].MediaItemId.Should().Be(2);
result.Anchor.NextScheduleItem.Should().Be(items[1]);
result.Anchor.MultipleRemaining.Should().Be(1);
}
[Test]
public async Task Alternating_Duration_Should_Maintain_Duration()
{
var collectionOne = new Collection
{
Id = 1,
Name = "Duration Items 1",
MediaItems = new List<MediaItem>
{
TestMovie(1, TimeSpan.FromHours(1), new DateTime(2020, 1, 1))
}
};
var collectionTwo = new Collection
{
Id = 2,
Name = "Duration Items 2",
MediaItems = new List<MediaItem>
{
TestMovie(2, TimeSpan.FromHours(1), new DateTime(2020, 1, 1))
}
};
var fakeRepository = new FakeMediaCollectionRepository(
Map(
(collectionOne.Id, collectionOne.MediaItems.ToList()),
(collectionTwo.Id, collectionTwo.MediaItems.ToList())));
var items = new List<ProgramScheduleItem>
{
new ProgramScheduleItemDuration
{
Id = 1,
Index = 1,
Collection = collectionOne,
CollectionId = collectionOne.Id,
StartTime = null,
PlayoutDuration = TimeSpan.FromHours(3),
OfflineTail = false
},
new ProgramScheduleItemDuration
{
Id = 2,
Index = 2,
Collection = collectionTwo,
CollectionId = collectionTwo.Id,
StartTime = null,
PlayoutDuration = TimeSpan.FromHours(3),
OfflineTail = false
}
};
var playout = new Playout
{
ProgramSchedule = new ProgramSchedule
{
Items = items,
MediaCollectionPlaybackOrder = PlaybackOrder.Chronological
},
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
Anchor = new PlayoutAnchor
{
NextStart = HoursAfterMidnight(1).UtcDateTime,
NextScheduleItem = items[0],
NextScheduleItemId = 1,
DurationFinish = HoursAfterMidnight(3).UtcDateTime
}
};
var televisionRepo = new FakeTelevisionRepository();
var builder = new PlayoutBuilder(fakeRepository, televisionRepo, _logger);
DateTimeOffset start = HoursAfterMidnight(0);
DateTimeOffset finish = start + TimeSpan.FromHours(5);
Playout result = await builder.BuildPlayoutItems(playout, start, finish);
result.Items.Count.Should().Be(4);
result.Items[0].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(1));
result.Items[0].MediaItemId.Should().Be(1);
result.Items[1].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(2));
result.Items[1].MediaItemId.Should().Be(1);
result.Items[2].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(3));
result.Items[2].MediaItemId.Should().Be(2);
result.Items[3].StartOffset.TimeOfDay.Should().Be(TimeSpan.FromHours(4));
result.Items[3].MediaItemId.Should().Be(2);
result.Anchor.NextScheduleItem.Should().Be(items[1]);
result.Anchor.DurationFinish.Should().Be(HoursAfterMidnight(6).UtcDateTime);
}
private static DateTimeOffset HoursAfterMidnight(int hours)
{
DateTimeOffset now = DateTimeOffset.Now;

8
ErsatzTV.Core/Domain/PlayoutAnchor.cs

@ -1,4 +1,6 @@ @@ -1,4 +1,6 @@
using System;
using LanguageExt;
using static LanguageExt.Prelude;
namespace ErsatzTV.Core.Domain
{
@ -9,7 +11,13 @@ namespace ErsatzTV.Core.Domain @@ -9,7 +11,13 @@ namespace ErsatzTV.Core.Domain
public ProgramScheduleItem NextScheduleItem { get; set; }
public DateTime NextStart { get; set; }
public int? MultipleRemaining { get; set; }
public DateTime? DurationFinish { get; set; }
public DateTimeOffset NextStartOffset => new DateTimeOffset(NextStart, TimeSpan.Zero).ToLocalTime();
public Option<DateTimeOffset> DurationFinishOffset =>
Optional(DurationFinish)
.Map(durationFinish => new DateTimeOffset(durationFinish, TimeSpan.Zero).ToLocalTime());
}
}

10
ErsatzTV.Core/Metadata/LocalMetadataProvider.cs

@ -113,7 +113,7 @@ namespace ErsatzTV.Core.Metadata @@ -113,7 +113,7 @@ namespace ErsatzTV.Core.Metadata
existing.Plot = metadata.Plot;
existing.Tagline = metadata.Tagline;
existing.Title = metadata.Title;
if (existing.DateAdded == DateTime.MinValue)
{
existing.DateAdded = metadata.DateAdded;
@ -149,17 +149,17 @@ namespace ErsatzTV.Core.Metadata @@ -149,17 +149,17 @@ namespace ErsatzTV.Core.Metadata
async existing =>
{
var updated = false;
existing.Outline = metadata.Outline;
existing.Plot = metadata.Plot;
existing.Tagline = metadata.Tagline;
existing.Title = metadata.Title;
if (existing.DateAdded == DateTime.MinValue)
{
existing.DateAdded = metadata.DateAdded;
}
existing.DateUpdated = metadata.DateUpdated;
existing.MetadataKind = metadata.MetadataKind;
existing.OriginalTitle = metadata.OriginalTitle;
@ -254,7 +254,7 @@ namespace ErsatzTV.Core.Metadata @@ -254,7 +254,7 @@ namespace ErsatzTV.Core.Metadata
existing.Plot = metadata.Plot;
existing.Tagline = metadata.Tagline;
existing.Title = metadata.Title;
if (existing.DateAdded == DateTime.MinValue)
{
existing.DateAdded = metadata.DateAdded;

10
ErsatzTV.Core/Scheduling/PlayoutBuilder.cs

@ -145,8 +145,10 @@ namespace ErsatzTV.Core.Scheduling @@ -145,8 +145,10 @@ namespace ErsatzTV.Core.Scheduling
// start with the previously-decided schedule item
int index = sortedScheduleItems.IndexOf(startAnchor.NextScheduleItem);
Option<int> multipleRemaining = None;
Option<DateTimeOffset> durationFinish = None;
// start with the previous multiple/duration states
Option<int> multipleRemaining = Optional(startAnchor.MultipleRemaining);
Option<DateTimeOffset> durationFinish = startAnchor.DurationFinishOffset;
// loop until we're done filling the desired amount of time
while (currentTime < playoutFinish)
{
@ -298,7 +300,9 @@ namespace ErsatzTV.Core.Scheduling @@ -298,7 +300,9 @@ namespace ErsatzTV.Core.Scheduling
{
NextScheduleItem = nextScheduleItem,
NextScheduleItemId = nextScheduleItem.Id,
NextStart = GetStartTimeAfter(nextScheduleItem, currentTime).UtcDateTime
NextStart = GetStartTimeAfter(nextScheduleItem, currentTime).UtcDateTime,
MultipleRemaining = multipleRemaining.IsSome ? multipleRemaining.ValueUnsafe() : null,
DurationFinish = durationFinish.IsSome ? durationFinish.ValueUnsafe().UtcDateTime : null
};
// build program schedule anchors

1820
ErsatzTV.Infrastructure/Migrations/20210329193151_Add_PlayoutAnchor_DurationMultiple.Designer.cs generated

File diff suppressed because it is too large Load Diff

34
ErsatzTV.Infrastructure/Migrations/20210329193151_Add_PlayoutAnchor_DurationMultiple.cs

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace ErsatzTV.Infrastructure.Migrations
{
public partial class Add_PlayoutAnchor_DurationMultiple : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
"Anchor_DurationFinish",
"Playout",
"TEXT",
nullable: true);
migrationBuilder.AddColumn<int>(
"Anchor_MultipleRemaining",
"Playout",
"INTEGER",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
"Anchor_DurationFinish",
"Playout");
migrationBuilder.DropColumn(
"Anchor_MultipleRemaining",
"Playout");
}
}
}

6
ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs

@ -1412,6 +1412,12 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1412,6 +1412,12 @@ namespace ErsatzTV.Infrastructure.Migrations
b1.Property<int>("PlayoutId")
.HasColumnType("INTEGER");
b1.Property<DateTime?>("DurationFinish")
.HasColumnType("TEXT");
b1.Property<int?>("MultipleRemaining")
.HasColumnType("INTEGER");
b1.Property<int>("NextScheduleItemId")
.HasColumnType("INTEGER");

Loading…
Cancel
Save