// -------------------------------------------------------------------------------------------------------------------- // // This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. // // // The preset service manages HandBrake's presets // // -------------------------------------------------------------------------------------------------------------------- namespace HandBrakeWPF.Services.Presets { using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Xml.Serialization; using HandBrake.ApplicationServices.Exceptions; using HandBrake.ApplicationServices.Model.Encoding; using HandBrake.ApplicationServices.Utilities; using HandBrakeWPF.Model.Preset; using HandBrakeWPF.Services.Presets.Interfaces; /// /// The preset service manages HandBrake's presets /// public class PresetService : IPresetService { #region Private Variables /// /// XML Serializer /// private static readonly XmlSerializer Ser = new XmlSerializer(typeof(List)); /// /// User Preset Default Catgory Name /// public static string UserPresetCatgoryName = "User Presets"; /// /// The User Preset file /// private readonly string userPresetFile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\user_presets.xml"; /// /// The Built In Presets File /// private readonly string builtInPresetFile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\presets.xml"; /// /// A Collection of presets /// private readonly ObservableCollection presets = new ObservableCollection(); #endregion /// /// Gets a Collection of presets. /// public ObservableCollection Presets { get { return this.presets; } } /// /// Gets or sets LastPresetAdded. /// public Preset LastPresetAdded { get; set; } /// /// Gets the DefaultPreset. /// public Preset DefaultPreset { get { return this.presets.FirstOrDefault(p => p.IsDefault); } } #region Public Methods /// /// The load. /// public void Load() { // If the preset file doesn't exist. Create it. if (!File.Exists(this.builtInPresetFile)) { this.UpdateBuiltInPresets(); } this.LoadPresets(); } /// /// Add a new preset to the system. /// Performs an Update if it already exists /// /// /// A Preset to add /// /// /// True if added, /// False if name already exists /// public bool Add(Preset preset) { if (this.CheckIfPresetExists(preset.Name) == false) { this.presets.Add(preset); this.LastPresetAdded = preset; // Update the presets file this.UpdatePresetFiles(); return true; } this.Update(preset); return true; } /// /// Update a preset /// /// /// The updated preset /// public void Update(Preset update) { // TODO - Change this to be a lookup foreach (Preset preset in this.presets) { if (preset.Name == update.Name) { preset.Task = update.Task; preset.UsePictureFilters = update.UsePictureFilters; preset.PictureSettingsMode = update.PictureSettingsMode; preset.Category = update.Category; preset.Description = update.Description; // Update the presets file this.UpdatePresetFiles(); break; } } } /// /// Remove a preset with a given name from either the built in or user preset list. /// /// /// The Preset to remove /// public void Remove(Preset preset) { if (preset == null || preset.IsDefault) { return; } this.presets.Remove(preset); this.UpdatePresetFiles(); } /// /// Remove a group of presets by category /// /// /// The Category to remove /// public void RemoveGroup(string category) { List removeList = this.presets.Where(p => p.Category == category).ToList(); foreach (Preset preset in removeList) { if (preset.IsDefault) { // Skip default preset continue; } this.presets.Remove(preset); } this.UpdatePresetFiles(); } /// /// Set Default Preset /// /// /// The name. /// public void SetDefault(Preset name) { foreach (Preset preset in this.presets) { preset.IsDefault = false; } name.IsDefault = true; this.UpdatePresetFiles(); } /// /// Get a Preset /// /// /// The name of the preset to get /// /// /// A Preset or null object /// public Preset GetPreset(string name) { return this.presets.FirstOrDefault(item => item.Name == name); } /// /// Clear Built-in Presets /// public void ClearBuiltIn() { List remove = this.presets.Where(p => p.IsBuildIn).ToList(); foreach (Preset preset in remove) { this.presets.Remove(preset); } } /// /// Clear all presets /// public void ClearAll() { this.presets.Clear(); } /// /// Reads the CLI's CLI output format and load's them into the preset List Preset /// public void UpdateBuiltInPresets() { // Create a new tempory file and execute the CLI to get the built in Presets. string handbrakeCLIPath = Path.Combine(System.Windows.Forms.Application.StartupPath, "HandBrakeCLI.exe"); string presetsPath = Path.Combine(Path.GetTempPath(), "temp_presets.dat"); string strCmdLine = String.Format(@"cmd /c """"{0}"" --preset-list >""{1}"" 2>&1""", handbrakeCLIPath, presetsPath); ProcessStartInfo getPresets = new ProcessStartInfo("CMD.exe", strCmdLine) { WindowStyle = ProcessWindowStyle.Hidden }; Process hbproc = Process.Start(getPresets); hbproc.WaitForExit(); hbproc.Dispose(); hbproc.Close(); // Clear the current built in Presets and now parse the tempory Presets file. this.ClearBuiltIn(); if (File.Exists(presetsPath)) { using (StreamReader presetInput = new StreamReader(presetsPath)) { StringBuilder contents = new StringBuilder(); string category = String.Empty; while (!presetInput.EndOfStream) { string line = presetInput.ReadLine(); contents.AppendLine(line); // Found the beginning of a preset block ) if (line != null && line.Contains("<") && !line.Contains("<<")) { category = line.Replace("<", string.Empty).Trim(); } // Found a preset if (line != null && line.Contains("+")) { Regex r = new Regex("(: )"); // Split on hyphens. string[] presetName = r.Split(line); Preset newPreset = new Preset { Category = category, Name = presetName[0].Replace("+", string.Empty).Trim(), Version = VersionHelper.GetVersion(), Description = string.Empty, // Maybe one day we will populate this. IsBuildIn = true, UsePictureFilters = true, Task = QueryParserUtility.Parse(presetName[2]) }; if (newPreset.Name == "iPod") { newPreset.Task.KeepDisplayAspect = true; } newPreset.Task.AllowedPassthruOptions = new AllowedPassthru(true); // We don't want to override the built-in preset if (newPreset.Name == "Normal") { newPreset.IsDefault = true; } this.presets.Add(newPreset); } } // Verify we have presets. if (this.presets.Count == 0) { throw new GeneralApplicationException("Failed to load built-in presets.", "Restarting HandBrake may resolve this issue", new Exception(contents.ToString())); } } } else { // Something really bad must have happened if the file is missing. throw new GeneralApplicationException("Preset Export Failed. Unable to load in Presets.", "Please restart HandBrake", null); } // Finally, Create a new or update the current Presets.xml file this.UpdatePresetFiles(); } /// /// Check if the built in Presets stored are not out of date. /// Update them if they are. /// /// true if out of date public bool CheckIfPresetsAreOutOfDate() { // Update built-in Presets if the built-in Presets belong to an older version. if (this.presets.Count != 0) { List preset = this.presets.Where(p => p.IsBuildIn).ToList(); if (preset.Count > 0) { if (preset[0].Version != VersionHelper.GetVersion()) { this.UpdateBuiltInPresets(); return true; } } } return false; } /// /// Check if the preset "name" exists in either Presets or UserPresets lists. /// /// Name of the preset /// True if found public bool CheckIfPresetExists(string name) { return name == string.Empty || this.presets.Any(item => item.Name == name); } /// /// Returns a value if the preset can be updated / resaved /// /// /// The name. /// /// /// True if it's not a built-in preset, false otherwise. /// public bool CanUpdatePreset(string name) { return this.presets.Where(preset => preset.Name == name).Any(preset => preset.IsBuildIn == false); } #endregion #region Private Helpers /// /// Recover from a courrpted preset file /// Add .old to the current filename, and delete the current file. /// /// /// The broken presets file. /// private static void RecoverFromCorruptedPresetFile(string file) { try { // Recover from Error. if (File.Exists(file)) { string disabledFile = string.Format("{0}.{1}", file, GeneralUtilities.ProcessId); File.Move(file, disabledFile); if (File.Exists(file)) { File.Delete(file); } } } catch (IOException) { // Give up } } /// /// Load in the Built-in and User presets into the collection /// private void LoadPresets() { // First clear the Presets arraylists this.presets.Clear(); // Load in the Presets from Presets.xml try { if (File.Exists(this.builtInPresetFile)) { using (StreamReader reader = new StreamReader(this.builtInPresetFile)) { List list = (List)Ser.Deserialize(reader); foreach (Preset preset in list) { preset.IsBuildIn = true; // Older versions did not have this flag so explicitly make sure it is set. this.presets.Add(preset); } } } } catch (Exception) { RecoverFromCorruptedPresetFile(this.builtInPresetFile); this.UpdateBuiltInPresets(); } // Load in the users Presets from UserPresets.xml try { if (File.Exists(this.userPresetFile)) { using (StreamReader reader = new StreamReader(this.userPresetFile)) { List list = (List)Ser.Deserialize(reader); foreach (Preset preset in list) { this.presets.Add(preset); } } } } catch (Exception exc) { RecoverFromCorruptedPresetFile(this.userPresetFile); throw new GeneralApplicationException("HandBrake has detected a problem with your presets.", "Your old presets file has been renamed so that it doesn't get loaded on next launch.", exc); } } /// /// Update the preset files /// private void UpdatePresetFiles() { try { string directory = Path.GetDirectoryName(this.userPresetFile); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } using (FileStream strm = new FileStream(this.builtInPresetFile, FileMode.Create, FileAccess.Write)) { Ser.Serialize(strm, this.presets.Where(p => p.IsBuildIn).ToList()); } using (FileStream strm = new FileStream(this.userPresetFile, FileMode.Create, FileAccess.Write)) { Ser.Serialize(strm, this.presets.Where(p => p.IsBuildIn == false).ToList()); } } catch (Exception exc) { throw new GeneralApplicationException("Unable to write to the presets file.", "The details section below may indicate why this error has occured.", exc); } } #endregion } }