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 @@ @@ -1,4 +1,5 @@
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using LanguageExt;
using MediatR;
@ -8,7 +9,7 @@ namespace ErsatzTV.Application.FFmpegProfiles.Commands @@ -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,

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

@ -41,7 +41,7 @@ namespace ErsatzTV.Application.FFmpegProfiles.Commands @@ -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,

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

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using LanguageExt;
using MediatR;
@ -9,7 +10,7 @@ namespace ErsatzTV.Application.FFmpegProfiles.Commands @@ -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,

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

@ -35,7 +35,7 @@ namespace ErsatzTV.Application.FFmpegProfiles.Commands @@ -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;

3
ErsatzTV.Application/FFmpegProfiles/FFmpegProfileViewModel.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using ErsatzTV.Application.Resolutions;
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.FFmpegProfiles
{
@ -7,7 +8,7 @@ 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,

2
ErsatzTV.Application/FFmpegProfiles/Mapper.cs

@ -11,7 +11,7 @@ namespace ErsatzTV.Application.FFmpegProfiles @@ -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,

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

@ -273,6 +273,29 @@ namespace ErsatzTV.Core.Tests.FFmpeg @@ -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 @@ -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 @@ -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 } };
}

2
ErsatzTV.Core/Domain/FFmpegProfile.cs

@ -6,7 +6,7 @@ @@ -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; }

9
ErsatzTV.Core/Domain/HardwareAccelerationKind.cs

@ -0,0 +1,9 @@ @@ -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 @@ @@ -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 @@ -9,7 +10,7 @@ namespace ErsatzTV.Core.FFmpeg
{
public int ThreadCount { get; set; }
public List<string> FormatFlags { get; set; }
public string HardwareAcceleration { get; set; }
public HardwareAccelerationKind HardwareAcceleration { get; set; }
public string VideoDecoder { get; set; }
public bool RealtimeOutput => true;
public Option<TimeSpan> StreamSeek { get; set; }

7
ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs

@ -67,10 +67,7 @@ namespace ErsatzTV.Core.FFmpeg @@ -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 @@ -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;
}

34
ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs

@ -52,12 +52,20 @@ namespace ErsatzTV.Core.FFmpeg @@ -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 @@ -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 @@ -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;
}

20
ErsatzTV.Infrastructure/Migrations/20210302122352_Add_FFmpegProfileQsvAcceleration.cs

@ -1,20 +0,0 @@ @@ -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; @@ -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 @@ -219,6 +219,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Property<int>("AudioVolume")
.HasColumnType("INTEGER");
b.Property<int>("HardwareAcceleration")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
@ -234,9 +237,6 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -234,9 +237,6 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Property<bool>("NormalizeVideoCodec")
.HasColumnType("INTEGER");
b.Property<bool>("QsvAcceleration")
.HasColumnType("INTEGER");
b.Property<int>("ResolutionId")
.HasColumnType("INTEGER");

24
ErsatzTV.Infrastructure/Migrations/20210302224925_Add_FFmpegProfileHardwareAcceleration.cs

@ -0,0 +1,24 @@ @@ -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 @@ @@ -1,14 +1,14 @@
// <auto-generated />
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 @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -66,9 +64,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -99,9 +95,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -115,9 +109,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("CollectionId")
.HasColumnType("INTEGER");
@ -132,9 +124,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -154,9 +144,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -205,9 +193,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -231,6 +217,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -231,6 +217,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Property<int>("AudioVolume")
.HasColumnType("INTEGER");
b.Property<int>("HardwareAcceleration")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
@ -246,9 +235,6 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -246,9 +235,6 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Property<bool>("NormalizeVideoCodec")
.HasColumnType("INTEGER");
b.Property<bool>("QsvAcceleration")
.HasColumnType("INTEGER");
b.Property<int>("ResolutionId")
.HasColumnType("INTEGER");
@ -274,9 +260,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -301,9 +285,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -322,9 +304,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -346,9 +326,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -364,9 +342,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -377,9 +353,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -433,9 +407,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -484,9 +456,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -510,9 +480,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -539,9 +507,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -575,9 +541,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -599,9 +563,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -623,9 +585,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -645,9 +605,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -682,9 +640,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -704,9 +660,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -749,9 +703,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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<int>("Id")
.ValueGeneratedOnAdd()
@ -800,18 +752,14 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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<int>("PlayoutId")
.HasColumnType("INTEGER");
@ -1200,9 +1097,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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 @@ -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 @@ -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<int>("PlayoutProgramScheduleAnchorId")
.HasColumnType("INTEGER");
@ -1280,9 +1170,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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");

9
ErsatzTV/Pages/FFmpegEditor.razor

@ -50,7 +50,12 @@ @@ -50,7 +50,12 @@
<MudTextField Disabled="@(!_model.Transcode)" Label="Buffer Size" @bind-Value="_model.VideoBufferSize" For="@(() => _model.VideoBufferSize)" Adornment="Adornment.End" AdornmentText="kBit"/>
</MudElement>
<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>
</MudItem>
<MudItem>
@ -74,7 +79,7 @@ @@ -74,7 +79,7 @@
</MudItem>
<MudItem>
<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">
<MudCheckBox Disabled="@(!_model.Transcode)" Label="Normalize Video Codec" @bind-Checked="@_model.NormalizeVideoCodec" For="@(() => _model.NormalizeVideoCodec)"/>
</MudElement>

30
ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs

@ -1,10 +1,15 @@ @@ -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<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()
{
RuleFor(x => x.Name).NotEmpty();
@ -25,12 +30,31 @@ namespace ErsatzTV.Validators @@ -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");
});
}
}
}

24
ErsatzTV/ViewModels/FFmpegProfileEditViewModel.cs

@ -1,11 +1,14 @@ @@ -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 @@ -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 @@ -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 @@ -58,7 +74,7 @@ namespace ErsatzTV.ViewModels
Name,
ThreadCount,
Transcode,
QsvAcceleration,
HardwareAcceleration,
Resolution.Id,
NormalizeResolution,
VideoCodec,
@ -81,7 +97,7 @@ namespace ErsatzTV.ViewModels @@ -81,7 +97,7 @@ namespace ErsatzTV.ViewModels
Name,
ThreadCount,
Transcode,
QsvAcceleration,
HardwareAcceleration,
Resolution.Id,
NormalizeResolution,
VideoCodec,

Loading…
Cancel
Save