diff --git a/ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfile.cs b/ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfile.cs index 4cc3e7a7a..419ebf5fb 100644 --- a/ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfile.cs +++ b/ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfile.cs @@ -1,4 +1,5 @@ using ErsatzTV.Core; +using ErsatzTV.Core.Domain; using LanguageExt; using MediatR; @@ -8,7 +9,7 @@ namespace ErsatzTV.Application.FFmpegProfiles.Commands string Name, int ThreadCount, bool Transcode, - bool QsvAcceleration, + HardwareAccelerationKind HardwareAcceleration, int ResolutionId, bool NormalizeResolution, string VideoCodec, diff --git a/ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfileHandler.cs b/ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfileHandler.cs index e088da9c9..b464242da 100644 --- a/ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfileHandler.cs +++ b/ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfileHandler.cs @@ -41,7 +41,7 @@ namespace ErsatzTV.Application.FFmpegProfiles.Commands Name = name, ThreadCount = threadCount, Transcode = request.Transcode, - QsvAcceleration = request.QsvAcceleration, + HardwareAcceleration = request.HardwareAcceleration, ResolutionId = resolutionId, NormalizeResolution = request.NormalizeResolution, VideoCodec = request.VideoCodec, diff --git a/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfile.cs b/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfile.cs index 8904e23a0..29cf7c8a0 100644 --- a/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfile.cs +++ b/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfile.cs @@ -1,4 +1,5 @@ using ErsatzTV.Core; +using ErsatzTV.Core.Domain; using LanguageExt; using MediatR; @@ -9,7 +10,7 @@ namespace ErsatzTV.Application.FFmpegProfiles.Commands string Name, int ThreadCount, bool Transcode, - bool QsvAcceleration, + HardwareAccelerationKind HardwareAcceleration, int ResolutionId, bool NormalizeResolution, string VideoCodec, diff --git a/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs b/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs index 93b9df5b0..8600c4da8 100644 --- a/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs +++ b/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs @@ -35,7 +35,7 @@ namespace ErsatzTV.Application.FFmpegProfiles.Commands p.Name = update.Name; p.ThreadCount = update.ThreadCount; p.Transcode = update.Transcode; - p.QsvAcceleration = update.QsvAcceleration; + p.HardwareAcceleration = update.HardwareAcceleration; p.ResolutionId = update.ResolutionId; p.NormalizeResolution = update.NormalizeResolution; p.VideoCodec = update.VideoCodec; diff --git a/ErsatzTV.Application/FFmpegProfiles/FFmpegProfileViewModel.cs b/ErsatzTV.Application/FFmpegProfiles/FFmpegProfileViewModel.cs index 6a5d87a14..8f16cb872 100644 --- a/ErsatzTV.Application/FFmpegProfiles/FFmpegProfileViewModel.cs +++ b/ErsatzTV.Application/FFmpegProfiles/FFmpegProfileViewModel.cs @@ -1,4 +1,5 @@ using ErsatzTV.Application.Resolutions; +using ErsatzTV.Core.Domain; namespace ErsatzTV.Application.FFmpegProfiles { @@ -7,7 +8,7 @@ namespace ErsatzTV.Application.FFmpegProfiles string Name, int ThreadCount, bool Transcode, - bool QsvAcceleration, + HardwareAccelerationKind HardwareAcceleration, ResolutionViewModel Resolution, bool NormalizeResolution, string VideoCodec, diff --git a/ErsatzTV.Application/FFmpegProfiles/Mapper.cs b/ErsatzTV.Application/FFmpegProfiles/Mapper.cs index 14ad1d7b7..e654926bf 100644 --- a/ErsatzTV.Application/FFmpegProfiles/Mapper.cs +++ b/ErsatzTV.Application/FFmpegProfiles/Mapper.cs @@ -11,7 +11,7 @@ namespace ErsatzTV.Application.FFmpegProfiles profile.Name, profile.ThreadCount, profile.Transcode, - profile.QsvAcceleration, + profile.HardwareAcceleration, Project(profile.Resolution), profile.NormalizeResolution, profile.VideoCodec, diff --git a/ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsServiceTests.cs b/ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsServiceTests.cs index 802b5f958..97afecab9 100644 --- a/ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsServiceTests.cs +++ b/ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsServiceTests.cs @@ -273,6 +273,29 @@ namespace ErsatzTV.Core.Tests.FFmpeg actual.ScaledSize.IsNone.Should().BeTrue(); 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] public void Should_SetDesiredVideoCodec_When_ContentIsPadded_ForTransportStream() @@ -741,11 +764,12 @@ namespace ErsatzTV.Core.Tests.FFmpeg private readonly FFmpegPlaybackSettingsCalculator _calculator; public CalculateSettingsQsv() => _calculator = new FFmpegPlaybackSettingsCalculator(); - + [Test] public void Should_UseHardwareAcceleration() { - FFmpegProfile ffmpegProfile = TestProfile() with { QsvAcceleration = true }; + FFmpegProfile ffmpegProfile = + TestProfile() with { HardwareAcceleration = HardwareAccelerationKind.Qsv }; FFmpegPlaybackSettings actual = _calculator.CalculateSettings( StreamingMode.TransportStream, @@ -754,10 +778,10 @@ namespace ErsatzTV.Core.Tests.FFmpeg DateTimeOffset.Now, DateTimeOffset.Now); - actual.HardwareAcceleration.Should().Be("qsv"); + actual.HardwareAcceleration.Should().Be(HardwareAccelerationKind.Qsv); } } - + private static FFmpegProfile TestProfile() => new() { Resolution = new Resolution { Width = 1920, Height = 1080 } }; } diff --git a/ErsatzTV.Core/Domain/FFmpegProfile.cs b/ErsatzTV.Core/Domain/FFmpegProfile.cs index 9bbe39784..7a453840b 100644 --- a/ErsatzTV.Core/Domain/FFmpegProfile.cs +++ b/ErsatzTV.Core/Domain/FFmpegProfile.cs @@ -6,7 +6,7 @@ public string Name { get; set; } public int ThreadCount { get; set; } public bool Transcode { get; set; } - public bool QsvAcceleration { get; set; } + public HardwareAccelerationKind HardwareAcceleration { get; set; } public int ResolutionId { get; set; } public Resolution Resolution { get; set; } public bool NormalizeResolution { get; set; } diff --git a/ErsatzTV.Core/Domain/HardwareAccelerationKind.cs b/ErsatzTV.Core/Domain/HardwareAccelerationKind.cs new file mode 100644 index 000000000..6d0a52e45 --- /dev/null +++ b/ErsatzTV.Core/Domain/HardwareAccelerationKind.cs @@ -0,0 +1,9 @@ +namespace ErsatzTV.Core.Domain +{ + public enum HardwareAccelerationKind + { + None = 0, + Qsv = 1, + Nvenc = 2 + } +} diff --git a/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettings.cs b/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettings.cs index e5a0e476b..6351e69e5 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettings.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettings.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.FFmpeg; using LanguageExt; @@ -9,7 +10,7 @@ namespace ErsatzTV.Core.FFmpeg { public int ThreadCount { get; set; } public List FormatFlags { get; set; } - public string HardwareAcceleration { get; set; } + public HardwareAccelerationKind HardwareAcceleration { get; set; } public string VideoDecoder { get; set; } public bool RealtimeOutput => true; public Option StreamSeek { get; set; } diff --git a/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs b/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs index 4c465a563..fb7c24357 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs @@ -67,10 +67,7 @@ namespace ErsatzTV.Core.FFmpeg result.Deinterlace = false; break; case StreamingMode.TransportStream: - if (ffmpegProfile.QsvAcceleration) - { - result.HardwareAcceleration = "qsv"; - } + result.HardwareAcceleration = ffmpegProfile.HardwareAcceleration; if (NeedToScale(ffmpegProfile, version)) { @@ -82,7 +79,7 @@ namespace ErsatzTV.Core.FFmpeg } IDisplaySize sizeAfterScaling = result.ScaledSize.IfNone(version); - if (!sizeAfterScaling.IsSameSizeAs(ffmpegProfile.Resolution)) + if (ffmpegProfile.NormalizeResolution && !sizeAfterScaling.IsSameSizeAs(ffmpegProfile.Resolution)) { result.PadToDesiredResolution = true; } diff --git a/ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs b/ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs index c29536ca3..dc8d6d34b 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs @@ -52,12 +52,20 @@ namespace ErsatzTV.Core.FFmpeg return this; } - public FFmpegProcessBuilder WithHardwareAcceleration(string hwAccel) + public FFmpegProcessBuilder WithHardwareAcceleration(HardwareAccelerationKind hwAccel) { - if (!string.IsNullOrWhiteSpace(hwAccel)) + switch (hwAccel) { - _arguments.Add("-hwaccel"); - _arguments.Add(hwAccel); + case HardwareAccelerationKind.Qsv: + _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; @@ -127,9 +135,9 @@ namespace ErsatzTV.Core.FFmpeg 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(qsvCodec); @@ -273,12 +281,18 @@ namespace ErsatzTV.Core.FFmpeg return this; } - public FFmpegProcessBuilder WithScaling(IDisplaySize displaySize, string hwAccel, string algorithm) + public FFmpegProcessBuilder WithScaling( + IDisplaySize displaySize, + HardwareAccelerationKind hwAccel, + string algorithm) { _videoFilters.Enqueue( - hwAccel == "qsv" - ? $"scale_qsv=w={displaySize.Width}:h={displaySize.Height}" - : $"scale={displaySize.Width}:{displaySize.Height}:flags={algorithm}"); + hwAccel switch + { + 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; } diff --git a/ErsatzTV.Infrastructure/Migrations/20210302122352_Add_FFmpegProfileQsvAcceleration.cs b/ErsatzTV.Infrastructure/Migrations/20210302122352_Add_FFmpegProfileQsvAcceleration.cs deleted file mode 100644 index 776a8a3b0..000000000 --- a/ErsatzTV.Infrastructure/Migrations/20210302122352_Add_FFmpegProfileQsvAcceleration.cs +++ /dev/null @@ -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( - "QsvAcceleration", - "FFmpegProfile", - "INTEGER", - nullable: false, - defaultValue: false); - - protected override void Down(MigrationBuilder migrationBuilder) => - migrationBuilder.DropColumn( - "QsvAcceleration", - "FFmpegProfile"); - } -} diff --git a/ErsatzTV.Infrastructure/Migrations/20210302122352_Add_FFmpegProfileQsvAcceleration.Designer.cs b/ErsatzTV.Infrastructure/Migrations/20210302224925_Add_FFmpegProfileHardwareAcceleration.Designer.cs similarity index 99% rename from ErsatzTV.Infrastructure/Migrations/20210302122352_Add_FFmpegProfileQsvAcceleration.Designer.cs rename to ErsatzTV.Infrastructure/Migrations/20210302224925_Add_FFmpegProfileHardwareAcceleration.Designer.cs index 553925a69..3c5ef0852 100644 --- a/ErsatzTV.Infrastructure/Migrations/20210302122352_Add_FFmpegProfileQsvAcceleration.Designer.cs +++ b/ErsatzTV.Infrastructure/Migrations/20210302224925_Add_FFmpegProfileHardwareAcceleration.Designer.cs @@ -9,8 +9,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace ErsatzTV.Infrastructure.Migrations { [DbContext(typeof(TvContext))] - [Migration("20210302122352_Add_FFmpegProfileQsvAcceleration")] - partial class Add_FFmpegProfileQsvAcceleration + [Migration("20210302224925_Add_FFmpegProfileHardwareAcceleration")] + partial class Add_FFmpegProfileHardwareAcceleration { protected override void BuildTargetModel(ModelBuilder modelBuilder) { @@ -219,6 +219,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.Property("AudioVolume") .HasColumnType("INTEGER"); + b.Property("HardwareAcceleration") + .HasColumnType("INTEGER"); + b.Property("Name") .HasColumnType("TEXT"); @@ -234,9 +237,6 @@ namespace ErsatzTV.Infrastructure.Migrations b.Property("NormalizeVideoCodec") .HasColumnType("INTEGER"); - b.Property("QsvAcceleration") - .HasColumnType("INTEGER"); - b.Property("ResolutionId") .HasColumnType("INTEGER"); diff --git a/ErsatzTV.Infrastructure/Migrations/20210302224925_Add_FFmpegProfileHardwareAcceleration.cs b/ErsatzTV.Infrastructure/Migrations/20210302224925_Add_FFmpegProfileHardwareAcceleration.cs new file mode 100644 index 000000000..07fb3becf --- /dev/null +++ b/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( + name: "HardwareAcceleration", + table: "FFmpegProfile", + type: "INTEGER", + nullable: false, + defaultValue: 0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "HardwareAcceleration", + table: "FFmpegProfile"); + } + } +} diff --git a/ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs b/ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs index 7a399c147..b71540915 100644 --- a/ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs +++ b/ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs @@ -1,14 +1,14 @@ // - using System; using ErsatzTV.Infrastructure.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace ErsatzTV.Infrastructure.Migrations { [DbContext(typeof(TvContext))] - internal class TvContextModelSnapshot : ModelSnapshot + partial class TvContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) { @@ -16,9 +16,7 @@ namespace ErsatzTV.Infrastructure.Migrations modelBuilder .HasAnnotation("ProductVersion", "5.0.3"); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Artwork", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -66,9 +64,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("Artwork"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Channel", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -99,9 +95,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("Channel"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Collection", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Collection", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -115,9 +109,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("Collection"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.CollectionItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => { b.Property("CollectionId") .HasColumnType("INTEGER"); @@ -132,9 +124,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("CollectionItem"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ConfigElement", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -154,9 +144,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("ConfigElement"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.EpisodeMetadata", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -205,9 +193,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("EpisodeMetadata"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.FFmpegProfile", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -231,6 +217,9 @@ namespace ErsatzTV.Infrastructure.Migrations b.Property("AudioVolume") .HasColumnType("INTEGER"); + b.Property("HardwareAcceleration") + .HasColumnType("INTEGER"); + b.Property("Name") .HasColumnType("TEXT"); @@ -246,9 +235,6 @@ namespace ErsatzTV.Infrastructure.Migrations b.Property("NormalizeVideoCodec") .HasColumnType("INTEGER"); - b.Property("QsvAcceleration") - .HasColumnType("INTEGER"); - b.Property("ResolutionId") .HasColumnType("INTEGER"); @@ -274,9 +260,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("FFmpegProfile"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Library", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -301,9 +285,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("Library"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.LibraryPath", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -322,9 +304,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("LibraryPath"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.MediaFile", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -346,9 +326,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("MediaFile"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.MediaItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -364,9 +342,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("MediaItem"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.MediaSource", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -377,9 +353,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("MediaSource"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.MediaVersion", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -433,9 +407,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("MediaVersion"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.MovieMetadata", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -484,9 +456,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("MovieMetadata"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Playout", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -510,9 +480,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("Playout"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlayoutItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -539,9 +507,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("PlayoutItem"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -575,9 +541,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("PlayoutProgramScheduleAnchor"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexConnection", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -599,9 +563,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("PlexConnection"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexPathReplacement", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexPathReplacement", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -623,9 +585,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("PlexPathReplacement"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramSchedule", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -645,9 +605,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("ProgramSchedule"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -682,9 +640,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("ProgramScheduleItem"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Resolution", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -704,9 +660,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("Resolution"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.SeasonMetadata", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -749,9 +703,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("SeasonMetadata"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ShowMetadata", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -800,18 +752,14 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("ShowMetadata"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.LocalLibrary", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => { b.HasBaseType("ErsatzTV.Core.Domain.Library"); b.ToTable("LocalLibrary"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexLibrary", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => { b.HasBaseType("ErsatzTV.Core.Domain.Library"); @@ -824,9 +772,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("PlexLibrary"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexMediaFile", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaFile", b => { b.HasBaseType("ErsatzTV.Core.Domain.MediaFile"); @@ -839,9 +785,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("PlexMediaFile"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Episode", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => { b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); @@ -856,18 +800,14 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("Episode"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Movie", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => { b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); b.ToTable("Movie"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Season", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => { b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); @@ -882,27 +822,21 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("Season"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Show", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => { b.HasBaseType("ErsatzTV.Core.Domain.MediaItem"); b.ToTable("Show"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.LocalMediaSource", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => { b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); b.ToTable("LocalMediaSource"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexMediaSource", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => { b.HasBaseType("ErsatzTV.Core.Domain.MediaSource"); @@ -918,9 +852,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("PlexMediaSource"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItemDuration", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => { b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); @@ -933,18 +865,14 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("ProgramScheduleDurationItem"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItemFlood", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => { b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); b.ToTable("ProgramScheduleFloodItem"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => { b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); @@ -954,18 +882,14 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("ProgramScheduleMultipleItem"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItemOne", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => { b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem"); b.ToTable("ProgramScheduleOneItem"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexMovie", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => { b.HasBaseType("ErsatzTV.Core.Domain.Movie"); @@ -975,9 +899,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.ToTable("PlexMovie"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Artwork", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Artwork", b => { b.HasOne("ErsatzTV.Core.Domain.Channel", null) .WithMany("Artwork") @@ -1005,9 +927,7 @@ namespace ErsatzTV.Infrastructure.Migrations .OnDelete(DeleteBehavior.Cascade); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Channel", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => { b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile") .WithMany() @@ -1018,9 +938,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("FFmpegProfile"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.CollectionItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.CollectionItem", b => { b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") .WithMany("CollectionItems") @@ -1039,9 +957,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("MediaItem"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.EpisodeMetadata", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.EpisodeMetadata", b => { b.HasOne("ErsatzTV.Core.Domain.Episode", "Episode") .WithMany("EpisodeMetadata") @@ -1052,9 +968,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Episode"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.FFmpegProfile", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b => { b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution") .WithMany() @@ -1065,9 +979,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Resolution"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Library", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b => { b.HasOne("ErsatzTV.Core.Domain.MediaSource", "MediaSource") .WithMany("Libraries") @@ -1078,9 +990,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("MediaSource"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.LibraryPath", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryPath", b => { b.HasOne("ErsatzTV.Core.Domain.Library", "Library") .WithMany("Paths") @@ -1091,9 +1001,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Library"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.MediaFile", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaFile", b => { b.HasOne("ErsatzTV.Core.Domain.MediaVersion", "MediaVersion") .WithMany("MediaFiles") @@ -1104,9 +1012,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("MediaVersion"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.MediaItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b => { b.HasOne("ErsatzTV.Core.Domain.LibraryPath", "LibraryPath") .WithMany("MediaItems") @@ -1117,9 +1023,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("LibraryPath"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.MediaVersion", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.MediaVersion", b => { b.HasOne("ErsatzTV.Core.Domain.Episode", null) .WithMany("MediaVersions") @@ -1132,9 +1036,7 @@ namespace ErsatzTV.Infrastructure.Migrations .OnDelete(DeleteBehavior.Cascade); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.MovieMetadata", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.MovieMetadata", b => { b.HasOne("ErsatzTV.Core.Domain.Movie", "Movie") .WithMany("MovieMetadata") @@ -1145,9 +1047,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Movie"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Playout", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => { b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel") .WithMany("Playouts") @@ -1161,10 +1061,7 @@ namespace ErsatzTV.Infrastructure.Migrations .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.OwnsOne( - "ErsatzTV.Core.Domain.PlayoutAnchor", - "Anchor", - b1 => + b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 => { b1.Property("PlayoutId") .HasColumnType("INTEGER"); @@ -1200,9 +1097,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("ProgramSchedule"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlayoutItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b => { b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem") .WithMany() @@ -1221,9 +1116,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Playout"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b => { b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") .WithMany() @@ -1247,10 +1140,7 @@ namespace ErsatzTV.Infrastructure.Migrations .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.OwnsOne( - "ErsatzTV.Core.Domain.CollectionEnumeratorState", - "EnumeratorState", - b1 => + b.OwnsOne("ErsatzTV.Core.Domain.CollectionEnumeratorState", "EnumeratorState", b1 => { b1.Property("PlayoutProgramScheduleAnchorId") .HasColumnType("INTEGER"); @@ -1280,9 +1170,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("ProgramSchedule"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexConnection", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexConnection", b => { b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") .WithMany("Connections") @@ -1293,9 +1181,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("PlexMediaSource"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexPathReplacement", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexPathReplacement", b => { b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", "PlexMediaSource") .WithMany("PathReplacements") @@ -1306,9 +1192,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("PlexMediaSource"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItem", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b => { b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection") .WithMany() @@ -1333,9 +1217,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("ProgramSchedule"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.SeasonMetadata", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b => { b.HasOne("ErsatzTV.Core.Domain.Season", "Season") .WithMany("SeasonMetadata") @@ -1346,9 +1228,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Season"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ShowMetadata", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ShowMetadata", b => { b.HasOne("ErsatzTV.Core.Domain.Show", "Show") .WithMany("ShowMetadata") @@ -1359,9 +1239,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Show"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.LocalLibrary", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b => { b.HasOne("ErsatzTV.Core.Domain.Library", null) .WithOne() @@ -1370,9 +1248,7 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexLibrary", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b => { b.HasOne("ErsatzTV.Core.Domain.Library", null) .WithOne() @@ -1381,9 +1257,7 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexMediaFile", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaFile", b => { b.HasOne("ErsatzTV.Core.Domain.MediaFile", null) .WithOne() @@ -1392,9 +1266,7 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Episode", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => { b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) .WithOne() @@ -1411,9 +1283,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Season"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Movie", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => { b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) .WithOne() @@ -1422,9 +1292,7 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Season", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => { b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) .WithOne() @@ -1441,9 +1309,7 @@ namespace ErsatzTV.Infrastructure.Migrations b.Navigation("Show"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Show", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => { b.HasOne("ErsatzTV.Core.Domain.MediaItem", null) .WithOne() @@ -1452,9 +1318,7 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.LocalMediaSource", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b => { b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) .WithOne() @@ -1463,9 +1327,7 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexMediaSource", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => { b.HasOne("ErsatzTV.Core.Domain.MediaSource", null) .WithOne() @@ -1474,9 +1336,7 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItemDuration", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b => { b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) .WithOne() @@ -1485,9 +1345,7 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItemFlood", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b => { b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) .WithOne() @@ -1496,9 +1354,7 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b => { b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) .WithOne() @@ -1507,9 +1363,7 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramScheduleItemOne", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b => { b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null) .WithOne() @@ -1518,9 +1372,7 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexMovie", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b => { b.HasOne("ErsatzTV.Core.Domain.Movie", null) .WithOne() @@ -1529,92 +1381,106 @@ namespace ErsatzTV.Infrastructure.Migrations .IsRequired(); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Channel", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => { b.Navigation("Artwork"); 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( - "ErsatzTV.Core.Domain.Playout", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b => { b.Navigation("Items"); b.Navigation("ProgramScheduleAnchors"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.ProgramSchedule", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b => { b.Navigation("Items"); 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( - "ErsatzTV.Core.Domain.Episode", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b => { b.Navigation("EpisodeMetadata"); b.Navigation("MediaVersions"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Movie", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b => { b.Navigation("MediaVersions"); b.Navigation("MovieMetadata"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Season", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b => { b.Navigation("Episodes"); b.Navigation("SeasonMetadata"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.Show", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b => { b.Navigation("Seasons"); b.Navigation("ShowMetadata"); }); - modelBuilder.Entity( - "ErsatzTV.Core.Domain.PlexMediaSource", - b => + modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b => { b.Navigation("Connections"); diff --git a/ErsatzTV/Pages/FFmpegEditor.razor b/ErsatzTV/Pages/FFmpegEditor.razor index bcb573e86..e359f8c66 100644 --- a/ErsatzTV/Pages/FFmpegEditor.razor +++ b/ErsatzTV/Pages/FFmpegEditor.razor @@ -50,7 +50,12 @@ - + + @foreach (HardwareAccelerationKind hwAccel in Enum.GetValues()) + { + @hwAccel + } + @@ -74,7 +79,7 @@ Normalization - + diff --git a/ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs b/ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs index 65eb87ca5..4c82cced7 100644 --- a/ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs +++ b/ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs @@ -1,10 +1,15 @@ -using ErsatzTV.ViewModels; +using System.Collections.Generic; +using ErsatzTV.Core.Domain; +using ErsatzTV.ViewModels; using FluentValidation; namespace ErsatzTV.Validators { public class FFmpegProfileEditViewModelValidator : AbstractValidator { + private static readonly List QsvEncoders = new() { "h264_qsv", "hevc_qsv", "mpeg2_qsv" }; + private static readonly List NvencEncoders = new() { "h264_nvenc", "hevc_nvenc" }; + public FFmpegProfileEditViewModelValidator() { RuleFor(x => x.Name).NotEmpty(); @@ -25,12 +30,31 @@ namespace ErsatzTV.Validators }); 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)"); }); + + 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"); + }); } } } diff --git a/ErsatzTV/ViewModels/FFmpegProfileEditViewModel.cs b/ErsatzTV/ViewModels/FFmpegProfileEditViewModel.cs index 191946048..5ac313e25 100644 --- a/ErsatzTV/ViewModels/FFmpegProfileEditViewModel.cs +++ b/ErsatzTV/ViewModels/FFmpegProfileEditViewModel.cs @@ -1,11 +1,14 @@ using ErsatzTV.Application.FFmpegProfiles; using ErsatzTV.Application.FFmpegProfiles.Commands; using ErsatzTV.Application.Resolutions; +using ErsatzTV.Core.Domain; namespace ErsatzTV.ViewModels { public class FFmpegProfileEditViewModel { + private HardwareAccelerationKind _hardwareAcceleration; + public FFmpegProfileEditViewModel() { } @@ -27,7 +30,7 @@ namespace ErsatzTV.ViewModels Resolution = viewModel.Resolution; ThreadCount = viewModel.ThreadCount; Transcode = viewModel.Transcode; - QsvAcceleration = viewModel.QsvAcceleration; + HardwareAcceleration = viewModel.HardwareAcceleration; VideoBitrate = viewModel.VideoBitrate; VideoBufferSize = viewModel.VideoBufferSize; VideoCodec = viewModel.VideoCodec; @@ -48,7 +51,20 @@ namespace ErsatzTV.ViewModels public ResolutionViewModel Resolution { get; set; } public int ThreadCount { 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 VideoBufferSize { get; set; } public string VideoCodec { get; set; } @@ -58,7 +74,7 @@ namespace ErsatzTV.ViewModels Name, ThreadCount, Transcode, - QsvAcceleration, + HardwareAcceleration, Resolution.Id, NormalizeResolution, VideoCodec, @@ -81,7 +97,7 @@ namespace ErsatzTV.ViewModels Name, ThreadCount, Transcode, - QsvAcceleration, + HardwareAcceleration, Resolution.Id, NormalizeResolution, VideoCodec,