Browse Source

add basic nvenc support

pull/41/head
Jason Dove 5 years ago
parent
commit
63997bfd03
  1. 3
      ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfile.cs
  2. 2
      ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfileHandler.cs
  3. 3
      ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfile.cs
  4. 2
      ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs
  5. 3
      ErsatzTV.Application/FFmpegProfiles/FFmpegProfileViewModel.cs
  6. 2
      ErsatzTV.Application/FFmpegProfiles/Mapper.cs
  7. 32
      ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsServiceTests.cs
  8. 2
      ErsatzTV.Core/Domain/FFmpegProfile.cs
  9. 9
      ErsatzTV.Core/Domain/HardwareAccelerationKind.cs
  10. 3
      ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettings.cs
  11. 7
      ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs
  12. 34
      ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs
  13. 20
      ErsatzTV.Infrastructure/Migrations/20210302122352_Add_FFmpegProfileQsvAcceleration.cs
  14. 10
      ErsatzTV.Infrastructure/Migrations/20210302224925_Add_FFmpegProfileHardwareAcceleration.Designer.cs
  15. 24
      ErsatzTV.Infrastructure/Migrations/20210302224925_Add_FFmpegProfileHardwareAcceleration.cs
  16. 386
      ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs
  17. 9
      ErsatzTV/Pages/FFmpegEditor.razor
  18. 30
      ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs
  19. 24
      ErsatzTV/ViewModels/FFmpegProfileEditViewModel.cs

3
ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfile.cs

@ -1,4 +1,5 @@
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using LanguageExt; using LanguageExt;
using MediatR; using MediatR;
@ -8,7 +9,7 @@ namespace ErsatzTV.Application.FFmpegProfiles.Commands
string Name, string Name,
int ThreadCount, int ThreadCount,
bool Transcode, bool Transcode,
bool QsvAcceleration, HardwareAccelerationKind HardwareAcceleration,
int ResolutionId, int ResolutionId,
bool NormalizeResolution, bool NormalizeResolution,
string VideoCodec, string VideoCodec,

2
ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfileHandler.cs

@ -41,7 +41,7 @@ namespace ErsatzTV.Application.FFmpegProfiles.Commands
Name = name, Name = name,
ThreadCount = threadCount, ThreadCount = threadCount,
Transcode = request.Transcode, Transcode = request.Transcode,
QsvAcceleration = request.QsvAcceleration, HardwareAcceleration = request.HardwareAcceleration,
ResolutionId = resolutionId, ResolutionId = resolutionId,
NormalizeResolution = request.NormalizeResolution, NormalizeResolution = request.NormalizeResolution,
VideoCodec = request.VideoCodec, VideoCodec = request.VideoCodec,

3
ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfile.cs

@ -1,4 +1,5 @@
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using LanguageExt; using LanguageExt;
using MediatR; using MediatR;
@ -9,7 +10,7 @@ namespace ErsatzTV.Application.FFmpegProfiles.Commands
string Name, string Name,
int ThreadCount, int ThreadCount,
bool Transcode, bool Transcode,
bool QsvAcceleration, HardwareAccelerationKind HardwareAcceleration,
int ResolutionId, int ResolutionId,
bool NormalizeResolution, bool NormalizeResolution,
string VideoCodec, string VideoCodec,

2
ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs

@ -35,7 +35,7 @@ namespace ErsatzTV.Application.FFmpegProfiles.Commands
p.Name = update.Name; p.Name = update.Name;
p.ThreadCount = update.ThreadCount; p.ThreadCount = update.ThreadCount;
p.Transcode = update.Transcode; p.Transcode = update.Transcode;
p.QsvAcceleration = update.QsvAcceleration; p.HardwareAcceleration = update.HardwareAcceleration;
p.ResolutionId = update.ResolutionId; p.ResolutionId = update.ResolutionId;
p.NormalizeResolution = update.NormalizeResolution; p.NormalizeResolution = update.NormalizeResolution;
p.VideoCodec = update.VideoCodec; p.VideoCodec = update.VideoCodec;

3
ErsatzTV.Application/FFmpegProfiles/FFmpegProfileViewModel.cs

