// -------------------------------------------------------------------------------------------------------------------- // // This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. // // // Plist Preset Converter // // -------------------------------------------------------------------------------------------------------------------- namespace HandBrake.ApplicationServices.Utilities { using System.Text; using System.Xml; using HandBrake.ApplicationServices.Model; using HandBrake.ApplicationServices.Model.Encoding; using HandBrake.Interop.Model.Encoding; /// /// Plist Preset Converter /// public class PlistUtility { #region Export /// /// Export a MacGui style plist preset. /// /// /// The path. /// /// /// The preset. /// /// /// The build.PictureModulusPictureModulus /// public static void Export(string path, Preset preset, string build) { if (string.IsNullOrEmpty(path)) { return; } EncodeTask parsed = new EncodeTask(preset.Task); using (XmlTextWriter xmlWriter = new XmlTextWriter(path, Encoding.UTF8) { Formatting = Formatting.Indented }) { // Header xmlWriter.WriteStartDocument(); xmlWriter.WriteDocType( "plist", "-//Apple//DTD PLIST 1.0//EN", @"http://www.apple.com/DTDs/PropertyList-1.0.dtd", null); xmlWriter.WriteStartElement("plist"); xmlWriter.WriteStartElement("array"); // Add New Preset Here. Can write multiple presets here if required in future. WritePreset(xmlWriter, parsed, preset, build); // Footer xmlWriter.WriteEndElement(); xmlWriter.WriteEndElement(); xmlWriter.WriteEndDocument(); // Closeout xmlWriter.Close(); } } /// /// Write the Preset to a file /// /// /// The xml writer. /// /// /// The parsed. /// /// /// The preset. /// /// /// The build. /// private static void WritePreset(XmlTextWriter xmlWriter, EncodeTask parsed, Preset preset, string build) { xmlWriter.WriteStartElement("dict"); AudioListArrayDict(xmlWriter, parsed); AddEncodeSettings(xmlWriter, parsed, preset, build); xmlWriter.WriteEndElement(); } /// /// The get null bool value. /// /// /// The value. /// /// /// The System.String. /// private static string getNullBoolValue(bool? value) { if (!value.HasValue) { return "1"; } return value.Value ? "1" : "0"; } /// /// Add the encode settings to the preset /// /// /// The xml writer. /// /// /// The parsed. /// /// /// The preset. /// /// /// The build. /// private static void AddEncodeSettings(XmlTextWriter xmlWriter, EncodeTask parsed, Preset preset, string build) { AddEncodeElement(xmlWriter, "AudioAllowAACPass", "integer", getNullBoolValue(parsed.AllowedPassthruOptions.AudioAllowAACPass)); AddEncodeElement(xmlWriter, "AudioAllowAC3Pass", "integer", getNullBoolValue(parsed.AllowedPassthruOptions.AudioAllowAC3Pass)); AddEncodeElement(xmlWriter, "AudioAllowDTSHDPass", "integer", getNullBoolValue(parsed.AllowedPassthruOptions.AudioAllowDTSHDPass)); AddEncodeElement(xmlWriter, "AudioAllowDTSPass", "integer", getNullBoolValue(parsed.AllowedPassthruOptions.AudioAllowDTSPass)); AddEncodeElement(xmlWriter, "AudioAllowMP3Pass", "integer", getNullBoolValue(parsed.AllowedPassthruOptions.AudioAllowMP3Pass)); AddEncodeElement(xmlWriter, "AudioEncoderFallback", "string", EnumHelper.GetDisplay(parsed.AllowedPassthruOptions.AudioEncoderFallback)); AddEncodeElement(xmlWriter, "ChapterMarkers", "integer", parsed.IncludeChapterMarkers ? "1" : "0"); AddEncodeElement(xmlWriter, "Default", "integer", "0"); AddEncodeElement(xmlWriter, "FileFormat", "string", (parsed.OutputFormat == OutputFormat.Mp4 || parsed.OutputFormat == OutputFormat.M4V) ? "MP4 file" : "MKV file"); AddBooleanElement(xmlWriter, "Folder", false); AddEncodeElement(xmlWriter, "Mp4HttpOptimize", "integer", parsed.OptimizeMP4 ? "1" : "0"); AddEncodeElement(xmlWriter, "Mp4iPodCompatible", "integer", parsed.IPod5GSupport ? "1" : "0"); AddEncodeElement(xmlWriter, "PictureAutoCrop", "integer", "1"); AddEncodeElement(xmlWriter, "PictureBottomCrop", "integer", parsed.Cropping.Bottom.ToString()); // Filters AddEncodeElement(xmlWriter, "PictureDeblock", "integer", parsed.Deblock.ToString()); switch (parsed.Decomb) { case Decomb.Off: AddEncodeElement(xmlWriter, "PictureDecomb", "integer", "0"); AddEncodeElement(xmlWriter, "PictureDecombCustom", "string", string.Empty); break; case Decomb.Default: AddEncodeElement(xmlWriter, "PictureDecomb", "integer", "2"); AddEncodeElement(xmlWriter, "PictureDecombCustom", "string", string.Empty); break; case Decomb.Fast: AddEncodeElement(xmlWriter, "PictureDecomb", "integer", "3"); AddEncodeElement(xmlWriter, "PictureDecombCustom", "string", string.Empty); break; case Decomb.Bob: AddEncodeElement(xmlWriter, "PictureDecomb", "integer", "4"); AddEncodeElement(xmlWriter, "PictureDecombCustom", "string", string.Empty); break; case Decomb.Custom: AddEncodeElement(xmlWriter, "PictureDecomb", "integer", "1"); AddEncodeElement(xmlWriter, "PictureDecombCustom", "string", parsed.CustomDecomb); break; } AddEncodeElement(xmlWriter, "PictureDecombDeinterlace", "integer", parsed.Decomb != Decomb.Off ? "0" : "1"); switch (parsed.Deinterlace) { case Deinterlace.Off: AddEncodeElement(xmlWriter, "PictureDeinterlace", "integer", "0"); AddEncodeElement(xmlWriter, "PictureDeinterlaceCustom", "string", string.Empty); break; case Deinterlace.Fast: AddEncodeElement(xmlWriter, "PictureDeinterlace", "integer", "2"); AddEncodeElement(xmlWriter, "PictureDeinterlaceCustom", "string", string.Empty); break; case Deinterlace.Slow: AddEncodeElement(xmlWriter, "PictureDeinterlace", "integer", "3"); AddEncodeElement(xmlWriter, "PictureDeinterlaceCustom", "string", string.Empty); break; case Deinterlace.Slower: AddEncodeElement(xmlWriter, "PictureDeinterlace", "integer", "4"); AddEncodeElement(xmlWriter, "PictureDeinterlaceCustom", "string", string.Empty); break; case Deinterlace.Bob: AddEncodeElement(xmlWriter, "PictureDeinterlace", "integer", "5"); AddEncodeElement(xmlWriter, "PictureDeinterlaceCustom", "string", string.Empty); break; case Deinterlace.Custom: AddEncodeElement(xmlWriter, "PictureDeinterlace", "integer", "1"); AddEncodeElement(xmlWriter, "PictureDeinterlaceCustom", "string", parsed.CustomDeinterlace); break; } if (parsed.Denoise == Denoise.hqdn3d) { switch (parsed.DenoisePreset) { case DenoisePreset.Weak: AddEncodeElement(xmlWriter, "PictureDenoise", "integer", "2"); AddEncodeElement(xmlWriter, "PictureDenoiseCustom", "string", string.Empty); break; case DenoisePreset.Medium: AddEncodeElement(xmlWriter, "PictureDenoise", "integer", "3"); AddEncodeElement(xmlWriter, "PictureDenoiseCustom", "string", string.Empty); break; case DenoisePreset.Strong: AddEncodeElement(xmlWriter, "PictureDenoise", "integer", "4"); AddEncodeElement(xmlWriter, "PictureDenoiseCustom", "string", string.Empty); break; case DenoisePreset.Custom: AddEncodeElement(xmlWriter, "PictureDenoise", "integer", "1"); AddEncodeElement(xmlWriter, "PictureDenoiseCustom", "string", parsed.CustomDenoise); break; } } // These keys are still TBD. TODO Update these if (parsed.Denoise == Denoise.NlMeans) { switch (parsed.DenoisePreset) { case DenoisePreset.Ultralight: AddEncodeElement(xmlWriter, "DenoisePreset", "integer", "1"); break; case DenoisePreset.Light: AddEncodeElement(xmlWriter, "DenoisePreset", "integer", "2"); break; case DenoisePreset.Medium: AddEncodeElement(xmlWriter, "DenoisePreset", "integer", "3"); break; case DenoisePreset.Strong: AddEncodeElement(xmlWriter, "DenoisePreset", "integer", "4"); break; } switch (parsed.DenoiseTune) { case DenoiseTune.None: AddEncodeElement(xmlWriter, "DenoiseTune", "integer", "0"); break; case DenoiseTune.Animation: AddEncodeElement(xmlWriter, "DenoiseTune", "integer", "1"); break; case DenoiseTune.Film: AddEncodeElement(xmlWriter, "DenoiseTune", "integer", "2"); break; case DenoiseTune.Grain: AddEncodeElement(xmlWriter, "DenoiseTune", "integer", "3"); break; case DenoiseTune.HighMotion: AddEncodeElement(xmlWriter, "DenoiseTune", "integer", "4"); break; } } int detelecine = 0; switch (parsed.Detelecine) { case Detelecine.Off: detelecine = 0; break; case Detelecine.Default: detelecine = 2; break; case Detelecine.Custom: detelecine = 1; break; } AddEncodeElement(xmlWriter, "PictureDetelecine", "integer", detelecine.ToString()); AddEncodeElement(xmlWriter, "PictureDetelecineCustom", "string", detelecine == 1 ? parsed.CustomDecomb : string.Empty); // Picture Settings AddEncodeElement(xmlWriter, "PictureHeight", "integer", parsed.Height.ToString()); AddEncodeElement(xmlWriter, "PictureKeepRatio", "integer", parsed.KeepDisplayAspect ? "1" : "0"); AddEncodeElement(xmlWriter, "PictureLeftCrop", "integer", parsed.Cropping.Left.ToString()); AddEncodeElement(xmlWriter, "PictureModulus", "integer", parsed.Modulus.ToString()); AddEncodeElement(xmlWriter, "PicturePAR", "integer", ((int)parsed.Anamorphic).ToString()); AddEncodeElement(xmlWriter, "PictureRightCrop", "integer", parsed.Cropping.Right.ToString()); AddEncodeElement(xmlWriter, "PictureTopCrop", "integer", parsed.Cropping.Top.ToString()); AddEncodeElement(xmlWriter, "PictureWidth", "integer", parsed.Width.ToString()); // Preset Information AddEncodeElement(xmlWriter, "PresetBuildNumber", "string", build); AddEncodeElement(xmlWriter, "PresetDescription", "string", "No Description"); AddEncodeElement(xmlWriter, "PresetName", "string", preset.Name); AddEncodeElement(xmlWriter, "Type", "integer", "1"); // 1 is user preset, 0 is built in // Preset Settings AddEncodeElement(xmlWriter, "UsesMaxPictureSettings", "integer", (parsed.MaxWidth != 0 || parsed.MaxHeight != 0) ? "1" : "0"); AddEncodeElement(xmlWriter, "UsesPictureFilters", "integer", "1"); AddEncodeElement(xmlWriter, "UsesPictureSettings", "integer", "2"); // Video Settings AddEncodeElement(xmlWriter, "VideoAvgBitrate", "string", parsed.VideoBitrate.ToString()); AddEncodeElement(xmlWriter, "VideoEncoder", "string", EnumHelper.GetDisplay(parsed.VideoEncoder)); AddEncodeElement(xmlWriter, "VideoFramerate", "string", parsed.Framerate == null ? "Same as source" : parsed.Framerate.ToString()); AddEncodeElement(xmlWriter, "VideoFramerateMode", "string", parsed.FramerateMode.ToString().ToLower()); AddEncodeElement(xmlWriter, "VideoGrayScale", "integer", parsed.Grayscale ? "1" : "0"); AddEncodeElement(xmlWriter, "VideoQualitySlider", "real", parsed.Quality.ToString()); AddEncodeElement(xmlWriter, "h264Level", "string", parsed.H264Level); AddEncodeElement(xmlWriter, "x264OptionExtra", "string", parsed.AdvancedEncoderOptions); AddEncodeElement(xmlWriter, "x264Preset", "string", parsed.X264Preset.ToString().ToLower()); AddEncodeElement(xmlWriter, "h264Profile", "string", parsed.H264Profile.ToString().ToLower()); string tune = parsed.X264Tune.ToString().ToLower(); if (parsed.FastDecode) { tune = tune == "none" ? "fastdecode" : tune + ",fastdecode"; } AddEncodeElement(xmlWriter, "x264Tune", "string", tune); AddEncodeElement(xmlWriter, "x264UseAdvancedOptions", "integer", parsed.ShowAdvancedTab ? "1" : "0"); AddEncodeElement(xmlWriter, "qsvPreset", "string", parsed.QsvPreset.ToString().ToLower()); AddEncodeElement(xmlWriter, "h265Profile", "string", parsed.H265Profile.ToString().ToLower()); AddEncodeElement(xmlWriter, "x265Preset", "string", parsed.X265Preset.ToString().ToLower()); AddEncodeElement(xmlWriter, "x265Tune", "string", parsed.X265Tune.ToString().ToLower()); int videoQualityType = 0; if (parsed.VideoBitrate != null) videoQualityType = 1; else if (parsed.Quality != null) videoQualityType = 2; AddEncodeElement(xmlWriter, "VideoQualityType", "integer", videoQualityType.ToString()); AddEncodeElement(xmlWriter, "VideoTargetSize", "string", string.Empty); AddEncodeElement(xmlWriter, "VideoTurboTwoPass", "integer", parsed.TurboFirstPass ? "1" : "0"); AddEncodeElement(xmlWriter, "VideoTwoPass", "integer", parsed.TwoPass ? "1" : "0"); // x264 string AddEncodeElement(xmlWriter, "x264Option", "string", parsed.AdvancedEncoderOptions); } /// /// Add a boolean element /// /// /// The xml writer. /// /// /// The key name. /// /// /// The value. /// private static void AddBooleanElement(XmlTextWriter xmlWriter, string keyName, bool value) { xmlWriter.WriteStartElement("key"); xmlWriter.WriteString(keyName); xmlWriter.WriteEndElement(); xmlWriter.WriteStartElement(value ? "true" : "false"); xmlWriter.WriteEndElement(); } /// /// Add an encode setting element /// /// /// The xml writer. /// /// /// The key name. /// /// /// The type. /// /// /// The value. /// private static void AddEncodeElement(XmlTextWriter xmlWriter, string keyName, string type, string value) { xmlWriter.WriteElementString("key", keyName); // This is a hack for Apples XML parser. It doesn't understand so instead, always set a default value // of 0 if the value is empty. if (type == "integer" && string.IsNullOrEmpty(value)) { value = "0"; } xmlWriter.WriteElementString(type, value); } /// /// Add an Audio Track Array Dict /// /// /// The xml writer. /// /// /// The parsed. /// private static void AudioListArrayDict(XmlTextWriter xmlWriter, EncodeTask parsed) { xmlWriter.WriteStartElement("key"); xmlWriter.WriteString("AudioList"); xmlWriter.WriteEndElement(); xmlWriter.WriteStartElement("array"); foreach (AudioTrack track in parsed.AudioTracks) { AddAudioItem(xmlWriter, track); } xmlWriter.WriteEndElement(); } /// /// Add an audio track /// /// /// The xml writer. /// /// /// The audio track. /// private static void AddAudioItem(XmlTextWriter xmlWriter, AudioTrack audioTrack) { xmlWriter.WriteStartElement("dict"); xmlWriter.WriteElementString("key", "AudioBitrate"); xmlWriter.WriteElementString("string", audioTrack.Bitrate.ToString()); xmlWriter.WriteElementString("key", "AudioEncoder"); xmlWriter.WriteElementString("string", EnumHelper.GetDisplay(audioTrack.Encoder)); xmlWriter.WriteElementString("key", "AudioMixdown"); xmlWriter.WriteElementString("string", EnumHelper.GetDisplay(audioTrack.MixDown)); xmlWriter.WriteElementString("key", "AudioSamplerate"); xmlWriter.WriteElementString("string", audioTrack.SampleRate.ToString().Replace("0", "Auto")); xmlWriter.WriteElementString("key", "AudioTrack"); xmlWriter.WriteElementString("integer", audioTrack.Track.ToString()); xmlWriter.WriteElementString("key", "AudioTrackDRCSlider"); xmlWriter.WriteElementString("real", audioTrack.DRC.ToString()); xmlWriter.WriteElementString("key", "AudioTrackDescription"); xmlWriter.WriteElementString("string", "Unknown"); xmlWriter.WriteElementString("key", "AudioTrackGainSlider"); xmlWriter.WriteElementString("real", audioTrack.Gain.ToString()); xmlWriter.WriteEndElement(); } #endregion } }