// // // To parse this JSON data, add NuGet 'Newtonsoft.Json' then do: // // using ErsatzTV.Core.Next.Config; // // var channelConfig = ChannelConfig.FromJson(jsonString); namespace ErsatzTV.Core.Next.Config { using System; using System.Collections.Generic; using System.Globalization; using Newtonsoft.Json; using Newtonsoft.Json.Converters; public partial class ChannelConfig { [JsonProperty("ffmpeg")] public Ffmpeg Ffmpeg { get; set; } [JsonProperty("normalization")] public Normalization Normalization { get; set; } [JsonProperty("playout")] public Playout Playout { get; set; } } public partial class Ffmpeg { [JsonProperty("disabled_filters", NullValueHandling = NullValueHandling.Ignore)] public List DisabledFilters { get; set; } [JsonProperty("ffmpeg_path")] public string FfmpegPath { get; set; } [JsonProperty("ffprobe_path")] public string FfprobePath { get; set; } [JsonProperty("preferred_filters", NullValueHandling = NullValueHandling.Ignore)] public List PreferredFilters { get; set; } } public partial class Normalization { [JsonProperty("audio")] public Audio Audio { get; set; } [JsonProperty("subtitle", NullValueHandling = NullValueHandling.Ignore)] public Subtitle Subtitle { get; set; } [JsonProperty("video")] public Video Video { get; set; } } public partial class Audio { [JsonProperty("bitrate_kbps")] public long? BitrateKbps { get; set; } [JsonProperty("buffer_kbps")] public long? BufferKbps { get; set; } [JsonProperty("channels")] public long? Channels { get; set; } [JsonProperty("format")] public AudioFormat? Format { get; set; } [JsonProperty("loudness")] public LoudnessClass Loudness { get; set; } [JsonProperty("normalize_loudness", NullValueHandling = NullValueHandling.Ignore)] public bool? NormalizeLoudness { get; set; } [JsonProperty("sample_rate_hz")] public long? SampleRateHz { get; set; } } public partial class LoudnessClass { [JsonProperty("integrated_target")] public double? IntegratedTarget { get; set; } [JsonProperty("range_target")] public double? RangeTarget { get; set; } [JsonProperty("true_peak")] public double? TruePeak { get; set; } } public partial class Subtitle { [JsonProperty("mode", NullValueHandling = NullValueHandling.Ignore)] public Mode? Mode { get; set; } } public partial class Video { [JsonProperty("accel")] public AccelEnum? Accel { get; set; } [JsonProperty("bit_depth")] public long? BitDepth { get; set; } [JsonProperty("bitrate_kbps")] public long? BitrateKbps { get; set; } [JsonProperty("buffer_kbps")] public long? BufferKbps { get; set; } [JsonProperty("deinterlace", NullValueHandling = NullValueHandling.Ignore)] public bool? Deinterlace { get; set; } [JsonProperty("format")] public VideoFormat? Format { get; set; } [JsonProperty("height")] public long? Height { get; set; } [JsonProperty("tonemap_algorithm")] public string TonemapAlgorithm { get; set; } [JsonProperty("vaapi_device")] public string VaapiDevice { get; set; } [JsonProperty("vaapi_driver")] public VaapiDriverEnum? VaapiDriver { get; set; } [JsonProperty("width")] public long? Width { get; set; } } public partial class Playout { [JsonProperty("folder")] public string Folder { get; set; } /// /// RFC3339 formatted date/time, e.g. 2026-04-13T00:24:21.527-05:00 /// [JsonProperty("virtual_start")] public string VirtualStart { get; set; } } public enum AudioFormat { Aac, Ac3 }; public enum Mode { Burn, Convert }; public enum AccelEnum { Cuda, Qsv, Vaapi, Videotoolbox, Vulkan }; public enum VideoFormat { H264, Hevc }; public enum VaapiDriverEnum { I965, Ihd, Radeonsi }; public partial class ChannelConfig { public static ChannelConfig FromJson(string json) => JsonConvert.DeserializeObject(json, ErsatzTV.Core.Next.Config.Converter.Settings); } public static class Serialize { public static string ToJson(this ChannelConfig self) => JsonConvert.SerializeObject(self, ErsatzTV.Core.Next.Config.Converter.Settings); } internal static class Converter { public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings { MetadataPropertyHandling = MetadataPropertyHandling.Ignore, DateParseHandling = DateParseHandling.None, Converters = { AudioFormatConverter.Singleton, ModeConverter.Singleton, AccelEnumConverter.Singleton, VideoFormatConverter.Singleton, VaapiDriverEnumConverter.Singleton, new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal } }, }; } internal class AudioFormatConverter : JsonConverter { public override bool CanConvert(Type t) => t == typeof(AudioFormat) || t == typeof(AudioFormat?); public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var value = serializer.Deserialize(reader); switch (value) { case "aac": return AudioFormat.Aac; case "ac3": return AudioFormat.Ac3; } throw new Exception("Cannot unmarshal type AudioFormat"); } public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) { if (untypedValue == null) { serializer.Serialize(writer, null); return; } var value = (AudioFormat)untypedValue; switch (value) { case AudioFormat.Aac: serializer.Serialize(writer, "aac"); return; case AudioFormat.Ac3: serializer.Serialize(writer, "ac3"); return; } throw new Exception("Cannot marshal type AudioFormat"); } public static readonly AudioFormatConverter Singleton = new AudioFormatConverter(); } internal class ModeConverter : JsonConverter { public override bool CanConvert(Type t) => t == typeof(Mode) || t == typeof(Mode?); public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var value = serializer.Deserialize(reader); switch (value) { case "burn": return Mode.Burn; case "convert": return Mode.Convert; } throw new Exception("Cannot unmarshal type Mode"); } public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) { if (untypedValue == null) { serializer.Serialize(writer, null); return; } var value = (Mode)untypedValue; switch (value) { case Mode.Burn: serializer.Serialize(writer, "burn"); return; case Mode.Convert: serializer.Serialize(writer, "convert"); return; } throw new Exception("Cannot marshal type Mode"); } public static readonly ModeConverter Singleton = new ModeConverter(); } internal class AccelEnumConverter : JsonConverter { public override bool CanConvert(Type t) => t == typeof(AccelEnum) || t == typeof(AccelEnum?); public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var value = serializer.Deserialize(reader); switch (value) { case "cuda": return AccelEnum.Cuda; case "qsv": return AccelEnum.Qsv; case "vaapi": return AccelEnum.Vaapi; case "videotoolbox": return AccelEnum.Videotoolbox; case "vulkan": return AccelEnum.Vulkan; } throw new Exception("Cannot unmarshal type AccelEnum"); } public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) { if (untypedValue == null) { serializer.Serialize(writer, null); return; } var value = (AccelEnum)untypedValue; switch (value) { case AccelEnum.Cuda: serializer.Serialize(writer, "cuda"); return; case AccelEnum.Qsv: serializer.Serialize(writer, "qsv"); return; case AccelEnum.Vaapi: serializer.Serialize(writer, "vaapi"); return; case AccelEnum.Videotoolbox: serializer.Serialize(writer, "videotoolbox"); return; case AccelEnum.Vulkan: serializer.Serialize(writer, "vulkan"); return; } throw new Exception("Cannot marshal type AccelEnum"); } public static readonly AccelEnumConverter Singleton = new AccelEnumConverter(); } internal class VideoFormatConverter : JsonConverter { public override bool CanConvert(Type t) => t == typeof(VideoFormat) || t == typeof(VideoFormat?); public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var value = serializer.Deserialize(reader); switch (value) { case "h264": return VideoFormat.H264; case "hevc": return VideoFormat.Hevc; } throw new Exception("Cannot unmarshal type VideoFormat"); } public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) { if (untypedValue == null) { serializer.Serialize(writer, null); return; } var value = (VideoFormat)untypedValue; switch (value) { case VideoFormat.H264: serializer.Serialize(writer, "h264"); return; case VideoFormat.Hevc: serializer.Serialize(writer, "hevc"); return; } throw new Exception("Cannot marshal type VideoFormat"); } public static readonly VideoFormatConverter Singleton = new VideoFormatConverter(); } internal class VaapiDriverEnumConverter : JsonConverter { public override bool CanConvert(Type t) => t == typeof(VaapiDriverEnum) || t == typeof(VaapiDriverEnum?); public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var value = serializer.Deserialize(reader); switch (value) { case "i965": return VaapiDriverEnum.I965; case "ihd": return VaapiDriverEnum.Ihd; case "radeonsi": return VaapiDriverEnum.Radeonsi; } throw new Exception("Cannot unmarshal type VaapiDriverEnum"); } public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) { if (untypedValue == null) { serializer.Serialize(writer, null); return; } var value = (VaapiDriverEnum)untypedValue; switch (value) { case VaapiDriverEnum.I965: serializer.Serialize(writer, "i965"); return; case VaapiDriverEnum.Ihd: serializer.Serialize(writer, "ihd"); return; case VaapiDriverEnum.Radeonsi: serializer.Serialize(writer, "radeonsi"); return; } throw new Exception("Cannot marshal type VaapiDriverEnum"); } public static readonly VaapiDriverEnumConverter Singleton = new VaapiDriverEnumConverter(); } }