Compare commits

..
16 changed files with 9011 additions and 6 deletions
+10 -1
View File
@@ -5,6 +5,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
## [0.8.4-beta] - 2023-12-02
### Fixed
- Fix playout builder crash with improperly configured pad filler preset
- Properly validate filler preset mode pad to require `filler pad to nearest minute` value
- Fix bug where previously-synchronized collection tags would disappear
- This bug affected Jellyfin, Emby and Plex collections
- Fix detection of AMF hardware acceleration on Windows
## [0.8.3-beta] - 2023-11-22
### Added
- Add `Scaling Behavior` option to FFmpeg Profile
@@ -1799,7 +1807,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Initial release to facilitate testing outside of Docker.
[Unreleased]: https://github.com/ErsatzTV/ErsatzTV/compare/v0.8.3-beta...HEAD
[Unreleased]: https://github.com/ErsatzTV/ErsatzTV/compare/v0.8.4-beta...HEAD
[0.8.4-beta]: https://github.com/ErsatzTV/ErsatzTV/compare/v0.8.3-beta...v0.8.4-beta
[0.8.3-beta]: https://github.com/ErsatzTV/ErsatzTV/compare/v0.8.2-beta...v0.8.3-beta
[0.8.2-beta]: https://github.com/ErsatzTV/ErsatzTV/compare/v0.8.1-beta...v0.8.2-beta
[0.8.1-beta]: https://github.com/ErsatzTV/ErsatzTV/compare/v0.8.0-beta...v0.8.1-beta
@@ -223,6 +223,17 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe
Logger.LogError("Multiple pad-to-nearest-minute values are invalid; no filler will be used");
return new List<PlayoutItem> { playoutItem };
}
// missing pad-to-nearest-minute value is invalid; use no filler
FillerPreset invalidPadFiller = allFiller
.FirstOrDefault(f => f.FillerMode == FillerMode.Pad && f.PadToNearestMinute.HasValue == false);
if (invalidPadFiller is not null)
{
Logger.LogError(
"Pad filler ({Filler}) without pad-to-nearest-minute value is invalid; no filler will be used",
invalidPadFiller.Name);
return new List<PlayoutItem> { playoutItem };
}
List<MediaChapter> effectiveChapters = chapters;
if (allFiller.All(fp => fp.FillerKind != FillerKind.MidRoll) || effectiveChapters.Count <= 1)
@@ -27,9 +27,15 @@ public class FFmpegCapabilities : IFFmpegCapabilities
public bool HasHardwareAcceleration(HardwareAccelerationMode hardwareAccelerationMode)
{
// AMF isn't a "hwaccel" in ffmpeg, so check for presence of encoders
if (hardwareAccelerationMode is HardwareAccelerationMode.Amf)
{
return _ffmpegEncoders.Any(
e => e.EndsWith($"_{FFmpegKnownHardwareAcceleration.Amf.Name}", StringComparison.OrdinalIgnoreCase));
}
Option<FFmpegKnownHardwareAcceleration> maybeAccelToCheck = hardwareAccelerationMode switch
{
HardwareAccelerationMode.Amf => FFmpegKnownHardwareAcceleration.Amf,
HardwareAccelerationMode.Nvenc => FFmpegKnownHardwareAcceleration.Cuda,
HardwareAccelerationMode.Qsv => FFmpegKnownHardwareAcceleration.Qsv,
HardwareAccelerationMode.Vaapi => FFmpegKnownHardwareAcceleration.Vaapi,
@@ -9,5 +9,11 @@ public record FFmpegKnownEncoder
this.Name = Name;
}
public static IList<string> AllEncoders => Array.Empty<string>();
// only list the encoders that we actually check for
public static IList<string> AllEncoders =>
new[]
{
"h264_amf",
"hevc_amf"
};
}
@@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.MySql.Migrations
{
/// <inheritdoc />
public partial class Reset_ExternalCollectionEtag : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("UPDATE PlexCollection SET Etag = NULL");
migrationBuilder.Sql("UPDATE PlexMediaSource SET LastCollectionsScan = NULL");
migrationBuilder.Sql("UPDATE EmbyCollection SET Etag = NULL");
migrationBuilder.Sql("UPDATE EmbyMediaSource SET LastCollectionsScan = NULL");
migrationBuilder.Sql("UPDATE JellyfinCollection SET Etag = NULL");
migrationBuilder.Sql("UPDATE JellyfinMediaSource SET LastCollectionsScan = NULL");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
@@ -16,7 +16,7 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.13")
.HasAnnotation("ProductVersion", "7.0.14")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("ErsatzTV.Core.Domain.Actor", b =>
@@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
/// <inheritdoc />
public partial class Reset_ExternalCollectionEtag : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("UPDATE PlexCollection SET Etag = NULL");
migrationBuilder.Sql("UPDATE PlexMediaSource SET LastCollectionsScan = NULL");
migrationBuilder.Sql("UPDATE EmbyCollection SET Etag = NULL");
migrationBuilder.Sql("UPDATE EmbyMediaSource SET LastCollectionsScan = NULL");
migrationBuilder.Sql("UPDATE JellyfinCollection SET Etag = NULL");
migrationBuilder.Sql("UPDATE JellyfinMediaSource SET LastCollectionsScan = NULL");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
@@ -15,7 +15,7 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.13");
modelBuilder.HasAnnotation("ProductVersion", "7.0.14");
modelBuilder.Entity("ErsatzTV.Core.Domain.Actor", b =>
{
@@ -733,6 +733,7 @@ public class EmbyTelevisionRepository : IEmbyTelevisionRepository
// tags
foreach (Tag tag in metadata.Tags
.Filter(g => incomingMetadata.Tags.All(g2 => g2.Name != g.Name))
.Filter(g => g.ExternalCollectionId is null)
.ToList())
{
metadata.Tags.Remove(tag);
@@ -737,6 +737,7 @@ public class JellyfinTelevisionRepository : IJellyfinTelevisionRepository
// tags
foreach (Tag tag in metadata.Tags
.Filter(g => incomingMetadata.Tags.All(g2 => g2.Name != g.Name))
.Filter(g => g.ExternalCollectionId is null)
.ToList())
{
metadata.Tags.Remove(tag);
@@ -10,7 +10,7 @@ public class FFmpegVersionHealthCheck : BaseHealthCheck, IFFmpegVersionHealthChe
{
private const string BundledVersion = "6.1";
private const string BundledVersionVaapi = "6.1";
private const string WindowsVersionPrefix = "6.1";
private const string WindowsVersionPrefix = "n6.1";
private readonly IConfigElementRepository _configElementRepository;
public FFmpegVersionHealthCheck(IConfigElementRepository configElementRepository) =>
@@ -318,6 +318,7 @@ public class PlexMovieLibraryScanner :
foreach (Tag tag in existingMetadata.Tags
.Filter(g => fullMetadata.Tags.All(g2 => g2.Name != g.Name))
.Filter(g => g.ExternalCollectionId is null)
.ToList())
{
existingMetadata.Tags.Remove(tag);
@@ -413,6 +413,7 @@ public class PlexTelevisionLibraryScanner :
foreach (Tag tag in existingMetadata.Tags
.Filter(g => fullMetadata.Tags.All(g2 => g2.Name != g.Name))
.Filter(g => g.ExternalCollectionId is null)
.ToList())
{
existingMetadata.Tags.Remove(tag);
@@ -479,6 +480,7 @@ public class PlexTelevisionLibraryScanner :
foreach (Tag tag in existingMetadata.Tags
.Filter(g => fullMetadata.Tags.All(g2 => g2.Name != g.Name))
.Filter(g => g.ExternalCollectionId is null)
.ToList())
{
existingMetadata.Tags.Remove(tag);
@@ -540,6 +542,7 @@ public class PlexTelevisionLibraryScanner :
foreach (Tag tag in existingMetadata.Tags
.Filter(g => fullMetadata.Tags.All(g2 => g2.Name != g.Name))
.Filter(g => g.ExternalCollectionId is null)
.ToList())
{
existingMetadata.Tags.Remove(tag);
@@ -21,6 +21,9 @@ public class FillerPresetEditViewModelValidator : AbstractValidator<FillerPreset
When(
fp => fp.FillerMode == FillerMode.Duration,
() => RuleFor(fp => fp.Duration).NotNull());
When(
fp => fp.FillerMode == FillerMode.Pad,
() => RuleFor(fp => fp.PadToNearestMinute).NotNull());
When(
fp => fp.CollectionType == ProgramScheduleItemCollectionType.Collection,