From 3b602895024aab8644c128d117be104b01c84efb Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sat, 12 Sep 2026 16:26:01 +1000 Subject: [PATCH] fix(config): read an unusable encoding.xml EncoderPreset as the default An element the enum cannot parse throws, and the configuration manager catches that and returns defaults, so one bad preset discarded every other encoding setting. - serialize EncoderPreset through a string surrogate in XML - fall back to auto for an empty, unknown or out-of-range value --- .../Configuration/EncodingOptions.cs | 22 ++++++++ .../Configuration/EncodingOptionsTests.cs | 52 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 tests/Jellyfin.Model.Tests/Configuration/EncodingOptionsTests.cs 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)!; + } +} -- 2.47.3