Browse Source

add deco default filler trim to fit setting (#1800)

* add deco default filler trim to fit setting

* implement trim to fit

* update changelog
pull/1802/head
Jason Dove 10 months ago committed by GitHub
parent
commit
b1b2c2a1e0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      CHANGELOG.md
  2. 1
      ErsatzTV.Application/Scheduling/Commands/UpdateDeco.cs
  3. 1
      ErsatzTV.Application/Scheduling/Commands/UpdateDecoHandler.cs
  4. 1
      ErsatzTV.Application/Scheduling/DecoViewModel.cs
  5. 1
      ErsatzTV.Application/Scheduling/Mapper.cs
  6. 121
      ErsatzTV.Core.Tests/FFmpeg/FFmpegStreamSelectorTests.cs
  7. 1
      ErsatzTV.Core/Domain/Scheduling/Deco.cs
  8. 13
      ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutFillerBuilder.cs
  9. 5857
      ErsatzTV.Infrastructure.MySql/Migrations/20240722190006_Add_Deco_DefaultFillerTrimToFit.Designer.cs
  10. 29
      ErsatzTV.Infrastructure.MySql/Migrations/20240722190006_Add_Deco_DefaultFillerTrimToFit.cs
  11. 3
      ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs
  12. 5696
      ErsatzTV.Infrastructure.Sqlite/Migrations/20240722185307_Add_Deco_DefaultFillerTrimToFit.Designer.cs
  13. 29
      ErsatzTV.Infrastructure.Sqlite/Migrations/20240722185307_Add_Deco_DefaultFillerTrimToFit.cs
  14. 3
      ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs
  15. 5
      ErsatzTV/Pages/DecoEditor.razor
  16. 1
      ErsatzTV/ViewModels/DecoEditViewModel.cs

2
CHANGELOG.md

@ -20,6 +20,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -20,6 +20,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- After all blocks are scheduled/added to the playout, a second pass will be made to insert filler
- Default filler will be shuffled and inserted in all unscheduled time between blocks
- Default filler will stop scheduling when the next item would extend into primary content
- Alternatively, default filler can be configured to `Trim To Fit`
- In this case, the last item that would extend into primary content is trimmed to end exactly when the primary content starts
### Fixed
- Add basic cache busting to XMLTV image URLs

1
ErsatzTV.Application/Scheduling/Commands/UpdateDeco.cs

@ -16,6 +16,7 @@ public record UpdateDeco( @@ -16,6 +16,7 @@ public record UpdateDeco(
int? DefaultFillerMediaItemId,
int? DefaultFillerMultiCollectionId,
int? DefaultFillerSmartCollectionId,
bool DefaultFillerTrimToFit,
DecoMode DeadAirFallbackMode,
ProgramScheduleItemCollectionType DeadAirFallbackCollectionType,
int? DeadAirFallbackCollectionId,

1
ErsatzTV.Application/Scheduling/Commands/UpdateDecoHandler.cs

@ -42,6 +42,7 @@ public class UpdateDecoHandler(IDbContextFactory<TvContext> dbContextFactory) @@ -42,6 +42,7 @@ public class UpdateDecoHandler(IDbContextFactory<TvContext> dbContextFactory)
existing.DefaultFillerSmartCollectionId = request.DefaultFillerMode is DecoMode.Override
? request.DefaultFillerSmartCollectionId
: null;
existing.DefaultFillerTrimToFit = request.DefaultFillerTrimToFit;
// dead air fallback
existing.DeadAirFallbackMode = request.DeadAirFallbackMode;

1
ErsatzTV.Application/Scheduling/DecoViewModel.cs

@ -15,6 +15,7 @@ public record DecoViewModel( @@ -15,6 +15,7 @@ public record DecoViewModel(
int? DefaultFillerMediaItemId,
int? DefaultFillerMultiCollectionId,
int? DefaultFillerSmartCollectionId,
bool DefaultFillerTrimToFit,
DecoMode DeadAirFallbackMode,
ProgramScheduleItemCollectionType DeadAirFallbackCollectionType,
int? DeadAirFallbackCollectionId,

1
ErsatzTV.Application/Scheduling/Mapper.cs

@ -62,6 +62,7 @@ internal static class Mapper @@ -62,6 +62,7 @@ internal static class Mapper
deco.DefaultFillerMediaItemId,
deco.DefaultFillerMultiCollectionId,
deco.DefaultFillerSmartCollectionId,
deco.DefaultFillerTrimToFit,
deco.DeadAirFallbackMode,
deco.DeadAirFallbackCollectionType,
deco.DeadAirFallbackCollectionId,

121
ErsatzTV.Core.Tests/FFmpeg/FFmpegStreamSelectorTests.cs

@ -0,0 +1,121 @@ @@ -0,0 +1,121 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.FFmpeg;
using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Infrastructure.Scripting;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using NSubstitute;
using NUnit.Framework;
namespace ErsatzTV.Core.Tests.FFmpeg;
[TestFixture]
public class FFmpegStreamSelectorTests
{
[TestFixture]
public class SelectAudioStream
{
[Test]
public async Task Should_Select_Audio_Stream_With_Preferred_Language()
{
// skip movie/episode script paths by using other video
var mediaItem = new OtherVideo();
var mediaVersion = new MediaVersion
{
Streams =
[
new MediaStream
{
Index = 0,
MediaStreamKind = MediaStreamKind.Audio,
Channels = 2,
Language = "ja",
Title = "Some Title",
},
new MediaStream
{
Index = 1,
MediaStreamKind = MediaStreamKind.Audio,
Channels = 6,
Language = "eng",
Title = "Another Title",
Default = true
}
]
};
var audioVersion = new MediaItemAudioVersion(mediaItem, mediaVersion);
var channel = new Channel(Guid.NewGuid())
{
PreferredAudioLanguageCode = "eng"
};
ISearchRepository searchRepository = Substitute.For<ISearchRepository>();
searchRepository.GetAllThreeLetterLanguageCodes(Arg.Any<List<string>>())
.Returns(Task.FromResult(new List<string> { "jpn" }));
var selector = new FFmpegStreamSelector(
new ScriptEngine(Substitute.For<ILogger<ScriptEngine>>()),
Substitute.For<IStreamSelectorRepository>(),
searchRepository,
Substitute.For<IConfigElementRepository>(),
Substitute.For<ILocalFileSystem>(),
Substitute.For<ILogger<FFmpegStreamSelector>>());
Option<MediaStream> selectedStream = await selector.SelectAudioStream(audioVersion, StreamingMode.TransportStream, channel, "jpn", "Whatever");
selectedStream.IsSome.Should().BeTrue();
foreach (MediaStream stream in selectedStream)
{
stream.Language.Should().Be("ja");
}
}
[Test]
public async Task Should_Select_Subtitle_Stream_With_Preferred_Language()
{
// skip movie/episode script paths by using other video
var subtitles = new List<Subtitle>
{
new()
{
StreamIndex = 0,
SubtitleKind = SubtitleKind.Sidecar,
Language = "eng",
Default = true
},
new()
{
StreamIndex = 1,
SubtitleKind = SubtitleKind.Sidecar,
Language = "he",
},
};
var channel = new Channel(Guid.NewGuid());
ISearchRepository searchRepository = Substitute.For<ISearchRepository>();
searchRepository.GetAllThreeLetterLanguageCodes(Arg.Any<List<string>>())
.Returns(Task.FromResult(new List<string> { "heb" }));
var selector = new FFmpegStreamSelector(
new ScriptEngine(Substitute.For<ILogger<ScriptEngine>>()),
Substitute.For<IStreamSelectorRepository>(),
searchRepository,
Substitute.For<IConfigElementRepository>(),
Substitute.For<ILocalFileSystem>(),
Substitute.For<ILogger<FFmpegStreamSelector>>());
Option<Subtitle> selectedStream = await selector.SelectSubtitleStream(
subtitles,
channel,
"heb",
ChannelSubtitleMode.Any);
selectedStream.IsSome.Should().BeTrue();
foreach (Subtitle stream in selectedStream)
{
stream.Language.Should().Be("he");
}
}
}
}

1
ErsatzTV.Core/Domain/Scheduling/Deco.cs

@ -24,6 +24,7 @@ public class Deco @@ -24,6 +24,7 @@ public class Deco
public MultiCollection DefaultFillerMultiCollection { get; set; }
public int? DefaultFillerSmartCollectionId { get; set; }
public SmartCollection DefaultFillerSmartCollection { get; set; }
public bool DefaultFillerTrimToFit { get; set; }
// dead air fallback
public DecoMode DeadAirFallbackMode { get; set; }

13
ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutFillerBuilder.cs

@ -108,13 +108,20 @@ public class BlockPlayoutFillerBuilder( @@ -108,13 +108,20 @@ public class BlockPlayoutFillerBuilder(
if (filler.FinishOffset > finish)
{
pastTime = true;
break;
if (deco.DefaultFillerTrimToFit)
{
filler.Finish = finish.UtcDateTime;
filler.OutPoint = filler.Finish - filler.Start;
}
else
{
pastTime = true;
break;
}
}
playout.Items.Add(filler);
// create a playout history record
var nextHistory = new PlayoutHistory
{

5857
ErsatzTV.Infrastructure.MySql/Migrations/20240722190006_Add_Deco_DefaultFillerTrimToFit.Designer.cs generated

File diff suppressed because it is too large Load Diff

29
ErsatzTV.Infrastructure.MySql/Migrations/20240722190006_Add_Deco_DefaultFillerTrimToFit.cs

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.MySql.Migrations
{
/// <inheritdoc />
public partial class Add_Deco_DefaultFillerTrimToFit : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "DefaultFillerTrimToFit",
table: "Deco",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "DefaultFillerTrimToFit",
table: "Deco");
}
}
}

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

@ -2303,6 +2303,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -2303,6 +2303,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
b.Property<int?>("DefaultFillerSmartCollectionId")
.HasColumnType("int");
b.Property<bool>("DefaultFillerTrimToFit")
.HasColumnType("tinyint(1)");
b.Property<string>("Name")
.HasColumnType("varchar(255)");

5696
ErsatzTV.Infrastructure.Sqlite/Migrations/20240722185307_Add_Deco_DefaultFillerTrimToFit.Designer.cs generated

File diff suppressed because it is too large Load Diff

29
ErsatzTV.Infrastructure.Sqlite/Migrations/20240722185307_Add_Deco_DefaultFillerTrimToFit.cs

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
/// <inheritdoc />
public partial class Add_Deco_DefaultFillerTrimToFit : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "DefaultFillerTrimToFit",
table: "Deco",
type: "INTEGER",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "DefaultFillerTrimToFit",
table: "Deco");
}
}
}

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

@ -2188,6 +2188,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -2188,6 +2188,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
b.Property<int?>("DefaultFillerSmartCollectionId")
.HasColumnType("INTEGER");
b.Property<bool>("DefaultFillerTrimToFit")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");

5
ErsatzTV/Pages/DecoEditor.razor

@ -159,6 +159,7 @@ @@ -159,6 +159,7 @@
}
</MudSelect>
}
<MudSwitch T="bool" Class="mt-3" Label="Trim To Fit" @bind-Value="_deco.DefaultFillerTrimToFit" Color="Color.Primary" />
</MudCardContent>
</MudCard>
<MudCard Class="mb-6" Style="width: 350px">
@ -173,7 +174,7 @@ @@ -173,7 +174,7 @@
</MudText>
</ChildContent>
<TooltipContent>
<MudText Typo="Typo.body2">When no playout item is found for the current time, *one* item will be randomly selected from this collection and looped and cut to exactly fit until the start of the next playout item.</MudText>
<MudText Typo="Typo.body2">When no playout item is found for the current time, *one* item will be randomly selected from this collection and looped and trimmed to exactly fit until the start of the next playout item.</MudText>
<MudText Typo="Typo.body2" Class="mt-3">This replaces the "Channel is Offline" image that would otherwise display.</MudText>
</TooltipContent>
</MudTooltip>
@ -359,6 +360,7 @@ @@ -359,6 +360,7 @@
DefaultFillerSmartCollection = deco.DefaultFillerSmartCollectionId.HasValue
? _smartCollections.Find(c => c.Id == deco.DefaultFillerSmartCollectionId.Value)
: null,
DefaultFillerTrimToFit = deco.DefaultFillerTrimToFit,
DeadAirFallbackMode = deco.DeadAirFallbackMode,
DeadAirFallbackCollectionType = deco.DeadAirFallbackCollectionType,
@ -393,6 +395,7 @@ @@ -393,6 +395,7 @@
_deco.DefaultFillerMediaItem?.MediaItemId,
_deco.DefaultFillerMultiCollection?.Id,
_deco.DefaultFillerSmartCollection?.Id,
_deco.DefaultFillerTrimToFit,
_deco.DeadAirFallbackMode,
_deco.DeadAirFallbackCollectionType,
_deco.DeadAirFallbackCollection?.Id,

1
ErsatzTV/ViewModels/DecoEditViewModel.cs

@ -18,6 +18,7 @@ public class DecoEditViewModel @@ -18,6 +18,7 @@ public class DecoEditViewModel
public MultiCollectionViewModel DefaultFillerMultiCollection { get; set; }
public SmartCollectionViewModel DefaultFillerSmartCollection { get; set; }
public NamedMediaItemViewModel DefaultFillerMediaItem { get; set; }
public bool DefaultFillerTrimToFit { get; set; }
public DecoMode DeadAirFallbackMode { get; set; }
public ProgramScheduleItemCollectionType DeadAirFallbackCollectionType { get; set; }

Loading…
Cancel
Save