diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 951d523f3e..bcab10bcb6 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -1,7 +1,10 @@ #pragma warning disable CA1819 // XML serialization handles collections improperly, so we need to use arrays #nullable disable +using System; using System.ComponentModel; +using System.Text.Json.Serialization; +using System.Xml.Serialization; using MediaBrowser.Model.Entities; namespace MediaBrowser.Model.Configuration; @@ -234,8 +237,27 @@ public class EncodingOptions /// /// Gets or sets the encoder preset. /// + [XmlIgnore] public EncoderPreset EncoderPreset { get; set; } + /// + /// Gets or sets the encoder preset as it is stored in the configuration file. Files written before the preset + /// list was validated can carry an empty element, which has to read back as the default rather than throw and + /// take the rest of the configuration down with it. + /// + [JsonIgnore] + [XmlElement(nameof(EncoderPreset))] + public string EncoderPresetXml + { + get => EncoderPreset.ToString(); + + // TryParse also accepts a number, which would yield a preset value nothing maps to, so the result has to be + // a declared one. + set => EncoderPreset = Enum.TryParse(value, true, out var encoderPreset) && Enum.IsDefined(encoderPreset) + ? encoderPreset + : EncoderPreset.auto; + } + /// /// Gets or sets a value indicating whether the framerate is doubled when deinterlacing. /// diff --git a/tests/Jellyfin.Model.Tests/Configuration/EncodingOptionsTests.cs b/tests/Jellyfin.Model.Tests/Configuration/EncodingOptionsTests.cs new file mode 100644 index 0000000000..afd7a18be3 --- /dev/null +++ b/tests/Jellyfin.Model.Tests/Configuration/EncodingOptionsTests.cs @@ -0,0 +1,52 @@ +using System.IO; +using System.Xml.Serialization; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using Xunit; + +namespace Jellyfin.Model.Tests.Configuration; + +public class EncodingOptionsTests +{ + [Theory] + [InlineData("")] + [InlineData("")] + [InlineData(" ")] + [InlineData("notapreset")] + [InlineData("42")] + [InlineData("-1")] + public void Deserialize_UnreadableEncoderPreset_FallsBackToDefault(string encoderPresetElement) + { + var options = Deserialize($"{encoderPresetElement}21"); + + Assert.Equal(EncoderPreset.auto, options.EncoderPreset); + + // The rest of the file has to survive: a throwing preset used to discard every other encoding setting. + Assert.Equal(21, options.H264Crf); + } + + [Fact] + public void Deserialize_KnownEncoderPreset_IsKept() + { + var options = Deserialize("veryfast"); + + Assert.Equal(EncoderPreset.veryfast, options.EncoderPreset); + } + + [Fact] + public void Serialize_WritesTheEncoderPresetElement() + { + var serializer = new XmlSerializer(typeof(EncodingOptions)); + using var writer = new StringWriter(); + serializer.Serialize(writer, new EncodingOptions { EncoderPreset = EncoderPreset.slow }); + + Assert.Contains("slow", writer.ToString(), System.StringComparison.Ordinal); + } + + private static EncodingOptions Deserialize(string xml) + { + var serializer = new XmlSerializer(typeof(EncodingOptions)); + using var reader = new StringReader(xml); + return (EncodingOptions)serializer.Deserialize(reader)!; + } +}