@ -1,4 +1,5 @@
using ErsatzTV.Application.Resolutions; using ErsatzTV.Application.Resolutions;
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.FFmpegProfiles namespace ErsatzTV.Application.FFmpegProfiles
{ {
@ -7,7 +8,7 @@ namespace ErsatzTV.Application.FFmpegProfiles
string Name, string Name,
int ThreadCount, int ThreadCount,
bool Transcode, bool Transcode,
bool QsvAcceleration, HardwareAccelerationKind HardwareAcceleration,
ResolutionViewModel Resolution, ResolutionViewModel Resolution,
bool NormalizeResolution, bool NormalizeResolution,
string VideoCodec, string VideoCodec,

2
ErsatzTV.Application/FFmpegProfiles/Mapper.cs

@ -11,7 +11,7 @@ namespace ErsatzTV.Application.FFmpegProfiles
profile.Name, profile.Name,
profile.ThreadCount, profile.ThreadCount,
profile.Transcode, profile.Transcode,
profile.QsvAcceleration, profile.HardwareAcceleration,
Project(profile.Resolution), Project(profile.Resolution),
profile.NormalizeResolution, profile.NormalizeResolution,
profile.VideoCodec, profile.VideoCodec,

32
ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsServiceTests.cs

@ -273,6 +273,29 @@ namespace ErsatzTV.Core.Tests.FFmpeg
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
actual.PadToDesiredResolution.Should().BeFalse(); actual.PadToDesiredResolution.Should().BeFalse();
} }
[Test]
public void Should_NotPadToDesiredResolution_When_NotNormalizingResolution()
{
FFmpegProfile ffmpegProfile = TestProfile() with
{
NormalizeResolution = false,
Resolution = new Resolution { Width = 1920, Height = 1080 }
};
// not anamorphic
var version = new MediaVersion { Width = 1918, Height = 1080, SampleAspectRatio = "1:1" };
FFmpegPlaybackSettings actual = _calculator.CalculateSettings(
StreamingMode.TransportStream,
ffmpegProfile,
version,
DateTimeOffset.Now,
DateTimeOffset.Now);
actual.ScaledSize.IsNone.Should().BeTrue();
actual.PadToDesiredResolution.Should().BeFalse();
}
[Test] [Test]
public void Should_SetDesiredVideoCodec_When_ContentIsPadded_ForTransportStream() public void Should_SetDesiredVideoCodec_When_ContentIsPadded_ForTransportStream()
@ -741,11 +764,12 @@ namespace ErsatzTV.Core.Tests.FFmpeg
private readonly FFmpegPlaybackSettingsCalculator _calculator; private readonly FFmpegPlaybackSettingsCalculator _calculator;
public CalculateSettingsQsv() => _calculator = new FFmpegPlaybackSettingsCalculator(); public CalculateSettingsQsv() => _calculator = new FFmpegPlaybackSettingsCalculator();
[Test] [Test]
public void Should_UseHardwareAcceleration() public void Should_UseHardwareAcceleration()
{ {
FFmpegProfile ffmpegProfile = TestProfile() with { QsvAcceleration = true }; FFmpegProfile ffmpegProfile =
TestProfile() with { HardwareAcceleration = HardwareAccelerationKind.Qsv };
FFmpegPlaybackSettings actual = _calculator.CalculateSettings( FFmpegPlaybackSettings actual = _calculator.CalculateSettings(
StreamingMode.TransportStream, StreamingMode.TransportStream,
@ -754,10 +778,10 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now); DateTimeOffset.Now);
actual.HardwareAcceleration.Should().Be("qsv"); actual.HardwareAcceleration.Should().Be(HardwareAccelerationKind.Qsv);
} }
} }
private static FFmpegProfile TestProfile() => private static FFmpegProfile TestProfile() =>
new() { Resolution = new Resolution { Width = 1920, Height = 1080 } }; new() { Resolution = new Resolution { Width = 1920, Height = 1080 } };
} }

2
ErsatzTV.Core/Domain/FFmpegProfile.cs

@ -6,7 +6,7 @@
public string Name { get; set; } public string Name { get; set; }
public int ThreadCount { get; set; } public int ThreadCount { get; set; }
public bool Transcode { get; set; } public bool Transcode { get; set; }
public bool QsvAcceleration { get; set; } public HardwareAccelerationKind HardwareAcceleration { get; set; }
public int ResolutionId { get; set; } public int ResolutionId { get; set; }
public Resolution Resolution { get; set; } public Resolution Resolution { get; set; }
public bool NormalizeResolution { get; set; } public bool NormalizeResolution { get; set; }

9
ErsatzTV.Core/Domain/HardwareAccelerationKind.cs

@ -0,0 +1,9 @@
namespace ErsatzTV.Core.Domain
{
public enum HardwareAccelerationKind
{
None = 0,
Qsv = 1,
Nvenc = 2
}
}

3
ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettings.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.FFmpeg; using ErsatzTV.Core.Interfaces.FFmpeg;
using LanguageExt; using LanguageExt;
@ -9,7 +10,7 @@ namespace ErsatzTV.Core.FFmpeg
{ {
public int ThreadCount { get; set; } public int ThreadCount { get; set; }
public List<string> FormatFlags { get; set; } public List<string> FormatFlags { get; set; }
public string HardwareAcceleration { get; set; } public HardwareAccelerationKind HardwareAcceleration { get; set; }
public string VideoDecoder { get; set; } public string VideoDecoder { get; set; }
public bool RealtimeOutput => true; public bool RealtimeOutput => true;
public Option<TimeSpan> StreamSeek { get; set; } public Option<TimeSpan> StreamSeek { get; set; }

7
ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs

@ -67,10 +67,7 @@ namespace ErsatzTV.Core.FFmpeg
result.Deinterlace = false; result.Deinterlace = false;
break; break;
case StreamingMode.TransportStream: case StreamingMode.TransportStream:
if (ffmpegProfile.QsvAcceleration) result.HardwareAcceleration = ffmpegProfile.HardwareAcceleration;
{
result.HardwareAcceleration = "qsv";
}
if (NeedToScale(ffmpegProfile, version)) if (NeedToScale(ffmpegProfile, version))
{ {
@ -82,7 +79,7 @@ namespace ErsatzTV.Core.FFmpeg
} }
IDisplaySize sizeAfterScaling = result.ScaledSize.IfNone(version); IDisplaySize sizeAfterScaling = result.ScaledSize.IfNone(version);
if (!sizeAfterScaling.IsSameSizeAs(ffmpegProfile.Resolution)) if (ffmpegProfile.NormalizeResolution && !sizeAfterScaling.IsSameSizeAs(ffmpegProfile.Resolution))
{ {
result.PadToDesiredResolution = true; result.PadToDesiredResolution = true;
} }

34
ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs

@ -52,12 +52,20 @@ namespace ErsatzTV.Core.FFmpeg
return this; return this;
} }
public FFmpegProcessBuilder WithHardwareAcceleration(string hwAccel) public FFmpegProcessBuilder WithHardwareAcceleration(HardwareAccelerationKind hwAccel)
{ {
if (!string.IsNullOrWhiteSpace(hwAccel)) switch (hwAccel)
{ {
_arguments.Add("-hwaccel"); case HardwareAccelerationKind.Qsv:
_arguments.Add(hwAccel); _arguments.Add("-hwaccel");
_arguments.Add("qsv");
break;
case HardwareAccelerationKind.Nvenc:
_arguments.Add("-hwaccel");
_arguments.Add("cuda");
_arguments.Add("-hwaccel_output_format");
_arguments.Add("cuda");
break;
} }
return this; return this;
@ -127,9 +135,9 @@ namespace ErsatzTV.Core.FFmpeg
return this; return this;
} }
public FFmpegProcessBuilder WithInputCodec(string input, string hwAccel, string codec) public FFmpegProcessBuilder WithInputCodec(string input, HardwareAccelerationKind hwAccel, string codec)
{ {
if (hwAccel == "qsv" && QsvMap.TryGetValue(codec, out string qsvCodec)) if (hwAccel == HardwareAccelerationKind.Qsv && QsvMap.TryGetValue(codec, out string qsvCodec))
{ {
_arguments.Add("-c:v"); _arguments.Add("-c:v");
_arguments.Add(qsvCodec); _arguments.Add(qsvCodec);
@ -273,12 +281,18 @@ namespace ErsatzTV.Core.FFmpeg
return this; return this;
} }
public FFmpegProcessBuilder WithScaling(IDisplaySize displaySize, string hwAccel, string algorithm) public FFmpegProcessBuilder WithScaling(
IDisplaySize displaySize,
HardwareAccelerationKind hwAccel,
string algorithm)
{ {
_videoFilters.Enqueue( _videoFilters.Enqueue(
hwAccel == "qsv" hwAccel switch
? $"scale_qsv=w={displaySize.Width}:h={displaySize.Height}" {
: $"scale={displaySize.Width}:{displaySize.Height}:flags={algorithm}"); HardwareAccelerationKind.Qsv => $"scale_qsv=w={displaySize.Width}:h={displaySize.Height}",
HardwareAccelerationKind.Nvenc => $"scale_cuda={displaySize.Width}:{displaySize.Height}",
_ => $"scale={displaySize.Width}:{displaySize.Height}:flags={algorithm}"
});
return this; return this;
} }

20
ErsatzTV.Infrastructure/Migrations/20210302122352_Add_FFmpegProfileQsvAcceleration.cs

@ -1,20 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace ErsatzTV.Infrastructure.Migrations
{
public partial class Add_FFmpegProfileQsvAcceleration : Migration
{
protected override void Up(MigrationBuilder migrationBuilder) =>
migrationBuilder.AddColumn<bool>(
"QsvAcceleration",
"FFmpegProfile",
"INTEGER",
nullable: false,
defaultValue: false);
protected override void Down(MigrationBuilder migrationBuilder) =>
migrationBuilder.DropColumn(
"QsvAcceleration",
"FFmpegProfile");
}
}

10
ErsatzTV.Infrastructure/Migrations/20210302122352_Add_FFmpegProfileQsvAcceleration.Designer.cs → ErsatzTV.Infrastructure/Migrations/20210302224925_Add_FFmpegProfileHardwareAcceleration.Designer.cs generated

@ -9,8 +9,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace ErsatzTV.Infrastructure.Migrations namespace ErsatzTV.Infrastructure.Migrations
{ {
[DbContext(typeof(TvContext))] [DbContext(typeof(TvContext))]
[Migration("20210302122352_Add_FFmpegProfileQsvAcceleration")] [Migration("20210302224925_Add_FFmpegProfileHardwareAcceleration")]
partial class Add_FFmpegProfileQsvAcceleration partial class Add_FFmpegProfileHardwareAcceleration
{ {
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)
{ {
@ -219,6 +219,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Property<int>("AudioVolume") b.Property<int>("AudioVolume")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<int>("HardwareAcceleration")
.HasColumnType("INTEGER");
b.Property<string>("Name") b.Property<string>("Name")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
@ -234,9 +237,6 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Property<bool>("NormalizeVideoCodec") b.Property<bool>("NormalizeVideoCodec")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<bool>("QsvAcceleration")
.HasColumnType("INTEGER");
b.Property<int>("ResolutionId") b.Property<int>("ResolutionId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");

24
ErsatzTV.Infrastructure/Migrations/20210302224925_Add_FFmpegProfileHardwareAcceleration.cs

@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace ErsatzTV.Infrastructure.Migrations
{
public partial class Add_FFmpegProfileHardwareAcceleration : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "HardwareAcceleration",
table: "FFmpegProfile",
type: "INTEGER",
nullable: false,
defaultValue: 0);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "HardwareAcceleration",
table: "FFmpegProfile");
}
}
}

386
ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs

@ -1,14 +1,14 @@
// <auto-generated /> // <auto-generated />
using System; using System;
using ErsatzTV.Infrastructure.Data; using ErsatzTV.Infrastructure.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace ErsatzTV.Infrastructure.Migrations namespace ErsatzTV.Infrastructure.Migrations
{ {
[DbContext(typeof(TvContext))] [DbContext(typeof(TvContext))]
internal class TvContextModelSnapshot : ModelSnapshot partial class TvContextModelSnapshot : ModelSnapshot
{ {
protected override void BuildModel(ModelBuilder modelBuilder) protected override void BuildModel(ModelBuilder modelBuilder)
{ {
@ -16,9 +16,7 @@ namespace ErsatzTV.Infrastructure.Migrations
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "5.0.3"); .HasAnnotation("ProductVersion", "5.0.3");
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b =>
"ErsatzTV.Core.Domain.Artwork",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -66,9 +64,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("Artwork"); b.ToTable("Artwork");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b =>
"ErsatzTV.Core.Domain.Channel",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -99,9 +95,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("Channel"); b.ToTable("Channel");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b =>
"ErsatzTV.Core.Domain.Collection",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -115,9 +109,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("Collection"); b.ToTable("Collection");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b =>
"ErsatzTV.Core.Domain.CollectionItem",
b =>
{ {
b.Property<int>("CollectionId") b.Property<int>("CollectionId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@ -132,9 +124,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("CollectionItem"); b.ToTable("CollectionItem");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b =>
"ErsatzTV.Core.Domain.ConfigElement",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -154,9 +144,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("ConfigElement"); b.ToTable("ConfigElement");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b =>
"ErsatzTV.Core.Domain.EpisodeMetadata",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -205,9 +193,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("EpisodeMetadata"); b.ToTable("EpisodeMetadata");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b =>
"ErsatzTV.Core.Domain.FFmpegProfile",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -231,6 +217,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Property<int>("AudioVolume") b.Property<int>("AudioVolume")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<int>("HardwareAcceleration")
.HasColumnType("INTEGER");
b.Property<string>("Name") b.Property<string>("Name")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
@ -246,9 +235,6 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Property<bool>("NormalizeVideoCodec") b.Property<bool>("NormalizeVideoCodec")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<bool>("QsvAcceleration")
.HasColumnType("INTEGER");
b.Property<int>("ResolutionId") b.Property<int>("ResolutionId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@ -274,9 +260,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("FFmpegProfile"); b.ToTable("FFmpegProfile");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b =>
"ErsatzTV.Core.Domain.Library",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -301,9 +285,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("Library"); b.ToTable("Library");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b =>
"ErsatzTV.Core.Domain.LibraryPath",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -322,9 +304,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("LibraryPath"); b.ToTable("LibraryPath");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b =>
"ErsatzTV.Core.Domain.MediaFile",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -346,9 +326,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("MediaFile"); b.ToTable("MediaFile");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b =>
"ErsatzTV.Core.Domain.MediaItem",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -364,9 +342,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("MediaItem"); b.ToTable("MediaItem");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b =>
"ErsatzTV.Core.Domain.MediaSource",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -377,9 +353,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("MediaSource"); b.ToTable("MediaSource");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b =>
"ErsatzTV.Core.Domain.MediaVersion",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -433,9 +407,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("MediaVersion"); b.ToTable("MediaVersion");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b =>
"ErsatzTV.Core.Domain.MovieMetadata",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -484,9 +456,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("MovieMetadata"); b.ToTable("MovieMetadata");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b =>
"ErsatzTV.Core.Domain.Playout",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -510,9 +480,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("Playout"); b.ToTable("Playout");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b =>
"ErsatzTV.Core.Domain.PlayoutItem",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -539,9 +507,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("PlayoutItem"); b.ToTable("PlayoutItem");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b =>
"ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -575,9 +541,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("PlayoutProgramScheduleAnchor"); b.ToTable("PlayoutProgramScheduleAnchor");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b =>
"ErsatzTV.Core.Domain.PlexConnection",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -599,9 +563,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("PlexConnection"); b.ToTable("PlexConnection");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.PlexPathReplacement", b =>
"ErsatzTV.Core.Domain.PlexPathReplacement",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -623,9 +585,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("PlexPathReplacement"); b.ToTable("PlexPathReplacement");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b =>
"ErsatzTV.Core.Domain.ProgramSchedule",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -645,9 +605,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("ProgramSchedule"); b.ToTable("ProgramSchedule");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b =>
"ErsatzTV.Core.Domain.ProgramScheduleItem",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -682,9 +640,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("ProgramScheduleItem"); b.ToTable("ProgramScheduleItem");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b =>
"ErsatzTV.Core.Domain.Resolution",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -704,9 +660,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("Resolution"); b.ToTable("Resolution");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b =>
"ErsatzTV.Core.Domain.SeasonMetadata",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -749,9 +703,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("SeasonMetadata"); b.ToTable("SeasonMetadata");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b =>
"ErsatzTV.Core.Domain.ShowMetadata",
b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -800,18 +752,14 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("ShowMetadata"); b.ToTable("ShowMetadata");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b =>
"ErsatzTV.Core.Domain.LocalLibrary",
b =>
{ {
b.HasBaseType("ErsatzTV.Core.Domain.Library"); b.HasBaseType("ErsatzTV.Core.Domain.Library");
b.ToTable("LocalLibrary"); b.ToTable("LocalLibrary");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b =>
"ErsatzTV.Core.Domain.PlexLibrary",
b =>
{ {
b.HasBaseType("ErsatzTV.Core.Domain.Library"); b.HasBaseType("ErsatzTV.Core.Domain.Library");
@ -824,9 +772,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("PlexLibrary"); b.ToTable("PlexLibrary");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaFile", b =>
"ErsatzTV.Core.Domain.PlexMediaFile",
b =>
{ {
b.HasBaseType("ErsatzTV.Core.Domain.MediaFile"); b.HasBaseType("ErsatzTV.Core.Domain.MediaFile");
@ -839,9 +785,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("PlexMediaFile"); b.ToTable("PlexMediaFile");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b =>
"ErsatzTV.Core.Domain.Episode",
b =>
{ {
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
@ -856,18 +800,14 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("Episode"); b.ToTable("Episode");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b =>
"ErsatzTV.Core.Domain.Movie",
b =>
{ {
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
b.ToTable("Movie"); b.ToTable("Movie");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b =>
"ErsatzTV.Core.Domain.Season",
b =>
{ {
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
@ -882,27 +822,21 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("Season"); b.ToTable("Season");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b =>
"ErsatzTV.Core.Domain.Show",
b =>
{ {
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
b.ToTable("Show"); b.ToTable("Show");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b =>
"ErsatzTV.Core.Domain.LocalMediaSource",
b =>
{ {
b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); b.HasBaseType("ErsatzTV.Core.Domain.MediaSource");
b.ToTable("LocalMediaSource"); b.ToTable("LocalMediaSource");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b =>
"ErsatzTV.Core.Domain.PlexMediaSource",
b =>
{ {
b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); b.HasBaseType("ErsatzTV.Core.Domain.MediaSource");
@ -918,9 +852,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("PlexMediaSource"); b.ToTable("PlexMediaSource");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b =>
"ErsatzTV.Core.Domain.ProgramScheduleItemDuration",
b =>
{ {
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
@ -933,18 +865,14 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("ProgramScheduleDurationItem"); b.ToTable("ProgramScheduleDurationItem");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b =>
"ErsatzTV.Core.Domain.ProgramScheduleItemFlood",
b =>
{ {
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
b.ToTable("ProgramScheduleFloodItem"); b.ToTable("ProgramScheduleFloodItem");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b =>
"ErsatzTV.Core.Domain.ProgramScheduleItemMultiple",
b =>
{ {
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
@ -954,18 +882,14 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("ProgramScheduleMultipleItem"); b.ToTable("ProgramScheduleMultipleItem");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b =>
"ErsatzTV.Core.Domain.ProgramScheduleItemOne",
b =>
{ {
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
b.ToTable("ProgramScheduleOneItem"); b.ToTable("ProgramScheduleOneItem");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b =>
"ErsatzTV.Core.Domain.PlexMovie",
b =>
{ {
b.HasBaseType("ErsatzTV.Core.Domain.Movie"); b.HasBaseType("ErsatzTV.Core.Domain.Movie");
@ -975,9 +899,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.ToTable("PlexMovie"); b.ToTable("PlexMovie");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b =>
"ErsatzTV.Core.Domain.Artwork",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.Channel", null) b.HasOne("ErsatzTV.Core.Domain.Channel", null)
.WithMany("Artwork") .WithMany("Artwork")
@ -1005,9 +927,7 @@ namespace ErsatzTV.Infrastructure.Migrations
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b =>
"ErsatzTV.Core.Domain.Channel",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile")
.WithMany() .WithMany()
@ -1018,9 +938,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("FFmpegProfile"); b.Navigation("FFmpegProfile");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b =>
"ErsatzTV.Core.Domain.CollectionItem",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection")
.WithMany("CollectionItems") .WithMany("CollectionItems")
@ -1039,9 +957,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("MediaItem"); b.Navigation("MediaItem");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b =>
"ErsatzTV.Core.Domain.EpisodeMetadata",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode")
.WithMany("EpisodeMetadata") .WithMany("EpisodeMetadata")
@ -1052,9 +968,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Episode"); b.Navigation("Episode");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b =>
"ErsatzTV.Core.Domain.FFmpegProfile",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution")
.WithMany() .WithMany()
@ -1065,9 +979,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Resolution"); b.Navigation("Resolution");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b =>
"ErsatzTV.Core.Domain.Library",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource")
.WithMany("Libraries") .WithMany("Libraries")
@ -1078,9 +990,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("MediaSource"); b.Navigation("MediaSource");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b =>
"ErsatzTV.Core.Domain.LibraryPath",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.Library", "Library") b.HasOne("ErsatzTV.Core.Domain.Library", "Library")
.WithMany("Paths") .WithMany("Paths")
@ -1091,9 +1001,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Library"); b.Navigation("Library");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b =>
"ErsatzTV.Core.Domain.MediaFile",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion")
.WithMany("MediaFiles") .WithMany("MediaFiles")
@ -1104,9 +1012,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("MediaVersion"); b.Navigation("MediaVersion");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b =>
"ErsatzTV.Core.Domain.MediaItem",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath")
.WithMany("MediaItems") .WithMany("MediaItems")
@ -1117,9 +1023,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("LibraryPath"); b.Navigation("LibraryPath");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b =>
"ErsatzTV.Core.Domain.MediaVersion",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.Episode", null) b.HasOne("ErsatzTV.Core.Domain.Episode", null)
.WithMany("MediaVersions") .WithMany("MediaVersions")
@ -1132,9 +1036,7 @@ namespace ErsatzTV.Infrastructure.Migrations
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b =>
"ErsatzTV.Core.Domain.MovieMetadata",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie")
.WithMany("MovieMetadata") .WithMany("MovieMetadata")
@ -1145,9 +1047,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Movie"); b.Navigation("Movie");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b =>
"ErsatzTV.Core.Domain.Playout",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel")
.WithMany("Playouts") .WithMany("Playouts")
@ -1161,10 +1061,7 @@ namespace ErsatzTV.Infrastructure.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.OwnsOne( b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 =>
"ErsatzTV.Core.Domain.PlayoutAnchor",
"Anchor",
b1 =>
{ {
b1.Property<int>("PlayoutId") b1.Property<int>("PlayoutId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@ -1200,9 +1097,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("ProgramSchedule"); b.Navigation("ProgramSchedule");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b =>
"ErsatzTV.Core.Domain.PlayoutItem",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem")
.WithMany() .WithMany()
@ -1221,9 +1116,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Playout"); b.Navigation("Playout");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b =>
"ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection")
.WithMany() .WithMany()
@ -1247,10 +1140,7 @@ namespace ErsatzTV.Infrastructure.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.OwnsOne( b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 =>
"ErsatzTV.Core.Domain.CollectionEnumeratorState",
"EnumeratorState",
b1 =>
{ {
b1.Property<int>("PlayoutProgramScheduleAnchorId") b1.Property<int>("PlayoutProgramScheduleAnchorId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@ -1280,9 +1170,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("ProgramSchedule"); b.Navigation("ProgramSchedule");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b =>
"ErsatzTV.Core.Domain.PlexConnection",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource")
.WithMany("Connections") .WithMany("Connections")
@ -1293,9 +1181,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("PlexMediaSource"); b.Navigation("PlexMediaSource");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.PlexPathReplacement", b =>
"ErsatzTV.Core.Domain.PlexPathReplacement",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource")
.WithMany("PathReplacements") .WithMany("PathReplacements")
@ -1306,9 +1192,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("PlexMediaSource"); b.Navigation("PlexMediaSource");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b =>
"ErsatzTV.Core.Domain.ProgramScheduleItem",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection")
.WithMany() .WithMany()
@ -1333,9 +1217,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("ProgramSchedule"); b.Navigation("ProgramSchedule");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b =>
"ErsatzTV.Core.Domain.SeasonMetadata",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.Season", "Season") b.HasOne("ErsatzTV.Core.Domain.Season", "Season")
.WithMany("SeasonMetadata") .WithMany("SeasonMetadata")
@ -1346,9 +1228,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Season"); b.Navigation("Season");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b =>
"ErsatzTV.Core.Domain.ShowMetadata",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.Show", "Show") b.HasOne("ErsatzTV.Core.Domain.Show", "Show")
.WithMany("ShowMetadata") .WithMany("ShowMetadata")
@ -1359,9 +1239,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Show"); b.Navigation("Show");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b =>
"ErsatzTV.Core.Domain.LocalLibrary",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.Library", null) b.HasOne("ErsatzTV.Core.Domain.Library", null)
.WithOne() .WithOne()
@ -1370,9 +1248,7 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b =>
"ErsatzTV.Core.Domain.PlexLibrary",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.Library", null) b.HasOne("ErsatzTV.Core.Domain.Library", null)
.WithOne() .WithOne()
@ -1381,9 +1257,7 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaFile", b =>
"ErsatzTV.Core.Domain.PlexMediaFile",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.MediaFile", null) b.HasOne("ErsatzTV.Core.Domain.MediaFile", null)
.WithOne() .WithOne()
@ -1392,9 +1266,7 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b =>
"ErsatzTV.Core.Domain.Episode",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
.WithOne() .WithOne()
@ -1411,9 +1283,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Season"); b.Navigation("Season");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b =>
"ErsatzTV.Core.Domain.Movie",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
.WithOne() .WithOne()
@ -1422,9 +1292,7 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b =>
"ErsatzTV.Core.Domain.Season",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
.WithOne() .WithOne()
@ -1441,9 +1309,7 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Navigation("Show"); b.Navigation("Show");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b =>
"ErsatzTV.Core.Domain.Show",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
.WithOne() .WithOne()
@ -1452,9 +1318,7 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b =>
"ErsatzTV.Core.Domain.LocalMediaSource",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) b.HasOne("ErsatzTV.Core.Domain.MediaSource", null)
.WithOne() .WithOne()
@ -1463,9 +1327,7 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b =>
"ErsatzTV.Core.Domain.PlexMediaSource",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) b.HasOne("ErsatzTV.Core.Domain.MediaSource", null)
.WithOne() .WithOne()
@ -1474,9 +1336,7 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b =>
"ErsatzTV.Core.Domain.ProgramScheduleItemDuration",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null)
.WithOne() .WithOne()
@ -1485,9 +1345,7 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b =>
"ErsatzTV.Core.Domain.ProgramScheduleItemFlood",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null)
.WithOne() .WithOne()
@ -1496,9 +1354,7 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b =>
"ErsatzTV.Core.Domain.ProgramScheduleItemMultiple",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null)
.WithOne() .WithOne()
@ -1507,9 +1363,7 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b =>
"ErsatzTV.Core.Domain.ProgramScheduleItemOne",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null)
.WithOne() .WithOne()
@ -1518,9 +1372,7 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b =>
"ErsatzTV.Core.Domain.PlexMovie",
b =>
{ {
b.HasOne("ErsatzTV.Core.Domain.Movie", null) b.HasOne("ErsatzTV.Core.Domain.Movie", null)
.WithOne() .WithOne()
@ -1529,92 +1381,106 @@ namespace ErsatzTV.Infrastructure.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b =>
"ErsatzTV.Core.Domain.Channel",
b =>
{ {
b.Navigation("Artwork"); b.Navigation("Artwork");
b.Navigation("Playouts"); b.Navigation("Playouts");
}); });
modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => { b.Navigation("CollectionItems"); }); modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b =>
{
b.Navigation("CollectionItems");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => { b.Navigation("Artwork"); }); modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b =>
{
b.Navigation("Artwork");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => { b.Navigation("Paths"); }); modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b =>
{
b.Navigation("Paths");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => { b.Navigation("MediaItems"); }); modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b =>
{
b.Navigation("MediaItems");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => { b.Navigation("CollectionItems"); }); modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b =>
{
b.Navigation("CollectionItems");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => { b.Navigation("Libraries"); }); modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b =>
{
b.Navigation("Libraries");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => { b.Navigation("MediaFiles"); }); modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b =>
{
b.Navigation("MediaFiles");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => { b.Navigation("Artwork"); }); modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b =>
{
b.Navigation("Artwork");
});
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b =>
"ErsatzTV.Core.Domain.Playout",
b =>
{ {
b.Navigation("Items"); b.Navigation("Items");
b.Navigation("ProgramScheduleAnchors"); b.Navigation("ProgramScheduleAnchors");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b =>
"ErsatzTV.Core.Domain.ProgramSchedule",
b =>
{ {
b.Navigation("Items"); b.Navigation("Items");
b.Navigation("Playouts"); b.Navigation("Playouts");
}); });
modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => { b.Navigation("Artwork"); }); modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b =>
{
b.Navigation("Artwork");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => { b.Navigation("Artwork"); }); modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b =>
{
b.Navigation("Artwork");
});
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b =>
"ErsatzTV.Core.Domain.Episode",
b =>
{ {
b.Navigation("EpisodeMetadata"); b.Navigation("EpisodeMetadata");
b.Navigation("MediaVersions"); b.Navigation("MediaVersions");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b =>
"ErsatzTV.Core.Domain.Movie",
b =>
{ {
b.Navigation("MediaVersions"); b.Navigation("MediaVersions");
b.Navigation("MovieMetadata"); b.Navigation("MovieMetadata");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b =>
"ErsatzTV.Core.Domain.Season",
b =>
{ {
b.Navigation("Episodes"); b.Navigation("Episodes");
b.Navigation("SeasonMetadata"); b.Navigation("SeasonMetadata");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b =>
"ErsatzTV.Core.Domain.Show",
b =>
{ {
b.Navigation("Seasons"); b.Navigation("Seasons");
b.Navigation("ShowMetadata"); b.Navigation("ShowMetadata");
}); });
modelBuilder.Entity( modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b =>
"ErsatzTV.Core.Domain.PlexMediaSource",
b =>
{ {
b.Navigation("Connections"); b.Navigation("Connections");

9
ErsatzTV/Pages/FFmpegEditor.razor

@ -50,7 +50,12 @@
<MudTextField Disabled="@(!_model.Transcode)" Label="Buffer Size" @bind-Value="_model.VideoBufferSize" For="@(() => _model.VideoBufferSize)" Adornment="Adornment.End" AdornmentText="kBit"/> <MudTextField Disabled="@(!_model.Transcode)" Label="Buffer Size" @bind-Value="_model.VideoBufferSize" For="@(() => _model.VideoBufferSize)" Adornment="Adornment.End" AdornmentText="kBit"/>
</MudElement> </MudElement>
<MudElement HtmlTag="div" Class="mt-3"> <MudElement HtmlTag="div" Class="mt-3">
<MudCheckBox Label="QSV Acceleration" @bind-Checked="@_model.QsvAcceleration" For="@(() => _model.QsvAcceleration)"/> <MudSelect Disabled="@(!_model.Transcode)" Label="Hardware Acceleration" @bind-Value="_model.HardwareAcceleration" For="@(() => _model.HardwareAcceleration)">
@foreach (HardwareAccelerationKind hwAccel in Enum.GetValues<HardwareAccelerationKind>())
{
<MudSelectItem Value="@hwAccel">@hwAccel</MudSelectItem>
}
</MudSelect>
</MudElement> </MudElement>
</MudItem> </MudItem>
<MudItem> <MudItem>
@ -74,7 +79,7 @@
</MudItem> </MudItem>
<MudItem> <MudItem>
<MudText Typo="Typo.h6">Normalization</MudText> <MudText Typo="Typo.h6">Normalization</MudText>
<MudCheckBox Disabled="@(!_model.Transcode)" Label="Normalize Resolution" @bind-Checked="@_model.NormalizeResolution" For="@(() => _model.NormalizeResolution)"/> <MudCheckBox Disabled="@(!_model.Transcode || _model.HardwareAcceleration == HardwareAccelerationKind.Nvenc)" Label="Normalize Resolution" @bind-Checked="@_model.NormalizeResolution" For="@(() => _model.NormalizeResolution)"/>
<MudElement HtmlTag="div" Class="mt-3"> <MudElement HtmlTag="div" Class="mt-3">
<MudCheckBox Disabled="@(!_model.Transcode)" Label="Normalize Video Codec" @bind-Checked="@_model.NormalizeVideoCodec" For="@(() => _model.NormalizeVideoCodec)"/> <MudCheckBox Disabled="@(!_model.Transcode)" Label="Normalize Video Codec" @bind-Checked="@_model.NormalizeVideoCodec" For="@(() => _model.NormalizeVideoCodec)"/>
</MudElement> </MudElement>

30
ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs

@ -1,10 +1,15 @@
using ErsatzTV.ViewModels; using System.Collections.Generic;
using ErsatzTV.Core.Domain;
using ErsatzTV.ViewModels;
using FluentValidation; using FluentValidation;
namespace ErsatzTV.Validators namespace ErsatzTV.Validators
{ {
public class FFmpegProfileEditViewModelValidator : AbstractValidator<FFmpegProfileEditViewModel> public class FFmpegProfileEditViewModelValidator : AbstractValidator<FFmpegProfileEditViewModel>
{ {
private static readonly List<string> QsvEncoders = new() { "h264_qsv", "hevc_qsv", "mpeg2_qsv" };
private static readonly List<string> NvencEncoders = new() { "h264_nvenc", "hevc_nvenc" };
public FFmpegProfileEditViewModelValidator() public FFmpegProfileEditViewModelValidator()
{ {
RuleFor(x => x.Name).NotEmpty(); RuleFor(x => x.Name).NotEmpty();
@ -25,12 +30,31 @@ namespace ErsatzTV.Validators
}); });
When( When(
x => x.QsvAcceleration, x => x.HardwareAcceleration == HardwareAccelerationKind.Qsv,
() => () =>
{ {
RuleFor(x => x.VideoCodec).Must(c => c.EndsWith("_qsv")) RuleFor(x => x.VideoCodec).Must(c => QsvEncoders.Contains(c))
.WithMessage("QSV codec is required (h264_qsv, hevc_qsv, mpeg2_qsv)"); .WithMessage("QSV codec is required (h264_qsv, hevc_qsv, mpeg2_qsv)");
}); });
When(
x => x.HardwareAcceleration == HardwareAccelerationKind.Nvenc,
() =>
{
RuleFor(x => x.VideoCodec).Must(c => NvencEncoders.Contains(c))
.WithMessage("NVENC codec is required (h264_nvenc, hevc_nvenc)");
RuleFor(x => x.NormalizeResolution).Must(x => x == false)
.WithMessage("Resolution normalization (scaling) is not yet supported with NVENC");
});
When(
x => x.HardwareAcceleration == HardwareAccelerationKind.None,
() =>
{
RuleFor(x => x.VideoCodec).Must(c => !QsvEncoders.Contains(c) && !NvencEncoders.Contains(c))
.WithMessage("Hardware acceleration is required for this codec");
});
} }
} }
} }

24
ErsatzTV/ViewModels/FFmpegProfileEditViewModel.cs

@ -1,11 +1,14 @@
using ErsatzTV.Application.FFmpegProfiles; using ErsatzTV.Application.FFmpegProfiles;
using ErsatzTV.Application.FFmpegProfiles.Commands; using ErsatzTV.Application.FFmpegProfiles.Commands;
using ErsatzTV.Application.Resolutions; using ErsatzTV.Application.Resolutions;
using ErsatzTV.Core.Domain;
namespace ErsatzTV.ViewModels namespace ErsatzTV.ViewModels
{ {
public class FFmpegProfileEditViewModel public class FFmpegProfileEditViewModel
{ {
private HardwareAccelerationKind _hardwareAcceleration;
public FFmpegProfileEditViewModel() public FFmpegProfileEditViewModel()
{ {
} }
@ -27,7 +30,7 @@ namespace ErsatzTV.ViewModels
Resolution = viewModel.Resolution; Resolution = viewModel.Resolution;
ThreadCount = viewModel.ThreadCount; ThreadCount = viewModel.ThreadCount;
Transcode = viewModel.Transcode; Transcode = viewModel.Transcode;
QsvAcceleration = viewModel.QsvAcceleration; HardwareAcceleration = viewModel.HardwareAcceleration;
VideoBitrate = viewModel.VideoBitrate; VideoBitrate = viewModel.VideoBitrate;
VideoBufferSize = viewModel.VideoBufferSize; VideoBufferSize = viewModel.VideoBufferSize;
VideoCodec = viewModel.VideoCodec; VideoCodec = viewModel.VideoCodec;
@ -48,7 +51,20 @@ namespace ErsatzTV.ViewModels
public ResolutionViewModel Resolution { get; set; } public ResolutionViewModel Resolution { get; set; }
public int ThreadCount { get; set; } public int ThreadCount { get; set; }
public bool Transcode { get; set; } public bool Transcode { get; set; }
public bool QsvAcceleration { get; set; }
public HardwareAccelerationKind HardwareAcceleration
{
get => _hardwareAcceleration;
set
{
_hardwareAcceleration = value;
if (_hardwareAcceleration == HardwareAccelerationKind.Nvenc)
{
NormalizeResolution = false;
}
}
}
public int VideoBitrate { get; set; } public int VideoBitrate { get; set; }
public int VideoBufferSize { get; set; } public int VideoBufferSize { get; set; }
public string VideoCodec { get; set; } public string VideoCodec { get; set; }
@ -58,7 +74,7 @@ namespace ErsatzTV.ViewModels
Name, Name,
ThreadCount, ThreadCount,
Transcode, Transcode,
QsvAcceleration, HardwareAcceleration,
Resolution.Id, Resolution.Id,
NormalizeResolution, NormalizeResolution,
VideoCodec, VideoCodec,
@ -81,7 +97,7 @@ namespace ErsatzTV.ViewModels
Name, Name,
ThreadCount, ThreadCount,
Transcode, Transcode,
QsvAcceleration, HardwareAcceleration,
Resolution.Id, Resolution.Id,
NormalizeResolution, NormalizeResolution,
VideoCodec, VideoCodec,

Loading…
Cancel
Save